From 49920bc66984a512f4bcc7735a61642cd0e4d6f2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 13 Oct 2011 15:15:27 +0530 Subject: dmaengine: add new enum dma_transfer_direction This new enum removes usage of dma_data_direction for dma direction. The new enum cleans tells the DMA direction and mode This further paves way for merging the dmaengine _prep operations and also for interleaved dma Suggested-by: Jassi Brar Reviewed-by: Barry Song Signed-off-by: Vinod Koul diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ace51af..d946ef7 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -23,7 +23,6 @@ #include #include -#include #include /** @@ -75,6 +74,19 @@ enum dma_transaction_type { /* last transaction type for creation of the capabilities mask */ #define DMA_TX_TYPE_END (DMA_CYCLIC + 1) +/** + * enum dma_transfer_direction - dma transfer mode and direction indicator + * @DMA_MEM_TO_MEM: Async/Memcpy mode + * @DMA_MEM_TO_DEV: Slave mode & From Memory to Device + * @DMA_DEV_TO_MEM: Slave mode & From Device to Memory + * @DMA_DEV_TO_DEV: Slave mode & From Device to Device + */ +enum dma_transfer_direction { + DMA_MEM_TO_MEM, + DMA_MEM_TO_DEV, + DMA_DEV_TO_MEM, + DMA_DEV_TO_DEV, +}; /** * enum dma_ctrl_flags - DMA flags to augment operation preparation, @@ -267,7 +279,7 @@ enum dma_slave_buswidth { * struct, if applicable. */ struct dma_slave_config { - enum dma_data_direction direction; + enum dma_transfer_direction direction; dma_addr_t src_addr; dma_addr_t dst_addr; enum dma_slave_buswidth src_addr_width; @@ -490,11 +502,11 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_slave_sg)( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction); + size_t period_len, enum dma_transfer_direction direction); int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg); @@ -520,7 +532,7 @@ static inline int dmaengine_slave_config(struct dma_chan *chan, static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( struct dma_chan *chan, void *buf, size_t len, - enum dma_data_direction dir, unsigned long flags) + enum dma_transfer_direction dir, unsigned long flags) { struct scatterlist sg; sg_init_one(&sg, buf, len); -- cgit v0.10.2 From db8196df4bb6f117caa163aa73b0f16fd62290bd Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 13 Oct 2011 22:34:23 +0530 Subject: dmaengine: move drivers to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves dma/drivers/* to use new enum Cc: Jassi Brar Cc: Russell King Cc: Viresh Kumar Cc: Linus Walleij Cc: Nicolas Ferre Cc: Mika Westerberg Cc: H Hartley Sweeten Cc: Li Yang Cc: Zhang Wei Cc: Sascha Hauer Cc: Guennadi Liakhovetski Cc: Shawn Guo Cc: Yong Wang Cc: Tomoya MORINAGA Cc: Boojin Kim Cc: Barry Song Acked-by: Mika Westerberg Acked-by: Linus Walleij Acked-by: Viresh Kumar Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h index 46d4d87..e82c642 100644 --- a/arch/arm/mach-ep93xx/include/mach/dma.h +++ b/arch/arm/mach-ep93xx/include/mach/dma.h @@ -37,7 +37,7 @@ */ struct ep93xx_dma_data { int port; - enum dma_data_direction direction; + enum dma_transfer_direction direction; const char *name; }; @@ -80,14 +80,14 @@ static inline bool ep93xx_dma_chan_is_m2p(struct dma_chan *chan) * channel supports given DMA direction. Only M2P channels have such * limitation, for M2M channels the direction is configurable. */ -static inline enum dma_data_direction +static inline enum dma_transfer_direction ep93xx_dma_chan_direction(struct dma_chan *chan) { if (!ep93xx_dma_chan_is_m2p(chan)) return DMA_NONE; /* even channels are for TX, odd for RX */ - return (chan->chan_id % 2 == 0) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + return (chan->chan_id % 2 == 0) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; } #endif /* __ASM_ARCH_DMA_H */ diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h index 685c787..38b041a 100644 --- a/arch/arm/plat-nomadik/include/plat/ste_dma40.h +++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h @@ -187,7 +187,7 @@ static inline struct dma_async_tx_descriptor *stedma40_slave_mem(struct dma_chan *chan, dma_addr_t addr, unsigned int size, - enum dma_data_direction direction, + enum dma_transfer_direction direction, unsigned long flags) { struct scatterlist sg; @@ -209,7 +209,7 @@ static inline struct dma_async_tx_descriptor *stedma40_slave_mem(struct dma_chan *chan, dma_addr_t addr, unsigned int size, - enum dma_data_direction direction, + enum dma_transfer_direction direction, unsigned long flags) { return NULL; diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index b7cbd1a..41c62fd 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -882,9 +882,9 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, ch->signal = ret; /* Assign the flow control signal to this channel */ - if (txd->direction == DMA_TO_DEVICE) + if (txd->direction == DMA_MEM_TO_DEV) txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT; - else if (txd->direction == DMA_FROM_DEVICE) + else if (txd->direction == DMA_DEV_TO_MEM) txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT; } @@ -1102,10 +1102,10 @@ static int dma_set_runtime_config(struct dma_chan *chan, /* Transfer direction */ plchan->runtime_direction = config->direction; - if (config->direction == DMA_TO_DEVICE) { + if (config->direction == DMA_MEM_TO_DEV) { addr_width = config->dst_addr_width; maxburst = config->dst_maxburst; - } else if (config->direction == DMA_FROM_DEVICE) { + } else if (config->direction == DMA_DEV_TO_MEM) { addr_width = config->src_addr_width; maxburst = config->src_maxburst; } else { @@ -1136,7 +1136,7 @@ static int dma_set_runtime_config(struct dma_chan *chan, cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT; cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT; - if (plchan->runtime_direction == DMA_FROM_DEVICE) { + if (plchan->runtime_direction == DMA_DEV_TO_MEM) { plchan->src_addr = config->src_addr; plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR | pl08x_select_bus(plchan->cd->periph_buses, @@ -1152,7 +1152,7 @@ static int dma_set_runtime_config(struct dma_chan *chan, "configured channel %s (%s) for %s, data width %d, " "maxburst %d words, LE, CCTL=0x%08x\n", dma_chan_name(chan), plchan->name, - (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX", + (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX", addr_width, maxburst, cctl); @@ -1322,7 +1322,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); @@ -1354,10 +1354,10 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( */ txd->direction = direction; - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { txd->cctl = plchan->dst_cctl; slave_addr = plchan->dst_addr; - } else if (direction == DMA_FROM_DEVICE) { + } else if (direction == DMA_DEV_TO_MEM) { txd->cctl = plchan->src_cctl; slave_addr = plchan->src_addr; } else { @@ -1368,10 +1368,10 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( } if (plchan->cd->device_fc) - tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER_PER : + tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER : PL080_FLOW_PER2MEM_PER; else - tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER : + tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER : PL080_FLOW_PER2MEM; txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT; @@ -1387,7 +1387,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( list_add_tail(&dsg->node, &txd->dsg_list); dsg->len = sg_dma_len(sg); - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { dsg->src_addr = sg_phys(sg); dsg->dst_addr = slave_addr; } else { diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index fcfa0a8..7e76574 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -660,7 +660,7 @@ err_desc_get: */ static struct dma_async_tx_descriptor * atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct at_dma_chan *atchan = to_at_dma_chan(chan); @@ -678,7 +678,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dev_vdbg(chan2dev(chan), "prep_slave_sg (%d): %s f0x%lx\n", sg_len, - direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", + direction == DMA_MEM_TO_DEV ? "TO DEVICE" : "FROM DEVICE", flags); if (unlikely(!atslave || !sg_len)) { @@ -692,7 +692,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctrlb = ATC_IEN; switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: ctrla |= ATC_DST_WIDTH(reg_width); ctrlb |= ATC_DST_ADDR_MODE_FIXED | ATC_SRC_ADDR_MODE_INCR @@ -725,7 +725,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, total_len += len; } break; - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: ctrla |= ATC_SRC_WIDTH(reg_width); ctrlb |= ATC_DST_ADDR_MODE_INCR | ATC_SRC_ADDR_MODE_FIXED @@ -787,7 +787,7 @@ err_desc_get: */ static int atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { if (period_len > (ATC_BTSIZE_MAX << reg_width)) goto err_out; @@ -795,7 +795,7 @@ atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, goto err_out; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto err_out; - if (unlikely(!(direction & (DMA_TO_DEVICE | DMA_FROM_DEVICE)))) + if (unlikely(!(direction & (DMA_DEV_TO_MEM | DMA_MEM_TO_DEV)))) goto err_out; return 0; @@ -810,7 +810,7 @@ err_out: static int atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, unsigned int period_index, dma_addr_t buf_addr, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { u32 ctrla; unsigned int reg_width = atslave->reg_width; @@ -822,7 +822,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, | period_len >> reg_width; switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: desc->lli.saddr = buf_addr + (period_len * period_index); desc->lli.daddr = atslave->tx_reg; desc->lli.ctrla = ctrla; @@ -833,7 +833,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, | ATC_DIF(AT_DMA_PER_IF); break; - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: desc->lli.saddr = atslave->rx_reg; desc->lli.daddr = buf_addr + (period_len * period_index); desc->lli.ctrla = ctrla; @@ -861,7 +861,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc, */ static struct dma_async_tx_descriptor * atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; @@ -872,7 +872,7 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, unsigned int i; dev_vdbg(chan2dev(chan), "prep_dma_cyclic: %s buf@0x%08x - %d (%d/%d)\n", - direction == DMA_TO_DEVICE ? "TO DEVICE" : "FROM DEVICE", + direction == DMA_MEM_TO_DEV ? "TO DEVICE" : "FROM DEVICE", buf_addr, periods, buf_len, period_len); diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 4234f41..d65a718 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -39,7 +39,7 @@ struct coh901318_desc { struct scatterlist *sg; unsigned int sg_len; struct coh901318_lli *lli; - enum dma_data_direction dir; + enum dma_transfer_direction dir; unsigned long flags; u32 head_config; u32 head_ctrl; @@ -1034,7 +1034,7 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, static struct dma_async_tx_descriptor * coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct coh901318_chan *cohc = to_coh901318_chan(chan); @@ -1077,7 +1077,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctrl_last |= cohc->runtime_ctrl; ctrl |= cohc->runtime_ctrl; - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE | COH901318_CX_CTRL_SRC_ADDR_INC_ENABLE; @@ -1085,7 +1085,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctrl_chained |= tx_flags; ctrl_last |= tx_flags; ctrl |= tx_flags; - } else if (direction == DMA_FROM_DEVICE) { + } else if (direction == DMA_DEV_TO_MEM) { u32 rx_flags = COH901318_CX_CTRL_PRDD_DEST | COH901318_CX_CTRL_DST_ADDR_INC_ENABLE; @@ -1274,11 +1274,11 @@ static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, int i = 0; /* We only support mem to per or per to mem transfers */ - if (config->direction == DMA_FROM_DEVICE) { + if (config->direction == DMA_DEV_TO_MEM) { addr = config->src_addr; addr_width = config->src_addr_width; maxburst = config->src_maxburst; - } else if (config->direction == DMA_TO_DEVICE) { + } else if (config->direction == DMA_MEM_TO_DEV) { addr = config->dst_addr; addr_width = config->dst_addr_width; maxburst = config->dst_maxburst; diff --git a/drivers/dma/coh901318_lli.c b/drivers/dma/coh901318_lli.c index 9f7e0e6a..6c0e2d4 100644 --- a/drivers/dma/coh901318_lli.c +++ b/drivers/dma/coh901318_lli.c @@ -7,11 +7,10 @@ * Author: Per Friden */ -#include #include -#include #include #include +#include #include #include "coh901318_lli.h" @@ -177,18 +176,18 @@ coh901318_lli_fill_single(struct coh901318_pool *pool, struct coh901318_lli *lli, dma_addr_t buf, unsigned int size, dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl_eom, - enum dma_data_direction dir) + enum dma_transfer_direction dir) { int s = size; dma_addr_t src; dma_addr_t dst; - if (dir == DMA_TO_DEVICE) { + if (dir == DMA_MEM_TO_DEV) { src = buf; dst = dev_addr; - } else if (dir == DMA_FROM_DEVICE) { + } else if (dir == DMA_DEV_TO_MEM) { src = dev_addr; dst = buf; @@ -215,9 +214,9 @@ coh901318_lli_fill_single(struct coh901318_pool *pool, lli = coh901318_lli_next(lli); - if (dir == DMA_TO_DEVICE) + if (dir == DMA_MEM_TO_DEV) src += block_size; - else if (dir == DMA_FROM_DEVICE) + else if (dir == DMA_DEV_TO_MEM) dst += block_size; } @@ -234,7 +233,7 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, struct scatterlist *sgl, unsigned int nents, dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl, u32 ctrl_last, - enum dma_data_direction dir, u32 ctrl_irq_mask) + enum dma_transfer_direction dir, u32 ctrl_irq_mask) { int i; struct scatterlist *sg; @@ -249,9 +248,9 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, spin_lock(&pool->lock); - if (dir == DMA_TO_DEVICE) + if (dir == DMA_MEM_TO_DEV) dst = dev_addr; - else if (dir == DMA_FROM_DEVICE) + else if (dir == DMA_DEV_TO_MEM) src = dev_addr; else goto err; @@ -269,7 +268,7 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, ctrl_sg = ctrl ? ctrl : ctrl_last; - if (dir == DMA_TO_DEVICE) + if (dir == DMA_MEM_TO_DEV) /* increment source address */ src = sg_phys(sg); else @@ -293,7 +292,7 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, lli->src_addr = src; lli->dst_addr = dst; - if (dir == DMA_FROM_DEVICE) + if (dir == DMA_DEV_TO_MEM) dst += elem_size; else src += elem_size; diff --git a/drivers/dma/coh901318_lli.h b/drivers/dma/coh901318_lli.h index 7a5c809..abff371 100644 --- a/drivers/dma/coh901318_lli.h +++ b/drivers/dma/coh901318_lli.h @@ -97,7 +97,7 @@ coh901318_lli_fill_single(struct coh901318_pool *pool, struct coh901318_lli *lli, dma_addr_t buf, unsigned int size, dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl_last, - enum dma_data_direction dir); + enum dma_transfer_direction dir); /** * coh901318_lli_fill_single() - Prepares the lli:s for dma scatter list transfer @@ -119,6 +119,6 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool, struct scatterlist *sg, unsigned int nents, dma_addr_t dev_addr, u32 ctrl_chained, u32 ctrl, u32 ctrl_last, - enum dma_data_direction dir, u32 ctrl_irq_mask); + enum dma_transfer_direction dir, u32 ctrl_irq_mask); #endif /* COH901318_LLI_H */ diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9bfd6d3..decca1c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -696,7 +696,7 @@ err_desc_get: static struct dma_async_tx_descriptor * dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); @@ -720,7 +720,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, prev = first = NULL; switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: ctllo = (DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_FIX @@ -777,7 +777,7 @@ slave_sg_todev_fill_desc: goto slave_sg_todev_fill_desc; } break; - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: ctllo = (DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_INC @@ -1165,7 +1165,7 @@ EXPORT_SYMBOL(dw_dma_cyclic_stop); */ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, - enum dma_data_direction direction) + enum dma_transfer_direction direction) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_cyclic_desc *cdesc; @@ -1206,7 +1206,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, goto out_err; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto out_err; - if (unlikely(!(direction & (DMA_TO_DEVICE | DMA_FROM_DEVICE)))) + if (unlikely(!(direction & (DMA_MEM_TO_DEV | DMA_DEV_TO_MEM)))) goto out_err; retval = ERR_PTR(-ENOMEM); @@ -1228,7 +1228,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, goto out_err_desc_get; switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: desc->lli.dar = dws->tx_reg; desc->lli.sar = buf_addr + (period_len * i); desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) @@ -1239,7 +1239,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, | DWC_CTLL_FC(dws->fc) | DWC_CTLL_INT_EN); break; - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: desc->lli.dar = buf_addr + (period_len * i); desc->lli.sar = dws->rx_reg; desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index b47e2b8..009851b 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -330,7 +330,7 @@ static void m2p_fill_desc(struct ep93xx_dma_chan *edmac) struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); u32 bus_addr; - if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_TO_DEVICE) + if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_MEM_TO_DEV) bus_addr = desc->src_addr; else bus_addr = desc->dst_addr; @@ -443,7 +443,7 @@ static int m2m_hw_setup(struct ep93xx_dma_chan *edmac) control = (5 << M2M_CONTROL_PWSC_SHIFT); control |= M2M_CONTROL_NO_HDSK; - if (data->direction == DMA_TO_DEVICE) { + if (data->direction == DMA_MEM_TO_DEV) { control |= M2M_CONTROL_DAH; control |= M2M_CONTROL_TM_TX; control |= M2M_CONTROL_RSS_SSPTX; @@ -463,7 +463,7 @@ static int m2m_hw_setup(struct ep93xx_dma_chan *edmac) control |= M2M_CONTROL_RSS_IDE; control |= M2M_CONTROL_PW_16; - if (data->direction == DMA_TO_DEVICE) { + if (data->direction == DMA_MEM_TO_DEV) { /* Worst case from the UG */ control = (3 << M2M_CONTROL_PWSC_SHIFT); control |= M2M_CONTROL_DAH; @@ -803,8 +803,8 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) switch (data->port) { case EP93XX_DMA_SSP: case EP93XX_DMA_IDE: - if (data->direction != DMA_TO_DEVICE && - data->direction != DMA_FROM_DEVICE) + if (data->direction != DMA_MEM_TO_DEV && + data->direction != DMA_DEV_TO_MEM) return -EINVAL; break; default: @@ -952,7 +952,7 @@ fail: */ static struct dma_async_tx_descriptor * ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction dir, + unsigned int sg_len, enum dma_transfer_direction dir, unsigned long flags) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); @@ -988,7 +988,7 @@ ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, goto fail; } - if (dir == DMA_TO_DEVICE) { + if (dir == DMA_MEM_TO_DEV) { desc->src_addr = sg_dma_address(sg); desc->dst_addr = edmac->runtime_addr; } else { @@ -1032,7 +1032,7 @@ fail: static struct dma_async_tx_descriptor * ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_data_direction dir) + enum dma_transfer_direction dir) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); struct ep93xx_dma_desc *desc, *first; @@ -1065,7 +1065,7 @@ ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, goto fail; } - if (dir == DMA_TO_DEVICE) { + if (dir == DMA_MEM_TO_DEV) { desc->src_addr = dma_addr + offset; desc->dst_addr = edmac->runtime_addr; } else { @@ -1133,12 +1133,12 @@ static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac, return -EINVAL; switch (config->direction) { - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: width = config->src_addr_width; addr = config->src_addr; break; - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: width = config->dst_addr_width; addr = config->dst_addr; break; diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 8a78154..b98070c 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -772,7 +772,7 @@ fail: */ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg( struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags) { /* * This operation is not supported on the Freescale DMA controller @@ -819,7 +819,7 @@ static int fsl_dma_device_control(struct dma_chan *dchan, return -ENXIO; /* we set the controller burst size depending on direction */ - if (config->direction == DMA_TO_DEVICE) + if (config->direction == DMA_MEM_TO_DEV) size = config->dst_addr_width * config->dst_maxburst; else size = config->src_addr_width * config->src_maxburst; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index d746899..678cd01 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -106,7 +106,7 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, imx_dma_disable(imxdmac->imxdma_channel); return 0; case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_FROM_DEVICE) { + if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { imxdmac->per_address = dmaengine_cfg->src_addr; imxdmac->watermark_level = dmaengine_cfg->src_maxburst; imxdmac->word_size = dmaengine_cfg->src_addr_width; @@ -223,7 +223,7 @@ static void imxdma_free_chan_resources(struct dma_chan *chan) static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); @@ -240,7 +240,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( dma_length += sg->length; } - if (direction == DMA_FROM_DEVICE) + if (direction == DMA_DEV_TO_MEM) dmamode = DMA_MODE_READ; else dmamode = DMA_MODE_WRITE; @@ -270,7 +270,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; @@ -316,7 +316,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( imxdmac->sg_list[periods].page_link = ((unsigned long)imxdmac->sg_list | 0x01) & ~0x02; - if (direction == DMA_FROM_DEVICE) + if (direction == DMA_DEV_TO_MEM) dmamode = DMA_MODE_READ; else dmamode = DMA_MODE_WRITE; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index eab1fe7..065de54 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -246,7 +246,7 @@ struct sdma_engine; struct sdma_channel { struct sdma_engine *sdma; unsigned int channel; - enum dma_data_direction direction; + enum dma_transfer_direction direction; enum sdma_peripheral_type peripheral_type; unsigned int event_id0; unsigned int event_id1; @@ -649,7 +649,7 @@ static int sdma_load_context(struct sdma_channel *sdmac) struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; int ret; - if (sdmac->direction == DMA_FROM_DEVICE) { + if (sdmac->direction == DMA_DEV_TO_MEM) { load_address = sdmac->pc_from_device; } else { load_address = sdmac->pc_to_device; @@ -910,7 +910,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan) static struct dma_async_tx_descriptor *sdma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct sdma_channel *sdmac = to_sdma_chan(chan); @@ -1007,7 +1007,7 @@ err_out: static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; @@ -1092,7 +1092,7 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, sdma_disable_channel(sdmac); return 0; case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_FROM_DEVICE) { + if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; sdmac->watermark_level = dmaengine_cfg->src_maxburst; sdmac->word_size = dmaengine_cfg->src_addr_width; diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 9e96c43..6deda25 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -394,10 +394,10 @@ static int midc_lli_fill_sg(struct intel_mid_dma_chan *midc, midc->dma->block_size); /*Populate SAR and DAR values*/ sg_phy_addr = sg_phys(sg); - if (desc->dirn == DMA_TO_DEVICE) { + if (desc->dirn == DMA_MEM_TO_DEV) { lli_bloc_desc->sar = sg_phy_addr; lli_bloc_desc->dar = mids->dma_slave.dst_addr; - } else if (desc->dirn == DMA_FROM_DEVICE) { + } else if (desc->dirn == DMA_DEV_TO_MEM) { lli_bloc_desc->sar = mids->dma_slave.src_addr; lli_bloc_desc->dar = sg_phy_addr; } @@ -631,13 +631,13 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_memcpy( if (midc->dma->pimr_mask) { cfg_hi.cfgx.protctl = 0x0; /*default value*/ cfg_hi.cfgx.fifo_mode = 1; - if (mids->dma_slave.direction == DMA_TO_DEVICE) { + if (mids->dma_slave.direction == DMA_MEM_TO_DEV) { cfg_hi.cfgx.src_per = 0; if (mids->device_instance == 0) cfg_hi.cfgx.dst_per = 3; if (mids->device_instance == 1) cfg_hi.cfgx.dst_per = 1; - } else if (mids->dma_slave.direction == DMA_FROM_DEVICE) { + } else if (mids->dma_slave.direction == DMA_DEV_TO_MEM) { if (mids->device_instance == 0) cfg_hi.cfgx.src_per = 2; if (mids->device_instance == 1) @@ -681,11 +681,11 @@ static struct dma_async_tx_descriptor *intel_mid_dma_prep_memcpy( ctl_lo.ctlx.sinc = 0; ctl_lo.ctlx.dinc = 0; } else { - if (mids->dma_slave.direction == DMA_TO_DEVICE) { + if (mids->dma_slave.direction == DMA_MEM_TO_DEV) { ctl_lo.ctlx.sinc = 0; ctl_lo.ctlx.dinc = 2; ctl_lo.ctlx.tt_fc = 1; - } else if (mids->dma_slave.direction == DMA_FROM_DEVICE) { + } else if (mids->dma_slave.direction == DMA_DEV_TO_MEM) { ctl_lo.ctlx.sinc = 2; ctl_lo.ctlx.dinc = 0; ctl_lo.ctlx.tt_fc = 2; @@ -731,7 +731,7 @@ err_desc_get: */ static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct intel_mid_dma_chan *midc = NULL; diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h index aea5ee8..c6de919 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h @@ -262,7 +262,7 @@ struct intel_mid_dma_desc { unsigned int lli_length; unsigned int current_lli; dma_addr_t next; - enum dma_data_direction dirn; + enum dma_transfer_direction dirn; enum dma_status status; enum dma_slave_buswidth width; /*width of DMA txn*/ enum intel_mid_dma_mode cfg_mode; /*mode configuration*/ diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 6815905..0cee3b3 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1362,7 +1362,7 @@ static void ipu_gc_tasklet(unsigned long arg) /* Allocate and initialise a transfer descriptor. */ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, unsigned long tx_flags) + enum dma_transfer_direction direction, unsigned long tx_flags) { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac_tx_desc *desc = NULL; @@ -1374,7 +1374,7 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan chan->chan_id != IDMAC_IC_7) return NULL; - if (direction != DMA_FROM_DEVICE && direction != DMA_TO_DEVICE) { + if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) { dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); return NULL; } diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index b4588bd..bdf4672 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -377,7 +377,7 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long append) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); @@ -450,7 +450,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits |= CCW_CHAIN; ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_TERM_FLUSH; - ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ? + ccw->bits |= BF_CCW(direction == DMA_DEV_TO_MEM ? MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND); @@ -472,7 +472,7 @@ err_out: static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -515,7 +515,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( ccw->bits |= CCW_IRQ; ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_TERM_FLUSH; - ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ? + ccw->bits |= BF_CCW(direction == DMA_DEV_TO_MEM ? MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND); dma_addr += period_len; diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index a6d0e3d..9944e82 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -99,7 +99,7 @@ struct pch_dma_desc { struct pch_dma_chan { struct dma_chan chan; void __iomem *membase; - enum dma_data_direction dir; + enum dma_transfer_direction dir; struct tasklet_struct tasklet; unsigned long err_status; @@ -224,7 +224,7 @@ static void pdc_set_dir(struct dma_chan *chan) mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS << (DMA_CTL0_BITS_PER_CH * chan->chan_id)); val &= mask_mode; - if (pd_chan->dir == DMA_TO_DEVICE) + if (pd_chan->dir == DMA_MEM_TO_DEV) val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id + DMA_CTL0_DIR_SHIFT_BITS); else @@ -242,7 +242,7 @@ static void pdc_set_dir(struct dma_chan *chan) mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS << (DMA_CTL0_BITS_PER_CH * ch)); val &= mask_mode; - if (pd_chan->dir == DMA_TO_DEVICE) + if (pd_chan->dir == DMA_MEM_TO_DEV) val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch + DMA_CTL0_DIR_SHIFT_BITS); else @@ -607,7 +607,7 @@ static void pd_issue_pending(struct dma_chan *chan) static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags) { struct pch_dma_chan *pd_chan = to_pd_chan(chan); struct pch_dma_slave *pd_slave = chan->private; @@ -623,9 +623,9 @@ static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan, return NULL; } - if (direction == DMA_FROM_DEVICE) + if (direction == DMA_DEV_TO_MEM) reg = pd_slave->rx_reg; - else if (direction == DMA_TO_DEVICE) + else if (direction == DMA_MEM_TO_DEV) reg = pd_slave->tx_reg; else return NULL; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 57104147..1e58eeb 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -320,14 +320,14 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned case DMA_SLAVE_CONFIG: slave_config = (struct dma_slave_config *)arg; - if (slave_config->direction == DMA_TO_DEVICE) { + if (slave_config->direction == DMA_MEM_TO_DEV) { if (slave_config->dst_addr) pch->fifo_addr = slave_config->dst_addr; if (slave_config->dst_addr_width) pch->burst_sz = __ffs(slave_config->dst_addr_width); if (slave_config->dst_maxburst) pch->burst_len = slave_config->dst_maxburst; - } else if (slave_config->direction == DMA_FROM_DEVICE) { + } else if (slave_config->direction == DMA_DEV_TO_MEM) { if (slave_config->src_addr) pch->fifo_addr = slave_config->src_addr; if (slave_config->src_addr_width) @@ -597,7 +597,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction) { struct dma_pl330_desc *desc; struct dma_pl330_chan *pch = to_pchan(chan); @@ -612,13 +612,13 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( } switch (direction) { - case DMA_TO_DEVICE: + case DMA_MEM_TO_DEV: desc->rqcfg.src_inc = 1; desc->rqcfg.dst_inc = 0; src = dma_addr; dst = pch->fifo_addr; break; - case DMA_FROM_DEVICE: + case DMA_DEV_TO_MEM: desc->rqcfg.src_inc = 0; desc->rqcfg.dst_inc = 1; src = pch->fifo_addr; @@ -687,7 +687,7 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, static struct dma_async_tx_descriptor * pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flg) { struct dma_pl330_desc *first, *desc = NULL; @@ -702,9 +702,9 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; /* Make sure the direction is consistent */ - if ((direction == DMA_TO_DEVICE && + if ((direction == DMA_MEM_TO_DEV && peri->rqtype != MEMTODEV) || - (direction == DMA_FROM_DEVICE && + (direction == DMA_DEV_TO_MEM && peri->rqtype != DEVTOMEM)) { dev_err(pch->dmac->pif.dev, "%s:%d Invalid Direction\n", __func__, __LINE__); @@ -747,7 +747,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, else list_add_tail(&desc->node, &first->node); - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { desc->rqcfg.src_inc = 1; desc->rqcfg.dst_inc = 0; fill_px(&desc->px, diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 81809c2..592304f 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -479,19 +478,19 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan) * @sh_chan: DMA channel * @flags: DMA transfer flags * @dest: destination DMA address, incremented when direction equals - * DMA_FROM_DEVICE or DMA_BIDIRECTIONAL + * DMA_DEV_TO_MEM * @src: source DMA address, incremented when direction equals - * DMA_TO_DEVICE or DMA_BIDIRECTIONAL + * DMA_MEM_TO_DEV * @len: DMA transfer length * @first: if NULL, set to the current descriptor and cookie set to -EBUSY * @direction: needed for slave DMA to decide which address to keep constant, - * equals DMA_BIDIRECTIONAL for MEMCPY + * equals DMA_MEM_TO_MEM for MEMCPY * Returns 0 or an error * Locks: called with desc_lock held */ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan, unsigned long flags, dma_addr_t *dest, dma_addr_t *src, size_t *len, - struct sh_desc **first, enum dma_data_direction direction) + struct sh_desc **first, enum dma_transfer_direction direction) { struct sh_desc *new; size_t copy_size; @@ -531,9 +530,9 @@ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan, new->direction = direction; *len -= copy_size; - if (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE) + if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV) *src += copy_size; - if (direction == DMA_BIDIRECTIONAL || direction == DMA_FROM_DEVICE) + if (direction == DMA_MEM_TO_MEM || direction == DMA_DEV_TO_MEM) *dest += copy_size; return new; @@ -546,12 +545,12 @@ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan, * converted to scatter-gather to guarantee consistent locking and a correct * list manipulation. For slave DMA direction carries the usual meaning, and, * logically, the SG list is RAM and the addr variable contains slave address, - * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_BIDIRECTIONAL + * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM * and the SG list contains only one element and points at the source buffer. */ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_chan, struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr, - enum dma_data_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags) { struct scatterlist *sg; struct sh_desc *first = NULL, *new = NULL /* compiler... */; @@ -592,7 +591,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c dev_dbg(sh_chan->dev, "Add SG #%d@%p[%d], dma %llx\n", i, sg, len, (unsigned long long)sg_addr); - if (direction == DMA_FROM_DEVICE) + if (direction == DMA_DEV_TO_MEM) new = sh_dmae_add_desc(sh_chan, flags, &sg_addr, addr, &len, &first, direction); @@ -646,13 +645,13 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy( sg_dma_address(&sg) = dma_src; sg_dma_len(&sg) = len; - return sh_dmae_prep_sg(sh_chan, &sg, 1, &dma_dest, DMA_BIDIRECTIONAL, + return sh_dmae_prep_sg(sh_chan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM, flags); } static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags) { struct sh_dmae_slave *param; struct sh_dmae_chan *sh_chan; @@ -996,7 +995,7 @@ static void dmae_do_tasklet(unsigned long data) spin_lock_irq(&sh_chan->desc_lock); list_for_each_entry(desc, &sh_chan->ld_queue, node) { if (desc->mark == DESC_SUBMITTED && - ((desc->direction == DMA_FROM_DEVICE && + ((desc->direction == DMA_DEV_TO_MEM && (desc->hw.dar + desc->hw.tcr) == dar_buf) || (desc->hw.sar + desc->hw.tcr) == sar_buf)) { dev_dbg(sh_chan->dev, "done #%d@%p dst %u\n", diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 467e4dc..0c6cbac 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -216,7 +216,7 @@ struct d40_chan { struct d40_log_lli_full *lcpa; /* Runtime reconfiguration */ dma_addr_t runtime_addr; - enum dma_data_direction runtime_direction; + enum dma_transfer_direction runtime_direction; }; /** @@ -1854,7 +1854,7 @@ err: } static dma_addr_t -d40_get_dev_addr(struct d40_chan *chan, enum dma_data_direction direction) +d40_get_dev_addr(struct d40_chan *chan, enum dma_transfer_direction direction) { struct stedma40_platform_data *plat = chan->base->plat_data; struct stedma40_chan_cfg *cfg = &chan->dma_cfg; @@ -1863,9 +1863,9 @@ d40_get_dev_addr(struct d40_chan *chan, enum dma_data_direction direction) if (chan->runtime_addr) return chan->runtime_addr; - if (direction == DMA_FROM_DEVICE) + if (direction == DMA_DEV_TO_MEM) addr = plat->dev_rx[cfg->src_dev_type]; - else if (direction == DMA_TO_DEVICE) + else if (direction == DMA_MEM_TO_DEV) addr = plat->dev_tx[cfg->dst_dev_type]; return addr; @@ -1874,7 +1874,7 @@ d40_get_dev_addr(struct d40_chan *chan, enum dma_data_direction direction) static struct dma_async_tx_descriptor * d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, struct scatterlist *sg_dst, unsigned int sg_len, - enum dma_data_direction direction, unsigned long dma_flags) + enum dma_transfer_direction direction, unsigned long dma_flags) { struct d40_chan *chan = container_of(dchan, struct d40_chan, chan); dma_addr_t src_dev_addr = 0; @@ -1901,9 +1901,9 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, if (direction != DMA_NONE) { dma_addr_t dev_addr = d40_get_dev_addr(chan, direction); - if (direction == DMA_FROM_DEVICE) + if (direction == DMA_DEV_TO_MEM) src_dev_addr = dev_addr; - else if (direction == DMA_TO_DEVICE) + else if (direction == DMA_MEM_TO_DEV) dst_dev_addr = dev_addr; } @@ -2107,10 +2107,10 @@ d40_prep_memcpy_sg(struct dma_chan *chan, static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, + enum dma_transfer_direction direction, unsigned long dma_flags) { - if (direction != DMA_FROM_DEVICE && direction != DMA_TO_DEVICE) + if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) return NULL; return d40_prep_sg(chan, sgl, sgl, sg_len, direction, dma_flags); @@ -2119,7 +2119,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, static struct dma_async_tx_descriptor * dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_data_direction direction) + enum dma_transfer_direction direction) { unsigned int periods = buf_len / period_len; struct dma_async_tx_descriptor *txd; @@ -2268,7 +2268,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, dst_addr_width = config->dst_addr_width; dst_maxburst = config->dst_maxburst; - if (config->direction == DMA_FROM_DEVICE) { + if (config->direction == DMA_DEV_TO_MEM) { dma_addr_t dev_addr_rx = d40c->base->plat_data->dev_rx[cfg->src_dev_type]; @@ -2291,7 +2291,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, if (dst_maxburst == 0) dst_maxburst = src_maxburst; - } else if (config->direction == DMA_TO_DEVICE) { + } else if (config->direction == DMA_MEM_TO_DEV) { dma_addr_t dev_addr_tx = d40c->base->plat_data->dev_tx[cfg->dst_dev_type]; @@ -2356,7 +2356,7 @@ static int d40_set_runtime_config(struct dma_chan *chan, "configured channel %s for %s, data width %d/%d, " "maxburst %d/%d elements, LE, no flow control\n", dma_chan_name(chan), - (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX", + (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX", src_addr_width, dst_addr_width, src_maxburst, dst_maxburst); diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index a4a398f..8c88072 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -90,7 +90,7 @@ struct timb_dma_chan { struct list_head queue; struct list_head free_list; unsigned int bytes_per_line; - enum dma_data_direction direction; + enum dma_transfer_direction direction; unsigned int descs; /* Descriptors to allocate */ unsigned int desc_elems; /* number of elems per descriptor */ }; @@ -235,7 +235,7 @@ static void __td_start_dma(struct timb_dma_chan *td_chan) "td_chan: %p, chan: %d, membase: %p\n", td_chan, td_chan->chan.chan_id, td_chan->membase); - if (td_chan->direction == DMA_FROM_DEVICE) { + if (td_chan->direction == DMA_DEV_TO_MEM) { /* descriptor address */ iowrite32(0, td_chan->membase + TIMBDMA_OFFS_RX_DHAR); @@ -278,7 +278,7 @@ static void __td_finish(struct timb_dma_chan *td_chan) txd->cookie); /* make sure to stop the transfer */ - if (td_chan->direction == DMA_FROM_DEVICE) + if (td_chan->direction == DMA_DEV_TO_MEM) iowrite32(0, td_chan->membase + TIMBDMA_OFFS_RX_ER); /* Currently no support for stopping DMA transfers else @@ -398,7 +398,7 @@ static struct timb_dma_desc *td_alloc_init_desc(struct timb_dma_chan *td_chan) td_desc->txd.flags = DMA_CTRL_ACK; td_desc->txd.phys = dma_map_single(chan2dmadev(chan), - td_desc->desc_list, td_desc->desc_list_len, DMA_TO_DEVICE); + td_desc->desc_list, td_desc->desc_list_len, DMA_MEM_TO_DEV); err = dma_mapping_error(chan2dmadev(chan), td_desc->txd.phys); if (err) { @@ -419,7 +419,7 @@ static void td_free_desc(struct timb_dma_desc *td_desc) { dev_dbg(chan2dev(td_desc->txd.chan), "Freeing desc: %p\n", td_desc); dma_unmap_single(chan2dmadev(td_desc->txd.chan), td_desc->txd.phys, - td_desc->desc_list_len, DMA_TO_DEVICE); + td_desc->desc_list_len, DMA_MEM_TO_DEV); kfree(td_desc->desc_list); kfree(td_desc); @@ -558,7 +558,7 @@ static void td_issue_pending(struct dma_chan *chan) static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags) { struct timb_dma_chan *td_chan = container_of(chan, struct timb_dma_chan, chan); @@ -606,7 +606,7 @@ static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan, } dma_sync_single_for_device(chan2dmadev(chan), td_desc->txd.phys, - td_desc->desc_list_len, DMA_TO_DEVICE); + td_desc->desc_list_len, DMA_MEM_TO_DEV); return &td_desc->txd; } @@ -775,8 +775,8 @@ static int __devinit td_probe(struct platform_device *pdev) td_chan->descs = pchan->descriptors; td_chan->desc_elems = pchan->descriptor_elements; td_chan->bytes_per_line = pchan->bytes_per_line; - td_chan->direction = pchan->rx ? DMA_FROM_DEVICE : - DMA_TO_DEVICE; + td_chan->direction = pchan->rx ? DMA_DEV_TO_MEM : + DMA_MEM_TO_DEV; td_chan->membase = td->membase + (i / 2) * TIMBDMA_INSTANCE_OFFSET + diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index cbd83e36..6122c36 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -845,7 +845,7 @@ txx9dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, static struct dma_async_tx_descriptor * txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, + unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct txx9dmac_chan *dc = to_txx9dmac_chan(chan); @@ -860,9 +860,9 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, BUG_ON(!ds || !ds->reg_width); if (ds->tx_reg) - BUG_ON(direction != DMA_TO_DEVICE); + BUG_ON(direction != DMA_MEM_TO_DEV); else - BUG_ON(direction != DMA_FROM_DEVICE); + BUG_ON(direction != DMA_DEV_TO_MEM); if (unlikely(!sg_len)) return NULL; @@ -882,7 +882,7 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem = sg_dma_address(sg); if (__is_dmac64(ddev)) { - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { desc->hwdesc.SAR = mem; desc->hwdesc.DAR = ds->tx_reg; } else { @@ -891,7 +891,7 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } desc->hwdesc.CNTR = sg_dma_len(sg); } else { - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { desc->hwdesc32.SAR = mem; desc->hwdesc32.DAR = ds->tx_reg; } else { @@ -900,7 +900,7 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } desc->hwdesc32.CNTR = sg_dma_len(sg); } - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { sai = ds->reg_width; dai = 0; } else { diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 9eabffb..033f6aa 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -134,7 +134,7 @@ struct pl08x_txd { struct dma_async_tx_descriptor tx; struct list_head node; struct list_head dsg_list; - enum dma_data_direction direction; + enum dma_transfer_direction direction; dma_addr_t llis_bus; struct pl08x_lli *llis_va; /* Default cctl value for LLIs */ @@ -197,7 +197,7 @@ struct pl08x_dma_chan { dma_addr_t dst_addr; u32 src_cctl; u32 dst_cctl; - enum dma_data_direction runtime_direction; + enum dma_transfer_direction runtime_direction; dma_cookie_t lc; struct list_head pend_list; struct pl08x_txd *at; diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 4bfe0a2..f2c64f9 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -127,7 +127,7 @@ struct dw_cyclic_desc { struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, - enum dma_data_direction direction); + enum dma_transfer_direction direction); void dw_dma_cyclic_free(struct dma_chan *chan); int dw_dma_cyclic_start(struct dma_chan *chan); void dw_dma_cyclic_stop(struct dma_chan *chan); diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index cb2dd11..62ef693 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -30,7 +30,7 @@ struct sh_desc { struct sh_dmae_regs hw; struct list_head node; struct dma_async_tx_descriptor async_tx; - enum dma_data_direction direction; + enum dma_transfer_direction direction; dma_cookie_t cookie; size_t partial; int chunks; -- cgit v0.10.2 From 20dd5a356efcb5bf574baa9adccc775158f13ae1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:35:16 +0530 Subject: plat-samsung: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves samsung drivers to use new enum Cc: Ben Dooks Cc: Kukjin Kim Cc: Boojin Kim Acked-by: Kukjin Kim Signed-off-by: Vinod Koul diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c index 6e3d9ab..3df0bad 100644 --- a/arch/arm/plat-samsung/dma-ops.c +++ b/arch/arm/plat-samsung/dma-ops.c @@ -35,14 +35,14 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch, chan = dma_request_channel(mask, pl330_filter, (void *)dma_ch); - if (info->direction == DMA_FROM_DEVICE) { + if (info->direction == DMA_DEV_TO_MEM) { memset(&slave_config, 0, sizeof(struct dma_slave_config)); slave_config.direction = info->direction; slave_config.src_addr = info->fifo; slave_config.src_addr_width = info->width; slave_config.src_maxburst = 1; dmaengine_slave_config(chan, &slave_config); - } else if (info->direction == DMA_TO_DEVICE) { + } else if (info->direction == DMA_MEM_TO_DEV) { memset(&slave_config, 0, sizeof(struct dma_slave_config)); slave_config.direction = info->direction; slave_config.dst_addr = info->fifo; diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h index 4c1a363..1256115 100644 --- a/arch/arm/plat-samsung/include/plat/dma-ops.h +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h @@ -17,7 +17,7 @@ struct samsung_dma_prep_info { enum dma_transaction_type cap; - enum dma_data_direction direction; + enum dma_transfer_direction direction; dma_addr_t buf; unsigned long period; unsigned long len; @@ -27,7 +27,7 @@ struct samsung_dma_prep_info { struct samsung_dma_info { enum dma_transaction_type cap; - enum dma_data_direction direction; + enum dma_transfer_direction direction; enum dma_slave_buswidth width; dma_addr_t fifo; struct s3c2410_dma_client *client; -- cgit v0.10.2 From 1003cab8276cd34d9deab8ca9d148ee59f7728a7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:39:23 +0530 Subject: media, video: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves media and video drivers to use new enum Cc: Mauro Carvalho Chehab Cc: Paul Mundt Cc: Guennadi Liakhovetski Acked-by: Guennadi Liakhovetski Acked-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index c045b47..ec98eae 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -251,7 +251,7 @@ static int mx3_videobuf_prepare(struct vb2_buffer *vb) sg_dma_len(sg) = new_size; buf->txd = ichan->dma_chan.device->device_prep_slave_sg( - &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, + &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!buf->txd) return -EIO; diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c index 84cd1b6..d542c8b 100644 --- a/drivers/media/video/timblogiw.c +++ b/drivers/media/video/timblogiw.c @@ -564,7 +564,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) spin_unlock_irq(&fh->queue_lock); desc = fh->chan->device->device_prep_slave_sg(fh->chan, - buf->sg, sg_elems, DMA_FROM_DEVICE, + buf->sg, sg_elems, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); if (!desc) { spin_lock_irq(&fh->queue_lock); diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 7e3a490..e2caf88 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -334,7 +334,7 @@ static void sdc_enable_channel(struct mx3fb_info *mx3_fbi) /* This enables the channel */ if (mx3_fbi->cookie < 0) { mx3_fbi->txd = dma_chan->device->device_prep_slave_sg(dma_chan, - &mx3_fbi->sg[0], 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT); + &mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!mx3_fbi->txd) { dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", dma_chan->chan_id); @@ -1104,7 +1104,7 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var, async_tx_ack(mx3_fbi->txd); txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg + - mx3_fbi->cur_ipu_buf, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT); + mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txd) { dev_err(fbi->device, "Error preparing a DMA transaction descriptor.\n"); -- cgit v0.10.2 From 1d0c81e876fe04295ef5df387ba057bf9cfae3d8 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:42:56 +0530 Subject: carma: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves carma drivers to use new enum Signed-off-by: Vinod Koul CC Ira W. Snyder diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index 7ce6065..20ef1ea 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -513,7 +513,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) * transaction, and then put it under external control */ memset(&config, 0, sizeof(config)); - config.direction = DMA_TO_DEVICE; + config.direction = DMA_MEM_TO_DEV; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; config.dst_maxburst = fpga_fifo_size(priv->regs) / 2 / 4; ret = chan->device->device_control(chan, DMA_SLAVE_CONFIG, -- cgit v0.10.2 From 05f5799cbe5c9e2c03f604b3de5783cf4d726227 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:45:11 +0530 Subject: mmc-host: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves mmc drivers to use new enum Cc: Nicolas Ferre Cc: Chris Ball Cc: Guennadi Liakhovetski Acked-by: Nicolas Ferre Acked-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index fa8cae1..ac87803 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -653,6 +653,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) struct scatterlist *sg; unsigned int i; enum dma_data_direction direction; + enum dma_transfer_direction slave_dirn; unsigned int sglen; /* @@ -681,16 +682,19 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data) if (atmci_is_mci2()) mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN); - if (data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) { direction = DMA_FROM_DEVICE; - else + slave_dirn = DMA_DEV_TO_MEM; + } else { direction = DMA_TO_DEVICE; + slave_dirn = DMA_MEM_TO_DEV; + } sglen = dma_map_sg(chan->device->dev, data->sg, data->sg_len, direction); desc = chan->device->device_prep_slave_sg(chan, - data->sg, sglen, direction, + data->sg, sglen, slave_dirn, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) goto unmap_exit; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 56e9a416..3abd51a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -372,6 +372,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, struct dma_chan *chan; struct dma_device *device; struct dma_async_tx_descriptor *desc; + enum dma_data_direction buffer_dirn; int nr_sg; /* Check if next job is already prepared */ @@ -385,10 +386,12 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, } if (data->flags & MMC_DATA_READ) { - conf.direction = DMA_FROM_DEVICE; + conf.direction = DMA_DEV_TO_MEM; + buffer_dirn = DMA_FROM_DEVICE; chan = host->dma_rx_channel; } else { - conf.direction = DMA_TO_DEVICE; + conf.direction = DMA_MEM_TO_DEV; + buffer_dirn = DMA_TO_DEVICE; chan = host->dma_tx_channel; } @@ -401,7 +404,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, return -EINVAL; device = chan->device; - nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len, conf.direction); + nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len, buffer_dirn); if (nr_sg == 0) return -EINVAL; @@ -424,7 +427,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, unmap_exit: if (!next) dmaengine_terminate_all(chan); - dma_unmap_sg(device->dev, data->sg, data->sg_len, conf.direction); + dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn); return -ENOMEM; } diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 14aa213..8a81663 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -217,6 +217,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) unsigned int blksz = data->blksz; unsigned int datasize = nob * blksz; struct scatterlist *sg; + enum dma_transfer_direction slave_dirn; int i, nents; if (data->flags & MMC_DATA_STREAM) @@ -239,10 +240,13 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) } } - if (data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) { host->dma_dir = DMA_FROM_DEVICE; - else + slave_dirn = DMA_DEV_TO_MEM; + } else { host->dma_dir = DMA_TO_DEVICE; + slave_dirn = DMA_MEM_TO_DEV; + } nents = dma_map_sg(host->dma->device->dev, data->sg, data->sg_len, host->dma_dir); @@ -250,7 +254,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) return -EINVAL; host->desc = host->dma->device->device_prep_slave_sg(host->dma, - data->sg, data->sg_len, host->dma_dir, + data->sg, data->sg_len, slave_dirn, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!host->desc) { diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index d513d47..996b9a9 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -153,6 +153,7 @@ struct mxs_mmc_host { struct dma_chan *dmach; struct mxs_dma_data dma_data; unsigned int dma_dir; + enum dma_transfer_direction slave_dirn; u32 ssp_pio_words[SSP_PIO_NUM]; unsigned int version; @@ -323,7 +324,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( } desc = host->dmach->device->device_prep_slave_sg(host->dmach, - sgl, sg_len, host->dma_dir, append); + sgl, sg_len, host->slave_dirn, append); if (desc) { desc->callback = mxs_mmc_dma_irq_callback; desc->callback_param = host; @@ -432,6 +433,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) int i; unsigned short dma_data_dir, timeout; + enum dma_transfer_direction slave_dirn; unsigned int data_size = 0, log2_blksz; unsigned int blocks = data->blocks; @@ -447,9 +449,11 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) if (data->flags & MMC_DATA_WRITE) { dma_data_dir = DMA_TO_DEVICE; + slave_dirn = DMA_MEM_TO_DEV; read = 0; } else { dma_data_dir = DMA_FROM_DEVICE; + slave_dirn = DMA_DEV_TO_MEM; read = BM_SSP_CTRL0_READ; } @@ -517,6 +521,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) WARN_ON(host->data != NULL); host->data = data; host->dma_dir = dma_data_dir; + host->slave_dirn = slave_dirn; desc = mxs_mmc_prep_dma(host, 1); if (!desc) goto out; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 557886b..0ae8d62 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -230,7 +230,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) if (ret > 0) { host->dma_active = true; desc = chan->device->device_prep_slave_sg(chan, sg, ret, - DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } if (desc) { @@ -278,7 +278,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) if (ret > 0) { host->dma_active = true; desc = chan->device->device_prep_slave_sg(chan, sg, ret, - DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } if (desc) { diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 86f259c..7a6e6cc 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -77,7 +77,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE); if (ret > 0) desc = chan->device->device_prep_slave_sg(chan, sg, ret, - DMA_FROM_DEVICE, DMA_CTRL_ACK); + DMA_DEV_TO_MEM, DMA_CTRL_ACK); if (desc) { cookie = dmaengine_submit(desc); @@ -158,7 +158,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE); if (ret > 0) desc = chan->device->device_prep_slave_sg(chan, sg, ret, - DMA_TO_DEVICE, DMA_CTRL_ACK); + DMA_MEM_TO_DEV, DMA_CTRL_ACK); if (desc) { cookie = dmaengine_submit(desc); -- cgit v0.10.2 From a485df4b4404379786c4bdd258bc528b2617449d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:47:38 +0530 Subject: spi, serial: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves spi, serial drivers to use new enum Signed-off-by: Vinod Koul Cc: Grant Likely Cc: Alan Cox Acked-by: Mika Westerberg diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 130e555..0a1fc8a 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -131,7 +131,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) rxchan = dws->rxchan; /* 2. Prepare the TX dma transfer */ - txconf.direction = DMA_TO_DEVICE; + txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = LNW_DMA_MSIZE_16; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -147,13 +147,13 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) txdesc = txchan->device->device_prep_slave_sg(txchan, &dws->tx_sgl, 1, - DMA_TO_DEVICE, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP); txdesc->callback = dw_spi_dma_done; txdesc->callback_param = dws; /* 3. Prepare the RX dma transfer */ - rxconf.direction = DMA_FROM_DEVICE; + rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = LNW_DMA_MSIZE_16; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -169,7 +169,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) rxdesc = rxchan->device->device_prep_slave_sg(rxchan, &dws->rx_sgl, 1, - DMA_FROM_DEVICE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP); rxdesc->callback = dw_spi_dma_done; rxdesc->callback_param = dws; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 1cf6454..0afd87d 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -550,6 +550,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) struct dma_async_tx_descriptor *txd; enum dma_slave_buswidth buswidth; struct dma_slave_config conf; + enum dma_transfer_direction slave_dirn; struct scatterlist *sg; struct sg_table *sgt; struct dma_chan *chan; @@ -572,6 +573,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) conf.src_addr = espi->sspdr_phys; conf.src_addr_width = buswidth; + slave_dirn = DMA_DEV_TO_MEM; } else { chan = espi->dma_tx; buf = t->tx_buf; @@ -579,6 +581,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) conf.dst_addr = espi->sspdr_phys; conf.dst_addr_width = buswidth; + slave_dirn = DMA_MEM_TO_DEV; } ret = dmaengine_slave_config(chan, &conf); @@ -630,7 +633,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir) return ERR_PTR(-ENOMEM); txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents, - dir, DMA_CTRL_ACK); + slave_dirn, DMA_CTRL_ACK); if (!txd) { dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir); return ERR_PTR(-ENOMEM); @@ -978,7 +981,7 @@ static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi) dma_cap_set(DMA_SLAVE, mask); espi->dma_rx_data.port = EP93XX_DMA_SSP; - espi->dma_rx_data.direction = DMA_FROM_DEVICE; + espi->dma_rx_data.direction = DMA_DEV_TO_MEM; espi->dma_rx_data.name = "ep93xx-spi-rx"; espi->dma_rx = dma_request_channel(mask, ep93xx_spi_dma_filter, @@ -989,7 +992,7 @@ static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi) } espi->dma_tx_data.port = EP93XX_DMA_SSP; - espi->dma_tx_data.direction = DMA_TO_DEVICE; + espi->dma_tx_data.direction = DMA_MEM_TO_DEV; espi->dma_tx_data.name = "ep93xx-spi-tx"; espi->dma_tx = dma_request_channel(mask, ep93xx_spi_dma_filter, diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 730b4a3..ddcddf4 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -910,11 +910,11 @@ static int configure_dma(struct pl022 *pl022) { struct dma_slave_config rx_conf = { .src_addr = SSP_DR(pl022->phybase), - .direction = DMA_FROM_DEVICE, + .direction = DMA_DEV_TO_MEM, }; struct dma_slave_config tx_conf = { .dst_addr = SSP_DR(pl022->phybase), - .direction = DMA_TO_DEVICE, + .direction = DMA_MEM_TO_DEV, }; unsigned int pages; int ret; @@ -1051,7 +1051,7 @@ static int configure_dma(struct pl022 *pl022) rxdesc = rxchan->device->device_prep_slave_sg(rxchan, pl022->sgt_rx.sgl, rx_sglen, - DMA_FROM_DEVICE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) goto err_rxdesc; @@ -1059,7 +1059,7 @@ static int configure_dma(struct pl022 *pl022) txdesc = txchan->device->device_prep_slave_sg(txchan, pl022->sgt_tx.sgl, tx_sglen, - DMA_TO_DEVICE, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) goto err_txdesc; diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 1d23f38..7eba26e 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1051,7 +1051,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) } sg = dma->sg_rx_p; desc_rx = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg, - num, DMA_FROM_DEVICE, + num, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_rx) { dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n", @@ -1086,7 +1086,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) } sg = dma->sg_tx_p; desc_tx = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx, - sg, num, DMA_TO_DEVICE, + sg, num, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n", diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index f5f6831..460ff7d 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -268,7 +268,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) struct dma_slave_config tx_conf = { .dst_addr = uap->port.mapbase + UART01x_DR, .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .direction = DMA_TO_DEVICE, + .direction = DMA_MEM_TO_DEV, .dst_maxburst = uap->fifosize >> 1, }; struct dma_chan *chan; @@ -301,7 +301,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) struct dma_slave_config rx_conf = { .src_addr = uap->port.mapbase + UART01x_DR, .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .direction = DMA_FROM_DEVICE, + .direction = DMA_DEV_TO_MEM, .src_maxburst = uap->fifosize >> 1, }; @@ -480,7 +480,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) return -EBUSY; } - desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_TO_DEVICE, + desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE); @@ -676,7 +676,7 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap) &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; dma_dev = rxchan->device; desc = rxchan->device->device_prep_slave_sg(rxchan, &sgbuf->sg, 1, - DMA_FROM_DEVICE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); /* * If the DMA engine is busy and cannot prepare a diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index b46218d..73201a1 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -747,7 +747,7 @@ static int dma_handle_rx(struct eg20t_port *priv) sg_dma_address(sg) = priv->rx_buf_dma; desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, - sg, 1, DMA_FROM_DEVICE, + sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) @@ -906,7 +906,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) } desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, - priv->sg_tx_p, nent, DMA_TO_DEVICE, + priv->sg_tx_p, nent, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(priv->port.dev, "%s:device_prep_slave_sg Failed\n", diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index dbd32a1..3683a69 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1207,7 +1207,7 @@ static void sci_submit_rx(struct sci_port *s) struct dma_async_tx_descriptor *desc; desc = chan->device->device_prep_slave_sg(chan, - sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); + sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (desc) { s->desc_rx[i] = desc; @@ -1322,7 +1322,7 @@ static void work_fn_tx(struct work_struct *work) BUG_ON(!sg_dma_len(sg)); desc = chan->device->device_prep_slave_sg(chan, - sg, s->sg_len_tx, DMA_TO_DEVICE, + sg, s->sg_len_tx, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { /* switch to PIO */ -- cgit v0.10.2 From 35e16581ed6bff55009a0bac34c755140407b03f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:49:30 +0530 Subject: sound-soc: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves asoc drivers to use new enum Signed-off-by: Vinod Koul Cc: Liam Girdwood Cc: Mark Brown Acked-by: Mark Brown diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c index 6fd9391..4fa1dbd 100644 --- a/sound/atmel/abdac.c +++ b/sound/atmel/abdac.c @@ -133,7 +133,7 @@ static int atmel_abdac_prepare_dma(struct atmel_abdac *dac, period_len = frames_to_bytes(runtime, runtime->period_size); cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len, - period_len, DMA_TO_DEVICE); + period_len, DMA_MEM_TO_DEV); if (IS_ERR(cdesc)) { dev_dbg(&dac->pdev->dev, "could not prepare cyclic DMA\n"); return PTR_ERR(cdesc); diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 6e5adde..cd9428b 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -102,7 +102,7 @@ static void atmel_ac97c_dma_capture_period_done(void *arg) static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip, struct snd_pcm_substream *substream, - enum dma_data_direction direction) + enum dma_transfer_direction direction) { struct dma_chan *chan; struct dw_cyclic_desc *cdesc; @@ -118,7 +118,7 @@ static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip, return -EINVAL; } - if (direction == DMA_TO_DEVICE) + if (direction == DMA_MEM_TO_DEV) chan = chip->dma.tx_chan; else chan = chip->dma.rx_chan; @@ -133,7 +133,7 @@ static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip, return PTR_ERR(cdesc); } - if (direction == DMA_TO_DEVICE) { + if (direction == DMA_MEM_TO_DEV) { cdesc->period_callback = atmel_ac97c_dma_playback_period_done; set_bit(DMA_TX_READY, &chip->flags); } else { @@ -393,7 +393,7 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) if (cpu_is_at32ap7000()) { if (!test_bit(DMA_TX_READY, &chip->flags)) retval = atmel_ac97c_prepare_dma(chip, substream, - DMA_TO_DEVICE); + DMA_MEM_TO_DEV); } else { /* Initialize and start the PDC */ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR); @@ -484,7 +484,7 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) if (cpu_is_at32ap7000()) { if (!test_bit(DMA_RX_READY, &chip->flags)) retval = atmel_ac97c_prepare_dma(chip, substream, - DMA_FROM_DEVICE); + DMA_DEV_TO_MEM); } else { /* Initialize and start the PDC */ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR); diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 8dfd3ad..faf7d4f 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -113,9 +113,9 @@ static int ep93xx_pcm_open(struct snd_pcm_substream *substream) rtd->dma_data.name = dma_params->name; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rtd->dma_data.direction = DMA_TO_DEVICE; + rtd->dma_data.direction = DMA_MEM_TO_DEV; else - rtd->dma_data.direction = DMA_FROM_DEVICE; + rtd->dma_data.direction = DMA_DEV_TO_MEM; rtd->dma_chan = dma_request_channel(mask, ep93xx_pcm_dma_filter, &rtd->dma_data); diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 43fdc24f..7d28de9 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -107,12 +107,12 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.direction = DMA_TO_DEVICE; + slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = dma_params->dma_addr; slave_config.dst_addr_width = buswidth; slave_config.dst_maxburst = dma_params->burstsize; } else { - slave_config.direction = DMA_FROM_DEVICE; + slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = dma_params->dma_addr; slave_config.src_addr_width = buswidth; slave_config.src_maxburst = dma_params->burstsize; @@ -159,7 +159,7 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, iprtd->period_bytes * iprtd->periods, iprtd->period_bytes, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); + DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); if (!iprtd->desc) { dev_err(&chan->dev->device, "cannot prepare slave dma\n"); return -EINVAL; diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 851346f..87a3957 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -85,7 +85,7 @@ static void dma_enqueue(struct snd_pcm_substream *substream) dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE); dma_info.direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK - ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); dma_info.fp = audio_buffdone; dma_info.fp_param = substream; dma_info.period = prtd->dma_period; @@ -170,7 +170,7 @@ static int dma_hw_params(struct snd_pcm_substream *substream, dma_info.client = prtd->params->client; dma_info.direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK - ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); dma_info.width = prtd->params->dma_size; dma_info.fifo = prtd->params->dma_addr; prtd->params->ch = prtd->params->ops->request( diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index f8f6816..0193e59 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -131,7 +131,7 @@ static int siu_pcm_wr_set(struct siu_port *port_info, sg_dma_address(&sg) = buff; desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan, - &sg, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(dev, "Failed to allocate a dma descriptor\n"); return -ENOMEM; @@ -181,7 +181,7 @@ static int siu_pcm_rd_set(struct siu_port *port_info, sg_dma_address(&sg) = buff; desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan, - &sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + &sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(dev, "Failed to allocate dma descriptor\n"); return -ENOMEM; diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 3de99af..602bb68 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -134,7 +134,7 @@ txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr) sg_dma_address(&sg) = buf_dma_addr; desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dmadata->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - DMA_TO_DEVICE : DMA_FROM_DEVICE, + DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_err(&chan->dev->device, "cannot prepare slave dma\n"); -- cgit v0.10.2 From 8af11fd3f013892c918a39acb691bc7469c5ad77 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 10:52:48 +0530 Subject: net-ks8842: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves net/ks8842 drivers to use new enum Signed-off-by: Vinod Koul Cc: David S. Miller Cc: Samuel Ortiz diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c index 4a6ae05..de9f2e2 100644 --- a/drivers/net/ks8842.c +++ b/drivers/net/ks8842.c @@ -459,7 +459,7 @@ static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev) sg_dma_len(&ctl->sg) += 4 - sg_dma_len(&ctl->sg) % 4; ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan, - &ctl->sg, 1, DMA_TO_DEVICE, + &ctl->sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); if (!ctl->adesc) return NETDEV_TX_BUSY; @@ -571,7 +571,7 @@ static int __ks8842_start_new_rx_dma(struct net_device *netdev) sg_dma_len(sg) = DMA_BUFFER_SIZE; ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan, - sg, 1, DMA_FROM_DEVICE, + sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); if (!ctl->adesc) -- cgit v0.10.2 From 0b934ccd707ff33a87f15a35a9916d1d8e85d30e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 4 Nov 2011 15:41:27 -0400 Subject: Xen: Export xen_biovec_phys_mergeable When Xen is enabled, using BIOVEC_PHYS_MERGEABLE in a module causes xen_biovec_phys_mergeable to be referenced, so it needs to be exported. Acked-by: Jens Axboe Signed-off-by: Matthew Wilcox diff --git a/drivers/xen/biomerge.c b/drivers/xen/biomerge.c index ba6eda4..18c1bb6 100644 --- a/drivers/xen/biomerge.c +++ b/drivers/xen/biomerge.c @@ -11,3 +11,4 @@ bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, return __BIOVEC_PHYS_MERGEABLE(vec1, vec2) && ((mfn1 == mfn2) || ((mfn1+1) == mfn2)); } +EXPORT_SYMBOL(xen_biovec_phys_mergeable); -- cgit v0.10.2 From b60503ba432b16fc84442a84e29a7aad2c0c363d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Jan 2011 12:50:14 -0500 Subject: NVMe: New driver This driver is for devices that follow the NVM Express standard Signed-off-by: Matthew Wilcox diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 54078ed..4840334 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -149,6 +149,7 @@ Code Seq#(hex) Include File Comments 'M' 01-03 drivers/scsi/megaraid/megaraid_sas.h 'M' 00-0F drivers/video/fsl-diu-fb.h conflict! 'N' 00-1F drivers/usb/scanner.h +'N' 40-7F drivers/block/nvme.c 'O' 00-06 mtd/ubi-user.h UBI 'P' all linux/soundcard.h conflict! 'P' 60-6F sound/sscape_ioctl.h conflict! diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 6f07ec1..35e56e1 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -315,6 +315,17 @@ config BLK_DEV_NBD If unsure, say N. +config BLK_DEV_NVME + tristate "NVM Express block device" + depends on PCI + ---help--- + The NVM Express driver is for solid state drives directly + connected to the PCI or PCI Express bus. If you know you + don't have one of these, it is safe to answer N. + + To compile this driver as a module, choose M here: the + module will be called nvme. + config BLK_DEV_OSD tristate "OSD object-as-blkdev support" depends on SCSI_OSD_ULD diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 76646e9..349539a 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_XILINX_SYSACE) += xsysace.o obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o obj-$(CONFIG_MG_DISK) += mg_disk.o obj-$(CONFIG_SUNVDC) += sunvdc.o +obj-$(CONFIG_BLK_DEV_NVME) += nvme.o obj-$(CONFIG_BLK_DEV_OSD) += osdblk.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c new file mode 100644 index 0000000..ef66ecc --- /dev/null +++ b/drivers/block/nvme.c @@ -0,0 +1,1043 @@ +/* + * NVM Express device driver + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NVME_Q_DEPTH 1024 +#define SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) +#define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) +#define NVME_MINORS 64 + +static int nvme_major; +module_param(nvme_major, int, 0); + +/* + * Represents an NVM Express device. Each nvme_dev is a PCI function. + */ +struct nvme_dev { + struct list_head node; + struct nvme_queue **queues; + u32 __iomem *dbs; + struct pci_dev *pci_dev; + int instance; + int queue_count; + u32 ctrl_config; + struct msix_entry *entry; + struct nvme_bar __iomem *bar; + struct list_head namespaces; +}; + +/* + * An NVM Express namespace is equivalent to a SCSI LUN + */ +struct nvme_ns { + struct list_head list; + + struct nvme_dev *dev; + struct request_queue *queue; + struct gendisk *disk; + + int ns_id; + int lba_shift; +}; + +/* + * An NVM Express queue. Each device has at least two (one for admin + * commands and one for I/O commands). + */ +struct nvme_queue { + struct device *q_dmadev; + spinlock_t q_lock; + struct nvme_command *sq_cmds; + volatile struct nvme_completion *cqes; + dma_addr_t sq_dma_addr; + dma_addr_t cq_dma_addr; + wait_queue_head_t sq_full; + struct bio_list sq_cong; + u32 __iomem *q_db; + u16 q_depth; + u16 cq_vector; + u16 sq_head; + u16 sq_tail; + u16 cq_head; + u16 cq_cycle; + unsigned long cmdid_data[]; +}; + +/* + * Check we didin't inadvertently grow the command struct + */ +static inline void _nvme_check_size(void) +{ + BUILD_BUG_ON(sizeof(struct nvme_rw_command) != 64); + BUILD_BUG_ON(sizeof(struct nvme_create_cq) != 64); + BUILD_BUG_ON(sizeof(struct nvme_create_sq) != 64); + BUILD_BUG_ON(sizeof(struct nvme_delete_queue) != 64); + BUILD_BUG_ON(sizeof(struct nvme_features) != 64); + BUILD_BUG_ON(sizeof(struct nvme_command) != 64); + BUILD_BUG_ON(sizeof(struct nvme_id_ctrl) != 4096); + BUILD_BUG_ON(sizeof(struct nvme_id_ns) != 4096); + BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64); +} + +/** + * alloc_cmdid - Allocate a Command ID + * @param nvmeq The queue that will be used for this command + * @param ctx A pointer that will be passed to the handler + * @param handler The ID of the handler to call + * + * Allocate a Command ID for a queue. The data passed in will + * be passed to the completion handler. This is implemented by using + * the bottom two bits of the ctx pointer to store the handler ID. + * Passing in a pointer that's not 4-byte aligned will cause a BUG. + * We can change this if it becomes a problem. + */ +static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler) +{ + int depth = nvmeq->q_depth; + unsigned long data = (unsigned long)ctx | handler; + int cmdid; + + BUG_ON((unsigned long)ctx & 3); + + do { + cmdid = find_first_zero_bit(nvmeq->cmdid_data, depth); + if (cmdid >= depth) + return -EBUSY; + } while (test_and_set_bit(cmdid, nvmeq->cmdid_data)); + + nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(depth)] = data; + return cmdid; +} + +static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, + int handler) +{ + int cmdid; + wait_event_killable(nvmeq->sq_full, + (cmdid = alloc_cmdid(nvmeq, ctx, handler)) >= 0); + return (cmdid < 0) ? -EINTR : cmdid; +} + +/* If you need more than four handlers, you'll need to change how + * alloc_cmdid and nvme_process_cq work + */ +enum { + sync_completion_id = 0, + bio_completion_id, +}; + +static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) +{ + unsigned long data; + + data = nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)]; + clear_bit(cmdid, nvmeq->cmdid_data); + wake_up(&nvmeq->sq_full); + return data; +} + +static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) +{ + return ns->dev->queues[1]; +} + +static void put_nvmeq(struct nvme_queue *nvmeq) +{ +} + +/** + * nvme_submit_cmd: Copy a command into a queue and ring the doorbell + * @nvmeq: The queue to use + * @cmd: The command to send + * + * Safe to use from interrupt context + */ +static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) +{ + unsigned long flags; + u16 tail; + /* XXX: Need to check tail isn't going to overrun head */ + spin_lock_irqsave(&nvmeq->q_lock, flags); + tail = nvmeq->sq_tail; + memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd)); + writel(tail, nvmeq->q_db); + if (++tail == nvmeq->q_depth) + tail = 0; + nvmeq->sq_tail = tail; + spin_unlock_irqrestore(&nvmeq->q_lock, flags); + + return 0; +} + +struct nvme_req_info { + struct bio *bio; + int nents; + struct scatterlist sg[0]; +}; + +/* XXX: use a mempool */ +static struct nvme_req_info *alloc_info(unsigned nseg, gfp_t gfp) +{ + return kmalloc(sizeof(struct nvme_req_info) + + sizeof(struct scatterlist) * nseg, gfp); +} + +static void free_info(struct nvme_req_info *info) +{ + kfree(info); +} + +static void bio_completion(struct nvme_queue *nvmeq, void *ctx, + struct nvme_completion *cqe) +{ + struct nvme_req_info *info = ctx; + struct bio *bio = info->bio; + u16 status = le16_to_cpup(&cqe->status) >> 1; + + dma_unmap_sg(nvmeq->q_dmadev, info->sg, info->nents, + bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + free_info(info); + bio_endio(bio, status ? -EIO : 0); +} + +static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, + struct bio *bio, enum dma_data_direction dma_dir, int psegs) +{ + struct bio_vec *bvec; + struct scatterlist *sg = info->sg; + int i, nsegs; + + sg_init_table(sg, psegs); + bio_for_each_segment(bvec, bio, i) { + sg_set_page(sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); + /* XXX: handle non-mergable here */ + nsegs++; + } + info->nents = nsegs; + + return dma_map_sg(dev, info->sg, info->nents, dma_dir); +} + +static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, + struct bio *bio) +{ + struct nvme_rw_command *cmnd; + struct nvme_req_info *info; + enum dma_data_direction dma_dir; + int cmdid; + u16 control; + u32 dsmgmt; + unsigned long flags; + int psegs = bio_phys_segments(ns->queue, bio); + + info = alloc_info(psegs, GFP_NOIO); + if (!info) + goto congestion; + info->bio = bio; + + cmdid = alloc_cmdid(nvmeq, info, bio_completion_id); + if (unlikely(cmdid < 0)) + goto free_info; + + control = 0; + if (bio->bi_rw & REQ_FUA) + control |= NVME_RW_FUA; + if (bio->bi_rw & (REQ_FAILFAST_DEV | REQ_RAHEAD)) + control |= NVME_RW_LR; + + dsmgmt = 0; + if (bio->bi_rw & REQ_RAHEAD) + dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH; + + spin_lock_irqsave(&nvmeq->q_lock, flags); + cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail].rw; + + if (bio_data_dir(bio)) { + cmnd->opcode = nvme_cmd_write; + dma_dir = DMA_TO_DEVICE; + } else { + cmnd->opcode = nvme_cmd_read; + dma_dir = DMA_FROM_DEVICE; + } + + nvme_map_bio(nvmeq->q_dmadev, info, bio, dma_dir, psegs); + + cmnd->flags = 1; + cmnd->command_id = cmdid; + cmnd->nsid = cpu_to_le32(ns->ns_id); + cmnd->prp1 = cpu_to_le64(sg_phys(info->sg)); + /* XXX: Support more than one PRP */ + cmnd->slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); + cmnd->length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); + cmnd->control = cpu_to_le16(control); + cmnd->dsmgmt = cpu_to_le32(dsmgmt); + + writel(nvmeq->sq_tail, nvmeq->q_db); + if (++nvmeq->sq_tail == nvmeq->q_depth) + nvmeq->sq_tail = 0; + + spin_unlock_irqrestore(&nvmeq->q_lock, flags); + + return 0; + + free_info: + free_info(info); + congestion: + return -EBUSY; +} + +/* + * NB: return value of non-zero would mean that we were a stacking driver. + * make_request must always succeed. + */ +static int nvme_make_request(struct request_queue *q, struct bio *bio) +{ + struct nvme_ns *ns = q->queuedata; + struct nvme_queue *nvmeq = get_nvmeq(ns); + + if (nvme_submit_bio_queue(nvmeq, ns, bio)) { + blk_set_queue_congested(q, rw_is_sync(bio->bi_rw)); + bio_list_add(&nvmeq->sq_cong, bio); + } + put_nvmeq(nvmeq); + + return 0; +} + +struct sync_cmd_info { + struct task_struct *task; + u32 result; + int status; +}; + +static void sync_completion(struct nvme_queue *nvmeq, void *ctx, + struct nvme_completion *cqe) +{ + struct sync_cmd_info *cmdinfo = ctx; + cmdinfo->result = le32_to_cpup(&cqe->result); + cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; + wake_up_process(cmdinfo->task); +} + +typedef void (*completion_fn)(struct nvme_queue *, void *, + struct nvme_completion *); + +static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) +{ + u16 head, cycle; + + static const completion_fn completions[4] = { + [sync_completion_id] = sync_completion, + [bio_completion_id] = bio_completion, + }; + + head = nvmeq->cq_head; + cycle = nvmeq->cq_cycle; + + for (;;) { + unsigned long data; + void *ptr; + unsigned char handler; + struct nvme_completion cqe = nvmeq->cqes[head]; + if ((le16_to_cpu(cqe.status) & 1) != cycle) + break; + nvmeq->sq_head = le16_to_cpu(cqe.sq_head); + if (++head == nvmeq->q_depth) { + head = 0; + cycle = !cycle; + } + + data = free_cmdid(nvmeq, cqe.command_id); + handler = data & 3; + ptr = (void *)(data & ~3UL); + completions[handler](nvmeq, ptr, &cqe); + } + + /* If the controller ignores the cq head doorbell and continuously + * writes to the queue, it is theoretically possible to wrap around + * the queue twice and mistakenly return IRQ_NONE. Linux only + * requires that 0.1% of your interrupts are handled, so this isn't + * a big problem. + */ + if (head == nvmeq->cq_head && cycle == nvmeq->cq_cycle) + return IRQ_NONE; + + writel(head, nvmeq->q_db + 1); + nvmeq->cq_head = head; + nvmeq->cq_cycle = cycle; + + return IRQ_HANDLED; +} + +static irqreturn_t nvme_irq(int irq, void *data) +{ + return nvme_process_cq(data); +} + +/* + * Returns 0 on success. If the result is negative, it's a Linux error code; + * if the result is positive, it's an NVM Express status code + */ +static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd, + u32 *result) +{ + int cmdid; + struct sync_cmd_info cmdinfo; + + cmdinfo.task = current; + cmdinfo.status = -EINTR; + + cmdid = alloc_cmdid_killable(q, &cmdinfo, sync_completion_id); + if (cmdid < 0) + return cmdid; + cmd->common.command_id = cmdid; + + set_current_state(TASK_UNINTERRUPTIBLE); + nvme_submit_cmd(q, cmd); + schedule(); + + if (result) + *result = cmdinfo.result; + + return cmdinfo.status; +} + +static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd, + u32 *result) +{ + return nvme_submit_sync_cmd(dev->queues[0], cmd, result); +} + +static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id) +{ + int status; + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.delete_queue.opcode = opcode; + c.delete_queue.qid = cpu_to_le16(id); + + status = nvme_submit_admin_cmd(dev, &c, NULL); + if (status) + return -EIO; + return 0; +} + +static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid, + struct nvme_queue *nvmeq) +{ + int status; + struct nvme_command c; + int flags = NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED; + + memset(&c, 0, sizeof(c)); + c.create_cq.opcode = nvme_admin_create_cq; + c.create_cq.prp1 = cpu_to_le64(nvmeq->cq_dma_addr); + c.create_cq.cqid = cpu_to_le16(qid); + c.create_cq.qsize = cpu_to_le16(nvmeq->q_depth - 1); + c.create_cq.cq_flags = cpu_to_le16(flags); + c.create_cq.irq_vector = cpu_to_le16(nvmeq->cq_vector); + + status = nvme_submit_admin_cmd(dev, &c, NULL); + if (status) + return -EIO; + return 0; +} + +static int adapter_alloc_sq(struct nvme_dev *dev, u16 qid, + struct nvme_queue *nvmeq) +{ + int status; + struct nvme_command c; + int flags = NVME_QUEUE_PHYS_CONTIG | NVME_SQ_PRIO_MEDIUM; + + memset(&c, 0, sizeof(c)); + c.create_sq.opcode = nvme_admin_create_sq; + c.create_sq.prp1 = cpu_to_le64(nvmeq->sq_dma_addr); + c.create_sq.sqid = cpu_to_le16(qid); + c.create_sq.qsize = cpu_to_le16(nvmeq->q_depth - 1); + c.create_sq.sq_flags = cpu_to_le16(flags); + c.create_sq.cqid = cpu_to_le16(qid); + + status = nvme_submit_admin_cmd(dev, &c, NULL); + if (status) + return -EIO; + return 0; +} + +static int adapter_delete_cq(struct nvme_dev *dev, u16 cqid) +{ + return adapter_delete_queue(dev, nvme_admin_delete_cq, cqid); +} + +static int adapter_delete_sq(struct nvme_dev *dev, u16 sqid) +{ + return adapter_delete_queue(dev, nvme_admin_delete_sq, sqid); +} + +static void nvme_free_queue(struct nvme_dev *dev, int qid) +{ + struct nvme_queue *nvmeq = dev->queues[qid]; + + free_irq(dev->entry[nvmeq->cq_vector].vector, nvmeq); + + /* Don't tell the adapter to delete the admin queue */ + if (qid) { + adapter_delete_sq(dev, qid); + adapter_delete_cq(dev, qid); + } + + dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth), + (void *)nvmeq->cqes, nvmeq->cq_dma_addr); + dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth), + nvmeq->sq_cmds, nvmeq->sq_dma_addr); + kfree(nvmeq); +} + +static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, + int depth, int vector) +{ + struct device *dmadev = &dev->pci_dev->dev; + unsigned extra = (depth + BITS_TO_LONGS(depth)) * sizeof(long); + struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL); + if (!nvmeq) + return NULL; + + nvmeq->cqes = dma_alloc_coherent(dmadev, CQ_SIZE(depth), + &nvmeq->cq_dma_addr, GFP_KERNEL); + if (!nvmeq->cqes) + goto free_nvmeq; + memset((void *)nvmeq->cqes, 0, CQ_SIZE(depth)); + + nvmeq->sq_cmds = dma_alloc_coherent(dmadev, SQ_SIZE(depth), + &nvmeq->sq_dma_addr, GFP_KERNEL); + if (!nvmeq->sq_cmds) + goto free_cqdma; + + nvmeq->q_dmadev = dmadev; + spin_lock_init(&nvmeq->q_lock); + nvmeq->cq_head = 0; + nvmeq->cq_cycle = 1; + init_waitqueue_head(&nvmeq->sq_full); + bio_list_init(&nvmeq->sq_cong); + nvmeq->q_db = &dev->dbs[qid * 2]; + nvmeq->q_depth = depth; + nvmeq->cq_vector = vector; + + return nvmeq; + + free_cqdma: + dma_free_coherent(dmadev, CQ_SIZE(nvmeq->q_depth), (void *)nvmeq->cqes, + nvmeq->cq_dma_addr); + free_nvmeq: + kfree(nvmeq); + return NULL; +} + +static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, + int qid, int cq_size, int vector) +{ + int result; + struct nvme_queue *nvmeq = nvme_alloc_queue(dev, qid, cq_size, vector); + + result = adapter_alloc_cq(dev, qid, nvmeq); + if (result < 0) + goto free_nvmeq; + + result = adapter_alloc_sq(dev, qid, nvmeq); + if (result < 0) + goto release_cq; + + result = request_irq(dev->entry[vector].vector, nvme_irq, + IRQF_DISABLED | IRQF_SHARED, "nvme", nvmeq); + if (result < 0) + goto release_sq; + + return nvmeq; + + release_sq: + adapter_delete_sq(dev, qid); + release_cq: + adapter_delete_cq(dev, qid); + free_nvmeq: + dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth), + (void *)nvmeq->cqes, nvmeq->cq_dma_addr); + dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth), + nvmeq->sq_cmds, nvmeq->sq_dma_addr); + kfree(nvmeq); + return NULL; +} + +static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) +{ + int result; + u32 aqa; + struct nvme_queue *nvmeq; + + dev->dbs = ((void __iomem *)dev->bar) + 4096; + + nvmeq = nvme_alloc_queue(dev, 0, 64, 0); + + aqa = nvmeq->q_depth - 1; + aqa |= aqa << 16; + + dev->ctrl_config = NVME_CC_ENABLE | NVME_CC_CSS_NVM; + dev->ctrl_config |= (PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT; + dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE; + + writel(aqa, &dev->bar->aqa); + writeq(nvmeq->sq_dma_addr, &dev->bar->asq); + writeq(nvmeq->cq_dma_addr, &dev->bar->acq); + writel(dev->ctrl_config, &dev->bar->cc); + + while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) { + msleep(100); + if (fatal_signal_pending(current)) + return -EINTR; + } + + result = request_irq(dev->entry[0].vector, nvme_irq, + IRQF_DISABLED | IRQF_SHARED, "nvme admin", nvmeq); + dev->queues[0] = nvmeq; + return result; +} + +static int nvme_identify(struct nvme_ns *ns, void __user *addr, int cns) +{ + struct nvme_dev *dev = ns->dev; + int status; + struct nvme_command c; + void *page; + dma_addr_t dma_addr; + + page = dma_alloc_coherent(&dev->pci_dev->dev, 4096, &dma_addr, + GFP_KERNEL); + + memset(&c, 0, sizeof(c)); + c.identify.opcode = nvme_admin_identify; + c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); + c.identify.prp1 = cpu_to_le64(dma_addr); + c.identify.cns = cpu_to_le32(cns); + + status = nvme_submit_admin_cmd(dev, &c, NULL); + + if (status) + status = -EIO; + else if (copy_to_user(addr, page, 4096)) + status = -EFAULT; + + dma_free_coherent(&dev->pci_dev->dev, 4096, page, dma_addr); + + return status; +} + +static int nvme_get_range_type(struct nvme_ns *ns, void __user *addr) +{ + struct nvme_dev *dev = ns->dev; + int status; + struct nvme_command c; + void *page; + dma_addr_t dma_addr; + + page = dma_alloc_coherent(&dev->pci_dev->dev, 4096, &dma_addr, + GFP_KERNEL); + + memset(&c, 0, sizeof(c)); + c.features.opcode = nvme_admin_get_features; + c.features.nsid = cpu_to_le32(ns->ns_id); + c.features.prp1 = cpu_to_le64(dma_addr); + c.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); + + status = nvme_submit_admin_cmd(dev, &c, NULL); + + /* XXX: Assuming first range for now */ + if (status) + status = -EIO; + else if (copy_to_user(addr, page, 64)) + status = -EFAULT; + + dma_free_coherent(&dev->pci_dev->dev, 4096, page, dma_addr); + + return status; +} + +static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, + unsigned long arg) +{ + struct nvme_ns *ns = bdev->bd_disk->private_data; + + switch (cmd) { + case NVME_IOCTL_IDENTIFY_NS: + return nvme_identify(ns, (void __user *)arg, 0); + case NVME_IOCTL_IDENTIFY_CTRL: + return nvme_identify(ns, (void __user *)arg, 1); + case NVME_IOCTL_GET_RANGE_TYPE: + return nvme_get_range_type(ns, (void __user *)arg); + default: + return -ENOTTY; + } +} + +static const struct block_device_operations nvme_fops = { + .owner = THIS_MODULE, + .ioctl = nvme_ioctl, +}; + +static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int index, + struct nvme_id_ns *id, struct nvme_lba_range_type *rt) +{ + struct nvme_ns *ns; + struct gendisk *disk; + int lbaf; + + if (rt->attributes & NVME_LBART_ATTRIB_HIDE) + return NULL; + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); + if (!ns) + return NULL; + ns->queue = blk_alloc_queue(GFP_KERNEL); + if (!ns->queue) + goto out_free_ns; + ns->queue->queue_flags = QUEUE_FLAG_DEFAULT | QUEUE_FLAG_NOMERGES | + QUEUE_FLAG_NONROT | QUEUE_FLAG_DISCARD; + blk_queue_make_request(ns->queue, nvme_make_request); + ns->dev = dev; + ns->queue->queuedata = ns; + + disk = alloc_disk(NVME_MINORS); + if (!disk) + goto out_free_queue; + ns->ns_id = index; + ns->disk = disk; + lbaf = id->flbas & 0xf; + ns->lba_shift = id->lbaf[lbaf].ds; + + disk->major = nvme_major; + disk->minors = NVME_MINORS; + disk->first_minor = NVME_MINORS * index; + disk->fops = &nvme_fops; + disk->private_data = ns; + disk->queue = ns->queue; + sprintf(disk->disk_name, "nvme%dn%d", dev->instance, index); + set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); + + return ns; + + out_free_queue: + blk_cleanup_queue(ns->queue); + out_free_ns: + kfree(ns); + return NULL; +} + +static void nvme_ns_free(struct nvme_ns *ns) +{ + put_disk(ns->disk); + blk_cleanup_queue(ns->queue); + kfree(ns); +} + +static int set_queue_count(struct nvme_dev *dev, int sq_count, int cq_count) +{ + int status; + u32 result; + struct nvme_command c; + u32 q_count = (sq_count - 1) | ((cq_count - 1) << 16); + + memset(&c, 0, sizeof(c)); + c.features.opcode = nvme_admin_get_features; + c.features.fid = cpu_to_le32(NVME_FEAT_NUM_QUEUES); + c.features.dword11 = cpu_to_le32(q_count); + + status = nvme_submit_admin_cmd(dev, &c, &result); + if (status) + return -EIO; + return min(result & 0xffff, result >> 16) + 1; +} + +/* XXX: Create per-CPU queues */ +static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) +{ + int this_cpu; + + set_queue_count(dev, 1, 1); + + this_cpu = get_cpu(); + dev->queues[1] = nvme_create_queue(dev, 1, NVME_Q_DEPTH, this_cpu); + put_cpu(); + if (!dev->queues[1]) + return -ENOMEM; + dev->queue_count++; + + return 0; +} + +static void nvme_free_queues(struct nvme_dev *dev) +{ + int i; + + for (i = dev->queue_count - 1; i >= 0; i--) + nvme_free_queue(dev, i); +} + +static int __devinit nvme_dev_add(struct nvme_dev *dev) +{ + int res, nn, i; + struct nvme_ns *ns, *next; + void *id; + dma_addr_t dma_addr; + struct nvme_command cid, crt; + + res = nvme_setup_io_queues(dev); + if (res) + return res; + + /* XXX: Switch to a SG list once prp2 works */ + id = dma_alloc_coherent(&dev->pci_dev->dev, 8192, &dma_addr, + GFP_KERNEL); + + memset(&cid, 0, sizeof(cid)); + cid.identify.opcode = nvme_admin_identify; + cid.identify.nsid = 0; + cid.identify.prp1 = cpu_to_le64(dma_addr); + cid.identify.cns = cpu_to_le32(1); + + res = nvme_submit_admin_cmd(dev, &cid, NULL); + if (res) { + res = -EIO; + goto out_free; + } + + nn = le32_to_cpup(&((struct nvme_id_ctrl *)id)->nn); + + cid.identify.cns = 0; + memset(&crt, 0, sizeof(crt)); + crt.features.opcode = nvme_admin_get_features; + crt.features.prp1 = cpu_to_le64(dma_addr + 4096); + crt.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); + + for (i = 0; i < nn; i++) { + cid.identify.nsid = cpu_to_le32(i); + res = nvme_submit_admin_cmd(dev, &cid, NULL); + if (res) + continue; + + if (((struct nvme_id_ns *)id)->ncap == 0) + continue; + + crt.features.nsid = cpu_to_le32(i); + res = nvme_submit_admin_cmd(dev, &crt, NULL); + if (res) + continue; + + ns = nvme_alloc_ns(dev, i, id, id + 4096); + if (ns) + list_add_tail(&ns->list, &dev->namespaces); + } + list_for_each_entry(ns, &dev->namespaces, list) + add_disk(ns->disk); + + dma_free_coherent(&dev->pci_dev->dev, 4096, id, dma_addr); + return 0; + + out_free: + list_for_each_entry_safe(ns, next, &dev->namespaces, list) { + list_del(&ns->list); + nvme_ns_free(ns); + } + + dma_free_coherent(&dev->pci_dev->dev, 4096, id, dma_addr); + return res; +} + +static int nvme_dev_remove(struct nvme_dev *dev) +{ + struct nvme_ns *ns, *next; + + /* TODO: wait all I/O finished or cancel them */ + + list_for_each_entry_safe(ns, next, &dev->namespaces, list) { + list_del(&ns->list); + del_gendisk(ns->disk); + nvme_ns_free(ns); + } + + nvme_free_queues(dev); + + return 0; +} + +/* XXX: Use an ida or something to let remove / add work correctly */ +static void nvme_set_instance(struct nvme_dev *dev) +{ + static int instance; + dev->instance = instance++; +} + +static void nvme_release_instance(struct nvme_dev *dev) +{ +} + +static int __devinit nvme_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int result = -ENOMEM; + struct nvme_dev *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->entry = kcalloc(num_possible_cpus(), sizeof(*dev->entry), + GFP_KERNEL); + if (!dev->entry) + goto free; + dev->queues = kcalloc(2, sizeof(void *), GFP_KERNEL); + if (!dev->queues) + goto free; + + INIT_LIST_HEAD(&dev->namespaces); + dev->pci_dev = pdev; + pci_set_drvdata(pdev, dev); + dma_set_mask(&dev->pci_dev->dev, DMA_BIT_MASK(64)); + nvme_set_instance(dev); + + dev->bar = ioremap(pci_resource_start(pdev, 0), 8192); + if (!dev->bar) { + result = -ENOMEM; + goto disable; + } + + result = nvme_configure_admin_queue(dev); + if (result) + goto unmap; + dev->queue_count++; + + result = nvme_dev_add(dev); + if (result) + goto delete; + return 0; + + delete: + nvme_free_queues(dev); + unmap: + iounmap(dev->bar); + disable: + pci_disable_msix(pdev); + nvme_release_instance(dev); + free: + kfree(dev->queues); + kfree(dev->entry); + kfree(dev); + return result; +} + +static void __devexit nvme_remove(struct pci_dev *pdev) +{ + struct nvme_dev *dev = pci_get_drvdata(pdev); + nvme_dev_remove(dev); + pci_disable_msix(pdev); + iounmap(dev->bar); + nvme_release_instance(dev); + kfree(dev->queues); + kfree(dev->entry); + kfree(dev); +} + +/* These functions are yet to be implemented */ +#define nvme_error_detected NULL +#define nvme_dump_registers NULL +#define nvme_link_reset NULL +#define nvme_slot_reset NULL +#define nvme_error_resume NULL +#define nvme_suspend NULL +#define nvme_resume NULL + +static struct pci_error_handlers nvme_err_handler = { + .error_detected = nvme_error_detected, + .mmio_enabled = nvme_dump_registers, + .link_reset = nvme_link_reset, + .slot_reset = nvme_slot_reset, + .resume = nvme_error_resume, +}; + +/* Move to pci_ids.h later */ +#define PCI_CLASS_STORAGE_EXPRESS 0x010802 + +static DEFINE_PCI_DEVICE_TABLE(nvme_id_table) = { + { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, nvme_id_table); + +static struct pci_driver nvme_driver = { + .name = "nvme", + .id_table = nvme_id_table, + .probe = nvme_probe, + .remove = __devexit_p(nvme_remove), + .suspend = nvme_suspend, + .resume = nvme_resume, + .err_handler = &nvme_err_handler, +}; + +static int __init nvme_init(void) +{ + int result; + + nvme_major = register_blkdev(nvme_major, "nvme"); + if (nvme_major <= 0) + return -EBUSY; + + result = pci_register_driver(&nvme_driver); + if (!result) + return 0; + + unregister_blkdev(nvme_major, "nvme"); + return result; +} + +static void __exit nvme_exit(void) +{ + pci_unregister_driver(&nvme_driver); + unregister_blkdev(nvme_major, "nvme"); +} + +MODULE_AUTHOR("Matthew Wilcox "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); +module_init(nvme_init); +module_exit(nvme_exit); diff --git a/include/linux/nvme.h b/include/linux/nvme.h new file mode 100644 index 0000000..9ba5358 --- /dev/null +++ b/include/linux/nvme.h @@ -0,0 +1,343 @@ +/* + * Definitions for the NVM Express interface + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _LINUX_NVME_H +#define _LINUX_NVME_H + +#include + +struct nvme_bar { + __u64 cap; /* Controller Capabilities */ + __u32 vs; /* Version */ + __u32 ims; /* Interrupt Mask Set */ + __u32 imc; /* Interrupt Mask Clear */ + __u32 cc; /* Controller Configuration */ + __u32 csts; /* Controller Status */ + __u32 aqa; /* Admin Queue Attributes */ + __u64 asq; /* Admin SQ Base Address */ + __u64 acq; /* Admin CQ Base Address */ +}; + +enum { + NVME_CC_ENABLE = 1 << 0, + NVME_CC_CSS_NVM = 0 << 4, + NVME_CC_MPS_SHIFT = 7, + NVME_CC_ARB_RR = 0 << 11, + NVME_CC_ARB_WRRU = 1 << 11, + NVME_CC_ARB_VS = 3 << 11, + NVME_CC_SHN_NONE = 0 << 13, + NVME_CC_SHN_NORMAL = 1 << 13, + NVME_CC_SHN_ABRUPT = 2 << 13, + NVME_CSTS_RDY = 1 << 0, + NVME_CSTS_CFS = 1 << 1, + NVME_CSTS_SHST_NORMAL = 0 << 2, + NVME_CSTS_SHST_OCCUR = 1 << 2, + NVME_CSTS_SHST_CMPLT = 2 << 2, +}; + +#define NVME_VS(major, minor) (major << 16 | minor) + +struct nvme_id_ctrl { + __le16 vid; + __le16 ssvid; + char sn[20]; + char mn[40]; + char fr[8]; + __le32 nn; + __u8 rab; + __u8 rsvd77[178]; + __le16 oacs; + __u8 acl; + __u8 aerl; + __u8 frmw; + __u8 lpa; + __u8 elpe; + __u8 npss; + __u8 rsvd264[248]; + __le64 psd[32]; + __le16 oncs; + __le16 fuses; + __u8 fna; + __u8 vwc; + __le16 awun; + __le16 awupf; + __u8 rsvd778[246]; + __u8 cmdset[2048]; + __u8 vs[1024]; +}; + +struct nvme_lbaf { + __le16 ms; + __u8 ds; + __u8 rp; +}; + +struct nvme_id_ns { + __le64 nsze; + __le64 ncap; + __le64 nuse; + __u8 nsfeat; + __u8 nlbaf; + __u8 flbas; + __u8 mc; + __u8 dpc; + __u8 dps; + __u8 rsvd30[98]; + struct nvme_lbaf lbaf[16]; + __u8 rsvd192[192]; + __u8 vs[3712]; +}; + +enum { + NVME_NS_FEAT_THIN = 1 << 0, + NVME_LBAF_RP_BEST = 0, + NVME_LBAF_RP_BETTER = 1, + NVME_LBAF_RP_GOOD = 2, + NVME_LBAF_RP_DEGRADED = 3, +}; + +struct nvme_lba_range_type { + __u8 type; + __u8 attributes; + __u8 rsvd2[14]; + __u64 slba; + __u64 nlb; + __u8 guid[16]; + __u8 rsvd48[16]; +}; + +enum { + NVME_LBART_TYPE_FS = 0x01, + NVME_LBART_TYPE_RAID = 0x02, + NVME_LBART_TYPE_CACHE = 0x03, + NVME_LBART_TYPE_SWAP = 0x04, + + NVME_LBART_ATTRIB_TEMP = 1 << 0, + NVME_LBART_ATTRIB_HIDE = 1 << 1, +}; + +/* I/O commands */ + +enum nvme_opcode { + nvme_cmd_flush = 0x00, + nvme_cmd_write = 0x01, + nvme_cmd_read = 0x02, + nvme_cmd_write_uncor = 0x04, + nvme_cmd_compare = 0x05, + nvme_cmd_dsm = 0x09, +}; + +struct nvme_rw_command { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2; + __le64 metadata; + __le64 prp1; + __le64 prp2; + __le64 slba; + __le16 length; + __le16 control; + __le32 dsmgmt; + __le32 reftag; + __le16 apptag; + __le16 appmask; +}; + +enum { + NVME_RW_LR = 1 << 15, + NVME_RW_FUA = 1 << 14, + NVME_RW_DSM_FREQ_UNSPEC = 0, + NVME_RW_DSM_FREQ_TYPICAL = 1, + NVME_RW_DSM_FREQ_RARE = 2, + NVME_RW_DSM_FREQ_READS = 3, + NVME_RW_DSM_FREQ_WRITES = 4, + NVME_RW_DSM_FREQ_RW = 5, + NVME_RW_DSM_FREQ_ONCE = 6, + NVME_RW_DSM_FREQ_PREFETCH = 7, + NVME_RW_DSM_FREQ_TEMP = 8, + NVME_RW_DSM_LATENCY_NONE = 0 << 4, + NVME_RW_DSM_LATENCY_IDLE = 1 << 4, + NVME_RW_DSM_LATENCY_NORM = 2 << 4, + NVME_RW_DSM_LATENCY_LOW = 3 << 4, + NVME_RW_DSM_SEQ_REQ = 1 << 6, + NVME_RW_DSM_COMPRESSED = 1 << 7, +}; + +/* Admin commands */ + +enum nvme_admin_opcode { + nvme_admin_delete_sq = 0x00, + nvme_admin_create_sq = 0x01, + nvme_admin_get_features = 0x02, + nvme_admin_delete_cq = 0x04, + nvme_admin_create_cq = 0x05, + nvme_admin_identify = 0x06, + nvme_admin_abort_cmd = 0x08, + nvme_admin_set_features = 0x09, + nvme_admin_get_log_page = 0x0a, + nvme_admin_async_event = 0x0c, + nvme_admin_download_fw = 0x0d, + nvme_admin_security_recv = 0x0e, + nvme_admin_format_nvm = 0x10, + nvme_admin_security_send = 0x11, + nvme_admin_activate_fw = 0x14, +}; + +enum { + NVME_QUEUE_PHYS_CONTIG = (1 << 0), + NVME_CQ_IRQ_ENABLED = (1 << 1), + NVME_SQ_PRIO_URGENT = (0 << 1), + NVME_SQ_PRIO_HIGH = (1 << 1), + NVME_SQ_PRIO_MEDIUM = (2 << 1), + NVME_SQ_PRIO_LOW = (3 << 1), + NVME_FEAT_ARBITRATION = 0x01, + NVME_FEAT_POWER_MGMT = 0x02, + NVME_FEAT_LBA_RANGE = 0x03, + NVME_FEAT_TEMP_THRESH = 0x04, + NVME_FEAT_ERR_RECOVERY = 0x05, + NVME_FEAT_VOLATILE_WC = 0x06, + NVME_FEAT_NUM_QUEUES = 0x07, + NVME_FEAT_IRQ_COALESCE = 0x08, + NVME_FEAT_IRQ_CONFIG = 0x09, + NVME_FEAT_WRITE_ATOMIC = 0x0a, + NVME_FEAT_ASYNC_EVENT = 0x0b, + NVME_FEAT_SW_PROGRESS = 0x0c, +}; + +struct nvme_identify { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2[2]; + __le64 prp1; + __le64 prp2; + __le32 cns; + __u32 rsvd11[5]; +}; + +struct nvme_features { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2[2]; + __le64 prp1; + __le64 prp2; + __le32 fid; + __le32 dword11; + __u32 rsvd12[4]; +}; + +struct nvme_create_cq { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 rsvd1[5]; + __le64 prp1; + __u64 rsvd8; + __le16 cqid; + __le16 qsize; + __le16 cq_flags; + __le16 irq_vector; + __u32 rsvd12[4]; +}; + +struct nvme_create_sq { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 rsvd1[5]; + __le64 prp1; + __u64 rsvd8; + __le16 sqid; + __le16 qsize; + __le16 sq_flags; + __le16 cqid; + __le32 rsvd12[4]; +}; + +struct nvme_delete_queue { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[9]; + __le16 qid; + __le16 rsvd10; + __le32 rsvd11[5]; +}; + +struct nvme_common_command { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u32 rsvd2[14]; +}; + +struct nvme_command { + union { + struct nvme_common_command common; + struct nvme_rw_command rw; + struct nvme_identify identify; + struct nvme_features features; + struct nvme_create_cq create_cq; + struct nvme_create_sq create_sq; + struct nvme_delete_queue delete_queue; + }; +}; + +/* XXX: Sync with spec */ +enum { + NVME_SC_SUCCESS = 0x0, + NVME_SC_INVALID_OPCODE = 0x1, + NVME_SC_INVALID_FIELD = 0x2, + NVME_SC_CMDID_CONFLICT = 0x3, + NVME_SC_DATA_XFER_ERROR = 0x4, + NVME_SC_POWER_LOSS = 0x5, + NVME_SC_INTERNAL = 0x6, + NVME_SC_ABORT_REQ = 0x7, + NVME_SC_ABORT_QUEUE = 0x8, + NVME_SC_FUSED_FAIL = 0x9, + NVME_SC_FUSED_MISSING = 0xa, + NVME_SC_LBA_RANGE = 0x80, + NVME_SC_CAP_EXCEEDED = 0x81, + NVME_SC_NS_NOT_READY = 0x82, + NVME_SC_CQ_INVALID = 0x100, + NVME_SC_QID_INVALID = 0x101, + NVME_SC_QUEUE_SIZE = 0x102, + NVME_SC_WRITE_FAULT = 0x280, + NVME_SC_READ_ERROR = 0x281, +}; + +struct nvme_completion { + __le32 result; /* Used by admin commands to return data */ + __le32 rsvd; + __le16 sq_head; /* how much of this queue may be reclaimed */ + __le16 sq_id; /* submission queue that generated this entry */ + __u16 command_id; /* of the command which completed */ + __le16 status; /* did the command fail, and if so, why? */ +}; + +#define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) +#define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) +#define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) + +#endif /* _LINUX_NVME_H */ -- cgit v0.10.2 From 3001082cac4bf6ffd09f72b39e6292ad6394ef17 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Jan 2011 09:10:15 -0500 Subject: NVMe: Factor out queue_request_irq() Two callers with an almost identical long string of arguments, and introducing a third soon. Time to factor out the commonalities. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index ef66ecc..b10e064 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -568,6 +568,13 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, return NULL; } +static int queue_request_irq(struct nvme_dev *dev, struct nvme_queue *nvmeq, + const char *name) +{ + return request_irq(dev->entry[nvmeq->cq_vector].vector, nvme_irq, + IRQF_DISABLED | IRQF_SHARED, name, nvmeq); +} + static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, int qid, int cq_size, int vector) { @@ -582,8 +589,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, if (result < 0) goto release_cq; - result = request_irq(dev->entry[vector].vector, nvme_irq, - IRQF_DISABLED | IRQF_SHARED, "nvme", nvmeq); + result = queue_request_irq(dev, nvmeq, "nvme"); if (result < 0) goto release_sq; @@ -630,8 +636,7 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) return -EINTR; } - result = request_irq(dev->entry[0].vector, nvme_irq, - IRQF_DISABLED | IRQF_SHARED, "nvme admin", nvmeq); + result = queue_request_irq(dev, nvmeq, "nvme admin"); dev->queues[0] = nvmeq; return result; } -- cgit v0.10.2 From b3b06812e199f248561ce7824a4a8a9cd573c05a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Jan 2011 09:14:34 -0500 Subject: NVMe: Reduce set_queue_count arguments by one sq_count and cq_count are always the same, so just call it 'count'. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b10e064..7efd7e9 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -777,12 +777,12 @@ static void nvme_ns_free(struct nvme_ns *ns) kfree(ns); } -static int set_queue_count(struct nvme_dev *dev, int sq_count, int cq_count) +static int set_queue_count(struct nvme_dev *dev, int count) { int status; u32 result; struct nvme_command c; - u32 q_count = (sq_count - 1) | ((cq_count - 1) << 16); + u32 q_count = (count - 1) | ((count - 1) << 16); memset(&c, 0, sizeof(c)); c.features.opcode = nvme_admin_get_features; @@ -800,7 +800,7 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) { int this_cpu; - set_queue_count(dev, 1, 1); + set_queue_count(dev, 1); this_cpu = get_cpu(); dev->queues[1] = nvme_create_queue(dev, 1, NVME_Q_DEPTH, this_cpu); -- cgit v0.10.2 From 1b23484bd012c078de2ea939249e2fb2e85a0a6e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Jan 2011 13:01:49 -0500 Subject: NVMe: Implement per-CPU queues Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 7efd7e9..b6a213c 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -172,11 +172,17 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) { - return ns->dev->queues[1]; + int qid, cpu = get_cpu(); + if (cpu < ns->dev->queue_count) + qid = cpu + 1; + else + qid = (cpu % rounddown_pow_of_two(ns->dev->queue_count)) + 1; + return ns->dev->queues[qid]; } static void put_nvmeq(struct nvme_queue *nvmeq) { + put_cpu(); } /** @@ -795,19 +801,51 @@ static int set_queue_count(struct nvme_dev *dev, int count) return min(result & 0xffff, result >> 16) + 1; } -/* XXX: Create per-CPU queues */ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) { - int this_cpu; + int result, cpu, i, nr_queues; - set_queue_count(dev, 1); + nr_queues = num_online_cpus(); + result = set_queue_count(dev, nr_queues); + if (result < 0) + return result; + if (result < nr_queues) + nr_queues = result; - this_cpu = get_cpu(); - dev->queues[1] = nvme_create_queue(dev, 1, NVME_Q_DEPTH, this_cpu); - put_cpu(); - if (!dev->queues[1]) - return -ENOMEM; - dev->queue_count++; + /* Deregister the admin queue's interrupt */ + free_irq(dev->entry[0].vector, dev->queues[0]); + + for (i = 0; i < nr_queues; i++) + dev->entry[i].entry = i; + for (;;) { + result = pci_enable_msix(dev->pci_dev, dev->entry, nr_queues); + if (result == 0) { + break; + } else if (result > 0) { + nr_queues = result; + continue; + } else { + nr_queues = 1; + break; + } + } + + result = queue_request_irq(dev, dev->queues[0], "nvme admin"); + /* XXX: handle failure here */ + + cpu = cpumask_first(cpu_online_mask); + for (i = 0; i < nr_queues; i++) { + irq_set_affinity_hint(dev->entry[i].vector, get_cpu_mask(cpu)); + cpu = cpumask_next(cpu, cpu_online_mask); + } + + for (i = 0; i < nr_queues; i++) { + dev->queues[i + 1] = nvme_create_queue(dev, i + 1, + NVME_Q_DEPTH, i); + if (!dev->queues[i + 1]) + return -ENOMEM; + dev->queue_count++; + } return 0; } @@ -931,7 +969,8 @@ static int __devinit nvme_probe(struct pci_dev *pdev, GFP_KERNEL); if (!dev->entry) goto free; - dev->queues = kcalloc(2, sizeof(void *), GFP_KERNEL); + dev->queues = kcalloc(num_possible_cpus() + 1, sizeof(void *), + GFP_KERNEL); if (!dev->queues) goto free; -- cgit v0.10.2 From 821234603b265f59d7eebce16d9e8beca2a5752d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Jan 2011 13:24:06 -0500 Subject: NVMe: Rename 'cycle' to 'phase' It's called the phase bit in the current draft Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b6a213c..3d917a8 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -93,7 +93,7 @@ struct nvme_queue { u16 sq_head; u16 sq_tail; u16 cq_head; - u16 cq_cycle; + u16 cq_phase; unsigned long cmdid_data[]; }; @@ -364,7 +364,7 @@ typedef void (*completion_fn)(struct nvme_queue *, void *, static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) { - u16 head, cycle; + u16 head, phase; static const completion_fn completions[4] = { [sync_completion_id] = sync_completion, @@ -372,19 +372,19 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) }; head = nvmeq->cq_head; - cycle = nvmeq->cq_cycle; + phase = nvmeq->cq_phase; for (;;) { unsigned long data; void *ptr; unsigned char handler; struct nvme_completion cqe = nvmeq->cqes[head]; - if ((le16_to_cpu(cqe.status) & 1) != cycle) + if ((le16_to_cpu(cqe.status) & 1) != phase) break; nvmeq->sq_head = le16_to_cpu(cqe.sq_head); if (++head == nvmeq->q_depth) { head = 0; - cycle = !cycle; + phase = !phase; } data = free_cmdid(nvmeq, cqe.command_id); @@ -399,12 +399,12 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) * requires that 0.1% of your interrupts are handled, so this isn't * a big problem. */ - if (head == nvmeq->cq_head && cycle == nvmeq->cq_cycle) + if (head == nvmeq->cq_head && phase == nvmeq->cq_phase) return IRQ_NONE; writel(head, nvmeq->q_db + 1); nvmeq->cq_head = head; - nvmeq->cq_cycle = cycle; + nvmeq->cq_phase = phase; return IRQ_HANDLED; } @@ -557,7 +557,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, nvmeq->q_dmadev = dmadev; spin_lock_init(&nvmeq->q_lock); nvmeq->cq_head = 0; - nvmeq->cq_cycle = 1; + nvmeq->cq_phase = 1; init_waitqueue_head(&nvmeq->sq_full); bio_list_init(&nvmeq->sq_cong); nvmeq->q_db = &dev->dbs[qid * 2]; -- cgit v0.10.2 From 53c9577e9ca68a633c6e9df2b54eaecacfa77f62 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Jan 2011 13:42:34 -0500 Subject: NVMe: Fix admin IRQ claim on real hardware The admin IRQ is supposed to use the pin-based (or single message MSI) interrupt. Accomplish this by filling in entry[0]'s vector with the INTx irq number. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 3d917a8..44a9d5e 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -979,6 +979,7 @@ static int __devinit nvme_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, dev); dma_set_mask(&dev->pci_dev->dev, DMA_BIT_MASK(64)); nvme_set_instance(dev); + dev->entry[0].vector = pdev->irq; dev->bar = ioremap(pci_resource_start(pdev, 0), 8192); if (!dev->bar) { -- cgit v0.10.2 From 36c14ed9caa957c686d4a48fd598a5ec2aa0331b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 24 Jan 2011 07:52:07 -0500 Subject: NVMe: Use PRP2 for the nvme_identify ioctl DMA the result straight to userspace instead of bounce-buffering in the kernel. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 44a9d5e..c0ef1dd 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -647,33 +647,57 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) return result; } -static int nvme_identify(struct nvme_ns *ns, void __user *addr, int cns) +static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) { struct nvme_dev *dev = ns->dev; - int status; + int i, err, count, nents, offset; struct nvme_command c; - void *page; - dma_addr_t dma_addr; - - page = dma_alloc_coherent(&dev->pci_dev->dev, 4096, &dma_addr, - GFP_KERNEL); + struct scatterlist sg[2]; + struct page *pages[2]; + + if (addr & 3) + return -EINVAL; + offset = offset_in_page(addr); + count = offset ? 2 : 1; + + err = get_user_pages_fast(addr, count, 1, pages); + if (err < count) { + count = err; + err = -EFAULT; + goto put_pages; + } + sg_init_table(sg, count); + for (i = 0; i < count; i++) + sg_set_page(&sg[i], pages[i], PAGE_SIZE, 0); + nents = dma_map_sg(&dev->pci_dev->dev, sg, count, DMA_FROM_DEVICE); + if (!nents) + goto put_pages; memset(&c, 0, sizeof(c)); c.identify.opcode = nvme_admin_identify; c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); - c.identify.prp1 = cpu_to_le64(dma_addr); + c.identify.prp1 = cpu_to_le64(sg_dma_address(&sg[0]) + offset); + if (count > 1) { + u64 dma_addr; + if (nents > 1) + dma_addr = sg_dma_address(&sg[1]); + else + dma_addr = sg_dma_address(&sg[0]) + PAGE_SIZE; + c.identify.prp2 = cpu_to_le64(dma_addr); + } c.identify.cns = cpu_to_le32(cns); - status = nvme_submit_admin_cmd(dev, &c, NULL); + err = nvme_submit_admin_cmd(dev, &c, NULL); - if (status) - status = -EIO; - else if (copy_to_user(addr, page, 4096)) - status = -EFAULT; + if (err) + err = -EIO; - dma_free_coherent(&dev->pci_dev->dev, 4096, page, dma_addr); + dma_unmap_sg(&dev->pci_dev->dev, sg, nents, DMA_FROM_DEVICE); + put_pages: + for (i = 0; i < count; i++) + put_page(pages[i]); - return status; + return err; } static int nvme_get_range_type(struct nvme_ns *ns, void __user *addr) @@ -713,9 +737,9 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, switch (cmd) { case NVME_IOCTL_IDENTIFY_NS: - return nvme_identify(ns, (void __user *)arg, 0); + return nvme_identify(ns, arg, 0); case NVME_IOCTL_IDENTIFY_CTRL: - return nvme_identify(ns, (void __user *)arg, 1); + return nvme_identify(ns, arg, 1); case NVME_IOCTL_GET_RANGE_TYPE: return nvme_get_range_type(ns, (void __user *)arg); default: -- cgit v0.10.2 From 7b4fe9b1cb4b9a6f4ae23a12ef96d08d96e2a5da Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Jan 2011 10:01:21 -0500 Subject: NVMe: Make nvme_common_command more featureful Add prp1, prp2 and the metadata prp to the common command, since the fields are generally used this way. Signed-off-by: Matthew Wilcox diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 9ba5358..1c0b5ef 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -142,6 +142,18 @@ enum nvme_opcode { nvme_cmd_dsm = 0x09, }; +struct nvme_common_command { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u64 rsvd2; + __le64 metadata; + __le64 prp1; + __le64 prp2; + __u32 rsvd10[6]; +}; + struct nvme_rw_command { __u8 opcode; __u8 flags; @@ -284,14 +296,6 @@ struct nvme_delete_queue { __le32 rsvd11[5]; }; -struct nvme_common_command { - __u8 opcode; - __u8 flags; - __u16 command_id; - __le32 nsid; - __u32 rsvd2[14]; -}; - struct nvme_command { union { struct nvme_common_command common; -- cgit v0.10.2 From ff22b54fda2078fc3cd1bcdcb7a5ce5d08fd6591 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Jan 2011 10:02:29 -0500 Subject: NVMe: Add nvme_setup_prps() Generalise the code from nvme_identify() that sets PRP1 & PRP2 so that it's usable for commands sent by nvme_submit_bio_queue(). Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index c0ef1dd..1e57737 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -240,6 +240,36 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, bio_endio(bio, status ? -EIO : 0); } +/* length is in bytes */ +static void nvme_setup_prps(struct nvme_common_command *cmd, + struct scatterlist *sg, int length) +{ + int dma_len = sg_dma_len(sg); + u64 dma_addr = sg_dma_address(sg); + int offset = offset_in_page(dma_addr); + + cmd->prp1 = cpu_to_le64(dma_addr); + length -= (PAGE_SIZE - offset); + if (length <= 0) + return; + + dma_len -= (PAGE_SIZE - offset); + if (dma_len) { + dma_addr += (PAGE_SIZE - offset); + } else { + sg = sg_next(sg); + dma_addr = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + } + + if (length <= PAGE_SIZE) { + cmd->prp2 = cpu_to_le64(dma_addr); + return; + } + + /* XXX: support PRP lists */ +} + static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, struct bio *bio, enum dma_data_direction dma_dir, int psegs) { @@ -261,7 +291,7 @@ static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct bio *bio) { - struct nvme_rw_command *cmnd; + struct nvme_command *cmnd; struct nvme_req_info *info; enum dma_data_direction dma_dir; int cmdid; @@ -290,27 +320,26 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH; spin_lock_irqsave(&nvmeq->q_lock, flags); - cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail].rw; + cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail]; if (bio_data_dir(bio)) { - cmnd->opcode = nvme_cmd_write; + cmnd->rw.opcode = nvme_cmd_write; dma_dir = DMA_TO_DEVICE; } else { - cmnd->opcode = nvme_cmd_read; + cmnd->rw.opcode = nvme_cmd_read; dma_dir = DMA_FROM_DEVICE; } nvme_map_bio(nvmeq->q_dmadev, info, bio, dma_dir, psegs); - cmnd->flags = 1; - cmnd->command_id = cmdid; - cmnd->nsid = cpu_to_le32(ns->ns_id); - cmnd->prp1 = cpu_to_le64(sg_phys(info->sg)); - /* XXX: Support more than one PRP */ - cmnd->slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); - cmnd->length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); - cmnd->control = cpu_to_le16(control); - cmnd->dsmgmt = cpu_to_le32(dsmgmt); + cmnd->rw.flags = 1; + cmnd->rw.command_id = cmdid; + cmnd->rw.nsid = cpu_to_le32(ns->ns_id); + nvme_setup_prps(&cmnd->common, info->sg, bio->bi_size); + cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); + cmnd->rw.length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); + cmnd->rw.control = cpu_to_le16(control); + cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt); writel(nvmeq->sq_tail, nvmeq->q_db); if (++nvmeq->sq_tail == nvmeq->q_depth) @@ -667,8 +696,9 @@ static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) goto put_pages; } sg_init_table(sg, count); - for (i = 0; i < count; i++) - sg_set_page(&sg[i], pages[i], PAGE_SIZE, 0); + sg_set_page(&sg[0], pages[0], PAGE_SIZE - offset, offset); + if (count > 1) + sg_set_page(&sg[1], pages[1], offset, 0); nents = dma_map_sg(&dev->pci_dev->dev, sg, count, DMA_FROM_DEVICE); if (!nents) goto put_pages; @@ -676,15 +706,7 @@ static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) memset(&c, 0, sizeof(c)); c.identify.opcode = nvme_admin_identify; c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); - c.identify.prp1 = cpu_to_le64(sg_dma_address(&sg[0]) + offset); - if (count > 1) { - u64 dma_addr; - if (nents > 1) - dma_addr = sg_dma_address(&sg[1]); - else - dma_addr = sg_dma_address(&sg[0]) + PAGE_SIZE; - c.identify.prp2 = cpu_to_le64(dma_addr); - } + nvme_setup_prps(&c.common, sg, 4096); c.identify.cns = cpu_to_le32(cns); err = nvme_submit_admin_cmd(dev, &c, NULL); -- cgit v0.10.2 From b8deb62cf271fa9381edc8cf52bcae2f0225c55a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Jan 2011 10:08:25 -0500 Subject: NVMe: Zero the command before we send it Make sure there's no left-over bits set from previous commands that used this slot. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 1e57737..25ca7af 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -322,6 +322,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, spin_lock_irqsave(&nvmeq->q_lock, flags); cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail]; + memset(cmnd, 0, sizeof(*cmnd)); if (bio_data_dir(bio)) { cmnd->rw.opcode = nvme_cmd_write; dma_dir = DMA_TO_DEVICE; -- cgit v0.10.2 From bd38c5557cf482fc195e2264b32ea62eed60730a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Jan 2011 14:34:32 -0500 Subject: NVMe: Change NVME_IOCTL_GET_RANGE_TYPE to return all the ranges Factor out most of nvme_identify() into a new nvme_submit_user_admin_command() function. Change nvme_get_range_type() to call it and change nvme_ioctl to realise that it's getting back all 64 ranges. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 25ca7af..b28d188 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -677,18 +677,17 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) return result; } -static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) +static int nvme_submit_user_admin_command(struct nvme_dev *dev, unsigned long addr, + unsigned length, struct nvme_command *cmd) { - struct nvme_dev *dev = ns->dev; int i, err, count, nents, offset; - struct nvme_command c; struct scatterlist sg[2]; struct page *pages[2]; if (addr & 3) return -EINVAL; offset = offset_in_page(addr); - count = offset ? 2 : 1; + count = ((offset + length) > PAGE_SIZE) ? 2 : 1; err = get_user_pages_fast(addr, count, 1, pages); if (err < count) { @@ -704,13 +703,9 @@ static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) if (!nents) goto put_pages; - memset(&c, 0, sizeof(c)); - c.identify.opcode = nvme_admin_identify; - c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); - nvme_setup_prps(&c.common, sg, 4096); - c.identify.cns = cpu_to_le32(cns); + nvme_setup_prps(&cmd->common, sg, length); - err = nvme_submit_admin_cmd(dev, &c, NULL); + err = nvme_submit_admin_cmd(dev, cmd, NULL); if (err) err = -EIO; @@ -723,34 +718,28 @@ static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) return err; } -static int nvme_get_range_type(struct nvme_ns *ns, void __user *addr) +static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) { - struct nvme_dev *dev = ns->dev; - int status; struct nvme_command c; - void *page; - dma_addr_t dma_addr; - page = dma_alloc_coherent(&dev->pci_dev->dev, 4096, &dma_addr, - GFP_KERNEL); + memset(&c, 0, sizeof(c)); + c.identify.opcode = nvme_admin_identify; + c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); + c.identify.cns = cpu_to_le32(cns); + + return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); +} + +static int nvme_get_range_type(struct nvme_ns *ns, unsigned long addr) +{ + struct nvme_command c; memset(&c, 0, sizeof(c)); c.features.opcode = nvme_admin_get_features; c.features.nsid = cpu_to_le32(ns->ns_id); - c.features.prp1 = cpu_to_le64(dma_addr); c.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); - status = nvme_submit_admin_cmd(dev, &c, NULL); - - /* XXX: Assuming first range for now */ - if (status) - status = -EIO; - else if (copy_to_user(addr, page, 64)) - status = -EFAULT; - - dma_free_coherent(&dev->pci_dev->dev, 4096, page, dma_addr); - - return status; + return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); } static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, @@ -764,7 +753,7 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, case NVME_IOCTL_IDENTIFY_CTRL: return nvme_identify(ns, arg, 1); case NVME_IOCTL_GET_RANGE_TYPE: - return nvme_get_range_type(ns, (void __user *)arg); + return nvme_get_range_type(ns, arg); default: return -ENOTTY; } -- cgit v0.10.2 From 7fc3cdabba75c2516b8b645eb0ca7907aea70415 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 26 Jan 2011 17:05:50 -0500 Subject: NVMe: Create nvme_map_user_pages() and nvme_unmap_user_pages() These are generalisations of the code that was in nvme_submit_user_admin_command(). Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b28d188..f44d6cd 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -677,17 +677,22 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) return result; } -static int nvme_submit_user_admin_command(struct nvme_dev *dev, unsigned long addr, - unsigned length, struct nvme_command *cmd) +static int nvme_map_user_pages(struct nvme_dev *dev, int write, + unsigned long addr, unsigned length, + struct scatterlist **sgp) { int i, err, count, nents, offset; - struct scatterlist sg[2]; - struct page *pages[2]; + struct scatterlist *sg; + struct page **pages; if (addr & 3) return -EINVAL; + if (!length) + return -EINVAL; + offset = offset_in_page(addr); - count = ((offset + length) > PAGE_SIZE) ? 2 : 1; + count = DIV_ROUND_UP(offset + length, PAGE_SIZE); + pages = kcalloc(count, sizeof(*pages), GFP_KERNEL); err = get_user_pages_fast(addr, count, 1, pages); if (err < count) { @@ -695,27 +700,60 @@ static int nvme_submit_user_admin_command(struct nvme_dev *dev, unsigned long ad err = -EFAULT; goto put_pages; } + + sg = kcalloc(count, sizeof(*sg), GFP_KERNEL); sg_init_table(sg, count); sg_set_page(&sg[0], pages[0], PAGE_SIZE - offset, offset); - if (count > 1) - sg_set_page(&sg[1], pages[1], offset, 0); - nents = dma_map_sg(&dev->pci_dev->dev, sg, count, DMA_FROM_DEVICE); + length -= (PAGE_SIZE - offset); + for (i = 1; i < count; i++) { + sg_set_page(&sg[i], pages[i], min_t(int, length, PAGE_SIZE), 0); + length -= PAGE_SIZE; + } + + err = -ENOMEM; + nents = dma_map_sg(&dev->pci_dev->dev, sg, count, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); if (!nents) goto put_pages; - nvme_setup_prps(&cmd->common, sg, length); + kfree(pages); + *sgp = sg; + return nents; - err = nvme_submit_admin_cmd(dev, cmd, NULL); + put_pages: + for (i = 0; i < count; i++) + put_page(pages[i]); + kfree(pages); + return err; +} - if (err) - err = -EIO; +static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, + unsigned long addr, int length, + struct scatterlist *sg, int nents) +{ + int i, count; + count = DIV_ROUND_UP(offset_in_page(addr) + length, PAGE_SIZE); dma_unmap_sg(&dev->pci_dev->dev, sg, nents, DMA_FROM_DEVICE); - put_pages: + for (i = 0; i < count; i++) - put_page(pages[i]); + put_page(sg_page(&sg[i])); +} - return err; +static int nvme_submit_user_admin_command(struct nvme_dev *dev, + unsigned long addr, unsigned length, + struct nvme_command *cmd) +{ + int err, nents; + struct scatterlist *sg; + + nents = nvme_map_user_pages(dev, 0, addr, length, &sg); + if (nents < 0) + return nents; + nvme_setup_prps(&cmd->common, sg, length); + err = nvme_submit_admin_cmd(dev, cmd, NULL); + nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); + return err ? -EIO : 0; } static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) -- cgit v0.10.2 From a53295b6998f62d961c29e54051c1cf1d738c2b3 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 16:13:29 -0500 Subject: NVMe: Add NVME_IOCTL_SUBMIT_IO Allow userspace to submit synchronous I/O like the SCSI sg interface does. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f44d6cd..40fb2e1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -780,6 +780,47 @@ static int nvme_get_range_type(struct nvme_ns *ns, unsigned long addr) return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); } +static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) +{ + struct nvme_dev *dev = ns->dev; + struct nvme_queue *nvmeq; + struct nvme_user_io io; + struct nvme_command c; + unsigned length; + u32 result; + int nents, status; + struct scatterlist *sg; + + if (copy_from_user(&io, uio, sizeof(io))) + return -EFAULT; + length = io.nblocks << io.block_shift; + nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, length, &sg); + if (nents < 0) + return nents; + + memset(&c, 0, sizeof(c)); + c.rw.opcode = io.opcode; + c.rw.flags = io.flags; + c.rw.nsid = cpu_to_le32(io.nsid); + c.rw.slba = cpu_to_le64(io.slba); + c.rw.length = cpu_to_le16(io.nblocks - 1); + c.rw.control = cpu_to_le16(io.control); + c.rw.dsmgmt = cpu_to_le16(io.dsmgmt); + c.rw.reftag = cpu_to_le32(io.reftag); /* XXX: endian? */ + c.rw.apptag = cpu_to_le16(io.apptag); + c.rw.appmask = cpu_to_le16(io.appmask); + /* XXX: metadata */ + nvme_setup_prps(&c.common, sg, length); + + nvmeq = get_nvmeq(ns); + status = nvme_submit_sync_cmd(nvmeq, &c, &result); + put_nvmeq(nvmeq); + + nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); + put_user(result, &uio->result); + return status; +} + static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { @@ -792,6 +833,8 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, return nvme_identify(ns, arg, 1); case NVME_IOCTL_GET_RANGE_TYPE: return nvme_get_range_type(ns, arg); + case NVME_IOCTL_SUBMIT_IO: + return nvme_submit_io(ns, (void __user *)arg); default: return -ENOTTY; } diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 1c0b5ef..0aaecb0 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -340,8 +340,26 @@ struct nvme_completion { __le16 status; /* did the command fail, and if so, why? */ }; +struct nvme_user_io { + __u8 opcode; + __u8 flags; + __u16 control; + __u32 nsid; + __u64 metadata; + __u64 addr; + __u64 slba; + __u16 nblocks; + __u16 block_shift; + __u32 dsmgmt; + __u32 reftag; + __u16 apptag; + __u16 appmask; + __u32 result; +}; + #define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) #define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) #define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) +#define NVME_IOCTL_SUBMIT_IO _IOWR('N', 0x43, struct nvme_rw_command) #endif /* _LINUX_NVME_H */ -- cgit v0.10.2 From 51814232ecae90f888c902e252306df8d017f0dd Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 16:18:08 -0500 Subject: NVMe: Read the model, serial & firmware rev from the controller Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 40fb2e1..12e37c1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -58,6 +58,9 @@ struct nvme_dev { struct msix_entry *entry; struct nvme_bar __iomem *bar; struct list_head namespaces; + char serial[20]; + char model[40]; + char firmware_rev[8]; }; /* @@ -979,6 +982,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) { int res, nn, i; struct nvme_ns *ns, *next; + struct nvme_id_ctrl *ctrl; void *id; dma_addr_t dma_addr; struct nvme_command cid, crt; @@ -1003,7 +1007,11 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) goto out_free; } - nn = le32_to_cpup(&((struct nvme_id_ctrl *)id)->nn); + ctrl = id; + nn = le32_to_cpup(&ctrl->nn); + memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn)); + memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn)); + memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr)); cid.identify.cns = 0; memset(&crt, 0, sizeof(crt)); -- cgit v0.10.2 From 8e9f0e71150bf6277d0ea40bc8feb1338ddf13fd Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 31 Jan 2011 10:46:14 -0500 Subject: NVMe: Remove 'node' from nvme_dev We don't keep a list of nvme_dev any more Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 12e37c1..9377cf3 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -48,7 +48,6 @@ module_param(nvme_major, int, 0); * Represents an NVM Express device. Each nvme_dev is a PCI function. */ struct nvme_dev { - struct list_head node; struct nvme_queue **queues; u32 __iomem *dbs; struct pci_dev *pci_dev; -- cgit v0.10.2 From 3f85d50b609e8a5ef151656210203a6e94c19538 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 08:39:04 -0500 Subject: NVMe: Check returns from nvme_alloc_queue() It can return NULL, so handle that. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 9377cf3..dc82177 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -619,6 +619,9 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, int result; struct nvme_queue *nvmeq = nvme_alloc_queue(dev, qid, cq_size, vector); + if (!nvmeq) + return NULL; + result = adapter_alloc_cq(dev, qid, nvmeq); if (result < 0) goto free_nvmeq; @@ -655,6 +658,8 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) dev->dbs = ((void __iomem *)dev->bar) + 4096; nvmeq = nvme_alloc_queue(dev, 0, 64, 0); + if (!nvmeq) + return -ENOMEM; aqa = nvmeq->q_depth - 1; aqa |= aqa << 16; -- cgit v0.10.2 From 0ee5a7d7cb9309bd393a25c395f19fb12a842602 Mon Sep 17 00:00:00 2001 From: Shane Michael Matthews Date: Tue, 1 Feb 2011 08:49:30 -0500 Subject: NVMe: Enable and disable the PCI device Call pci_enable_device_mem() at initialisation and pci_disable_device at exit. Signed-off-by: Shane Michael Matthews Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index dc82177..1dda4b5 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1103,6 +1103,9 @@ static int __devinit nvme_probe(struct pci_dev *pdev, if (!dev->queues) goto free; + if (pci_enable_device_mem(pdev)) + goto free; + INIT_LIST_HEAD(&dev->namespaces); dev->pci_dev = pdev; pci_set_drvdata(pdev, dev); @@ -1133,6 +1136,7 @@ static int __devinit nvme_probe(struct pci_dev *pdev, disable: pci_disable_msix(pdev); nvme_release_instance(dev); + pci_disable_device(pdev); free: kfree(dev->queues); kfree(dev->entry); @@ -1147,6 +1151,7 @@ static void __devexit nvme_remove(struct pci_dev *pdev) pci_disable_msix(pdev); iounmap(dev->bar); nvme_release_instance(dev); + pci_disable_device(pdev); kfree(dev->queues); kfree(dev->entry); kfree(dev); -- cgit v0.10.2 From f64d3365a3e5cb46e69db7e2c82a7cb9a5bed1b8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 09:01:59 -0500 Subject: NVMe: Enable device DMA Need to call pci_set_master() to enable device DMA Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 1dda4b5..128fd70 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1105,6 +1105,7 @@ static int __devinit nvme_probe(struct pci_dev *pdev, if (pci_enable_device_mem(pdev)) goto free; + pci_set_master(pdev); INIT_LIST_HEAD(&dev->namespaces); dev->pci_dev = pdev; -- cgit v0.10.2 From 2930353f9f2b9e4629e935acd970cb73c1171229 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 16:23:39 -0500 Subject: NVMe: Allow queues to be allocated above 4GB Need to call dma_set_coherent_mask() to allow queues to be allocated above 4GB. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 128fd70..46f8720 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1110,7 +1110,8 @@ static int __devinit nvme_probe(struct pci_dev *pdev, INIT_LIST_HEAD(&dev->namespaces); dev->pci_dev = pdev; pci_set_drvdata(pdev, dev); - dma_set_mask(&dev->pci_dev->dev, DMA_BIT_MASK(64)); + dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); nvme_set_instance(dev); dev->entry[0].vector = pdev->irq; -- cgit v0.10.2 From 574e8b95bc3780e10e9b5e9d51074d503dd3d5d9 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 16:24:35 -0500 Subject: NVMe: Request I/O regions Calling pci_request_selected_regions() reserves these regions for our use. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 46f8720..bda9117 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1088,7 +1088,7 @@ static void nvme_release_instance(struct nvme_dev *dev) static int __devinit nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int result = -ENOMEM; + int bars, result = -ENOMEM; struct nvme_dev *dev; dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -1106,6 +1106,9 @@ static int __devinit nvme_probe(struct pci_dev *pdev, if (pci_enable_device_mem(pdev)) goto free; pci_set_master(pdev); + bars = pci_select_bars(pdev, IORESOURCE_MEM); + if (pci_request_selected_regions(pdev, bars, "nvme")) + goto disable; INIT_LIST_HEAD(&dev->namespaces); dev->pci_dev = pdev; @@ -1118,7 +1121,7 @@ static int __devinit nvme_probe(struct pci_dev *pdev, dev->bar = ioremap(pci_resource_start(pdev, 0), 8192); if (!dev->bar) { result = -ENOMEM; - goto disable; + goto disable_msix; } result = nvme_configure_admin_queue(dev); @@ -1135,10 +1138,12 @@ static int __devinit nvme_probe(struct pci_dev *pdev, nvme_free_queues(dev); unmap: iounmap(dev->bar); - disable: + disable_msix: pci_disable_msix(pdev); nvme_release_instance(dev); + disable: pci_disable_device(pdev); + pci_release_regions(pdev); free: kfree(dev->queues); kfree(dev->entry); @@ -1154,6 +1159,7 @@ static void __devexit nvme_remove(struct pci_dev *pdev) iounmap(dev->bar); nvme_release_instance(dev); pci_disable_device(pdev); + pci_release_regions(pdev); kfree(dev->queues); kfree(dev->entry); kfree(dev); -- cgit v0.10.2 From 5911f20039ce59d7e7834f0c42151cf759b6f786 Mon Sep 17 00:00:00 2001 From: Shane Michael Matthews Date: Tue, 1 Feb 2011 11:31:55 -0500 Subject: NVMe: Disable the device before we write the admin queues In case the card has been left in a partially-configured state, write 0 to the Enable bit. Signed-off-by: Shane Michael Matthews Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index bda9117..e3d9215 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -668,6 +668,7 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) dev->ctrl_config |= (PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT; dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE; + writel(0, &dev->bar->cc); writel(aqa, &dev->bar->aqa); writeq(nvmeq->sq_dma_addr, &dev->bar->asq); writeq(nvmeq->cq_dma_addr, &dev->bar->acq); -- cgit v0.10.2 From 388f037f4e7f0a24bac6b1a24f144f5d939f58cf Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 1 Feb 2011 12:49:38 -0500 Subject: NVMe: Move sysfs entries to the right place Because I wasn't setting driverfs_dev, the devices were showing up under /sys/devices/virtual/block. Now they appear underneath the PCI device which they belong to. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index e3d9215..744db38 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -889,6 +889,7 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int index, disk->fops = &nvme_fops; disk->private_data = ns; disk->queue = ns->queue; + disk->driverfs_dev = &dev->pci_dev->dev; sprintf(disk->disk_name, "nvme%dn%d", dev->instance, index); set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); -- cgit v0.10.2 From 7a63e07b9a98b77dd075e06b93c1d8dc871ddad5 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 3 Feb 2011 09:20:57 -0500 Subject: NVMe: Add remaining status codes Signed-off-by: Matthew Wilcox diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 0aaecb0..dbbdc12 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -321,14 +321,29 @@ enum { NVME_SC_ABORT_QUEUE = 0x8, NVME_SC_FUSED_FAIL = 0x9, NVME_SC_FUSED_MISSING = 0xa, + NVME_SC_INVALID_NS = 0xb, NVME_SC_LBA_RANGE = 0x80, NVME_SC_CAP_EXCEEDED = 0x81, NVME_SC_NS_NOT_READY = 0x82, NVME_SC_CQ_INVALID = 0x100, NVME_SC_QID_INVALID = 0x101, NVME_SC_QUEUE_SIZE = 0x102, + NVME_SC_ABORT_LIMIT = 0x103, + NVME_SC_ABORT_MISSING = 0x104, + NVME_SC_ASYNC_LIMIT = 0x105, + NVME_SC_FIRMWARE_SLOT = 0x106, + NVME_SC_FIRMWARE_IMAGE = 0x107, + NVME_SC_INVALID_VECTOR = 0x108, + NVME_SC_INVALID_LOG_PAGE = 0x109, + NVME_SC_INVALID_FORMAT = 0x10a, + NVME_SC_BAD_ATTRIBUTES = 0x180, NVME_SC_WRITE_FAULT = 0x280, NVME_SC_READ_ERROR = 0x281, + NVME_SC_GUARD_CHECK = 0x282, + NVME_SC_APPTAG_CHECK = 0x283, + NVME_SC_REFTAG_CHECK = 0x284, + NVME_SC_COMPARE_FAILED = 0x285, + NVME_SC_ACCESS_DENIED = 0x286, }; struct nvme_completion { -- cgit v0.10.2 From 6ee44cdced04a53dc4f27eb97067e6cd33784726 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 3 Feb 2011 10:58:26 -0500 Subject: NVMe: Add download / activate firmware ioctls Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 744db38..7cdf7f6 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -829,6 +829,47 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) return status; } +static int nvme_download_firmware(struct nvme_ns *ns, + struct nvme_dlfw __user *udlfw) +{ + struct nvme_dev *dev = ns->dev; + struct nvme_dlfw dlfw; + struct nvme_command c; + int nents, status; + struct scatterlist *sg; + + if (copy_from_user(&dlfw, udlfw, sizeof(dlfw))) + return -EFAULT; + if (dlfw.length >= (1 << 30)) + return -EINVAL; + + nents = nvme_map_user_pages(dev, 1, dlfw.addr, dlfw.length * 4, &sg); + if (nents < 0) + return nents; + + memset(&c, 0, sizeof(c)); + c.dlfw.opcode = nvme_admin_download_fw; + c.dlfw.numd = cpu_to_le32(dlfw.length); + c.dlfw.offset = cpu_to_le32(dlfw.offset); + nvme_setup_prps(&c.common, sg, dlfw.length * 4); + + status = nvme_submit_admin_cmd(dev, &c, NULL); + nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents); + return status; +} + +static int nvme_activate_firmware(struct nvme_ns *ns, unsigned long arg) +{ + struct nvme_dev *dev = ns->dev; + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.common.opcode = nvme_admin_activate_fw; + c.common.rsvd10[0] = cpu_to_le32(arg); + + return nvme_submit_admin_cmd(dev, &c, NULL); +} + static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { @@ -843,6 +884,10 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, return nvme_get_range_type(ns, arg); case NVME_IOCTL_SUBMIT_IO: return nvme_submit_io(ns, (void __user *)arg); + case NVME_IOCTL_DOWNLOAD_FW: + return nvme_download_firmware(ns, (void __user *)arg); + case NVME_IOCTL_ACTIVATE_FW: + return nvme_activate_firmware(ns, arg); default: return -ENOTTY; } diff --git a/include/linux/nvme.h b/include/linux/nvme.h index dbbdc12..8eed0e4 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -262,7 +262,7 @@ struct nvme_create_cq { __u8 opcode; __u8 flags; __u16 command_id; - __le32 rsvd1[5]; + __u32 rsvd1[5]; __le64 prp1; __u64 rsvd8; __le16 cqid; @@ -276,14 +276,14 @@ struct nvme_create_sq { __u8 opcode; __u8 flags; __u16 command_id; - __le32 rsvd1[5]; + __u32 rsvd1[5]; __le64 prp1; __u64 rsvd8; __le16 sqid; __le16 qsize; __le16 sq_flags; __le16 cqid; - __le32 rsvd12[4]; + __u32 rsvd12[4]; }; struct nvme_delete_queue { @@ -292,8 +292,20 @@ struct nvme_delete_queue { __u16 command_id; __u32 rsvd1[9]; __le16 qid; - __le16 rsvd10; - __le32 rsvd11[5]; + __u16 rsvd10; + __u32 rsvd11[5]; +}; + +struct nvme_download_firmware { + __u8 opcode; + __u8 flags; + __u16 command_id; + __u32 rsvd1[5]; + __le64 prp1; + __le64 prp2; + __le32 numd; + __le32 offset; + __u32 rsvd12[4]; }; struct nvme_command { @@ -305,6 +317,7 @@ struct nvme_command { struct nvme_create_cq create_cq; struct nvme_create_sq create_sq; struct nvme_delete_queue delete_queue; + struct nvme_download_firmware dlfw; }; }; @@ -348,7 +361,7 @@ enum { struct nvme_completion { __le32 result; /* Used by admin commands to return data */ - __le32 rsvd; + __u32 rsvd; __le16 sq_head; /* how much of this queue may be reclaimed */ __le16 sq_id; /* submission queue that generated this entry */ __u16 command_id; /* of the command which completed */ @@ -372,9 +385,17 @@ struct nvme_user_io { __u32 result; }; +struct nvme_dlfw { + __u64 addr; + __u32 length; /* In dwords */ + __u32 offset; /* In dwords */ +}; + #define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) #define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) #define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) #define NVME_IOCTL_SUBMIT_IO _IOWR('N', 0x43, struct nvme_rw_command) +#define NVME_IOCTL_DOWNLOAD_FW _IOR('N', 0x44, struct nvme_dlfw) +#define NVME_IOCTL_ACTIVATE_FW _IO('N', 0x45) #endif /* _LINUX_NVME_H */ -- cgit v0.10.2 From db5d0c198d673b6a932b449d4db95a2ad50c755e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 3 Feb 2011 14:36:07 -0500 Subject: NVMe: Release 0.2 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 7cdf7f6..06a6aea 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1272,6 +1272,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From 3c0cf138d7789feb3f335f6f1d24ad8fc8b3a23f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 4 Feb 2011 16:03:56 -0500 Subject: NVMe: Allow fatal signals to interrupt I/O If the user sends a fatal signal, sleeping in the TASK_KILLABLE state permits the task to be aborted. The only wrinkle is making sure that if/when the command completes later that it doesn't upset anything. Handle this by setting the data pointer to 0, and checking the value isn't NULL in the sync completion path. Eventually, bios can be cancelled through this path too. Note that the cmdid isn't freed to prevent reuse. We should also abort the command in the future, but this is a good start. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 06a6aea..4bfed59 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -155,7 +155,9 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, } /* If you need more than four handlers, you'll need to change how - * alloc_cmdid and nvme_process_cq work + * alloc_cmdid and nvme_process_cq work. Also, aborted commands take + * the sync_completion path (if they complete), so don't put anything + * else in slot zero. */ enum { sync_completion_id = 0, @@ -172,6 +174,11 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) return data; } +static void clear_cmdid_data(struct nvme_queue *nvmeq, int cmdid) +{ + nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = 0; +} + static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) { int qid, cpu = get_cpu(); @@ -386,6 +393,8 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { struct sync_cmd_info *cmdinfo = ctx; + if (!cmdinfo) + return; /* Command aborted */ cmdinfo->result = le32_to_cpup(&cqe->result); cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; wake_up_process(cmdinfo->task); @@ -446,12 +455,19 @@ static irqreturn_t nvme_irq(int irq, void *data) return nvme_process_cq(data); } +static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) +{ + spin_lock_irq(&nvmeq->q_lock); + clear_cmdid_data(nvmeq, cmdid); + spin_unlock_irq(&nvmeq->q_lock); +} + /* * Returns 0 on success. If the result is negative, it's a Linux error code; * if the result is positive, it's an NVM Express status code */ -static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd, - u32 *result) +static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, + struct nvme_command *cmd, u32 *result) { int cmdid; struct sync_cmd_info cmdinfo; @@ -459,15 +475,20 @@ static int nvme_submit_sync_cmd(struct nvme_queue *q, struct nvme_command *cmd, cmdinfo.task = current; cmdinfo.status = -EINTR; - cmdid = alloc_cmdid_killable(q, &cmdinfo, sync_completion_id); + cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id); if (cmdid < 0) return cmdid; cmd->common.command_id = cmdid; - set_current_state(TASK_UNINTERRUPTIBLE); - nvme_submit_cmd(q, cmd); + set_current_state(TASK_KILLABLE); + nvme_submit_cmd(nvmeq, cmd); schedule(); + if (cmdinfo.status == -EINTR) { + nvme_abort_command(nvmeq, cmdid); + return -EINTR; + } + if (result) *result = cmdinfo.result; -- cgit v0.10.2 From b1ad37efcafe396ac3944853589688dd0ec3c64e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 4 Feb 2011 16:14:30 -0500 Subject: NVMe: Call put_nvmeq() before calling nvme_submit_sync_cmd() We can't have preemption disabled when we call schedule(). Accept the possibility that we'll get preempted, and it'll cost us some cacheline bounces. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 4bfed59..1c3cd6c 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -842,8 +842,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) nvme_setup_prps(&c.common, sg, length); nvmeq = get_nvmeq(ns); - status = nvme_submit_sync_cmd(nvmeq, &c, &result); + /* Since nvme_submit_sync_cmd sleeps, we can't keep preemption + * disabled. We may be preempted at any point, and be rescheduled + * to a different CPU. That will cause cacheline bouncing, but no + * additional races since q_lock already protects against other CPUs. + */ put_nvmeq(nvmeq); + status = nvme_submit_sync_cmd(nvmeq, &c, &result); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); put_user(result, &uio->result); -- cgit v0.10.2 From 58ffacb545f76fc2c65d1fbfa5acf5184a2a09e6 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 6 Feb 2011 07:28:06 -0500 Subject: NVMe: Add a module parameter to use a threaded interrupt We're currently calling bio_endio from hard interrupt context. This is not a good idea for preemptible kernels as it will cause longer latencies. Using a threaded interrupt will run the entire queue processing mechanism (including bio_endio) in a thread, which can be preempted. Unfortuantely, it also adds about 7us of latency to the single-I/O case, so make it a module parameter for the moment. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 1c3cd6c..60c3786 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -44,6 +44,9 @@ static int nvme_major; module_param(nvme_major, int, 0); +static int use_threaded_interrupts; +module_param(use_threaded_interrupts, int, 0); + /* * Represents an NVM Express device. Each nvme_dev is a PCI function. */ @@ -455,6 +458,25 @@ static irqreturn_t nvme_irq(int irq, void *data) return nvme_process_cq(data); } +static irqreturn_t nvme_irq_thread(int irq, void *data) +{ + irqreturn_t result; + struct nvme_queue *nvmeq = data; + spin_lock(&nvmeq->q_lock); + result = nvme_process_cq(nvmeq); + spin_unlock(&nvmeq->q_lock); + return result; +} + +static irqreturn_t nvme_irq_check(int irq, void *data) +{ + struct nvme_queue *nvmeq = data; + struct nvme_completion cqe = nvmeq->cqes[nvmeq->cq_head]; + if ((le16_to_cpu(cqe.status) & 1) != nvmeq->cq_phase) + return IRQ_NONE; + return IRQ_WAKE_THREAD; +} + static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) { spin_lock_irq(&nvmeq->q_lock); @@ -630,6 +652,11 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, static int queue_request_irq(struct nvme_dev *dev, struct nvme_queue *nvmeq, const char *name) { + if (use_threaded_interrupts) + return request_threaded_irq(dev->entry[nvmeq->cq_vector].vector, + nvme_irq_check, nvme_irq_thread, + IRQF_DISABLED | IRQF_SHARED, + name, nvmeq); return request_irq(dev->entry[nvmeq->cq_vector].vector, nvme_irq, IRQF_DISABLED | IRQF_SHARED, name, nvmeq); } -- cgit v0.10.2 From be7b62754e097adc0cb16c25c9ee86ee20de62fb Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 6 Feb 2011 07:53:23 -0500 Subject: NVMe: Use a symbolic name to represent cancelled commands instead of 0 I have plans for other special values in sync_completion. Plus, this is more self-documenting, and lets us detect bogus usages. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 60c3786..802d763 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -158,15 +159,17 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, } /* If you need more than four handlers, you'll need to change how - * alloc_cmdid and nvme_process_cq work. Also, aborted commands take - * the sync_completion path (if they complete), so don't put anything - * else in slot zero. + * alloc_cmdid and nvme_process_cq work. Consider using a special + * CMD_CTX value instead, if that works for your situation. */ enum { sync_completion_id = 0, bio_completion_id, }; +#define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) +#define CMD_CTX_CANCELLED (0x2008 + CMD_CTX_BASE) + static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { unsigned long data; @@ -177,9 +180,10 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) return data; } -static void clear_cmdid_data(struct nvme_queue *nvmeq, int cmdid) +static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) { - nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = 0; + nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = + CMD_CTX_CANCELLED; } static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) @@ -396,8 +400,8 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { struct sync_cmd_info *cmdinfo = ctx; - if (!cmdinfo) - return; /* Command aborted */ + if ((unsigned long)cmdinfo == CMD_CTX_CANCELLED) + return; cmdinfo->result = le32_to_cpup(&cqe->result); cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; wake_up_process(cmdinfo->task); @@ -480,7 +484,7 @@ static irqreturn_t nvme_irq_check(int irq, void *data) static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) { spin_lock_irq(&nvmeq->q_lock); - clear_cmdid_data(nvmeq, cmdid); + cancel_cmdid_data(nvmeq, cmdid); spin_unlock_irq(&nvmeq->q_lock); } -- cgit v0.10.2 From b36235df01ec4141b4e589571d6789076c346d88 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 6 Feb 2011 08:49:55 -0500 Subject: NVMe: Detect commands that are completed twice Set the context value to CMD_CTX_COMPLETED, and print a message in the sync_completion handler if we see it. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 802d763..2dd09e7 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -169,12 +169,15 @@ enum { #define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) #define CMD_CTX_CANCELLED (0x2008 + CMD_CTX_BASE) +#define CMD_CTX_COMPLETED (0x2010 + CMD_CTX_BASE) static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { unsigned long data; + unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); - data = nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)]; + data = nvmeq->cmdid_data[offset]; + nvmeq->cmdid_data[offset] = CMD_CTX_COMPLETED; clear_bit(cmdid, nvmeq->cmdid_data); wake_up(&nvmeq->sq_full); return data; @@ -182,8 +185,8 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) { - nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(nvmeq->q_depth)] = - CMD_CTX_CANCELLED; + unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); + nvmeq->cmdid_data[offset] = CMD_CTX_CANCELLED; } static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) @@ -402,6 +405,12 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, struct sync_cmd_info *cmdinfo = ctx; if ((unsigned long)cmdinfo == CMD_CTX_CANCELLED) return; + if (unlikely((unsigned long)cmdinfo == CMD_CTX_COMPLETED)) { + dev_warn(nvmeq->q_dmadev, + "completed id %d twice on queue %d\n", + cqe->command_id, le16_to_cpup(&cqe->sq_id)); + return; + } cmdinfo->result = le32_to_cpup(&cqe->result); cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; wake_up_process(cmdinfo->task); -- cgit v0.10.2 From 48e3d39816416b3bf03dee3a796c0c04427c1a31 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 6 Feb 2011 08:51:15 -0500 Subject: NVMe: Detect command IDs completing that are out of range If the adapter completes a command ID that is outside the bounds of the array, return CMD_CTX_INVALID instead of random data, and print a message in the sync_completion handler (which is rapidly becoming the misc completion handler :-) Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 2dd09e7..f4085d4 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -170,12 +170,15 @@ enum { #define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) #define CMD_CTX_CANCELLED (0x2008 + CMD_CTX_BASE) #define CMD_CTX_COMPLETED (0x2010 + CMD_CTX_BASE) +#define CMD_CTX_INVALID (0x2014 + CMD_CTX_BASE) static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { unsigned long data; unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); + if (cmdid > nvmeq->q_depth) + return CMD_CTX_INVALID; data = nvmeq->cmdid_data[offset]; nvmeq->cmdid_data[offset] = CMD_CTX_COMPLETED; clear_bit(cmdid, nvmeq->cmdid_data); @@ -411,6 +414,12 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, cqe->command_id, le16_to_cpup(&cqe->sq_id)); return; } + if (unlikely((unsigned long)cmdinfo == CMD_CTX_INVALID)) { + dev_warn(nvmeq->q_dmadev, + "invalid id %d completed on queue %d\n", + cqe->command_id, le16_to_cpup(&cqe->sq_id)); + return; + } cmdinfo->result = le32_to_cpup(&cqe->result); cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; wake_up_process(cmdinfo->task); -- cgit v0.10.2 From ec6ce618d65b5ce1bef83a5509255107a0feac44 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 6 Feb 2011 09:01:00 -0500 Subject: NVMe: Need to lock queue during interrupt handling If we're sharing a queue between multiple CPUs and we cancel a sync I/O, we must have the queue locked to avoid corrupting the stack of the thread that submitted the I/O. It turns out this is the same locking that's needed for the threaded irq handler, so share that code. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f4085d4..139e6fc 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -477,11 +477,6 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) static irqreturn_t nvme_irq(int irq, void *data) { - return nvme_process_cq(data); -} - -static irqreturn_t nvme_irq_thread(int irq, void *data) -{ irqreturn_t result; struct nvme_queue *nvmeq = data; spin_lock(&nvmeq->q_lock); @@ -676,7 +671,7 @@ static int queue_request_irq(struct nvme_dev *dev, struct nvme_queue *nvmeq, { if (use_threaded_interrupts) return request_threaded_irq(dev->entry[nvmeq->cq_vector].vector, - nvme_irq_check, nvme_irq_thread, + nvme_irq_check, nvme_irq, IRQF_DISABLED | IRQF_SHARED, name, nvmeq); return request_irq(dev->entry[nvmeq->cq_vector].vector, nvme_irq, -- cgit v0.10.2 From e85248e516c550382ba33ca325c272a0ca397e44 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 6 Feb 2011 18:30:16 -0500 Subject: NVMe: Record the timeout for each command In addition to recording the completion data for each command, record the anticipated completion time. Choose a timeout of 5 seconds for normal I/Os and 60 seconds for admin I/Os. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 139e6fc..60c1048 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -41,6 +41,8 @@ #define SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) #define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) #define NVME_MINORS 64 +#define IO_TIMEOUT (5 * HZ) +#define ADMIN_TIMEOUT (60 * HZ) static int nvme_major; module_param(nvme_major, int, 0); @@ -119,6 +121,16 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64); } +struct nvme_cmd_info { + unsigned long ctx; + unsigned long timeout; +}; + +static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq) +{ + return (void *)&nvmeq->cmdid_data[BITS_TO_LONGS(nvmeq->q_depth)]; +} + /** * alloc_cmdid - Allocate a Command ID * @param nvmeq The queue that will be used for this command @@ -131,10 +143,11 @@ static inline void _nvme_check_size(void) * Passing in a pointer that's not 4-byte aligned will cause a BUG. * We can change this if it becomes a problem. */ -static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler) +static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler, + unsigned timeout) { int depth = nvmeq->q_depth; - unsigned long data = (unsigned long)ctx | handler; + struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); int cmdid; BUG_ON((unsigned long)ctx & 3); @@ -145,16 +158,17 @@ static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler) return -EBUSY; } while (test_and_set_bit(cmdid, nvmeq->cmdid_data)); - nvmeq->cmdid_data[cmdid + BITS_TO_LONGS(depth)] = data; + info[cmdid].ctx = (unsigned long)ctx | handler; + info[cmdid].timeout = jiffies + timeout; return cmdid; } static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, - int handler) + int handler, unsigned timeout) { int cmdid; wait_event_killable(nvmeq->sq_full, - (cmdid = alloc_cmdid(nvmeq, ctx, handler)) >= 0); + (cmdid = alloc_cmdid(nvmeq, ctx, handler, timeout)) >= 0); return (cmdid < 0) ? -EINTR : cmdid; } @@ -175,12 +189,12 @@ enum { static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { unsigned long data; - unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); + struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); - if (cmdid > nvmeq->q_depth) + if (cmdid >= nvmeq->q_depth) return CMD_CTX_INVALID; - data = nvmeq->cmdid_data[offset]; - nvmeq->cmdid_data[offset] = CMD_CTX_COMPLETED; + data = info[cmdid].ctx; + info[cmdid].ctx = CMD_CTX_COMPLETED; clear_bit(cmdid, nvmeq->cmdid_data); wake_up(&nvmeq->sq_full); return data; @@ -188,8 +202,8 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) { - unsigned offset = cmdid + BITS_TO_LONGS(nvmeq->q_depth); - nvmeq->cmdid_data[offset] = CMD_CTX_CANCELLED; + struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); + info[cmdid].ctx = CMD_CTX_CANCELLED; } static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) @@ -327,7 +341,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, goto congestion; info->bio = bio; - cmdid = alloc_cmdid(nvmeq, info, bio_completion_id); + cmdid = alloc_cmdid(nvmeq, info, bio_completion_id, IO_TIMEOUT); if (unlikely(cmdid < 0)) goto free_info; @@ -506,7 +520,7 @@ static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) * if the result is positive, it's an NVM Express status code */ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, - struct nvme_command *cmd, u32 *result) + struct nvme_command *cmd, u32 *result, unsigned timeout) { int cmdid; struct sync_cmd_info cmdinfo; @@ -514,7 +528,8 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, cmdinfo.task = current; cmdinfo.status = -EINTR; - cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id); + cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id, + timeout); if (cmdid < 0) return cmdid; cmd->common.command_id = cmdid; @@ -537,7 +552,7 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, static int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd, u32 *result) { - return nvme_submit_sync_cmd(dev->queues[0], cmd, result); + return nvme_submit_sync_cmd(dev->queues[0], cmd, result, ADMIN_TIMEOUT); } static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id) @@ -630,7 +645,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, int depth, int vector) { struct device *dmadev = &dev->pci_dev->dev; - unsigned extra = (depth + BITS_TO_LONGS(depth)) * sizeof(long); + unsigned extra = (depth / 8) + (depth * sizeof(struct nvme_cmd_info)); struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL); if (!nvmeq) return NULL; @@ -892,7 +907,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) * additional races since q_lock already protects against other CPUs. */ put_nvmeq(nvmeq); - status = nvme_submit_sync_cmd(nvmeq, &c, &result); + status = nvme_submit_sync_cmd(nvmeq, &c, &result, IO_TIMEOUT); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); put_user(result, &uio->result); -- cgit v0.10.2 From 9294bbed78926a895516ec016ba23033f58d1a88 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 7 Feb 2011 12:45:24 -0500 Subject: NVMe: Handle the congestion list a little better In the bio completion handler, check for bios on the congestion list for this NVM queue. Also, lock the congestion list in the make_request function as the queue may end up being shared between multiple CPUs. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 60c1048..2a0dd5e 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -105,6 +105,8 @@ struct nvme_queue { unsigned long cmdid_data[]; }; +static void nvme_resubmit_bio(struct nvme_queue *nvmeq, struct bio *bio); + /* * Check we didin't inadvertently grow the command struct */ @@ -274,6 +276,9 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); free_info(info); bio_endio(bio, status ? -EIO : 0); + bio = bio_list_pop(&nvmeq->sq_cong); + if (bio) + nvme_resubmit_bio(nvmeq, bio); } /* length is in bytes */ @@ -392,6 +397,16 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return -EBUSY; } +static void nvme_resubmit_bio(struct nvme_queue *nvmeq, struct bio *bio) +{ + struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data; + if (nvme_submit_bio_queue(nvmeq, ns, bio)) + bio_list_add_head(&nvmeq->sq_cong, bio); + else if (bio_list_empty(&nvmeq->sq_cong)) + blk_clear_queue_congested(ns->queue, rw_is_sync(bio->bi_rw)); + /* XXX: Need to duplicate the logic from __freed_request here */ +} + /* * NB: return value of non-zero would mean that we were a stacking driver. * make_request must always succeed. @@ -403,7 +418,9 @@ static int nvme_make_request(struct request_queue *q, struct bio *bio) if (nvme_submit_bio_queue(nvmeq, ns, bio)) { blk_set_queue_congested(q, rw_is_sync(bio->bi_rw)); + spin_lock_irq(&nvmeq->q_lock); bio_list_add(&nvmeq->sq_cong, bio); + spin_unlock_irq(&nvmeq->q_lock); } put_nvmeq(nvmeq); -- cgit v0.10.2 From d2d8703481f60d67f49e3177196cbe474b11377c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 7 Feb 2011 15:55:59 -0500 Subject: NVMe: Renumber the special context values If POISON_POINTER_DELTA isn't defined, ensure they're in page 0 which should never be mapped. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 2a0dd5e..71bdf6f 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -184,9 +184,9 @@ enum { }; #define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) -#define CMD_CTX_CANCELLED (0x2008 + CMD_CTX_BASE) -#define CMD_CTX_COMPLETED (0x2010 + CMD_CTX_BASE) -#define CMD_CTX_INVALID (0x2014 + CMD_CTX_BASE) +#define CMD_CTX_CANCELLED (0x30C + CMD_CTX_BASE) +#define CMD_CTX_COMPLETED (0x310 + CMD_CTX_BASE) +#define CMD_CTX_INVALID (0x314 + CMD_CTX_BASE) static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { -- cgit v0.10.2 From 51882d00f07da9601cc962a3596e48aafb4f4163 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 08:49:59 -0500 Subject: NVMe: Advance the sg pointer when filling in an sg list For multipage BIOs, we were always using sg[0] instead of advancing through the list. Oops :-) Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 71bdf6f..903e7f1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -321,6 +321,7 @@ static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, sg_init_table(sg, psegs); bio_for_each_segment(bvec, bio, i) { sg_set_page(sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); + sg++; /* XXX: handle non-mergable here */ nsegs++; } -- cgit v0.10.2 From e025344c56e08b155f43ea09647969286c78377c Mon Sep 17 00:00:00 2001 From: Shane Michael Matthews Date: Thu, 10 Feb 2011 08:51:24 -0500 Subject: NVMe: Initial PRP List support Add a pointer to the nvme_req_info to hold a new data structure (nvme_prps) which contains a list of the pages allocated to this particular request for holding PRP list entries. nvme_setup_prps() now returns this pointer. To allocate and free the memory used for PRP lists, we need a struct device, so we need to pass the nvme_queue pointer to many functions which didn't use to need it. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 903e7f1..b1e8445 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -247,21 +247,55 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) return 0; } +static __le64 *alloc_prp_list(struct nvme_queue *nvmeq, int length, + dma_addr_t *addr) +{ + return dma_alloc_coherent(nvmeq->q_dmadev, PAGE_SIZE, addr, GFP_ATOMIC); +} + +struct nvme_prps { + int npages; + dma_addr_t first_dma; + __le64 *list[0]; +}; + +static void nvme_free_prps(struct nvme_queue *nvmeq, struct nvme_prps *prps) +{ + const int last_prp = PAGE_SIZE / 8 - 1; + int i; + dma_addr_t prp_dma; + + if (!prps) + return; + + prp_dma = prps->first_dma; + for (i = 0; i < prps->npages; i++) { + __le64 *prp_list = prps->list[i]; + dma_addr_t next_prp_dma = le64_to_cpu(prp_list[last_prp]); + dma_free_coherent(nvmeq->q_dmadev, PAGE_SIZE, prp_list, + prp_dma); + prp_dma = next_prp_dma; + } + kfree(prps); +} + struct nvme_req_info { struct bio *bio; int nents; + struct nvme_prps *prps; struct scatterlist sg[0]; }; /* XXX: use a mempool */ static struct nvme_req_info *alloc_info(unsigned nseg, gfp_t gfp) { - return kmalloc(sizeof(struct nvme_req_info) + + return kzalloc(sizeof(struct nvme_req_info) + sizeof(struct scatterlist) * nseg, gfp); } -static void free_info(struct nvme_req_info *info) +static void free_info(struct nvme_queue *nvmeq, struct nvme_req_info *info) { + nvme_free_prps(nvmeq, info->prps); kfree(info); } @@ -274,7 +308,7 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, dma_unmap_sg(nvmeq->q_dmadev, info->sg, info->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - free_info(info); + free_info(nvmeq, info); bio_endio(bio, status ? -EIO : 0); bio = bio_list_pop(&nvmeq->sq_cong); if (bio) @@ -282,17 +316,22 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, } /* length is in bytes */ -static void nvme_setup_prps(struct nvme_common_command *cmd, +static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, + struct nvme_common_command *cmd, struct scatterlist *sg, int length) { int dma_len = sg_dma_len(sg); u64 dma_addr = sg_dma_address(sg); int offset = offset_in_page(dma_addr); + __le64 *prp_list; + dma_addr_t prp_dma; + int nprps, npages, i, prp_page; + struct nvme_prps *prps = NULL; cmd->prp1 = cpu_to_le64(dma_addr); length -= (PAGE_SIZE - offset); if (length <= 0) - return; + return prps; dma_len -= (PAGE_SIZE - offset); if (dma_len) { @@ -305,10 +344,42 @@ static void nvme_setup_prps(struct nvme_common_command *cmd, if (length <= PAGE_SIZE) { cmd->prp2 = cpu_to_le64(dma_addr); - return; + return prps; } - /* XXX: support PRP lists */ + nprps = DIV_ROUND_UP(length, PAGE_SIZE); + npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE); + prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC); + prps->npages = npages; + prp_page = 0; + prp_list = alloc_prp_list(nvmeq, length, &prp_dma); + prps->list[prp_page++] = prp_list; + prps->first_dma = prp_dma; + cmd->prp2 = cpu_to_le64(prp_dma); + i = 0; + for (;;) { + if (i == PAGE_SIZE / 8 - 1) { + __le64 *old_prp_list = prp_list; + prp_list = alloc_prp_list(nvmeq, length, &prp_dma); + prps->list[prp_page++] = prp_list; + old_prp_list[i] = cpu_to_le64(prp_dma); + i = 0; + } + prp_list[i++] = cpu_to_le64(dma_addr); + dma_len -= PAGE_SIZE; + dma_addr += PAGE_SIZE; + length -= PAGE_SIZE; + if (length <= 0) + break; + if (dma_len > 0) + continue; + BUG_ON(dma_len < 0); + sg = sg_next(sg); + dma_addr = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + } + + return prps; } static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, @@ -378,7 +449,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, cmnd->rw.flags = 1; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); - nvme_setup_prps(&cmnd->common, info->sg, bio->bi_size); + info->prps = nvme_setup_prps(nvmeq, &cmnd->common, info->sg, + bio->bi_size); cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); cmnd->rw.length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); cmnd->rw.control = cpu_to_le16(control); @@ -393,7 +465,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return 0; free_info: - free_info(info); + free_info(nvmeq, info); congestion: return -EBUSY; } @@ -852,13 +924,15 @@ static int nvme_submit_user_admin_command(struct nvme_dev *dev, { int err, nents; struct scatterlist *sg; + struct nvme_prps *prps; nents = nvme_map_user_pages(dev, 0, addr, length, &sg); if (nents < 0) return nents; - nvme_setup_prps(&cmd->common, sg, length); + prps = nvme_setup_prps(dev->queues[0], &cmd->common, sg, length); err = nvme_submit_admin_cmd(dev, cmd, NULL); nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); + nvme_free_prps(dev->queues[0], prps); return err ? -EIO : 0; } @@ -896,6 +970,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) u32 result; int nents, status; struct scatterlist *sg; + struct nvme_prps *prps; if (copy_from_user(&io, uio, sizeof(io))) return -EFAULT; @@ -915,10 +990,10 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.reftag = cpu_to_le32(io.reftag); /* XXX: endian? */ c.rw.apptag = cpu_to_le16(io.apptag); c.rw.appmask = cpu_to_le16(io.appmask); + nvmeq = get_nvmeq(ns); /* XXX: metadata */ - nvme_setup_prps(&c.common, sg, length); + prps = nvme_setup_prps(nvmeq, &c.common, sg, length); - nvmeq = get_nvmeq(ns); /* Since nvme_submit_sync_cmd sleeps, we can't keep preemption * disabled. We may be preempted at any point, and be rescheduled * to a different CPU. That will cause cacheline bouncing, but no @@ -928,6 +1003,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) status = nvme_submit_sync_cmd(nvmeq, &c, &result, IO_TIMEOUT); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); + nvme_free_prps(nvmeq, prps); put_user(result, &uio->result); return status; } @@ -940,6 +1016,7 @@ static int nvme_download_firmware(struct nvme_ns *ns, struct nvme_command c; int nents, status; struct scatterlist *sg; + struct nvme_prps *prps; if (copy_from_user(&dlfw, udlfw, sizeof(dlfw))) return -EFAULT; @@ -954,10 +1031,11 @@ static int nvme_download_firmware(struct nvme_ns *ns, c.dlfw.opcode = nvme_admin_download_fw; c.dlfw.numd = cpu_to_le32(dlfw.length); c.dlfw.offset = cpu_to_le32(dlfw.offset); - nvme_setup_prps(&c.common, sg, dlfw.length * 4); + prps = nvme_setup_prps(dev->queues[0], &c.common, sg, dlfw.length * 4); status = nvme_submit_admin_cmd(dev, &c, NULL); nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents); + nvme_free_prps(dev->queues[0], prps); return status; } -- cgit v0.10.2 From d534df3c730af9073a9ddc076d9fd65cbdca22b3 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 09:03:06 -0500 Subject: NVMe: Rename nvme_req_info to nvme_bio There are too many things called 'info' in this driver. This data structure is auxiliary information for a struct bio, so call it nvme_bio, or nbio when used as a variable. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b1e8445..11df0e9 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -279,7 +279,7 @@ static void nvme_free_prps(struct nvme_queue *nvmeq, struct nvme_prps *prps) kfree(prps); } -struct nvme_req_info { +struct nvme_bio { struct bio *bio; int nents; struct nvme_prps *prps; @@ -287,28 +287,28 @@ struct nvme_req_info { }; /* XXX: use a mempool */ -static struct nvme_req_info *alloc_info(unsigned nseg, gfp_t gfp) +static struct nvme_bio *alloc_nbio(unsigned nseg, gfp_t gfp) { - return kzalloc(sizeof(struct nvme_req_info) + + return kzalloc(sizeof(struct nvme_bio) + sizeof(struct scatterlist) * nseg, gfp); } -static void free_info(struct nvme_queue *nvmeq, struct nvme_req_info *info) +static void free_nbio(struct nvme_queue *nvmeq, struct nvme_bio *nbio) { - nvme_free_prps(nvmeq, info->prps); - kfree(info); + nvme_free_prps(nvmeq, nbio->prps); + kfree(nbio); } static void bio_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { - struct nvme_req_info *info = ctx; - struct bio *bio = info->bio; + struct nvme_bio *nbio = ctx; + struct bio *bio = nbio->bio; u16 status = le16_to_cpup(&cqe->status) >> 1; - dma_unmap_sg(nvmeq->q_dmadev, info->sg, info->nents, + dma_unmap_sg(nvmeq->q_dmadev, nbio->sg, nbio->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - free_info(nvmeq, info); + free_nbio(nvmeq, nbio); bio_endio(bio, status ? -EIO : 0); bio = bio_list_pop(&nvmeq->sq_cong); if (bio) @@ -382,11 +382,11 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, return prps; } -static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, +static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, struct bio *bio, enum dma_data_direction dma_dir, int psegs) { struct bio_vec *bvec; - struct scatterlist *sg = info->sg; + struct scatterlist *sg = nbio->sg; int i, nsegs; sg_init_table(sg, psegs); @@ -396,16 +396,16 @@ static int nvme_map_bio(struct device *dev, struct nvme_req_info *info, /* XXX: handle non-mergable here */ nsegs++; } - info->nents = nsegs; + nbio->nents = nsegs; - return dma_map_sg(dev, info->sg, info->nents, dma_dir); + return dma_map_sg(dev, nbio->sg, nbio->nents, dma_dir); } static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct bio *bio) { struct nvme_command *cmnd; - struct nvme_req_info *info; + struct nvme_bio *nbio; enum dma_data_direction dma_dir; int cmdid; u16 control; @@ -413,14 +413,14 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, unsigned long flags; int psegs = bio_phys_segments(ns->queue, bio); - info = alloc_info(psegs, GFP_NOIO); - if (!info) + nbio = alloc_nbio(psegs, GFP_NOIO); + if (!nbio) goto congestion; - info->bio = bio; + nbio->bio = bio; - cmdid = alloc_cmdid(nvmeq, info, bio_completion_id, IO_TIMEOUT); + cmdid = alloc_cmdid(nvmeq, nbio, bio_completion_id, IO_TIMEOUT); if (unlikely(cmdid < 0)) - goto free_info; + goto free_nbio; control = 0; if (bio->bi_rw & REQ_FUA) @@ -444,12 +444,12 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dma_dir = DMA_FROM_DEVICE; } - nvme_map_bio(nvmeq->q_dmadev, info, bio, dma_dir, psegs); + nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs); cmnd->rw.flags = 1; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); - info->prps = nvme_setup_prps(nvmeq, &cmnd->common, info->sg, + nbio->prps = nvme_setup_prps(nvmeq, &cmnd->common, nbio->sg, bio->bi_size); cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); cmnd->rw.length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); @@ -464,8 +464,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return 0; - free_info: - free_info(nvmeq, info); + free_nbio: + free_nbio(nvmeq, nbio); congestion: return -EBUSY; } -- cgit v0.10.2 From 091b609258b8e01cc45b01a41ca5e496f674d989 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 09:56:01 -0500 Subject: NVMe: Switch to use DMA Pool API Calling dma_free_coherent from interrupt context causes warnings. Using the DMA pools delays freeing until pool destruction, so avoids the problem. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 11df0e9..80fe6a7 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -57,6 +57,7 @@ struct nvme_dev { struct nvme_queue **queues; u32 __iomem *dbs; struct pci_dev *pci_dev; + struct dma_pool *prp_page_pool; int instance; int queue_count; u32 ctrl_config; @@ -88,6 +89,7 @@ struct nvme_ns { */ struct nvme_queue { struct device *q_dmadev; + struct nvme_dev *dev; spinlock_t q_lock; struct nvme_command *sq_cmds; volatile struct nvme_completion *cqes; @@ -247,10 +249,9 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) return 0; } -static __le64 *alloc_prp_list(struct nvme_queue *nvmeq, int length, - dma_addr_t *addr) +static __le64 *alloc_prp_list(struct nvme_dev *dev, dma_addr_t *addr) { - return dma_alloc_coherent(nvmeq->q_dmadev, PAGE_SIZE, addr, GFP_ATOMIC); + return dma_pool_alloc(dev->prp_page_pool, GFP_ATOMIC, addr); } struct nvme_prps { @@ -262,6 +263,7 @@ struct nvme_prps { static void nvme_free_prps(struct nvme_queue *nvmeq, struct nvme_prps *prps) { const int last_prp = PAGE_SIZE / 8 - 1; + struct nvme_dev *dev = nvmeq->dev; int i; dma_addr_t prp_dma; @@ -272,8 +274,7 @@ static void nvme_free_prps(struct nvme_queue *nvmeq, struct nvme_prps *prps) for (i = 0; i < prps->npages; i++) { __le64 *prp_list = prps->list[i]; dma_addr_t next_prp_dma = le64_to_cpu(prp_list[last_prp]); - dma_free_coherent(nvmeq->q_dmadev, PAGE_SIZE, prp_list, - prp_dma); + dma_pool_free(dev->prp_page_pool, prp_list, prp_dma); prp_dma = next_prp_dma; } kfree(prps); @@ -320,6 +321,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, struct nvme_common_command *cmd, struct scatterlist *sg, int length) { + struct nvme_dev *dev = nvmeq->dev; int dma_len = sg_dma_len(sg); u64 dma_addr = sg_dma_address(sg); int offset = offset_in_page(dma_addr); @@ -352,7 +354,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC); prps->npages = npages; prp_page = 0; - prp_list = alloc_prp_list(nvmeq, length, &prp_dma); + prp_list = alloc_prp_list(dev, &prp_dma); prps->list[prp_page++] = prp_list; prps->first_dma = prp_dma; cmd->prp2 = cpu_to_le64(prp_dma); @@ -360,7 +362,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, for (;;) { if (i == PAGE_SIZE / 8 - 1) { __le64 *old_prp_list = prp_list; - prp_list = alloc_prp_list(nvmeq, length, &prp_dma); + prp_list = alloc_prp_list(dev, &prp_dma); prps->list[prp_page++] = prp_list; old_prp_list[i] = cpu_to_le64(prp_dma); i = 0; @@ -752,6 +754,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, goto free_cqdma; nvmeq->q_dmadev = dmadev; + nvmeq->dev = dev; spin_lock_init(&nvmeq->q_lock); nvmeq->cq_head = 0; nvmeq->cq_phase = 1; @@ -1302,6 +1305,22 @@ static int nvme_dev_remove(struct nvme_dev *dev) return 0; } +static int nvme_setup_prp_pools(struct nvme_dev *dev) +{ + struct device *dmadev = &dev->pci_dev->dev; + dev->prp_page_pool = dma_pool_create("prp list page", dmadev, + PAGE_SIZE, PAGE_SIZE, 0); + if (!dev->prp_page_pool) + return -ENOMEM; + + return 0; +} + +static void nvme_release_prp_pools(struct nvme_dev *dev) +{ + dma_pool_destroy(dev->prp_page_pool); +} + /* XXX: Use an ida or something to let remove / add work correctly */ static void nvme_set_instance(struct nvme_dev *dev) { @@ -1346,6 +1365,10 @@ static int __devinit nvme_probe(struct pci_dev *pdev, nvme_set_instance(dev); dev->entry[0].vector = pdev->irq; + result = nvme_setup_prp_pools(dev); + if (result) + goto disable_msix; + dev->bar = ioremap(pci_resource_start(pdev, 0), 8192); if (!dev->bar) { result = -ENOMEM; @@ -1369,6 +1392,7 @@ static int __devinit nvme_probe(struct pci_dev *pdev, disable_msix: pci_disable_msix(pdev); nvme_release_instance(dev); + nvme_release_prp_pools(dev); disable: pci_disable_device(pdev); pci_release_regions(pdev); @@ -1386,6 +1410,7 @@ static void __devexit nvme_remove(struct pci_dev *pdev) pci_disable_msix(pdev); iounmap(dev->bar); nvme_release_instance(dev); + nvme_release_prp_pools(dev); pci_disable_device(pdev); pci_release_regions(pdev); kfree(dev->queues); -- cgit v0.10.2 From 99802a7aee2b3dd720e382c52b892cc6a8122b11 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 10:30:34 -0500 Subject: NVMe: Optimise memory usage for I/Os between 4k and 128k Add a second memory pool for smaller I/Os. We can pack 16 of these on a single page instead of using an entire page for each one. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 80fe6a7..cd7aeba 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -58,6 +58,7 @@ struct nvme_dev { u32 __iomem *dbs; struct pci_dev *pci_dev; struct dma_pool *prp_page_pool; + struct dma_pool *prp_small_pool; int instance; int queue_count; u32 ctrl_config; @@ -249,11 +250,6 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) return 0; } -static __le64 *alloc_prp_list(struct nvme_dev *dev, dma_addr_t *addr) -{ - return dma_pool_alloc(dev->prp_page_pool, GFP_ATOMIC, addr); -} - struct nvme_prps { int npages; dma_addr_t first_dma; @@ -271,6 +267,9 @@ static void nvme_free_prps(struct nvme_queue *nvmeq, struct nvme_prps *prps) return; prp_dma = prps->first_dma; + + if (prps->npages == 0) + dma_pool_free(dev->prp_small_pool, prps->list[0], prp_dma); for (i = 0; i < prps->npages; i++) { __le64 *prp_list = prps->list[i]; dma_addr_t next_prp_dma = le64_to_cpu(prp_list[last_prp]); @@ -322,6 +321,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, struct scatterlist *sg, int length) { struct nvme_dev *dev = nvmeq->dev; + struct dma_pool *pool; int dma_len = sg_dma_len(sg); u64 dma_addr = sg_dma_address(sg); int offset = offset_in_page(dma_addr); @@ -352,9 +352,16 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, nprps = DIV_ROUND_UP(length, PAGE_SIZE); npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE); prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC); - prps->npages = npages; prp_page = 0; - prp_list = alloc_prp_list(dev, &prp_dma); + if (nprps <= (256 / 8)) { + pool = dev->prp_small_pool; + prps->npages = 0; + } else { + pool = dev->prp_page_pool; + prps->npages = npages; + } + + prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); prps->list[prp_page++] = prp_list; prps->first_dma = prp_dma; cmd->prp2 = cpu_to_le64(prp_dma); @@ -362,7 +369,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, for (;;) { if (i == PAGE_SIZE / 8 - 1) { __le64 *old_prp_list = prp_list; - prp_list = alloc_prp_list(dev, &prp_dma); + prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); prps->list[prp_page++] = prp_list; old_prp_list[i] = cpu_to_le64(prp_dma); i = 0; @@ -1313,12 +1320,20 @@ static int nvme_setup_prp_pools(struct nvme_dev *dev) if (!dev->prp_page_pool) return -ENOMEM; + /* Optimisation for I/Os between 4k and 128k */ + dev->prp_small_pool = dma_pool_create("prp list 256", dmadev, + 256, 256, 0); + if (!dev->prp_small_pool) { + dma_pool_destroy(dev->prp_page_pool); + return -ENOMEM; + } return 0; } static void nvme_release_prp_pools(struct nvme_dev *dev) { dma_pool_destroy(dev->prp_page_pool); + dma_pool_destroy(dev->prp_small_pool); } /* XXX: Use an ida or something to let remove / add work correctly */ -- cgit v0.10.2 From d567760c409f981d35fc755b51d5bf56a99a467b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 10:47:55 -0500 Subject: NVMe: Pass the nvme_dev to nvme_free_prps and nvme_setup_prps We were passing the nvme_queue to access the q_dmadev for the dma_alloc_coherent calls, but since we moved to the dma pool API, we really only need the nvme_dev. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index cd7aeba..2948043 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -256,10 +256,9 @@ struct nvme_prps { __le64 *list[0]; }; -static void nvme_free_prps(struct nvme_queue *nvmeq, struct nvme_prps *prps) +static void nvme_free_prps(struct nvme_dev *dev, struct nvme_prps *prps) { const int last_prp = PAGE_SIZE / 8 - 1; - struct nvme_dev *dev = nvmeq->dev; int i; dma_addr_t prp_dma; @@ -295,7 +294,7 @@ static struct nvme_bio *alloc_nbio(unsigned nseg, gfp_t gfp) static void free_nbio(struct nvme_queue *nvmeq, struct nvme_bio *nbio) { - nvme_free_prps(nvmeq, nbio->prps); + nvme_free_prps(nvmeq->dev, nbio->prps); kfree(nbio); } @@ -316,11 +315,10 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, } /* length is in bytes */ -static struct nvme_prps *nvme_setup_prps(struct nvme_queue *nvmeq, +static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, struct scatterlist *sg, int length) { - struct nvme_dev *dev = nvmeq->dev; struct dma_pool *pool; int dma_len = sg_dma_len(sg); u64 dma_addr = sg_dma_address(sg); @@ -458,7 +456,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, cmnd->rw.flags = 1; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); - nbio->prps = nvme_setup_prps(nvmeq, &cmnd->common, nbio->sg, + nbio->prps = nvme_setup_prps(nvmeq->dev, &cmnd->common, nbio->sg, bio->bi_size); cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); cmnd->rw.length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); @@ -939,10 +937,10 @@ static int nvme_submit_user_admin_command(struct nvme_dev *dev, nents = nvme_map_user_pages(dev, 0, addr, length, &sg); if (nents < 0) return nents; - prps = nvme_setup_prps(dev->queues[0], &cmd->common, sg, length); + prps = nvme_setup_prps(dev, &cmd->common, sg, length); err = nvme_submit_admin_cmd(dev, cmd, NULL); nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); - nvme_free_prps(dev->queues[0], prps); + nvme_free_prps(dev, prps); return err ? -EIO : 0; } @@ -1000,10 +998,10 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.reftag = cpu_to_le32(io.reftag); /* XXX: endian? */ c.rw.apptag = cpu_to_le16(io.apptag); c.rw.appmask = cpu_to_le16(io.appmask); - nvmeq = get_nvmeq(ns); /* XXX: metadata */ - prps = nvme_setup_prps(nvmeq, &c.common, sg, length); + prps = nvme_setup_prps(dev, &c.common, sg, length); + nvmeq = get_nvmeq(ns); /* Since nvme_submit_sync_cmd sleeps, we can't keep preemption * disabled. We may be preempted at any point, and be rescheduled * to a different CPU. That will cause cacheline bouncing, but no @@ -1013,7 +1011,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) status = nvme_submit_sync_cmd(nvmeq, &c, &result, IO_TIMEOUT); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); - nvme_free_prps(nvmeq, prps); + nvme_free_prps(dev, prps); put_user(result, &uio->result); return status; } @@ -1041,11 +1039,11 @@ static int nvme_download_firmware(struct nvme_ns *ns, c.dlfw.opcode = nvme_admin_download_fw; c.dlfw.numd = cpu_to_le32(dlfw.length); c.dlfw.offset = cpu_to_le32(dlfw.offset); - prps = nvme_setup_prps(dev->queues[0], &c.common, sg, dlfw.length * 4); + prps = nvme_setup_prps(dev, &c.common, sg, dlfw.length * 4); status = nvme_submit_admin_cmd(dev, &c, NULL); nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents); - nvme_free_prps(dev->queues[0], prps); + nvme_free_prps(dev, prps); return status; } -- cgit v0.10.2 From 1974b1ae8852324a75fb8cfecbc7b758fd5a2c3c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 12:01:09 -0500 Subject: NVMe: Check for DMA mapping failure If dma_map_sg returns 0 (failure), we need to fail the I/O. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 2948043..bfdca3a 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -451,7 +451,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dma_dir = DMA_FROM_DEVICE; } - nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs); + if (nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs) == 0) + goto mapping_failed; cmnd->rw.flags = 1; cmnd->rw.command_id = cmdid; @@ -471,6 +472,11 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return 0; + mapping_failed: + free_nbio(nvmeq, nbio); + bio_endio(bio, -ENOMEM); + return 0; + free_nbio: free_nbio(nvmeq, nbio); congestion: -- cgit v0.10.2 From 768308400f5b4ce665a072eb976a851978b7706e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 10 Feb 2011 13:55:39 -0500 Subject: NVMe: Handle physical merging of bvec entries In order to not overrun the sg array, we have to merge physically contiguous pages into a single sg entry. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index bfdca3a..c0e84b6 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -392,19 +392,25 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, struct bio *bio, enum dma_data_direction dma_dir, int psegs) { - struct bio_vec *bvec; - struct scatterlist *sg = nbio->sg; - int i, nsegs; + struct bio_vec *bvec, *bvprv = NULL; + struct scatterlist *sg = NULL; + int i, nsegs = 0; - sg_init_table(sg, psegs); + sg_init_table(nbio->sg, psegs); bio_for_each_segment(bvec, bio, i) { - sg_set_page(sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); - sg++; - /* XXX: handle non-mergable here */ - nsegs++; + if (bvprv && BIOVEC_PHYS_MERGEABLE(bvprv, bvec)) { + sg->length += bvec->bv_len; + } else { + /* Check bvprv && offset == 0 */ + sg = sg ? sg + 1 : nbio->sg; + sg_set_page(sg, bvec->bv_page, bvec->bv_len, + bvec->bv_offset); + nsegs++; + } + bvprv = bvec; } nbio->nents = nsegs; - + sg_mark_end(sg); return dma_map_sg(dev, nbio->sg, nbio->nents, dma_dir); } -- cgit v0.10.2 From 897cfe1ce7db152fa6dde576f4213a6160bf6502 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 14 Feb 2011 12:20:15 -0500 Subject: NVMe: Update BAR structure to match the current spec Add two reserved registers in the middle of the BAR to match the 1.0 spec plus ECN 0002. Also rename IMC and ISC to INTMC and INTSC to conform with the spec. We still don't need to use them :-) Signed-off-by: Matthew Wilcox diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 8eed0e4..757faa7 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -24,10 +24,12 @@ struct nvme_bar { __u64 cap; /* Controller Capabilities */ __u32 vs; /* Version */ - __u32 ims; /* Interrupt Mask Set */ - __u32 imc; /* Interrupt Mask Clear */ + __u32 intms; /* Interrupt Mask Set */ + __u32 intmc; /* Interrupt Mask Clear */ __u32 cc; /* Controller Configuration */ + __u32 rsvd1; /* Reserved */ __u32 csts; /* Controller Status */ + __u32 rsvd2; /* Reserved */ __u32 aqa; /* Admin Queue Attributes */ __u64 asq; /* Admin SQ Base Address */ __u64 acq; /* Admin CQ Base Address */ -- cgit v0.10.2 From eeee322647a67c20d9277c5e02c42b2126ea74bc Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 14 Feb 2011 15:55:33 -0500 Subject: NVMe: Handle failures differently in nvme_submit_bio_queue() Return -EBUSY if the queue is full or -ENOMEM if we failed to allocate memory (or map a scatterlist). Also use GFP_ATOMIC to allocate the nvme_bio and move the locking to the callers of nvme_submit_bio_queue(). In nvme_make_request(), don't permit an I/O to jump the queue -- if the congestion list already has an entry, just add to the tail, rather than trying to submit. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index c0e84b6..61a2417 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -420,17 +420,17 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct nvme_command *cmnd; struct nvme_bio *nbio; enum dma_data_direction dma_dir; - int cmdid; + int cmdid, result = -ENOMEM; u16 control; u32 dsmgmt; - unsigned long flags; int psegs = bio_phys_segments(ns->queue, bio); - nbio = alloc_nbio(psegs, GFP_NOIO); + nbio = alloc_nbio(psegs, GFP_ATOMIC); if (!nbio) - goto congestion; + goto nomem; nbio->bio = bio; + result = -EBUSY; cmdid = alloc_cmdid(nvmeq, nbio, bio_completion_id, IO_TIMEOUT); if (unlikely(cmdid < 0)) goto free_nbio; @@ -445,7 +445,6 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, if (bio->bi_rw & REQ_RAHEAD) dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH; - spin_lock_irqsave(&nvmeq->q_lock, flags); cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail]; memset(cmnd, 0, sizeof(*cmnd)); @@ -457,8 +456,9 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dma_dir = DMA_FROM_DEVICE; } + result = -ENOMEM; if (nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs) == 0) - goto mapping_failed; + goto free_nbio; cmnd->rw.flags = 1; cmnd->rw.command_id = cmdid; @@ -474,19 +474,12 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, if (++nvmeq->sq_tail == nvmeq->q_depth) nvmeq->sq_tail = 0; - spin_unlock_irqrestore(&nvmeq->q_lock, flags); - - return 0; - - mapping_failed: - free_nbio(nvmeq, nbio); - bio_endio(bio, -ENOMEM); return 0; free_nbio: free_nbio(nvmeq, nbio); - congestion: - return -EBUSY; + nomem: + return result; } static void nvme_resubmit_bio(struct nvme_queue *nvmeq, struct bio *bio) @@ -507,13 +500,18 @@ static int nvme_make_request(struct request_queue *q, struct bio *bio) { struct nvme_ns *ns = q->queuedata; struct nvme_queue *nvmeq = get_nvmeq(ns); + int result = -EBUSY; - if (nvme_submit_bio_queue(nvmeq, ns, bio)) { - blk_set_queue_congested(q, rw_is_sync(bio->bi_rw)); - spin_lock_irq(&nvmeq->q_lock); + spin_lock_irq(&nvmeq->q_lock); + if (bio_list_empty(&nvmeq->sq_cong)) + result = nvme_submit_bio_queue(nvmeq, ns, bio); + if (unlikely(result)) { + if (bio_list_empty(&nvmeq->sq_cong)) + add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); bio_list_add(&nvmeq->sq_cong, bio); - spin_unlock_irq(&nvmeq->q_lock); } + + spin_unlock_irq(&nvmeq->q_lock); put_nvmeq(nvmeq); return 0; -- cgit v0.10.2 From 1fa6aeadf18aeebd7a217d7a3a933856448375b6 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 2 Mar 2011 18:37:18 -0500 Subject: NVMe: Add a kthread to handle the congestion list Instead of trying to resubmit I/Os in the I/O completion path (in interrupt context), wake up a kthread which will resubmit I/O from user context. This allows mke2fs to run to completion. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 61a2417..606371e 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -50,10 +51,15 @@ module_param(nvme_major, int, 0); static int use_threaded_interrupts; module_param(use_threaded_interrupts, int, 0); +static DEFINE_SPINLOCK(dev_list_lock); +static LIST_HEAD(dev_list); +static struct task_struct *nvme_thread; + /* * Represents an NVM Express device. Each nvme_dev is a PCI function. */ struct nvme_dev { + struct list_head node; struct nvme_queue **queues; u32 __iomem *dbs; struct pci_dev *pci_dev; @@ -97,6 +103,7 @@ struct nvme_queue { dma_addr_t sq_dma_addr; dma_addr_t cq_dma_addr; wait_queue_head_t sq_full; + wait_queue_t sq_cong_wait; struct bio_list sq_cong; u32 __iomem *q_db; u16 q_depth; @@ -108,8 +115,6 @@ struct nvme_queue { unsigned long cmdid_data[]; }; -static void nvme_resubmit_bio(struct nvme_queue *nvmeq, struct bio *bio); - /* * Check we didin't inadvertently grow the command struct */ @@ -309,9 +314,6 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); free_nbio(nvmeq, nbio); bio_endio(bio, status ? -EIO : 0); - bio = bio_list_pop(&nvmeq->sq_cong); - if (bio) - nvme_resubmit_bio(nvmeq, bio); } /* length is in bytes */ @@ -482,16 +484,6 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return result; } -static void nvme_resubmit_bio(struct nvme_queue *nvmeq, struct bio *bio) -{ - struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data; - if (nvme_submit_bio_queue(nvmeq, ns, bio)) - bio_list_add_head(&nvmeq->sq_cong, bio); - else if (bio_list_empty(&nvmeq->sq_cong)) - blk_clear_queue_congested(ns->queue, rw_is_sync(bio->bi_rw)); - /* XXX: Need to duplicate the logic from __freed_request here */ -} - /* * NB: return value of non-zero would mean that we were a stacking driver. * make_request must always succeed. @@ -774,6 +766,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, nvmeq->cq_head = 0; nvmeq->cq_phase = 1; init_waitqueue_head(&nvmeq->sq_full); + init_waitqueue_entry(&nvmeq->sq_cong_wait, nvme_thread); bio_list_init(&nvmeq->sq_cong); nvmeq->q_db = &dev->dbs[qid * 2]; nvmeq->q_depth = depth; @@ -1097,6 +1090,43 @@ static const struct block_device_operations nvme_fops = { .ioctl = nvme_ioctl, }; +static void nvme_resubmit_bios(struct nvme_queue *nvmeq) +{ + while (bio_list_peek(&nvmeq->sq_cong)) { + struct bio *bio = bio_list_pop(&nvmeq->sq_cong); + struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data; + if (nvme_submit_bio_queue(nvmeq, ns, bio)) { + bio_list_add_head(&nvmeq->sq_cong, bio); + break; + } + } +} + +static int nvme_kthread(void *data) +{ + struct nvme_dev *dev; + + while (!kthread_should_stop()) { + __set_current_state(TASK_RUNNING); + spin_lock(&dev_list_lock); + list_for_each_entry(dev, &dev_list, node) { + int i; + for (i = 0; i < dev->queue_count; i++) { + struct nvme_queue *nvmeq = dev->queues[i]; + spin_lock_irq(&nvmeq->q_lock); + if (nvme_process_cq(nvmeq)) + printk("process_cq did something\n"); + nvme_resubmit_bios(nvmeq); + spin_unlock_irq(&nvmeq->q_lock); + } + } + spin_unlock(&dev_list_lock); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + return 0; +} + static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int index, struct nvme_id_ns *id, struct nvme_lba_range_type *rt) { @@ -1307,6 +1337,10 @@ static int nvme_dev_remove(struct nvme_dev *dev) { struct nvme_ns *ns, *next; + spin_lock(&dev_list_lock); + list_del(&dev->node); + spin_unlock(&dev_list_lock); + /* TODO: wait all I/O finished or cancel them */ list_for_each_entry_safe(ns, next, &dev->namespaces, list) { @@ -1406,6 +1440,11 @@ static int __devinit nvme_probe(struct pci_dev *pdev, result = nvme_dev_add(dev); if (result) goto delete; + + spin_lock(&dev_list_lock); + list_add(&dev->node, &dev_list); + spin_unlock(&dev_list_lock); + return 0; delete: @@ -1479,17 +1518,25 @@ static struct pci_driver nvme_driver = { static int __init nvme_init(void) { - int result; + int result = -EBUSY; + + nvme_thread = kthread_run(nvme_kthread, NULL, "nvme"); + if (IS_ERR(nvme_thread)) + return PTR_ERR(nvme_thread); nvme_major = register_blkdev(nvme_major, "nvme"); if (nvme_major <= 0) - return -EBUSY; + goto kill_kthread; result = pci_register_driver(&nvme_driver); - if (!result) - return 0; + if (result) + goto unregister_blkdev; + return 0; + unregister_blkdev: unregister_blkdev(nvme_major, "nvme"); + kill_kthread: + kthread_stop(nvme_thread); return result; } @@ -1497,6 +1544,7 @@ static void __exit nvme_exit(void) { pci_unregister_driver(&nvme_driver); unregister_blkdev(nvme_major, "nvme"); + kthread_stop(nvme_thread); } MODULE_AUTHOR("Matthew Wilcox "); -- cgit v0.10.2 From ad8a5df97cb060aa4d817af25587c99e2d2fda97 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 14 Feb 2011 17:35:00 -0500 Subject: NVMe: Release 0.3 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 606371e..7554625 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1549,6 +1549,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From ca1615424c9adfdbe7d484771d7a7c5ecc4bb6d2 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 15 Feb 2011 13:44:13 -0500 Subject: NVMe: Remove setting of 'flags' in rw command This was the data transfer bit until spec rev 0.92 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 7554625..37cdf07 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -462,7 +462,6 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, if (nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs) == 0) goto free_nbio; - cmnd->rw.flags = 1; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); nbio->prps = nvme_setup_prps(nvmeq->dev, &cmnd->common, nbio->sg, -- cgit v0.10.2 From b348b7d54368c87811907a8e88f0d96713c43009 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 15 Feb 2011 16:16:02 -0500 Subject: NVMe: Rename nr_queues to nr_io_queues I got confused about whether this included the admin queue or not, and had to resort to reading the spec. It doesn't include the admin queue, so make that clear in the name. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 37cdf07..f3aa809 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1202,29 +1202,30 @@ static int set_queue_count(struct nvme_dev *dev, int count) static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) { - int result, cpu, i, nr_queues; + int result, cpu, i, nr_io_queues; - nr_queues = num_online_cpus(); - result = set_queue_count(dev, nr_queues); + nr_io_queues = num_online_cpus(); + result = set_queue_count(dev, nr_io_queues); if (result < 0) return result; - if (result < nr_queues) - nr_queues = result; + if (result < nr_io_queues) + nr_io_queues = result; /* Deregister the admin queue's interrupt */ free_irq(dev->entry[0].vector, dev->queues[0]); - for (i = 0; i < nr_queues; i++) + for (i = 0; i < nr_io_queues; i++) dev->entry[i].entry = i; for (;;) { - result = pci_enable_msix(dev->pci_dev, dev->entry, nr_queues); + result = pci_enable_msix(dev->pci_dev, dev->entry, + nr_io_queues); if (result == 0) { break; } else if (result > 0) { - nr_queues = result; + nr_io_queues = result; continue; } else { - nr_queues = 1; + nr_io_queues = 1; break; } } @@ -1233,12 +1234,12 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) /* XXX: handle failure here */ cpu = cpumask_first(cpu_online_mask); - for (i = 0; i < nr_queues; i++) { + for (i = 0; i < nr_io_queues; i++) { irq_set_affinity_hint(dev->entry[i].vector, get_cpu_mask(cpu)); cpu = cpumask_next(cpu, cpu_online_mask); } - for (i = 0; i < nr_queues; i++) { + for (i = 0; i < nr_io_queues; i++) { dev->queues[i + 1] = nvme_create_queue(dev, i + 1, NVME_Q_DEPTH, i); if (!dev->queues[i + 1]) -- cgit v0.10.2 From 740216fc59cba54f65187c9ed92f29bce3cf8778 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 15 Feb 2011 16:28:20 -0500 Subject: NVMe: Let the kthread take care of devices earlier If interrupts are misconfigured, the kthread will be needed to process admin queue completions. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f3aa809..df1d8bd 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1112,6 +1112,8 @@ static int nvme_kthread(void *data) int i; for (i = 0; i < dev->queue_count; i++) { struct nvme_queue *nvmeq = dev->queues[i]; + if (!nvmeq) + continue; spin_lock_irq(&nvmeq->q_lock); if (nvme_process_cq(nvmeq)) printk("process_cq did something\n"); @@ -1437,17 +1439,21 @@ static int __devinit nvme_probe(struct pci_dev *pdev, goto unmap; dev->queue_count++; - result = nvme_dev_add(dev); - if (result) - goto delete; - spin_lock(&dev_list_lock); list_add(&dev->node, &dev_list); spin_unlock(&dev_list_lock); + result = nvme_dev_add(dev); + if (result) + goto delete; + return 0; delete: + spin_lock(&dev_list_lock); + list_del(&dev->node); + spin_unlock(&dev_list_lock); + nvme_free_queues(dev); unmap: iounmap(dev->bar); -- cgit v0.10.2 From 7547881d0951384f9833ec3a80fac8f3f16f3b98 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Feb 2011 09:59:59 -0500 Subject: NVMe: Correct SQ doorbell semantics The value written to the doorbell needs to be the first free index in the queue, not the most recently used index in the queue. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index df1d8bd..af45e28 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -246,9 +246,9 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) spin_lock_irqsave(&nvmeq->q_lock, flags); tail = nvmeq->sq_tail; memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd)); - writel(tail, nvmeq->q_db); if (++tail == nvmeq->q_depth) tail = 0; + writel(tail, nvmeq->q_db); nvmeq->sq_tail = tail; spin_unlock_irqrestore(&nvmeq->q_lock, flags); @@ -471,9 +471,9 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, cmnd->rw.control = cpu_to_le16(control); cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt); - writel(nvmeq->sq_tail, nvmeq->q_db); if (++nvmeq->sq_tail == nvmeq->q_depth) nvmeq->sq_tail = 0; + writel(nvmeq->sq_tail, nvmeq->q_db); return 0; -- cgit v0.10.2 From c42705592be2a539f3027b6f3907de8e8f9591a8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 22 Feb 2011 14:15:34 -0500 Subject: NVMe: Mark CMD_CTX_CANCELLED as being unlikely Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index af45e28..ce919b4 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -518,7 +518,7 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { struct sync_cmd_info *cmdinfo = ctx; - if ((unsigned long)cmdinfo == CMD_CTX_CANCELLED) + if (unlikely((unsigned long)cmdinfo == CMD_CTX_CANCELLED)) return; if (unlikely((unsigned long)cmdinfo == CMD_CTX_COMPLETED)) { dev_warn(nvmeq->q_dmadev, -- cgit v0.10.2 From 00df5cb4eb927078850086f8becc3286a69ea12e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 22 Feb 2011 14:18:30 -0500 Subject: NVMe: Implement Flush Linux implements Flush as a bit in the bio. That means there may also be data associated with the flush; if so the flush should be sent before the data. To avoid completing the bio twice, I add CMD_CTX_FLUSH to indicate the completion routine should do nothing. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index ce919b4..d99b400 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -191,10 +191,12 @@ enum { bio_completion_id, }; +/* Special values must be a multiple of 4, and less than 0x1000 */ #define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) #define CMD_CTX_CANCELLED (0x30C + CMD_CTX_BASE) #define CMD_CTX_COMPLETED (0x310 + CMD_CTX_BASE) #define CMD_CTX_INVALID (0x314 + CMD_CTX_BASE) +#define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE) static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { @@ -416,6 +418,33 @@ static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, return dma_map_sg(dev, nbio->sg, nbio->nents, dma_dir); } +static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns, + int cmdid) +{ + struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail]; + + memset(cmnd, 0, sizeof(*cmnd)); + cmnd->common.opcode = nvme_cmd_flush; + cmnd->common.command_id = cmdid; + cmnd->common.nsid = cpu_to_le32(ns->ns_id); + + if (++nvmeq->sq_tail == nvmeq->q_depth) + nvmeq->sq_tail = 0; + writel(nvmeq->sq_tail, nvmeq->q_db); + + return 0; +} + +static int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns) +{ + int cmdid = alloc_cmdid(nvmeq, (void *)CMD_CTX_FLUSH, + sync_completion_id, IO_TIMEOUT); + if (unlikely(cmdid < 0)) + return cmdid; + + return nvme_submit_flush(nvmeq, ns, cmdid); +} + static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct bio *bio) { @@ -427,6 +456,12 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, u32 dsmgmt; int psegs = bio_phys_segments(ns->queue, bio); + if ((bio->bi_rw & REQ_FLUSH) && psegs) { + result = nvme_submit_flush_data(nvmeq, ns); + if (result) + return result; + } + nbio = alloc_nbio(psegs, GFP_ATOMIC); if (!nbio) goto nomem; @@ -437,6 +472,9 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, if (unlikely(cmdid < 0)) goto free_nbio; + if ((bio->bi_rw & REQ_FLUSH) && !psegs) + return nvme_submit_flush(nvmeq, ns, cmdid); + control = 0; if (bio->bi_rw & REQ_FUA) control |= NVME_RW_FUA; @@ -520,6 +558,8 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, struct sync_cmd_info *cmdinfo = ctx; if (unlikely((unsigned long)cmdinfo == CMD_CTX_CANCELLED)) return; + if ((unsigned long)cmdinfo == CMD_CTX_FLUSH) + return; if (unlikely((unsigned long)cmdinfo == CMD_CTX_COMPLETED)) { dev_warn(nvmeq->q_dmadev, "completed id %d twice on queue %d\n", -- cgit v0.10.2 From 1ad2f8932a72bf375361727949ced2cb4e8cfcef Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 23 Feb 2011 15:20:00 -0500 Subject: NVMe: Handle bios that contain non-virtually contiguous addresses NVMe scatterlists must be virtually contiguous, like almost all I/Os. However, when the filesystem lays out files with a hole, it can be that adjacent LBAs map to non-adjacent virtual addresses. Handle this by submitting one NVMe command at a time for each virtually discontiguous range. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index d99b400..2409227 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -315,7 +315,14 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, dma_unmap_sg(nvmeq->q_dmadev, nbio->sg, nbio->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); free_nbio(nvmeq, nbio); - bio_endio(bio, status ? -EIO : 0); + if (status) + bio_endio(bio, -EIO); + if (bio->bi_vcnt > bio->bi_idx) { + bio_list_add(&nvmeq->sq_cong, bio); + wake_up_process(nvme_thread); + } else { + bio_endio(bio, 0); + } } /* length is in bytes */ @@ -393,29 +400,41 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, return prps; } +/* NVMe scatterlists require no holes in the virtual address */ +#define BIOVEC_NOT_VIRT_MERGEABLE(vec1, vec2) ((vec2)->bv_offset || \ + (((vec1)->bv_offset + (vec1)->bv_len) % PAGE_SIZE)) + static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, struct bio *bio, enum dma_data_direction dma_dir, int psegs) { struct bio_vec *bvec, *bvprv = NULL; struct scatterlist *sg = NULL; - int i, nsegs = 0; + int i, old_idx, length = 0, nsegs = 0; sg_init_table(nbio->sg, psegs); + old_idx = bio->bi_idx; bio_for_each_segment(bvec, bio, i) { if (bvprv && BIOVEC_PHYS_MERGEABLE(bvprv, bvec)) { sg->length += bvec->bv_len; } else { - /* Check bvprv && offset == 0 */ + if (bvprv && BIOVEC_NOT_VIRT_MERGEABLE(bvprv, bvec)) + break; sg = sg ? sg + 1 : nbio->sg; sg_set_page(sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); nsegs++; } + length += bvec->bv_len; bvprv = bvec; } + bio->bi_idx = i; nbio->nents = nsegs; sg_mark_end(sg); - return dma_map_sg(dev, nbio->sg, nbio->nents, dma_dir); + if (dma_map_sg(dev, nbio->sg, nbio->nents, dma_dir) == 0) { + bio->bi_idx = old_idx; + return -ENOMEM; + } + return length; } static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns, @@ -451,7 +470,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct nvme_command *cmnd; struct nvme_bio *nbio; enum dma_data_direction dma_dir; - int cmdid, result = -ENOMEM; + int cmdid, length, result = -ENOMEM; u16 control; u32 dsmgmt; int psegs = bio_phys_segments(ns->queue, bio); @@ -496,16 +515,17 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dma_dir = DMA_FROM_DEVICE; } - result = -ENOMEM; - if (nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs) == 0) + result = nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs); + if (result < 0) goto free_nbio; + length = result; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); nbio->prps = nvme_setup_prps(nvmeq->dev, &cmnd->common, nbio->sg, - bio->bi_size); + length); cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); - cmnd->rw.length = cpu_to_le16((bio->bi_size >> ns->lba_shift) - 1); + cmnd->rw.length = cpu_to_le16((length >> ns->lba_shift) - 1); cmnd->rw.control = cpu_to_le16(control); cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt); -- cgit v0.10.2 From d8ee9d69f275769aaad40ef7c944565ff8d2d24f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 24 Feb 2011 08:46:00 -0500 Subject: NVMe: Fix discontiguous accesses When we submit subsequent portions of the I/O, we need to access the updated block, not start reading again from the original position. This was showing up as miscompares in the XFS randholes testcase. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 2409227..562d75a 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -529,6 +529,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, cmnd->rw.control = cpu_to_le16(control); cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt); + bio->bi_sector += length >> 9; + if (++nvmeq->sq_tail == nvmeq->q_depth) nvmeq->sq_tail = 0; writel(nvmeq->sq_tail, nvmeq->q_db); -- cgit v0.10.2 From e6d15f79f997a98b3a69abbc462fc9041cc1a7b4 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 24 Feb 2011 08:49:41 -0500 Subject: NVMe: Reduce maximum queue depth by 1 The spec says we're not allowed to completely fill the submission queue. Solve this by reducing the number of allocatable cmdids by 1. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 562d75a..45bfae1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -156,7 +156,7 @@ static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq) static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler, unsigned timeout) { - int depth = nvmeq->q_depth; + int depth = nvmeq->q_depth - 1; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); int cmdid; -- cgit v0.10.2 From b57ab0fada358357571f0eb448cdf2f144785321 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 24 Feb 2011 16:20:14 -0500 Subject: NVMe: Version 0.4 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 45bfae1..a8549df 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1617,6 +1617,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.3"); +MODULE_VERSION("0.4"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From 2ddc4f74d8adcf3e1cdec7f3e72d19b5c878597c Mon Sep 17 00:00:00 2001 From: Krzysztof Wierzbicki Date: Mon, 28 Feb 2011 08:27:13 +0100 Subject: NVMe: Update admin opcodes to match the 1.0RC spec Signed-off-by: Krzysztof Wierzbicki Signed-off-by: Matthew Wilcox Signed-off-by: Matthew Wilcox diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 757faa7..c46a9b7 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -199,19 +199,19 @@ enum { enum nvme_admin_opcode { nvme_admin_delete_sq = 0x00, nvme_admin_create_sq = 0x01, - nvme_admin_get_features = 0x02, + nvme_admin_get_log_page = 0x02, nvme_admin_delete_cq = 0x04, nvme_admin_create_cq = 0x05, nvme_admin_identify = 0x06, nvme_admin_abort_cmd = 0x08, nvme_admin_set_features = 0x09, - nvme_admin_get_log_page = 0x0a, + nvme_admin_get_features = 0x0a, nvme_admin_async_event = 0x0c, - nvme_admin_download_fw = 0x0d, - nvme_admin_security_recv = 0x0e, - nvme_admin_format_nvm = 0x10, - nvme_admin_security_send = 0x11, - nvme_admin_activate_fw = 0x14, + nvme_admin_activate_fw = 0x10, + nvme_admin_download_fw = 0x11, + nvme_admin_format_nvm = 0x80, + nvme_admin_security_send = 0x81, + nvme_admin_security_recv = 0x82, }; enum { -- cgit v0.10.2 From 714a7a22884b74862540bc84955274d86b2f6040 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:28:24 -0400 Subject: NVMe: Convert comments to kernel-doc notation Reported-by: Randy Dunlap Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index a8549df..e392919 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -142,10 +142,10 @@ static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq) } /** - * alloc_cmdid - Allocate a Command ID - * @param nvmeq The queue that will be used for this command - * @param ctx A pointer that will be passed to the handler - * @param handler The ID of the handler to call + * alloc_cmdid() - Allocate a Command ID + * @nvmeq: The queue that will be used for this command + * @ctx: A pointer that will be passed to the handler + * @handler: The ID of the handler to call * * Allocate a Command ID for a queue. The data passed in will * be passed to the completion handler. This is implemented by using @@ -234,7 +234,7 @@ static void put_nvmeq(struct nvme_queue *nvmeq) } /** - * nvme_submit_cmd: Copy a command into a queue and ring the doorbell + * nvme_submit_cmd() - Copy a command into a queue and ring the doorbell * @nvmeq: The queue to use * @cmd: The command to send * -- cgit v0.10.2 From fa92282149842645931580225647238428374758 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:29:00 -0400 Subject: NVMe: Fix comment formatting Reported-by: Randy Dunlap Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index e392919..740a9c1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -182,7 +182,8 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, return (cmdid < 0) ? -EINTR : cmdid; } -/* If you need more than four handlers, you'll need to change how +/* + * If you need more than four handlers, you'll need to change how * alloc_cmdid and nvme_process_cq work. Consider using a special * CMD_CTX value instead, if that works for your situation. */ @@ -1066,7 +1067,8 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) prps = nvme_setup_prps(dev, &c.common, sg, length); nvmeq = get_nvmeq(ns); - /* Since nvme_submit_sync_cmd sleeps, we can't keep preemption + /* + * Since nvme_submit_sync_cmd sleeps, we can't keep preemption * disabled. We may be preempted at any point, and be rescheduled * to a different CPU. That will cause cacheline bouncing, but no * additional races since q_lock already protects against other CPUs. -- cgit v0.10.2 From 19e899b2f9f89f4a290dd5c9c24d15987a18ab21 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:29:24 -0400 Subject: NVMe: Remove outdated comments The head can never overrun the tail since we won't allocate enough command IDs to let that happen. The status codes are in sync with the spec. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 740a9c1..d4f95eb 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -245,7 +245,6 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) { unsigned long flags; u16 tail; - /* XXX: Need to check tail isn't going to overrun head */ spin_lock_irqsave(&nvmeq->q_lock, flags); tail = nvmeq->sq_tail; memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd)); diff --git a/include/linux/nvme.h b/include/linux/nvme.h index c46a9b7..6b5a8d1 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -323,7 +323,6 @@ struct nvme_command { }; }; -/* XXX: Sync with spec */ enum { NVME_SC_SUCCESS = 0x0, NVME_SC_INVALID_OPCODE = 0x1, -- cgit v0.10.2 From ac88c36a385b848cb9efcb877fdfc4153a60bcab Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:29:58 -0400 Subject: NVMe: Fix interpretation of 'Number of Namespaces' field The spec says this is a 0s based value. We don't need to handle the maximal value because it's reserved to mean "every namespace". Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index d4f95eb..0d5c918 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1364,7 +1364,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) crt.features.prp1 = cpu_to_le64(dma_addr + 4096); crt.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); - for (i = 0; i < nn; i++) { + for (i = 0; i <= nn; i++) { cid.identify.nsid = cpu_to_le32(i); res = nvme_submit_admin_cmd(dev, &cid, NULL); if (res) -- cgit v0.10.2 From 7523d834dd1573610078eb1ac0933f6490232f90 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:43:40 -0400 Subject: NVMe: Fix off-by-one when filling in PRP lists If the last element in the PRP list fits on the end of the page, there's no need to allocate an extra page to put that single element in. It can fit on the end of the page. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 0d5c918..cf89db8 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -376,12 +376,13 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, cmd->prp2 = cpu_to_le64(prp_dma); i = 0; for (;;) { - if (i == PAGE_SIZE / 8 - 1) { + if (i == PAGE_SIZE / 8) { __le64 *old_prp_list = prp_list; prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); prps->list[prp_page++] = prp_list; - old_prp_list[i] = cpu_to_le64(prp_dma); - i = 0; + prp_list[0] = old_prp_list[i - 1]; + old_prp_list[i - 1] = cpu_to_le64(prp_dma); + i = 1; } prp_list[i++] = cpu_to_le64(dma_addr); dma_len -= PAGE_SIZE; -- cgit v0.10.2 From 3cb967c03926edd2c414082f4cc0feb7b372edae Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:45:49 -0400 Subject: NVMe: Remove the kthread from the wait queue Once there are no more bios on the congestion list, we can stop waking up the nvme kthread every time a completion happens. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index cf89db8..8d3c0b7 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1162,6 +1162,9 @@ static void nvme_resubmit_bios(struct nvme_queue *nvmeq) bio_list_add_head(&nvmeq->sq_cong, bio); break; } + if (bio_list_empty(&nvmeq->sq_cong)) + remove_wait_queue(&nvmeq->sq_full, + &nvmeq->sq_cong_wait); } } -- cgit v0.10.2 From 9ecdc946212f7cd592986b2c519b470404caa6b8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 16 Mar 2011 16:52:19 -0400 Subject: NVMe: Simplify queue lookup Fill in all the num_possible_cpus() entries with duplicate pointers. This reduces the complexity of the frequently-called get_nvmeq(), as well as avoiding a bug in it when there are fewer queues than CPUs. Reported-by: Shane Michael Matthews Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 8d3c0b7..f94f173 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -221,12 +221,7 @@ static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) { - int qid, cpu = get_cpu(); - if (cpu < ns->dev->queue_count) - qid = cpu + 1; - else - qid = (cpu % rounddown_pow_of_two(ns->dev->queue_count)) + 1; - return ns->dev->queues[qid]; + return ns->dev->queues[get_cpu() + 1]; } static void put_nvmeq(struct nvme_queue *nvmeq) @@ -1316,6 +1311,11 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) dev->queue_count++; } + for (; i < num_possible_cpus(); i++) { + int target = i % rounddown_pow_of_two(dev->queue_count - 1); + dev->queues[i + 1] = dev->queues[target + 1]; + } + return 0; } -- cgit v0.10.2 From 4948168280b269a514045766ddd872cfac5968e1 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sat, 19 Mar 2011 14:55:38 -0400 Subject: NVMe: Add compat_ioctl Make ioctls work for 32-bit applications on 64-bit kernels. The structures are defined to be the same for both 32- and 64-bit applications, so we can use the same handler for both. Reported-by: Arnd Bergmann Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f94f173..d0b5262 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1146,6 +1146,7 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, static const struct block_device_operations nvme_fops = { .owner = THIS_MODULE, .ioctl = nvme_ioctl, + .compat_ioctl = nvme_ioctl, }; static void nvme_resubmit_bios(struct nvme_queue *nvmeq) -- cgit v0.10.2 From 9d4af1b7796ba02b73a79a8694399e5a3cd1c55d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 20 Mar 2011 07:27:10 -0400 Subject: NVMe: Correct the definitions of two ioctls NVME_IOCTL_SUBMIT_IO has a struct nvme_user_io, not a struct nvme_rw_command as a parameter, and NVME_IOCTL_DOWNLOAD_FW is a Write, not a Read. Reported-by: Arnd Bergmann Signed-off-by: Matthew Wilcox diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 6b5a8d1..fd10d59 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -395,8 +395,8 @@ struct nvme_dlfw { #define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) #define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) #define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) -#define NVME_IOCTL_SUBMIT_IO _IOWR('N', 0x43, struct nvme_rw_command) -#define NVME_IOCTL_DOWNLOAD_FW _IOR('N', 0x44, struct nvme_dlfw) +#define NVME_IOCTL_SUBMIT_IO _IOWR('N', 0x43, struct nvme_user_io) +#define NVME_IOCTL_DOWNLOAD_FW _IOW('N', 0x44, struct nvme_dlfw) #define NVME_IOCTL_ACTIVATE_FW _IO('N', 0x45) #endif /* _LINUX_NVME_H */ -- cgit v0.10.2 From 6c7d49455ceb63064f992347d9185ff5bf43497a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 21 Mar 2011 09:48:57 -0400 Subject: NVMe: Change the definition of nvme_user_io The read and write commands don't define a 'result', so there's no need to copy it back to userspace. Remove the ability of the ioctl to submit commands to a different namespace; it's just asking for trouble, and the use case I have in mind will be addressed througha different ioctl in the future. That removes the need for both the block_shift and nsid arguments. Check that the opcode is one of 'read' or 'write'. Future opcodes may be added in the future, but we will need a different structure definition for them. The nblocks field is redefined to be 0-based. This allows the user to request the full 65536 blocks. Don't byteswap the reftag, apptag and appmask. Martin Petersen tells me these are calculated in big-endian and are transmitted to the device in big-endian. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index d0b5262..90a96ec 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1035,29 +1035,37 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) struct nvme_user_io io; struct nvme_command c; unsigned length; - u32 result; int nents, status; struct scatterlist *sg; struct nvme_prps *prps; if (copy_from_user(&io, uio, sizeof(io))) return -EFAULT; - length = io.nblocks << io.block_shift; - nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, length, &sg); + length = (io.nblocks + 1) << ns->lba_shift; + + switch (io.opcode) { + case nvme_cmd_write: + case nvme_cmd_read: + nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, + length, &sg); + default: + return -EFAULT; + } + if (nents < 0) return nents; memset(&c, 0, sizeof(c)); c.rw.opcode = io.opcode; c.rw.flags = io.flags; - c.rw.nsid = cpu_to_le32(io.nsid); + c.rw.nsid = cpu_to_le32(ns->ns_id); c.rw.slba = cpu_to_le64(io.slba); - c.rw.length = cpu_to_le16(io.nblocks - 1); + c.rw.length = cpu_to_le16(io.nblocks); c.rw.control = cpu_to_le16(io.control); c.rw.dsmgmt = cpu_to_le16(io.dsmgmt); - c.rw.reftag = cpu_to_le32(io.reftag); /* XXX: endian? */ - c.rw.apptag = cpu_to_le16(io.apptag); - c.rw.appmask = cpu_to_le16(io.appmask); + c.rw.reftag = io.reftag; + c.rw.apptag = io.apptag; + c.rw.appmask = io.appmask; /* XXX: metadata */ prps = nvme_setup_prps(dev, &c.common, sg, length); @@ -1069,11 +1077,10 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) * additional races since q_lock already protects against other CPUs. */ put_nvmeq(nvmeq); - status = nvme_submit_sync_cmd(nvmeq, &c, &result, IO_TIMEOUT); + status = nvme_submit_sync_cmd(nvmeq, &c, NULL, IO_TIMEOUT); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); nvme_free_prps(dev, prps); - put_user(result, &uio->result); return status; } diff --git a/include/linux/nvme.h b/include/linux/nvme.h index fd10d59..347ad5f 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -373,17 +373,15 @@ struct nvme_user_io { __u8 opcode; __u8 flags; __u16 control; - __u32 nsid; + __u16 nblocks; + __u16 rsvd; __u64 metadata; __u64 addr; __u64 slba; - __u16 nblocks; - __u16 block_shift; __u32 dsmgmt; __u32 reftag; __u16 apptag; __u16 appmask; - __u32 result; }; struct nvme_dlfw { @@ -395,7 +393,7 @@ struct nvme_dlfw { #define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) #define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) #define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) -#define NVME_IOCTL_SUBMIT_IO _IOWR('N', 0x43, struct nvme_user_io) +#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x43, struct nvme_user_io) #define NVME_IOCTL_DOWNLOAD_FW _IOW('N', 0x44, struct nvme_dlfw) #define NVME_IOCTL_ACTIVATE_FW _IO('N', 0x45) -- cgit v0.10.2 From 8ef700678f65e2eef1c3a94cdedb79d757608392 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 21 Mar 2011 10:28:43 -0400 Subject: NVMe: Version 0.5 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 90a96ec..d3eeca5 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1630,6 +1630,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.4"); +MODULE_VERSION("0.5"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From 7f53f9d2424533256ae86f7df5661a17de743de8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 22 Mar 2011 15:55:45 -0400 Subject: NVMe: Correct the Controller Configuration settings The arbitration field was extended by one bit, shifting the shutdown notification bits by one. Also, the SQ/CQ entry size was made configurable for future extensions. Reported-by: Paul Luse Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index d3eeca5..014a7f6 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -905,6 +905,7 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) dev->ctrl_config = NVME_CC_ENABLE | NVME_CC_CSS_NVM; dev->ctrl_config |= (PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT; dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE; + dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES; writel(0, &dev->bar->cc); writel(aqa, &dev->bar->aqa); diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 347ad5f..9d6febb 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -41,10 +41,12 @@ enum { NVME_CC_MPS_SHIFT = 7, NVME_CC_ARB_RR = 0 << 11, NVME_CC_ARB_WRRU = 1 << 11, - NVME_CC_ARB_VS = 3 << 11, - NVME_CC_SHN_NONE = 0 << 13, - NVME_CC_SHN_NORMAL = 1 << 13, - NVME_CC_SHN_ABRUPT = 2 << 13, + NVME_CC_ARB_VS = 7 << 11, + NVME_CC_SHN_NONE = 0 << 14, + NVME_CC_SHN_NORMAL = 1 << 14, + NVME_CC_SHN_ABRUPT = 2 << 14, + NVME_CC_IOSQES = 6 << 16, + NVME_CC_IOCQES = 4 << 20, NVME_CSTS_RDY = 1 << 0, NVME_CSTS_CFS = 1 << 1, NVME_CSTS_SHST_NORMAL = 0 << 2, -- cgit v0.10.2 From aba2080f3f1639f9202f1a52993669844abcfb80 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 27 Mar 2011 08:52:06 -0400 Subject: NVMe: Fix warning in free_irq We need to clear the affinity mask before calling free_irq() Reported-by: Shane Michael Matthews Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 014a7f6..bcc780a 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -781,8 +781,10 @@ static int adapter_delete_sq(struct nvme_dev *dev, u16 sqid) static void nvme_free_queue(struct nvme_dev *dev, int qid) { struct nvme_queue *nvmeq = dev->queues[qid]; + int vector = dev->entry[nvmeq->cq_vector].vector; - free_irq(dev->entry[nvmeq->cq_vector].vector, nvmeq); + irq_set_affinity_hint(vector, NULL); + free_irq(vector, nvmeq); /* Don't tell the adapter to delete the admin queue */ if (qid) { -- cgit v0.10.2 From 22605f96810d073eb74051d0295b6577d6a6a563 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 19 Apr 2011 15:04:20 -0400 Subject: NVMe: Time out initialisation after a few seconds THe device reports (in its capability register) how long it will take to initialise. If that time elapses before the ready bit becomes set, conclude the device is broken and refuse to initialise it. Log a nice error message so the user knows why we did nothing. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index bcc780a..57f2b33a 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -893,6 +893,8 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) { int result; u32 aqa; + u64 cap; + unsigned long timeout; struct nvme_queue *nvmeq; dev->dbs = ((void __iomem *)dev->bar) + 4096; @@ -915,10 +917,18 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) writeq(nvmeq->cq_dma_addr, &dev->bar->acq); writel(dev->ctrl_config, &dev->bar->cc); + cap = readq(&dev->bar->cap); + timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies; + while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) { msleep(100); if (fatal_signal_pending(current)) return -EINTR; + if (time_after(jiffies, timeout)) { + dev_err(&dev->pci_dev->dev, + "Device not ready; aborting initialisation\n"); + return -ENODEV; + } } result = queue_request_irq(dev, nvmeq, "nvme admin"); diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 9d6febb..a19304f 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -35,6 +35,8 @@ struct nvme_bar { __u64 acq; /* Admin CQ Base Address */ }; +#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) + enum { NVME_CC_ENABLE = 1 << 0, NVME_CC_CSS_NVM = 0 << 4, -- cgit v0.10.2 From 09a58f536436efed02ead722e835cb4ce7674afc Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 28 Apr 2011 23:09:09 -0700 Subject: NVMe: Fix bug in error handling When an I/O completed with an error, we would call bio_endio twice (once with -EIO and once with 0). Found by inspection. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 57f2b33a..205405e 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -310,9 +310,9 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, dma_unmap_sg(nvmeq->q_dmadev, nbio->sg, nbio->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); free_nbio(nvmeq, nbio); - if (status) + if (status) { bio_endio(bio, -EIO); - if (bio->bi_vcnt > bio->bi_idx) { + } else if (bio->bi_vcnt > bio->bi_idx) { bio_list_add(&nvmeq->sq_cong, bio); wake_up_process(nvme_thread); } else { -- cgit v0.10.2 From 21075bdee0a6f56058920d889df4ae561bfed754 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 28 Apr 2011 23:17:36 -0700 Subject: NVMe: Rename cancel_cmdid_data to cancel_cmdid The trailing '_data' on the end was annoying and inconsistent. Also, make it actually return the data since this is needed for timing out commands. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 205405e..9ca9db9 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -213,10 +213,13 @@ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) return data; } -static void cancel_cmdid_data(struct nvme_queue *nvmeq, int cmdid) +static unsigned long cancel_cmdid(struct nvme_queue *nvmeq, int cmdid) { + unsigned long data; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); + data = info[cmdid].ctx; info[cmdid].ctx = CMD_CTX_CANCELLED; + return data; } static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) @@ -667,7 +670,7 @@ static irqreturn_t nvme_irq_check(int irq, void *data) static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) { spin_lock_irq(&nvmeq->q_lock); - cancel_cmdid_data(nvmeq, cmdid); + cancel_cmdid(nvmeq, cmdid); spin_unlock_irq(&nvmeq->q_lock); } -- cgit v0.10.2 From 8de055350fbaa96b6563892c195a60be583faa9c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 12 May 2011 13:50:28 -0400 Subject: NVMe: Add support for timing out I/Os In the kthread, walk the list of outstanding I/Os and check they've not hit the timeout. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 9ca9db9..9c0ab2a 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -601,15 +602,15 @@ static void sync_completion(struct nvme_queue *nvmeq, void *ctx, typedef void (*completion_fn)(struct nvme_queue *, void *, struct nvme_completion *); +static const completion_fn nvme_completions[4] = { + [sync_completion_id] = sync_completion, + [bio_completion_id] = bio_completion, +}; + static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) { u16 head, phase; - static const completion_fn completions[4] = { - [sync_completion_id] = sync_completion, - [bio_completion_id] = bio_completion, - }; - head = nvmeq->cq_head; phase = nvmeq->cq_phase; @@ -629,7 +630,7 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) data = free_cmdid(nvmeq, cqe.command_id); handler = data & 3; ptr = (void *)(data & ~3UL); - completions[handler](nvmeq, ptr, &cqe); + nvme_completions[handler](nvmeq, ptr, &cqe); } /* If the controller ignores the cq head doorbell and continuously @@ -1172,6 +1173,29 @@ static const struct block_device_operations nvme_fops = { .compat_ioctl = nvme_ioctl, }; +static void nvme_timeout_ios(struct nvme_queue *nvmeq) +{ + int depth = nvmeq->q_depth - 1; + struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); + unsigned long now = jiffies; + int cmdid; + + for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) { + unsigned long data; + void *ptr; + unsigned char handler; + static struct nvme_completion cqe = { .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1, }; + + if (!time_after(now, info[cmdid].timeout)) + continue; + dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid); + data = cancel_cmdid(nvmeq, cmdid); + handler = data & 3; + ptr = (void *)(data & ~3UL); + nvme_completions[handler](nvmeq, ptr, &cqe); + } +} + static void nvme_resubmit_bios(struct nvme_queue *nvmeq) { while (bio_list_peek(&nvmeq->sq_cong)) { @@ -1203,6 +1227,7 @@ static int nvme_kthread(void *data) spin_lock_irq(&nvmeq->q_lock); if (nvme_process_cq(nvmeq)) printk("process_cq did something\n"); + nvme_timeout_ios(nvmeq); nvme_resubmit_bios(nvmeq); spin_unlock_irq(&nvmeq->q_lock); } -- cgit v0.10.2 From fd63e9ceeeae58cfe877c2d49d41c1bf7532303c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 6 May 2011 08:37:54 -0400 Subject: NVMe: Add include of delay.h for msleep Previously it was being implicitly included through some other header file Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 9c0ab2a..b285a7e 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 5aff9382ddc8aac6eb0c70ffbb351652d71da69a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 6 May 2011 08:45:47 -0400 Subject: NVMe: Use an IDA to allocate minor numbers The current approach of using the namespace ID as the minor number doesn't work when there are multiple adapters in the machine. Rather than statically partitioning the number of namespaces between adapters, dynamically allocate minor numbers to namespaces as they are detected. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b285a7e..79012c5 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1240,7 +1241,34 @@ static int nvme_kthread(void *data) return 0; } -static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int index, +static DEFINE_IDA(nvme_index_ida); + +static int nvme_get_ns_idx(void) +{ + int index, error; + + do { + if (!ida_pre_get(&nvme_index_ida, GFP_KERNEL)) + return -1; + + spin_lock(&dev_list_lock); + error = ida_get_new(&nvme_index_ida, &index); + spin_unlock(&dev_list_lock); + } while (error == -EAGAIN); + + if (error) + index = -1; + return index; +} + +static void nvme_put_ns_idx(int index) +{ + spin_lock(&dev_list_lock); + ida_remove(&nvme_index_ida, index); + spin_unlock(&dev_list_lock); +} + +static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int nsid, struct nvme_id_ns *id, struct nvme_lba_range_type *rt) { struct nvme_ns *ns; @@ -1265,19 +1293,19 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int index, disk = alloc_disk(NVME_MINORS); if (!disk) goto out_free_queue; - ns->ns_id = index; + ns->ns_id = nsid; ns->disk = disk; lbaf = id->flbas & 0xf; ns->lba_shift = id->lbaf[lbaf].ds; disk->major = nvme_major; disk->minors = NVME_MINORS; - disk->first_minor = NVME_MINORS * index; + disk->first_minor = NVME_MINORS * nvme_get_ns_idx(); disk->fops = &nvme_fops; disk->private_data = ns; disk->queue = ns->queue; disk->driverfs_dev = &dev->pci_dev->dev; - sprintf(disk->disk_name, "nvme%dn%d", dev->instance, index); + sprintf(disk->disk_name, "nvme%dn%d", dev->instance, nsid); set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); return ns; @@ -1291,7 +1319,9 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int index, static void nvme_ns_free(struct nvme_ns *ns) { + int index = ns->disk->first_minor / NVME_MINORS; put_disk(ns->disk); + nvme_put_ns_idx(index); blk_cleanup_queue(ns->queue); kfree(ns); } -- cgit v0.10.2 From b77954cbddff28d55a36fad3c16f4daebb0f01df Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 12 May 2011 13:51:41 -0400 Subject: NVMe: Handle failures from memory allocations in nvme_setup_prps If any of the memory allocations in nvme_setup_prps fail, handle it by modifying the passed-in data length to reflect the number of bytes we are actually able to send. Also allow the caller to specify the GFP flags they need; for user-initiated commands, we can use GFP_KERNEL allocations. The various callers are updated to handle this possibility; the main I/O path is already prepared for this possibility (as it may happen due to nvme_map_bio being unable to map all the segments of the I/O). The other callers return -ENOMEM instead of doing partial I/Os. Reported-by: Andi Kleen Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 79012c5..ddc21ba 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -329,9 +329,11 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, /* length is in bytes */ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, - struct scatterlist *sg, int length) + struct scatterlist *sg, int *len, + gfp_t gfp) { struct dma_pool *pool; + int length = *len; int dma_len = sg_dma_len(sg); u64 dma_addr = sg_dma_address(sg); int offset = offset_in_page(dma_addr); @@ -361,7 +363,12 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, nprps = DIV_ROUND_UP(length, PAGE_SIZE); npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE); - prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC); + prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, gfp); + if (!prps) { + cmd->prp2 = cpu_to_le64(dma_addr); + *len = (*len - length) + PAGE_SIZE; + return prps; + } prp_page = 0; if (nprps <= (256 / 8)) { pool = dev->prp_small_pool; @@ -371,7 +378,13 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, prps->npages = npages; } - prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); + prp_list = dma_pool_alloc(pool, gfp, &prp_dma); + if (!prp_list) { + cmd->prp2 = cpu_to_le64(dma_addr); + *len = (*len - length) + PAGE_SIZE; + kfree(prps); + return NULL; + } prps->list[prp_page++] = prp_list; prps->first_dma = prp_dma; cmd->prp2 = cpu_to_le64(prp_dma); @@ -379,7 +392,11 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, for (;;) { if (i == PAGE_SIZE / 8) { __le64 *old_prp_list = prp_list; - prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); + prp_list = dma_pool_alloc(pool, gfp, &prp_dma); + if (!prp_list) { + *len = (*len - length); + return prps; + } prps->list[prp_page++] = prp_list; prp_list[0] = old_prp_list[i - 1]; old_prp_list[i - 1] = cpu_to_le64(prp_dma); @@ -525,7 +542,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); nbio->prps = nvme_setup_prps(nvmeq->dev, &cmnd->common, nbio->sg, - length); + &length, GFP_ATOMIC); cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); cmnd->rw.length = cpu_to_le16((length >> ns->lba_shift) - 1); cmnd->rw.control = cpu_to_le16(control); @@ -1009,15 +1026,18 @@ static int nvme_submit_user_admin_command(struct nvme_dev *dev, unsigned long addr, unsigned length, struct nvme_command *cmd) { - int err, nents; + int err, nents, tmplen = length; struct scatterlist *sg; struct nvme_prps *prps; nents = nvme_map_user_pages(dev, 0, addr, length, &sg); if (nents < 0) return nents; - prps = nvme_setup_prps(dev, &cmd->common, sg, length); - err = nvme_submit_admin_cmd(dev, cmd, NULL); + prps = nvme_setup_prps(dev, &cmd->common, sg, &tmplen, GFP_KERNEL); + if (tmplen != length) + err = -ENOMEM; + else + err = nvme_submit_admin_cmd(dev, cmd, NULL); nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); nvme_free_prps(dev, prps); return err ? -EIO : 0; @@ -1086,7 +1106,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.apptag = io.apptag; c.rw.appmask = io.appmask; /* XXX: metadata */ - prps = nvme_setup_prps(dev, &c.common, sg, length); + prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); nvmeq = get_nvmeq(ns); /* @@ -1096,7 +1116,10 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) * additional races since q_lock already protects against other CPUs. */ put_nvmeq(nvmeq); - status = nvme_submit_sync_cmd(nvmeq, &c, NULL, IO_TIMEOUT); + if (length != (io.nblocks + 1) << ns->lba_shift) + status = -ENOMEM; + else + status = nvme_submit_sync_cmd(nvmeq, &c, NULL, IO_TIMEOUT); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); nvme_free_prps(dev, prps); @@ -1109,7 +1132,7 @@ static int nvme_download_firmware(struct nvme_ns *ns, struct nvme_dev *dev = ns->dev; struct nvme_dlfw dlfw; struct nvme_command c; - int nents, status; + int nents, status, length; struct scatterlist *sg; struct nvme_prps *prps; @@ -1117,8 +1140,9 @@ static int nvme_download_firmware(struct nvme_ns *ns, return -EFAULT; if (dlfw.length >= (1 << 30)) return -EINVAL; + length = dlfw.length * 4; - nents = nvme_map_user_pages(dev, 1, dlfw.addr, dlfw.length * 4, &sg); + nents = nvme_map_user_pages(dev, 1, dlfw.addr, length, &sg); if (nents < 0) return nents; @@ -1126,9 +1150,11 @@ static int nvme_download_firmware(struct nvme_ns *ns, c.dlfw.opcode = nvme_admin_download_fw; c.dlfw.numd = cpu_to_le32(dlfw.length); c.dlfw.offset = cpu_to_le32(dlfw.offset); - prps = nvme_setup_prps(dev, &c.common, sg, dlfw.length * 4); - - status = nvme_submit_admin_cmd(dev, &c, NULL); + prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); + if (length != dlfw.length * 4) + status = -ENOMEM; + else + status = nvme_submit_admin_cmd(dev, &c, NULL); nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents); nvme_free_prps(dev, prps); return status; -- cgit v0.10.2 From 184d2944cb3b92a2e8e1733c59d1e531ad6e924a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 11 May 2011 21:36:38 -0400 Subject: NVMe: Add a few calling convention notes For the benefit of reviewers, add comments to a few functions describing their calling context Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index ddc21ba..12062c1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -155,6 +155,9 @@ static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq) * the bottom two bits of the ctx pointer to store the handler ID. * Passing in a pointer that's not 4-byte aligned will cause a BUG. * We can change this if it becomes a problem. + * + * May be called with local interrupts disabled and the q_lock held, + * or with interrupts enabled and no locks held. */ static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler, unsigned timeout) @@ -202,6 +205,9 @@ enum { #define CMD_CTX_INVALID (0x314 + CMD_CTX_BASE) #define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE) +/* + * Called with local interrupts disabled and the q_lock held. May not sleep. + */ static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) { unsigned long data; @@ -326,7 +332,7 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, } } -/* length is in bytes */ +/* length is in bytes. gfp flags indicates whether we may sleep. */ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, struct scatterlist *sg, int *len, @@ -483,6 +489,9 @@ static int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns) return nvme_submit_flush(nvmeq, ns, cmdid); } +/* + * Called with local interrupts disabled and the q_lock held. May not sleep. + */ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct bio *bio) { -- cgit v0.10.2 From be5e09484078e95af20acb13e215cd8aec705893 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 11 May 2011 21:38:57 -0400 Subject: NVMe: Version 0.6 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 12062c1..d1cd91b 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1737,6 +1737,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.5"); +MODULE_VERSION("0.6"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From 6f0f54499f2edf7e25410cdd99e6f030f3485fd1 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 11 May 2011 13:30:59 -0700 Subject: NVMe: Return real error from nvme_create_queue nvme_setup_io_queues() was assuming that a NULL return from nvme_create_queue() was an out-of-memory error. That's not necessarily true; the adapter might return -EIO, for example. Change the calling convention to return an ERR_PTR on failure instead of NULL. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index d1cd91b..843edbd 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -892,7 +892,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, struct nvme_queue *nvmeq = nvme_alloc_queue(dev, qid, cq_size, vector); if (!nvmeq) - return NULL; + return ERR_PTR(-ENOMEM); result = adapter_alloc_cq(dev, qid, nvmeq); if (result < 0) @@ -918,7 +918,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth), nvmeq->sq_cmds, nvmeq->sq_dma_addr); kfree(nvmeq); - return NULL; + return ERR_PTR(result); } static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) @@ -1421,8 +1421,8 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) for (i = 0; i < nr_io_queues; i++) { dev->queues[i + 1] = nvme_create_queue(dev, i + 1, NVME_Q_DEPTH, i); - if (!dev->queues[i + 1]) - return -ENOMEM; + if (IS_ERR(dev->queues[i + 1])) + return PTR_ERR(dev->queues[i + 1]); dev->queue_count++; } -- cgit v0.10.2 From eac623ba7a91474a688eb5d0fcd0eaa6a56dc41c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 20 May 2011 09:34:43 -0400 Subject: NVMe: Add the nvme thread to the wait queue before waking it up If the I/O was not completed by a single NVMe command, we add the bio to the congestion list and wake up the kthread to resubmit it. But the kthread calls remove_wait_queue() unconditionally, which will oops if it's not on the wait queue. So add the kthread to the wait queue before waking it up. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 843edbd..f5e51a6 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -325,6 +325,8 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx, if (status) { bio_endio(bio, -EIO); } else if (bio->bi_vcnt > bio->bi_idx) { + if (bio_list_empty(&nvmeq->sq_cong)) + add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); bio_list_add(&nvmeq->sq_cong, bio); wake_up_process(nvme_thread); } else { -- cgit v0.10.2 From 6bbf1acddeed0bfb345a5578f9fcada16f1e514f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 20 May 2011 13:03:42 -0400 Subject: NVMe: Rework ioctls Remove the special-purpose IDENTIFY, GET_RANGE_TYPE, DOWNLOAD_FIRMWARE and ACTIVATE_FIRMWARE commands. Replace them with a generic ADMIN_CMD ioctl that can submit any admin command. Add a new ID ioctl that returns the namespace ID of the queried device. It corresponds to the SCSI Idlun ioctl. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f5e51a6..9e3c724 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1033,51 +1033,6 @@ static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, put_page(sg_page(&sg[i])); } -static int nvme_submit_user_admin_command(struct nvme_dev *dev, - unsigned long addr, unsigned length, - struct nvme_command *cmd) -{ - int err, nents, tmplen = length; - struct scatterlist *sg; - struct nvme_prps *prps; - - nents = nvme_map_user_pages(dev, 0, addr, length, &sg); - if (nents < 0) - return nents; - prps = nvme_setup_prps(dev, &cmd->common, sg, &tmplen, GFP_KERNEL); - if (tmplen != length) - err = -ENOMEM; - else - err = nvme_submit_admin_cmd(dev, cmd, NULL); - nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); - nvme_free_prps(dev, prps); - return err ? -EIO : 0; -} - -static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) -{ - struct nvme_command c; - - memset(&c, 0, sizeof(c)); - c.identify.opcode = nvme_admin_identify; - c.identify.nsid = cns ? 0 : cpu_to_le32(ns->ns_id); - c.identify.cns = cpu_to_le32(cns); - - return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); -} - -static int nvme_get_range_type(struct nvme_ns *ns, unsigned long addr) -{ - struct nvme_command c; - - memset(&c, 0, sizeof(c)); - c.features.opcode = nvme_admin_get_features; - c.features.nsid = cpu_to_le32(ns->ns_id); - c.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); - - return nvme_submit_user_admin_command(ns->dev, addr, 4096, &c); -} - static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) { struct nvme_dev *dev = ns->dev; @@ -1096,10 +1051,11 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) switch (io.opcode) { case nvme_cmd_write: case nvme_cmd_read: + case nvme_cmd_compare: nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, length, &sg); default: - return -EFAULT; + return -EINVAL; } if (nents < 0) @@ -1137,70 +1093,66 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) return status; } -static int nvme_download_firmware(struct nvme_ns *ns, - struct nvme_dlfw __user *udlfw) +static int nvme_user_admin_cmd(struct nvme_ns *ns, + struct nvme_admin_cmd __user *ucmd) { struct nvme_dev *dev = ns->dev; - struct nvme_dlfw dlfw; + struct nvme_admin_cmd cmd; struct nvme_command c; - int nents, status, length; + int status, length, nents = 0; struct scatterlist *sg; - struct nvme_prps *prps; + struct nvme_prps *prps = NULL; - if (copy_from_user(&dlfw, udlfw, sizeof(dlfw))) + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (copy_from_user(&cmd, ucmd, sizeof(cmd))) return -EFAULT; - if (dlfw.length >= (1 << 30)) - return -EINVAL; - length = dlfw.length * 4; - - nents = nvme_map_user_pages(dev, 1, dlfw.addr, length, &sg); - if (nents < 0) - return nents; memset(&c, 0, sizeof(c)); - c.dlfw.opcode = nvme_admin_download_fw; - c.dlfw.numd = cpu_to_le32(dlfw.length); - c.dlfw.offset = cpu_to_le32(dlfw.offset); - prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); - if (length != dlfw.length * 4) + c.common.opcode = cmd.opcode; + c.common.flags = cmd.flags; + c.common.nsid = cpu_to_le32(cmd.nsid); + c.common.cdw2[0] = cpu_to_le32(cmd.cdw2); + c.common.cdw2[1] = cpu_to_le32(cmd.cdw3); + c.common.cdw10[0] = cpu_to_le32(cmd.cdw10); + c.common.cdw10[1] = cpu_to_le32(cmd.cdw11); + c.common.cdw10[2] = cpu_to_le32(cmd.cdw12); + c.common.cdw10[3] = cpu_to_le32(cmd.cdw13); + c.common.cdw10[4] = cpu_to_le32(cmd.cdw14); + c.common.cdw10[5] = cpu_to_le32(cmd.cdw15); + + length = cmd.data_len; + if (cmd.data_len) { + nents = nvme_map_user_pages(dev, 1, cmd.addr, length, &sg); + if (nents < 0) + return nents; + prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); + } + + if (length != cmd.data_len) status = -ENOMEM; else status = nvme_submit_admin_cmd(dev, &c, NULL); - nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents); - nvme_free_prps(dev, prps); + if (cmd.data_len) { + nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, sg, + nents); + nvme_free_prps(dev, prps); + } return status; } -static int nvme_activate_firmware(struct nvme_ns *ns, unsigned long arg) -{ - struct nvme_dev *dev = ns->dev; - struct nvme_command c; - - memset(&c, 0, sizeof(c)); - c.common.opcode = nvme_admin_activate_fw; - c.common.rsvd10[0] = cpu_to_le32(arg); - - return nvme_submit_admin_cmd(dev, &c, NULL); -} - static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct nvme_ns *ns = bdev->bd_disk->private_data; switch (cmd) { - case NVME_IOCTL_IDENTIFY_NS: - return nvme_identify(ns, arg, 0); - case NVME_IOCTL_IDENTIFY_CTRL: - return nvme_identify(ns, arg, 1); - case NVME_IOCTL_GET_RANGE_TYPE: - return nvme_get_range_type(ns, arg); + case NVME_IOCTL_ID: + return ns->ns_id; + case NVME_IOCTL_ADMIN_CMD: + return nvme_user_admin_cmd(ns, (void __user *)arg); case NVME_IOCTL_SUBMIT_IO: return nvme_submit_io(ns, (void __user *)arg); - case NVME_IOCTL_DOWNLOAD_FW: - return nvme_download_firmware(ns, (void __user *)arg); - case NVME_IOCTL_ACTIVATE_FW: - return nvme_activate_firmware(ns, arg); default: return -ENOTTY; } diff --git a/include/linux/nvme.h b/include/linux/nvme.h index a19304f..c96ab0f 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -153,11 +153,11 @@ struct nvme_common_command { __u8 flags; __u16 command_id; __le32 nsid; - __u64 rsvd2; + __u32 cdw2[2]; __le64 metadata; __le64 prp1; __le64 prp2; - __u32 rsvd10[6]; + __u32 cdw10[6]; }; struct nvme_rw_command { @@ -388,17 +388,29 @@ struct nvme_user_io { __u16 appmask; }; -struct nvme_dlfw { +struct nvme_admin_cmd { + __u8 opcode; + __u8 flags; + __u16 rsvd1; + __u32 nsid; + __u32 cdw2; + __u32 cdw3; + __u64 metadata; __u64 addr; - __u32 length; /* In dwords */ - __u32 offset; /* In dwords */ + __u32 metadata_len; + __u32 data_len; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u32 timeout_ms; + __u32 result; }; -#define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns) -#define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl) -#define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type) -#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x43, struct nvme_user_io) -#define NVME_IOCTL_DOWNLOAD_FW _IOW('N', 0x44, struct nvme_dlfw) -#define NVME_IOCTL_ACTIVATE_FW _IO('N', 0x45) +#define NVME_IOCTL_ID _IO('N', 0x40) +#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd) +#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io) #endif /* _LINUX_NVME_H */ -- cgit v0.10.2 From 6413214c5d424fd5aae6567848340f962ad2ce0f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 9 Aug 2011 12:56:37 -0400 Subject: Fix bug in NVME_IOCTL_SUBMIT_IO Missing 'break' in the switch statement meant that we'd fall through to the 'return -EINVAL' case. diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 9e3c724..0956e12 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1054,6 +1054,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) case nvme_cmd_compare: nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, length, &sg); + break; default: return -EINVAL; } -- cgit v0.10.2 From d0ba1e497bca83a3d353eb47c9658afc54d83228 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 13 Sep 2011 17:01:39 -0400 Subject: NVMe: Correct sg list setup in nvme_map_user_pages Our SG list was constructed to always fill the entire first page, even if that was more than the length of the I/O. This is probably harmless, but some IOMMUs might do something bad. Correcting the first call to sg_set_page() made it look a lot closer to the sg_set_page() in the loop, so fold the first call to sg_set_page() into the loop. Reported-by: Nisheeth Bhat Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 0956e12..5843409 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -996,11 +996,11 @@ static int nvme_map_user_pages(struct nvme_dev *dev, int write, sg = kcalloc(count, sizeof(*sg), GFP_KERNEL); sg_init_table(sg, count); - sg_set_page(&sg[0], pages[0], PAGE_SIZE - offset, offset); - length -= (PAGE_SIZE - offset); - for (i = 1; i < count; i++) { - sg_set_page(&sg[i], pages[i], min_t(int, length, PAGE_SIZE), 0); - length -= PAGE_SIZE; + for (i = 0; i < count; i++) { + sg_set_page(&sg[i], pages[i], + min_t(int, length, PAGE_SIZE - offset), offset); + length -= (PAGE_SIZE - offset); + offset = 0; } err = -ENOMEM; -- cgit v0.10.2 From d1a490e026efb22851ed60588b5fad1281d80ec3 Mon Sep 17 00:00:00 2001 From: Nisheeth Bhat Date: Thu, 15 Sep 2011 16:52:24 -0400 Subject: NVMe: Fix calls to dma_unmap_sg dma_unmap_sg() must be called with the same 'nents' passed to dma_map_sg(), not the number returned from dma_map_sg(). Signed-off-by: Nisheeth Bhat Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 5843409..a7f82fb 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1021,13 +1021,12 @@ static int nvme_map_user_pages(struct nvme_dev *dev, int write, } static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, - unsigned long addr, int length, - struct scatterlist *sg, int nents) + unsigned long addr, int length, struct scatterlist *sg) { int i, count; count = DIV_ROUND_UP(offset_in_page(addr) + length, PAGE_SIZE); - dma_unmap_sg(&dev->pci_dev->dev, sg, nents, DMA_FROM_DEVICE); + dma_unmap_sg(&dev->pci_dev->dev, sg, count, DMA_FROM_DEVICE); for (i = 0; i < count; i++) put_page(sg_page(&sg[i])); @@ -1089,7 +1088,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) else status = nvme_submit_sync_cmd(nvmeq, &c, NULL, IO_TIMEOUT); - nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg, nents); + nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg); nvme_free_prps(dev, prps); return status; } @@ -1135,8 +1134,7 @@ static int nvme_user_admin_cmd(struct nvme_ns *ns, else status = nvme_submit_admin_cmd(dev, &c, NULL); if (cmd.data_len) { - nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, sg, - nents); + nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, sg); nvme_free_prps(dev, prps); } return status; -- cgit v0.10.2 From 684f5c2025b067a23722e620d0b3b858d8dc5d01 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 19 Sep 2011 17:14:53 -0400 Subject: NVMe: Fix memory leak in nvme_dev_add() The driver was allocating 8k of memory, then freeing 4k of it. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index a7f82fb..705f66e 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1457,7 +1457,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) list_for_each_entry(ns, &dev->namespaces, list) add_disk(ns->disk); - dma_free_coherent(&dev->pci_dev->dev, 4096, id, dma_addr); + dma_free_coherent(&dev->pci_dev->dev, 8192, id, dma_addr); return 0; out_free: @@ -1466,7 +1466,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) nvme_ns_free(ns); } - dma_free_coherent(&dev->pci_dev->dev, 4096, id, dma_addr); + dma_free_coherent(&dev->pci_dev->dev, 8192, mem, dma_addr); return res; } -- cgit v0.10.2 From bc5fc7e4b22ca855902aba02b28c96f09b446407 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 19 Sep 2011 17:08:14 -0400 Subject: NVMe: Create nvme_identify and nvme_get_features functions Instead of open-coding calls to nvme_submit_admin_cmd, these small wrappers are simpler to use (the patch removes 14 lines from nvme_dev_add() for example). Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 705f66e..b77894a 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -812,6 +812,34 @@ static int adapter_delete_sq(struct nvme_dev *dev, u16 sqid) return adapter_delete_queue(dev, nvme_admin_delete_sq, sqid); } +static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns, + dma_addr_t dma_addr) +{ + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.identify.opcode = nvme_admin_identify; + c.identify.nsid = cpu_to_le32(nsid); + c.identify.prp1 = cpu_to_le64(dma_addr); + c.identify.cns = cpu_to_le32(cns); + + return nvme_submit_admin_cmd(dev, &c, NULL); +} + +static int nvme_get_features(struct nvme_dev *dev, unsigned fid, + unsigned dword11, dma_addr_t dma_addr, u32 *result) +{ + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.features.opcode = nvme_admin_get_features; + c.features.prp1 = cpu_to_le64(dma_addr); + c.features.fid = cpu_to_le32(fid); + c.features.dword11 = cpu_to_le32(dword11); + + return nvme_submit_admin_cmd(dev, &c, result); +} + static void nvme_free_queue(struct nvme_dev *dev, int qid) { struct nvme_queue *nvmeq = dev->queues[qid]; @@ -1318,15 +1346,10 @@ static int set_queue_count(struct nvme_dev *dev, int count) { int status; u32 result; - struct nvme_command c; u32 q_count = (count - 1) | ((count - 1) << 16); - memset(&c, 0, sizeof(c)); - c.features.opcode = nvme_admin_get_features; - c.features.fid = cpu_to_le32(NVME_FEAT_NUM_QUEUES); - c.features.dword11 = cpu_to_le32(q_count); - - status = nvme_submit_admin_cmd(dev, &c, &result); + status = nvme_get_features(dev, NVME_FEAT_NUM_QUEUES, q_count, 0, + &result); if (status) return -EIO; return min(result & 0xffff, result >> 16) + 1; @@ -1400,65 +1423,51 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) int res, nn, i; struct nvme_ns *ns, *next; struct nvme_id_ctrl *ctrl; - void *id; + struct nvme_id_ns *id_ns; + void *mem; dma_addr_t dma_addr; - struct nvme_command cid, crt; res = nvme_setup_io_queues(dev); if (res) return res; - /* XXX: Switch to a SG list once prp2 works */ - id = dma_alloc_coherent(&dev->pci_dev->dev, 8192, &dma_addr, + mem = dma_alloc_coherent(&dev->pci_dev->dev, 8192, &dma_addr, GFP_KERNEL); - memset(&cid, 0, sizeof(cid)); - cid.identify.opcode = nvme_admin_identify; - cid.identify.nsid = 0; - cid.identify.prp1 = cpu_to_le64(dma_addr); - cid.identify.cns = cpu_to_le32(1); - - res = nvme_submit_admin_cmd(dev, &cid, NULL); + res = nvme_identify(dev, 0, 1, dma_addr); if (res) { res = -EIO; goto out_free; } - ctrl = id; + ctrl = mem; nn = le32_to_cpup(&ctrl->nn); memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn)); memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn)); memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr)); - cid.identify.cns = 0; - memset(&crt, 0, sizeof(crt)); - crt.features.opcode = nvme_admin_get_features; - crt.features.prp1 = cpu_to_le64(dma_addr + 4096); - crt.features.fid = cpu_to_le32(NVME_FEAT_LBA_RANGE); - + id_ns = mem; for (i = 0; i <= nn; i++) { - cid.identify.nsid = cpu_to_le32(i); - res = nvme_submit_admin_cmd(dev, &cid, NULL); + res = nvme_identify(dev, i, 0, dma_addr); if (res) continue; - if (((struct nvme_id_ns *)id)->ncap == 0) + if (id_ns->ncap == 0) continue; - crt.features.nsid = cpu_to_le32(i); - res = nvme_submit_admin_cmd(dev, &crt, NULL); + res = nvme_get_features(dev, NVME_FEAT_LBA_RANGE, i, + dma_addr + 4096, NULL); if (res) continue; - ns = nvme_alloc_ns(dev, i, id, id + 4096); + ns = nvme_alloc_ns(dev, i, mem, mem + 4096); if (ns) list_add_tail(&ns->list, &dev->namespaces); } list_for_each_entry(ns, &dev->namespaces, list) add_disk(ns->disk); - dma_free_coherent(&dev->pci_dev->dev, 8192, id, dma_addr); - return 0; + goto out; out_free: list_for_each_entry_safe(ns, next, &dev->namespaces, list) { @@ -1466,6 +1475,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) nvme_ns_free(ns); } + out: dma_free_coherent(&dev->pci_dev->dev, 8192, mem, dma_addr); return res; } -- cgit v0.10.2 From 0d1bc9125890426b52ca2de6abedd32e31722e5c Mon Sep 17 00:00:00 2001 From: Nisheeth Bhat Date: Thu, 29 Sep 2011 10:10:10 -0400 Subject: Fix calculation of number of pages in a PRP List The existing calculation underestimated the number of pages required as it did not take into account the pointer at the end of each page. The replacement calculation may overestimate the number of pages required if the last page in the PRP List is entirely full. By using ->npages as a counter as we fill in the pages, we ensure that we don't try to free a page that was never allocated. Signed-off-by: Nisheeth Bhat Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b77894a..3afdc75 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -265,7 +265,7 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) } struct nvme_prps { - int npages; + int npages; /* 0 means small pool in use */ dma_addr_t first_dma; __le64 *list[0]; }; @@ -347,7 +347,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, int offset = offset_in_page(dma_addr); __le64 *prp_list; dma_addr_t prp_dma; - int nprps, npages, i, prp_page; + int nprps, npages, i; struct nvme_prps *prps = NULL; cmd->prp1 = cpu_to_le64(dma_addr); @@ -370,20 +370,20 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, } nprps = DIV_ROUND_UP(length, PAGE_SIZE); - npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE); + npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE - 8); prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, gfp); if (!prps) { cmd->prp2 = cpu_to_le64(dma_addr); *len = (*len - length) + PAGE_SIZE; return prps; } - prp_page = 0; + if (nprps <= (256 / 8)) { pool = dev->prp_small_pool; prps->npages = 0; } else { pool = dev->prp_page_pool; - prps->npages = npages; + prps->npages = 1; } prp_list = dma_pool_alloc(pool, gfp, &prp_dma); @@ -393,7 +393,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, kfree(prps); return NULL; } - prps->list[prp_page++] = prp_list; + prps->list[0] = prp_list; prps->first_dma = prp_dma; cmd->prp2 = cpu_to_le64(prp_dma); i = 0; @@ -405,7 +405,7 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, *len = (*len - length); return prps; } - prps->list[prp_page++] = prp_list; + prps->list[prps->npages++] = prp_list; prp_list[0] = old_prp_list[i - 1]; old_prp_list[i - 1] = cpu_to_le64(prp_dma); i = 1; -- cgit v0.10.2 From 2b2c1896871838cdf549442e8ad0264be5fa74e3 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 7 Oct 2011 13:10:13 -0400 Subject: NVMe: Don't probe namespace 0 ECN 001 documented that namespace 0 is not valid. Sending an Identify with CNS of 0 and Namespace of 0 is an undefined command. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 3afdc75..660aa5d 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1447,7 +1447,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr)); id_ns = mem; - for (i = 0; i <= nn; i++) { + for (i = 1; i <= nn; i++) { res = nvme_identify(dev, i, 0, dma_addr); if (res) continue; -- cgit v0.10.2 From ce38c149576fd0a3360fec3bef4012212d42e736 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 7 Oct 2011 13:20:37 -0400 Subject: NVMe: Version 0.7 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 660aa5d..cfe5932 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1700,6 +1700,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.6"); +MODULE_VERSION("0.7"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From f1938f6e1ee1583c87ec74dc406fdd8694e99ac8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Thu, 20 Oct 2011 17:00:41 -0400 Subject: NVMe: Implement doorbell stride capability The doorbell stride allows devices to spread out their doorbells instead of packing them tightly. This feature was added as part of ECN 003. This patch also enables support for more than 512 queues :-) Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index cfe5932..a17f80f 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -70,6 +70,7 @@ struct nvme_dev { struct dma_pool *prp_small_pool; int instance; int queue_count; + int db_stride; u32 ctrl_config; struct msix_entry *entry; struct nvme_bar __iomem *bar; @@ -672,7 +673,7 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) if (head == nvmeq->cq_head && phase == nvmeq->cq_phase) return IRQ_NONE; - writel(head, nvmeq->q_db + 1); + writel(head, nvmeq->q_db + (1 << nvmeq->dev->db_stride)); nvmeq->cq_head = head; nvmeq->cq_phase = phase; @@ -889,7 +890,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, init_waitqueue_head(&nvmeq->sq_full); init_waitqueue_entry(&nvmeq->sq_cong_wait, nvme_thread); bio_list_init(&nvmeq->sq_cong); - nvmeq->q_db = &dev->dbs[qid * 2]; + nvmeq->q_db = &dev->dbs[qid << (dev->db_stride + 1)]; nvmeq->q_depth = depth; nvmeq->cq_vector = vector; @@ -981,6 +982,7 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) cap = readq(&dev->bar->cap); timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies; + dev->db_stride = NVME_CAP_STRIDE(cap); while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) { msleep(100); @@ -1357,7 +1359,7 @@ static int set_queue_count(struct nvme_dev *dev, int count) static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) { - int result, cpu, i, nr_io_queues; + int result, cpu, i, nr_io_queues, db_bar_size; nr_io_queues = num_online_cpus(); result = set_queue_count(dev, nr_io_queues); @@ -1369,6 +1371,15 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev) /* Deregister the admin queue's interrupt */ free_irq(dev->entry[0].vector, dev->queues[0]); + db_bar_size = 4096 + ((nr_io_queues + 1) << (dev->db_stride + 3)); + if (db_bar_size > 8192) { + iounmap(dev->bar); + dev->bar = ioremap(pci_resource_start(dev->pci_dev, 0), + db_bar_size); + dev->dbs = ((void __iomem *)dev->bar) + 4096; + dev->queues[0]->q_db = dev->dbs; + } + for (i = 0; i < nr_io_queues; i++) dev->entry[i].entry = i; for (;;) { diff --git a/include/linux/nvme.h b/include/linux/nvme.h index c96ab0f..2a2c535 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -36,6 +36,7 @@ struct nvme_bar { }; #define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) +#define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) enum { NVME_CC_ENABLE = 1 << 0, -- cgit v0.10.2 From 010e646ba2fdfc558048a97da746381c35836280 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 4 Nov 2011 16:24:23 -0400 Subject: NVMe: Update Identify Controller data structure The driver was still using an old definition of Identify Controller which only came to light once we started using the 'number of namespaces' field properly. Reported-by: Nisheeth Bhat Reported-by: Khosrow Panah Signed-off-by: Matthew Wilcox diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 2a2c535..9490a00 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -57,6 +57,18 @@ enum { NVME_CSTS_SHST_CMPLT = 2 << 2, }; +struct nvme_id_power_state { + __le16 max_power; /* centiwatts */ + __u16 rsvd2; + __le32 entry_lat; /* microseconds */ + __le32 exit_lat; /* microseconds */ + __u8 read_tput; + __u8 read_lat; + __u8 write_tput; + __u8 write_lat; + __u8 rsvd16[16]; +}; + #define NVME_VS(major, minor) (major << 16 | minor) struct nvme_id_ctrl { @@ -65,9 +77,11 @@ struct nvme_id_ctrl { char sn[20]; char mn[40]; char fr[8]; - __le32 nn; __u8 rab; - __u8 rsvd77[178]; + __u8 ieee[3]; + __u8 mic; + __u8 mdts; + __u8 rsvd78[178]; __le16 oacs; __u8 acl; __u8 aerl; @@ -76,15 +90,18 @@ struct nvme_id_ctrl { __u8 elpe; __u8 npss; __u8 rsvd264[248]; - __le64 psd[32]; + __u8 sqes; + __u8 cqes; + __u8 rsvd514[2]; + __le32 nn; __le16 oncs; __le16 fuses; __u8 fna; __u8 vwc; __le16 awun; __le16 awupf; - __u8 rsvd778[246]; - __u8 cmdset[2048]; + __u8 rsvd530[1518]; + struct nvme_id_power_state psd[32]; __u8 vs[1024]; }; -- cgit v0.10.2 From ed6c038c2167bee886291b62a6865f0da825e8a6 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 7 Nov 2011 16:11:24 +0300 Subject: CIFS: Add descriptions to the brlock cache functions Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/file.c b/fs/cifs/file.c index cf0b153..4dd9283 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -702,6 +702,13 @@ cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock, lock->type, lock->netfid, conf_lock); } +/* + * Check if there is another lock that prevents us to set the lock (mandatory + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ static int cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, __u8 type, __u16 netfid, struct file_lock *flock) @@ -739,6 +746,12 @@ cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock) mutex_unlock(&cinode->lock_mutex); } +/* + * Set the byte-range lock (mandatory style). Returns: + * 1) 0, if we set the lock and don't need to request to the server; + * 2) 1, if no locks prevent us but we need to request to the server; + * 3) -EACCESS, if there is a lock that prevents us and wait is false. + */ static int cifs_lock_add_if(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock, bool wait) @@ -778,6 +791,13 @@ try_again: return rc; } +/* + * Check if there is another lock that prevents us to set the lock (posix + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ static int cifs_posix_lock_test(struct file *file, struct file_lock *flock) { @@ -800,6 +820,12 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock) return rc; } +/* + * Set the byte-range lock (posix style). Returns: + * 1) 0, if we set the lock and don't need to request to the server; + * 2) 1, if we need to request to the server; + * 3) <0, if the error occurs while setting the lock. + */ static int cifs_posix_lock_set(struct file *file, struct file_lock *flock) { -- cgit v0.10.2 From 67348450b86cb1b42aa4dd55cf7cde19c2e53461 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 17 Oct 2011 14:56:40 +0200 Subject: dmaengine: at_hdmac: platform data move to use .id_table We remove the use of platform data from DMA controller driver. We now use of .id_table to distinguish between compatible types. The two implementations allow to determine the number of channels and the capabilities of the controller. Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index fcfa0a8..d1869c5 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1175,6 +1175,18 @@ static void atc_free_chan_resources(struct dma_chan *chan) /*-- Module Management -----------------------------------------------*/ +static struct platform_device_id atdma_devtypes[] = { + { + .name = "at91sam9rl_dma", + .driver_data = ATDMA_DEVTYPE_SAM9RL, + }, { + .name = "at91sam9g45_dma", + .driver_data = ATDMA_DEVTYPE_SAM9G45, + }, { + /* sentinel */ + } +}; + /** * at_dma_off - disable DMA controller * @atdma: the Atmel HDAMC device @@ -1193,18 +1205,32 @@ static void at_dma_off(struct at_dma *atdma) static int __init at_dma_probe(struct platform_device *pdev) { - struct at_dma_platform_data *pdata; struct resource *io; struct at_dma *atdma; size_t size; int irq; int err; int i; + u32 nr_channels; + dma_cap_mask_t cap_mask = {}; + enum atdma_devtype atdmatype; + + dma_cap_set(DMA_MEMCPY, cap_mask); + + /* get DMA parameters from controller type */ + atdmatype = platform_get_device_id(pdev)->driver_data; - /* get DMA Controller parameters from platform */ - pdata = pdev->dev.platform_data; - if (!pdata || pdata->nr_channels > AT_DMA_MAX_NR_CHANNELS) + switch (atdmatype) { + case ATDMA_DEVTYPE_SAM9RL: + nr_channels = 2; + break; + case ATDMA_DEVTYPE_SAM9G45: + nr_channels = 8; + dma_cap_set(DMA_SLAVE, cap_mask); + break; + default: return -EINVAL; + } io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) @@ -1215,14 +1241,15 @@ static int __init at_dma_probe(struct platform_device *pdev) return irq; size = sizeof(struct at_dma); - size += pdata->nr_channels * sizeof(struct at_dma_chan); + size += nr_channels * sizeof(struct at_dma_chan); atdma = kzalloc(size, GFP_KERNEL); if (!atdma) return -ENOMEM; - /* discover transaction capabilites from the platform data */ - atdma->dma_common.cap_mask = pdata->cap_mask; - atdma->all_chan_mask = (1 << pdata->nr_channels) - 1; + /* discover transaction capabilities */ + atdma->dma_common.cap_mask = cap_mask; + atdma->all_chan_mask = (1 << nr_channels) - 1; + atdma->devtype = atdmatype; size = resource_size(io); if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { @@ -1268,7 +1295,7 @@ static int __init at_dma_probe(struct platform_device *pdev) /* initialize channels related values */ INIT_LIST_HEAD(&atdma->dma_common.channels); - for (i = 0; i < pdata->nr_channels; i++) { + for (i = 0; i < nr_channels; i++) { struct at_dma_chan *atchan = &atdma->chan[i]; atchan->chan_common.device = &atdma->dma_common; @@ -1313,7 +1340,7 @@ static int __init at_dma_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n", dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "", dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "", - pdata->nr_channels); + nr_channels); dma_async_device_register(&atdma->dma_common); @@ -1495,6 +1522,7 @@ static const struct dev_pm_ops at_dma_dev_pm_ops = { static struct platform_driver at_dma_driver = { .remove = __exit_p(at_dma_remove), .shutdown = at_dma_shutdown, + .id_table = atdma_devtypes, .driver = { .name = "at_hdmac", .pm = &at_dma_dev_pm_ops, diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index aa4c9ae..d7d6737 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -248,9 +248,16 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) /*-- Controller ------------------------------------------------------*/ +enum atdma_devtype { + ATDMA_DEVTYPE_UNDEFINED = 0, + ATDMA_DEVTYPE_SAM9RL, /* compatible with SAM9RL DMA controller */ + ATDMA_DEVTYPE_SAM9G45, /* compatible with SAM9G45 DMA controller */ +}; + /** * struct at_dma - internal representation of an Atmel HDMA Controller * @chan_common: common dmaengine dma_device object members + * @atdma_devtype: identifier of DMA controller compatibility * @ch_regs: memory mapped register base * @clk: dma controller clock * @save_imr: interrupt mask register that is saved on suspend/resume cycle @@ -260,6 +267,7 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) */ struct at_dma { struct dma_device dma_common; + enum atdma_devtype devtype; void __iomem *regs; struct clk *clk; u32 save_imr; -- cgit v0.10.2 From c511595390a373e19a774246c659b4f563a4c3f3 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 17 Oct 2011 14:56:41 +0200 Subject: dmaengine: at_hdmac: add device tree support Add device tree probe support for atmel at_hdmac DMA driver. Bindings are added to specify DMA controller configuration. Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Vinod Koul diff --git a/Documentation/devicetree/bindings/dma/atmel-dma.txt b/Documentation/devicetree/bindings/dma/atmel-dma.txt new file mode 100644 index 0000000..3c046ee --- /dev/null +++ b/Documentation/devicetree/bindings/dma/atmel-dma.txt @@ -0,0 +1,14 @@ +* Atmel Direct Memory Access Controller (DMA) + +Required properties: +- compatible: Should be "atmel,-dma" +- reg: Should contain DMA registers location and length +- interrupts: Should contain DMA interrupt + +Examples: + +dma@ffffec00 { + compatible = "atmel,at91sam9g45-dma"; + reg = <0xffffec00 0x200>; + interrupts = <21>; +}; diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index d1869c5..f3cb4a0 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "at_hdmac_regs.h" @@ -1175,6 +1177,20 @@ static void atc_free_chan_resources(struct dma_chan *chan) /*-- Module Management -----------------------------------------------*/ +#if defined(CONFIG_OF) +static const struct of_device_id atmel_dma_dt_ids[] = { + { + .compatible = "atmel,at91sam9rl-dma", + .data = (void *)ATDMA_DEVTYPE_SAM9RL + }, { + .compatible = "atmel,at91sam9g45-dma", + .data = (void *)ATDMA_DEVTYPE_SAM9G45 + }, { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_dma_dt_ids); +#endif + static struct platform_device_id atdma_devtypes[] = { { .name = "at91sam9rl_dma", @@ -1187,6 +1203,19 @@ static struct platform_device_id atdma_devtypes[] = { } }; +static inline enum atdma_devtype __init at_dma_get_driver_data( + struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(atmel_dma_dt_ids, pdev->dev.of_node); + if (match == NULL) + return ATDMA_DEVTYPE_UNDEFINED; + return (enum atdma_devtype)match->data; + } + return platform_get_device_id(pdev)->driver_data; +} + /** * at_dma_off - disable DMA controller * @atdma: the Atmel HDAMC device @@ -1218,7 +1247,7 @@ static int __init at_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, cap_mask); /* get DMA parameters from controller type */ - atdmatype = platform_get_device_id(pdev)->driver_data; + atdmatype = at_dma_get_driver_data(pdev); switch (atdmatype) { case ATDMA_DEVTYPE_SAM9RL: @@ -1526,6 +1555,7 @@ static struct platform_driver at_dma_driver = { .driver = { .name = "at_hdmac", .pm = &at_dma_dev_pm_ops, + .of_match_table = of_match_ptr(atmel_dma_dt_ids), }, }; -- cgit v0.10.2 From 035c17dac4ce1f03d6831ff403f5aea7dcb927b4 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 10 Nov 2011 09:54:42 +0100 Subject: ARM i.MX5: remove unnecessary includes from board files Signed-off-by: Sascha Hauer diff --git a/arch/arm/mach-mx5/board-mx53_ard.c b/arch/arm/mach-mx5/board-mx53_ard.c index 0d7f0ff..b88a2bc 100644 --- a/arch/arm/mach-mx5/board-mx53_ard.c +++ b/arch/arm/mach-mx5/board-mx53_ard.c @@ -32,7 +32,6 @@ #include #include -#include "crm_regs.h" #include "devices-imx53.h" #define ARD_ETHERNET_INT_B IMX_GPIO_NR(2, 31) diff --git a/arch/arm/mach-mx5/board-mx53_evk.c b/arch/arm/mach-mx5/board-mx53_evk.c index 6bea31a..c69413d 100644 --- a/arch/arm/mach-mx5/board-mx53_evk.c +++ b/arch/arm/mach-mx5/board-mx53_evk.c @@ -37,7 +37,6 @@ #define EVK_ECSPI1_CS1 IMX_GPIO_NR(3, 19) #define MX53EVK_LED IMX_GPIO_NR(7, 7) -#include "crm_regs.h" #include "devices-imx53.h" static iomux_v3_cfg_t mx53_evk_pads[] = { diff --git a/arch/arm/mach-mx5/board-mx53_loco.c b/arch/arm/mach-mx5/board-mx53_loco.c index 7678f77..e64a8f7 100644 --- a/arch/arm/mach-mx5/board-mx53_loco.c +++ b/arch/arm/mach-mx5/board-mx53_loco.c @@ -32,7 +32,6 @@ #include #include -#include "crm_regs.h" #include "devices-imx53.h" #define MX53_LOCO_POWER IMX_GPIO_NR(1, 8) diff --git a/arch/arm/mach-mx5/board-mx53_smd.c b/arch/arm/mach-mx5/board-mx53_smd.c index 59c0845..d498573 100644 --- a/arch/arm/mach-mx5/board-mx53_smd.c +++ b/arch/arm/mach-mx5/board-mx53_smd.c @@ -31,7 +31,6 @@ #include #include -#include "crm_regs.h" #include "devices-imx53.h" #define SMD_FEC_PHY_RST IMX_GPIO_NR(7, 6) -- cgit v0.10.2 From dab6538e29e68af6206ec1889c847b428f290989 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Thu, 3 Nov 2011 15:48:49 +0900 Subject: DMA: PL330: Fix build warning This patch adds to fix the build warning as following. drivers/dma/pl330.c: In function 'pl330_probe': drivers/dma/pl330.c:859: warning: comparison of distinct pointer types lacks a cast Signed-off-by: Boojin Kim Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 57104147..186b822 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -856,7 +856,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) INIT_LIST_HEAD(&pd->channels); /* Initialize channel parameters */ - num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan); + num_chan = max(pdat ? (int)pdat->nr_valid_peri : 0, + (int)pi->pcfg.num_chan); pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL); for (i = 0; i < num_chan; i++) { -- cgit v0.10.2 From 9c32c63bb70b2fafc3b18bee29959c3bf245ceba Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 10 Nov 2011 12:48:20 -0600 Subject: cifs: Fix sparse warning when calling cifs_strtoUCS Fix sparse endian check warning while calling cifs_strtoUCS CHECK fs/cifs/smbencrypt.c fs/cifs/smbencrypt.c:216:37: warning: incorrect type in argument 1 (different base types) fs/cifs/smbencrypt.c:216:37: expected restricted __le16 [usertype] * fs/cifs/smbencrypt.c:216:37: got unsigned short * Signed-off-by: Steve French Acked-by: Shirish Pargaonkar Date: Mon, 7 Nov 2011 12:36:48 +0100 Subject: ARM i.MX: Merge i.MX5 support into mach-imx This patch moves the contents of arch/arm/mach-mx5 to arch/arm/mach-imx and adjusts the Makefile/Kconfig entries in a way that it's possible to compile i.MX5 together with i.MX3/6. Signed-off-by: Sascha Hauer Tested-by: Shawn Guo Tested-by: Dirk Behme diff --git a/arch/arm/Makefile b/arch/arm/Makefile index dfcf3b0..cf7d467 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -160,7 +160,6 @@ machine-$(CONFIG_ARCH_MSM) := msm machine-$(CONFIG_ARCH_MV78XX0) := mv78xx0 machine-$(CONFIG_ARCH_IMX_V4_V5) := imx machine-$(CONFIG_ARCH_IMX_V6_V7) := imx -machine-$(CONFIG_ARCH_MX5) := mx5 machine-$(CONFIG_ARCH_MXS) := mxs machine-$(CONFIG_ARCH_NETX) := netx machine-$(CONFIG_ARCH_NOMADIK) := nomadik diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 5f7f9c2..ef6a6b8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -33,6 +33,18 @@ config ARCH_MX31 config ARCH_MX35 bool +config ARCH_MX5 + bool + +config ARCH_MX50 + bool + +config ARCH_MX51 + bool + +config ARCH_MX53 + bool + config SOC_IMX1 bool select ARCH_MX1 @@ -86,6 +98,32 @@ config SOC_IMX35 select MXC_AVIC select SMP_ON_UP if SMP +config SOC_IMX5 + select CPU_V7 + select ARM_L1_CACHE_SHIFT_6 + select MXC_TZIC + select ARCH_MXC_IOMUX_V3 + select ARCH_MXC_AUDMUX_V2 + select ARCH_HAS_CPUFREQ + select ARCH_MX5 + bool + +config SOC_IMX50 + bool + select SOC_IMX5 + select ARCH_MX50 + +config SOC_IMX51 + bool + select SOC_IMX5 + select ARCH_MX5 + select ARCH_MX51 + +config SOC_IMX53 + bool + select SOC_IMX5 + select ARCH_MX5 + select ARCH_MX53 if ARCH_IMX_V4_V5 @@ -604,6 +642,207 @@ config MACH_VPR200 Include support for VPR200 platform. This includes specific configurations for the board and its peripherals. +comment "i.MX5 platforms:" + +config MACH_MX50_RDP + bool "Support MX50 reference design platform" + depends on BROKEN + select SOC_IMX50 + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_SPI_IMX + help + Include support for MX50 reference design platform (RDP) board. This + includes specific configurations for the board and its peripherals. + +comment "i.MX51 machines:" + +config MACH_IMX51_DT + bool "Support i.MX51 platforms from device tree" + select SOC_IMX51 + select USE_OF + select MACH_MX51_BABBAGE + help + Include support for Freescale i.MX51 based platforms + using the device tree for discovery + +config MACH_MX51_BABBAGE + bool "Support MX51 BABBAGE platforms" + select SOC_IMX51 + select IMX_HAVE_PLATFORM_FSL_USB2_UDC + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_MXC_EHCI + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_SPI_IMX + help + Include support for MX51 Babbage platform, also known as MX51EVK in + u-boot. This includes specific configurations for the board and its + peripherals. + +config MACH_MX51_3DS + bool "Support MX51PDK (3DS)" + select SOC_IMX51 + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_KEYPAD + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_SPI_IMX + select MXC_DEBUG_BOARD + help + Include support for MX51PDK (3DS) platform. This includes specific + configurations for the board and its peripherals. + +config MACH_EUKREA_CPUIMX51 + bool "Support Eukrea CPUIMX51 module" + select SOC_IMX51 + select IMX_HAVE_PLATFORM_FSL_USB2_UDC + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_MXC_EHCI + select IMX_HAVE_PLATFORM_MXC_NAND + select IMX_HAVE_PLATFORM_SPI_IMX + help + Include support for Eukrea CPUIMX51 platform. This includes + specific configurations for the module and its peripherals. + +choice + prompt "Baseboard" + depends on MACH_EUKREA_CPUIMX51 + default MACH_EUKREA_MBIMX51_BASEBOARD + +config MACH_EUKREA_MBIMX51_BASEBOARD + prompt "Eukrea MBIMX51 development board" + bool + select IMX_HAVE_PLATFORM_IMX_KEYPAD + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select LEDS_GPIO_REGISTER + help + This adds board specific devices that can be found on Eukrea's + MBIMX51 evaluation board. + +endchoice + +config MACH_EUKREA_CPUIMX51SD + bool "Support Eukrea CPUIMX51SD module" + select SOC_IMX51 + select IMX_HAVE_PLATFORM_FSL_USB2_UDC + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_MXC_EHCI + select IMX_HAVE_PLATFORM_MXC_NAND + select IMX_HAVE_PLATFORM_SPI_IMX + help + Include support for Eukrea CPUIMX51SD platform. This includes + specific configurations for the module and its peripherals. + +choice + prompt "Baseboard" + depends on MACH_EUKREA_CPUIMX51SD + default MACH_EUKREA_MBIMXSD51_BASEBOARD + +config MACH_EUKREA_MBIMXSD51_BASEBOARD + prompt "Eukrea MBIMXSD development board" + bool + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select LEDS_GPIO_REGISTER + help + This adds board specific devices that can be found on Eukrea's + MBIMXSD evaluation board. + +endchoice + +config MX51_EFIKA_COMMON + bool + select SOC_IMX51 + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_MXC_EHCI + select IMX_HAVE_PLATFORM_PATA_IMX + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_SPI_IMX + select MXC_ULPI if USB_ULPI + +config MACH_MX51_EFIKAMX + bool "Support MX51 Genesi Efika MX nettop" + select LEDS_GPIO_REGISTER + select MX51_EFIKA_COMMON + help + Include support for Genesi Efika MX nettop. This includes specific + configurations for the board and its peripherals. + +config MACH_MX51_EFIKASB + bool "Support MX51 Genesi Efika Smartbook" + select LEDS_GPIO_REGISTER + select MX51_EFIKA_COMMON + help + Include support for Genesi Efika Smartbook. This includes specific + configurations for the board and its peripherals. + +comment "i.MX53 machines:" + +config MACH_IMX53_DT + bool "Support i.MX53 platforms from device tree" + select SOC_IMX53 + select USE_OF + select MACH_MX53_ARD + select MACH_MX53_EVK + select MACH_MX53_LOCO + select MACH_MX53_SMD + help + Include support for Freescale i.MX53 based platforms + using the device tree for discovery + +config MACH_MX53_EVK + bool "Support MX53 EVK platforms" + select SOC_IMX53 + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_SPI_IMX + select LEDS_GPIO_REGISTER + help + Include support for MX53 EVK platform. This includes specific + configurations for the board and its peripherals. + +config MACH_MX53_SMD + bool "Support MX53 SMD platforms" + select SOC_IMX53 + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + help + Include support for MX53 SMD platform. This includes specific + configurations for the board and its peripherals. + +config MACH_MX53_LOCO + bool "Support MX53 LOCO platforms" + select SOC_IMX53 + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_GPIO_KEYS + select LEDS_GPIO_REGISTER + help + Include support for MX53 LOCO platform. This includes specific + configurations for the board and its peripherals. + +config MACH_MX53_ARD + bool "Support MX53 ARD platforms" + select SOC_IMX53 + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_GPIO_KEYS + help + Include support for MX53 ARD platform. This includes specific + configurations for the board and its peripherals. + comment "i.MX6 family:" config SOC_IMX6Q diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index aba7321..9cf630a 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_SOC_IMX27) += clock-imx27.o mm-imx27.o ehci-imx27.o obj-$(CONFIG_SOC_IMX31) += mm-imx3.o cpu-imx31.o clock-imx31.o iomux-imx31.o ehci-imx31.o obj-$(CONFIG_SOC_IMX35) += mm-imx3.o cpu-imx35.o clock-imx35.o ehci-imx35.o +obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clock-mx51-mx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o + # Support for CMOS sensor interface obj-$(CONFIG_MX1_VIDEO) += mx1-camera-fiq.o mx1-camera-fiq-ksym.o @@ -71,3 +73,22 @@ obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o obj-$(CONFIG_SOC_IMX6Q) += clock-imx6q.o mach-imx6q.o pm-imx6q.o + +# i.MX5 based machines +obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o +obj-$(CONFIG_MACH_MX51_3DS) += mach-mx51_3ds.o +obj-$(CONFIG_MACH_MX53_EVK) += mach-mx53_evk.o +obj-$(CONFIG_MACH_MX53_SMD) += mach-mx53_smd.o +obj-$(CONFIG_MACH_MX53_LOCO) += mach-mx53_loco.o +obj-$(CONFIG_MACH_MX53_ARD) += mach-mx53_ard.o +obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += mach-cpuimx51.o +obj-$(CONFIG_MACH_EUKREA_MBIMX51_BASEBOARD) += eukrea_mbimx51-baseboard.o +obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += mach-cpuimx51sd.o +obj-$(CONFIG_MACH_EUKREA_MBIMXSD51_BASEBOARD) += eukrea_mbimxsd-baseboard.o +obj-$(CONFIG_MX51_EFIKA_COMMON) += mx51_efika.o +obj-$(CONFIG_MACH_MX51_EFIKAMX) += mach-mx51_efikamx.o +obj-$(CONFIG_MACH_MX51_EFIKASB) += mach-mx51_efikasb.o +obj-$(CONFIG_MACH_MX50_RDP) += mach-mx50_rdp.o + +obj-$(CONFIG_MACH_IMX51_DT) += imx51-dt.o +obj-$(CONFIG_MACH_IMX53_DT) += imx53-dt.o diff --git a/arch/arm/mach-imx/Makefile.boot b/arch/arm/mach-imx/Makefile.boot index 22d8588..2c12b78 100644 --- a/arch/arm/mach-imx/Makefile.boot +++ b/arch/arm/mach-imx/Makefile.boot @@ -18,6 +18,18 @@ zreladdr-$(CONFIG_ARCH_MX3) += 0x80008000 params_phys-$(CONFIG_ARCH_MX3) := 0x80000100 initrd_phys-$(CONFIG_ARCH_MX3) := 0x80800000 +zreladdr-$(CONFIG_SOC_IMX50) += 0x70008000 +params_phys-$(CONFIG_SOC_IMX50) := 0x70000100 +initrd_phys-$(CONFIG_SOC_IMX50) := 0x70800000 + +zreladdr-$(CONFIG_SOC_IMX51) += 0x90008000 +params_phys-$(CONFIG_SOC_IMX51) := 0x90000100 +initrd_phys-$(CONFIG_SOC_IMX51) := 0x90800000 + +zreladdr-$(CONFIG_SOC_IMX53) += 0x70008000 +params_phys-$(CONFIG_SOC_IMX53) := 0x70000100 +initrd_phys-$(CONFIG_SOC_IMX53) := 0x70800000 + zreladdr-$(CONFIG_SOC_IMX6Q) += 0x10008000 params_phys-$(CONFIG_SOC_IMX6Q) := 0x10000100 initrd_phys-$(CONFIG_SOC_IMX6Q) := 0x10800000 diff --git a/arch/arm/mach-imx/clock-mx51-mx53.c b/arch/arm/mach-imx/clock-mx51-mx53.c new file mode 100644 index 0000000..a2c654d --- /dev/null +++ b/arch/arm/mach-imx/clock-mx51-mx53.c @@ -0,0 +1,1673 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009-2010 Amit Kucheria + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "crm-regs-imx5.h" + +/* External clock values passed-in by the board code */ +static unsigned long external_high_reference, external_low_reference; +static unsigned long oscillator_reference, ckih2_reference; + +static struct clk osc_clk; +static struct clk pll1_main_clk; +static struct clk pll1_sw_clk; +static struct clk pll2_sw_clk; +static struct clk pll3_sw_clk; +static struct clk mx53_pll4_sw_clk; +static struct clk lp_apm_clk; +static struct clk periph_apm_clk; +static struct clk ahb_clk; +static struct clk ipg_clk; +static struct clk usboh3_clk; +static struct clk emi_fast_clk; +static struct clk ipu_clk; +static struct clk mipi_hsc1_clk; +static struct clk esdhc1_clk; +static struct clk esdhc2_clk; +static struct clk esdhc3_mx53_clk; + +#define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ + +/* calculate best pre and post dividers to get the required divider */ +static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post, + u32 max_pre, u32 max_post) +{ + if (div >= max_pre * max_post) { + *pre = max_pre; + *post = max_post; + } else if (div >= max_pre) { + u32 min_pre, temp_pre, old_err, err; + min_pre = DIV_ROUND_UP(div, max_post); + old_err = max_pre; + for (temp_pre = max_pre; temp_pre >= min_pre; temp_pre--) { + err = div % temp_pre; + if (err == 0) { + *pre = temp_pre; + break; + } + err = temp_pre - err; + if (err < old_err) { + old_err = err; + *pre = temp_pre; + } + } + *post = DIV_ROUND_UP(div, *pre); + } else { + *pre = div; + *post = 1; + } +} + +static void _clk_ccgr_setclk(struct clk *clk, unsigned mode) +{ + u32 reg = __raw_readl(clk->enable_reg); + + reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift); + reg |= mode << clk->enable_shift; + + __raw_writel(reg, clk->enable_reg); +} + +static int _clk_ccgr_enable(struct clk *clk) +{ + _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_ON); + return 0; +} + +static void _clk_ccgr_disable(struct clk *clk) +{ + _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_OFF); +} + +static int _clk_ccgr_enable_inrun(struct clk *clk) +{ + _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_IDLE); + return 0; +} + +static void _clk_ccgr_disable_inwait(struct clk *clk) +{ + _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_IDLE); +} + +/* + * For the 4-to-1 muxed input clock + */ +static inline u32 _get_mux(struct clk *parent, struct clk *m0, + struct clk *m1, struct clk *m2, struct clk *m3) +{ + if (parent == m0) + return 0; + else if (parent == m1) + return 1; + else if (parent == m2) + return 2; + else if (parent == m3) + return 3; + else + BUG(); + + return -EINVAL; +} + +static inline void __iomem *_mx51_get_pll_base(struct clk *pll) +{ + if (pll == &pll1_main_clk) + return MX51_DPLL1_BASE; + else if (pll == &pll2_sw_clk) + return MX51_DPLL2_BASE; + else if (pll == &pll3_sw_clk) + return MX51_DPLL3_BASE; + else + BUG(); + + return NULL; +} + +static inline void __iomem *_mx53_get_pll_base(struct clk *pll) +{ + if (pll == &pll1_main_clk) + return MX53_DPLL1_BASE; + else if (pll == &pll2_sw_clk) + return MX53_DPLL2_BASE; + else if (pll == &pll3_sw_clk) + return MX53_DPLL3_BASE; + else if (pll == &mx53_pll4_sw_clk) + return MX53_DPLL4_BASE; + else + BUG(); + + return NULL; +} + +static inline void __iomem *_get_pll_base(struct clk *pll) +{ + if (cpu_is_mx51()) + return _mx51_get_pll_base(pll); + else + return _mx53_get_pll_base(pll); +} + +static unsigned long clk_pll_get_rate(struct clk *clk) +{ + long mfi, mfn, mfd, pdf, ref_clk, mfn_abs; + unsigned long dp_op, dp_mfd, dp_mfn, dp_ctl, pll_hfsm, dbl; + void __iomem *pllbase; + s64 temp; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + pllbase = _get_pll_base(clk); + + dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); + pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM; + dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN; + + if (pll_hfsm == 0) { + dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP); + dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD); + dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN); + } else { + dp_op = __raw_readl(pllbase + MXC_PLL_DP_HFS_OP); + dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFD); + dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFN); + } + pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK; + mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET; + mfi = (mfi <= 5) ? 5 : mfi; + mfd = dp_mfd & MXC_PLL_DP_MFD_MASK; + mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK; + /* Sign extend to 32-bits */ + if (mfn >= 0x04000000) { + mfn |= 0xFC000000; + mfn_abs = -mfn; + } + + ref_clk = 2 * parent_rate; + if (dbl != 0) + ref_clk *= 2; + + ref_clk /= (pdf + 1); + temp = (u64) ref_clk * mfn_abs; + do_div(temp, mfd + 1); + if (mfn < 0) + temp = -temp; + temp = (ref_clk * mfi) + temp; + + return temp; +} + +static int _clk_pll_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg; + void __iomem *pllbase; + + long mfi, pdf, mfn, mfd = 999999; + s64 temp64; + unsigned long quad_parent_rate; + unsigned long pll_hfsm, dp_ctl; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + pllbase = _get_pll_base(clk); + + quad_parent_rate = 4 * parent_rate; + pdf = mfi = -1; + while (++pdf < 16 && mfi < 5) + mfi = rate * (pdf+1) / quad_parent_rate; + if (mfi > 15) + return -EINVAL; + pdf--; + + temp64 = rate * (pdf+1) - quad_parent_rate * mfi; + do_div(temp64, quad_parent_rate/1000000); + mfn = (long)temp64; + + dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); + /* use dpdck0_2 */ + __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL); + pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM; + if (pll_hfsm == 0) { + reg = mfi << 4 | pdf; + __raw_writel(reg, pllbase + MXC_PLL_DP_OP); + __raw_writel(mfd, pllbase + MXC_PLL_DP_MFD); + __raw_writel(mfn, pllbase + MXC_PLL_DP_MFN); + } else { + reg = mfi << 4 | pdf; + __raw_writel(reg, pllbase + MXC_PLL_DP_HFS_OP); + __raw_writel(mfd, pllbase + MXC_PLL_DP_HFS_MFD); + __raw_writel(mfn, pllbase + MXC_PLL_DP_HFS_MFN); + } + + return 0; +} + +static int _clk_pll_enable(struct clk *clk) +{ + u32 reg; + void __iomem *pllbase; + int i = 0; + + pllbase = _get_pll_base(clk); + reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); + if (reg & MXC_PLL_DP_CTL_UPEN) + return 0; + + reg |= MXC_PLL_DP_CTL_UPEN; + __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); + + /* Wait for lock */ + do { + reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); + if (reg & MXC_PLL_DP_CTL_LRF) + break; + + udelay(1); + } while (++i < MAX_DPLL_WAIT_TRIES); + + if (i == MAX_DPLL_WAIT_TRIES) { + pr_err("MX5: pll locking failed\n"); + return -EINVAL; + } + + return 0; +} + +static void _clk_pll_disable(struct clk *clk) +{ + u32 reg; + void __iomem *pllbase; + + pllbase = _get_pll_base(clk); + reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN; + __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); +} + +static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg, step; + + reg = __raw_readl(MXC_CCM_CCSR); + + /* When switching from pll_main_clk to a bypass clock, first select a + * multiplexed clock in 'step_sel', then shift the glitchless mux + * 'pll1_sw_clk_sel'. + * + * When switching back, do it in reverse order + */ + if (parent == &pll1_main_clk) { + /* Switch to pll1_main_clk */ + reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL; + __raw_writel(reg, MXC_CCM_CCSR); + /* step_clk mux switched to lp_apm, to save power. */ + reg = __raw_readl(MXC_CCM_CCSR); + reg &= ~MXC_CCM_CCSR_STEP_SEL_MASK; + reg |= (MXC_CCM_CCSR_STEP_SEL_LP_APM << + MXC_CCM_CCSR_STEP_SEL_OFFSET); + } else { + if (parent == &lp_apm_clk) { + step = MXC_CCM_CCSR_STEP_SEL_LP_APM; + } else if (parent == &pll2_sw_clk) { + step = MXC_CCM_CCSR_STEP_SEL_PLL2_DIVIDED; + } else if (parent == &pll3_sw_clk) { + step = MXC_CCM_CCSR_STEP_SEL_PLL3_DIVIDED; + } else + return -EINVAL; + + reg &= ~MXC_CCM_CCSR_STEP_SEL_MASK; + reg |= (step << MXC_CCM_CCSR_STEP_SEL_OFFSET); + + __raw_writel(reg, MXC_CCM_CCSR); + /* Switch to step_clk */ + reg = __raw_readl(MXC_CCM_CCSR); + reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL; + } + __raw_writel(reg, MXC_CCM_CCSR); + return 0; +} + +static unsigned long clk_pll1_sw_get_rate(struct clk *clk) +{ + u32 reg, div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CCSR); + + if (clk->parent == &pll2_sw_clk) { + div = ((reg & MXC_CCM_CCSR_PLL2_PODF_MASK) >> + MXC_CCM_CCSR_PLL2_PODF_OFFSET) + 1; + } else if (clk->parent == &pll3_sw_clk) { + div = ((reg & MXC_CCM_CCSR_PLL3_PODF_MASK) >> + MXC_CCM_CCSR_PLL3_PODF_OFFSET) + 1; + } else + div = 1; + return parent_rate / div; +} + +static int _clk_pll2_sw_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CCSR); + + if (parent == &pll2_sw_clk) + reg &= ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL; + else + reg |= MXC_CCM_CCSR_PLL2_SW_CLK_SEL; + + __raw_writel(reg, MXC_CCM_CCSR); + return 0; +} + +static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + if (parent == &osc_clk) + reg = __raw_readl(MXC_CCM_CCSR) & ~MXC_CCM_CCSR_LP_APM_SEL; + else + return -EINVAL; + + __raw_writel(reg, MXC_CCM_CCSR); + + return 0; +} + +static unsigned long clk_cpu_get_rate(struct clk *clk) +{ + u32 cacrr, div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + cacrr = __raw_readl(MXC_CCM_CACRR); + div = (cacrr & MXC_CCM_CACRR_ARM_PODF_MASK) + 1; + + return parent_rate / div; +} + +static int clk_cpu_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, cpu_podf; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + cpu_podf = parent_rate / rate - 1; + /* use post divider to change freq */ + reg = __raw_readl(MXC_CCM_CACRR); + reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; + reg |= cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; + __raw_writel(reg, MXC_CCM_CACRR); + + return 0; +} + +static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg, mux; + int i = 0; + + mux = _get_mux(parent, &pll1_sw_clk, &pll3_sw_clk, &lp_apm_clk, NULL); + + reg = __raw_readl(MXC_CCM_CBCMR) & ~MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK; + reg |= mux << MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET; + __raw_writel(reg, MXC_CCM_CBCMR); + + /* Wait for lock */ + do { + reg = __raw_readl(MXC_CCM_CDHIPR); + if (!(reg & MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY)) + break; + + udelay(1); + } while (++i < MAX_DPLL_WAIT_TRIES); + + if (i == MAX_DPLL_WAIT_TRIES) { + pr_err("MX5: Set parent for periph_apm clock failed\n"); + return -EINVAL; + } + + return 0; +} + +static int _clk_main_bus_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CBCDR); + + if (parent == &pll2_sw_clk) + reg &= ~MXC_CCM_CBCDR_PERIPH_CLK_SEL; + else if (parent == &periph_apm_clk) + reg |= MXC_CCM_CBCDR_PERIPH_CLK_SEL; + else + return -EINVAL; + + __raw_writel(reg, MXC_CCM_CBCDR); + + return 0; +} + +static struct clk main_bus_clk = { + .parent = &pll2_sw_clk, + .set_parent = _clk_main_bus_set_parent, +}; + +static unsigned long clk_ahb_get_rate(struct clk *clk) +{ + u32 reg, div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CBCDR); + div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >> + MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1; + return parent_rate / div; +} + + +static int _clk_ahb_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div; + unsigned long parent_rate; + int i = 0; + + parent_rate = clk_get_rate(clk->parent); + + div = parent_rate / rate; + if (div > 8 || div < 1 || ((parent_rate / div) != rate)) + return -EINVAL; + + reg = __raw_readl(MXC_CCM_CBCDR); + reg &= ~MXC_CCM_CBCDR_AHB_PODF_MASK; + reg |= (div - 1) << MXC_CCM_CBCDR_AHB_PODF_OFFSET; + __raw_writel(reg, MXC_CCM_CBCDR); + + /* Wait for lock */ + do { + reg = __raw_readl(MXC_CCM_CDHIPR); + if (!(reg & MXC_CCM_CDHIPR_AHB_PODF_BUSY)) + break; + + udelay(1); + } while (++i < MAX_DPLL_WAIT_TRIES); + + if (i == MAX_DPLL_WAIT_TRIES) { + pr_err("MX5: clk_ahb_set_rate failed\n"); + return -EINVAL; + } + + return 0; +} + +static unsigned long _clk_ahb_round_rate(struct clk *clk, + unsigned long rate) +{ + u32 div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + div = parent_rate / rate; + if (div > 8) + div = 8; + else if (div == 0) + div++; + return parent_rate / div; +} + + +static int _clk_max_enable(struct clk *clk) +{ + u32 reg; + + _clk_ccgr_enable(clk); + + /* Handshake with MAX when LPM is entered. */ + reg = __raw_readl(MXC_CCM_CLPCR); + if (cpu_is_mx51()) + reg &= ~MX51_CCM_CLPCR_BYPASS_MAX_LPM_HS; + else if (cpu_is_mx53()) + reg &= ~MX53_CCM_CLPCR_BYPASS_MAX_LPM_HS; + __raw_writel(reg, MXC_CCM_CLPCR); + + return 0; +} + +static void _clk_max_disable(struct clk *clk) +{ + u32 reg; + + _clk_ccgr_disable_inwait(clk); + + /* No Handshake with MAX when LPM is entered as its disabled. */ + reg = __raw_readl(MXC_CCM_CLPCR); + if (cpu_is_mx51()) + reg |= MX51_CCM_CLPCR_BYPASS_MAX_LPM_HS; + else if (cpu_is_mx53()) + reg &= ~MX53_CCM_CLPCR_BYPASS_MAX_LPM_HS; + __raw_writel(reg, MXC_CCM_CLPCR); +} + +static unsigned long clk_ipg_get_rate(struct clk *clk) +{ + u32 reg, div; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CBCDR); + div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >> + MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1; + + return parent_rate / div; +} + +static unsigned long clk_ipg_per_get_rate(struct clk *clk) +{ + u32 reg, prediv1, prediv2, podf; + unsigned long parent_rate; + + parent_rate = clk_get_rate(clk->parent); + + if (clk->parent == &main_bus_clk || clk->parent == &lp_apm_clk) { + /* the main_bus_clk is the one before the DVFS engine */ + reg = __raw_readl(MXC_CCM_CBCDR); + prediv1 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED1_MASK) >> + MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET) + 1; + prediv2 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED2_MASK) >> + MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET) + 1; + podf = ((reg & MXC_CCM_CBCDR_PERCLK_PODF_MASK) >> + MXC_CCM_CBCDR_PERCLK_PODF_OFFSET) + 1; + return parent_rate / (prediv1 * prediv2 * podf); + } else if (clk->parent == &ipg_clk) + return parent_rate; + else + BUG(); +} + +static int _clk_ipg_per_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CBCMR); + + reg &= ~MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL; + reg &= ~MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL; + + if (parent == &ipg_clk) + reg |= MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL; + else if (parent == &lp_apm_clk) + reg |= MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL; + else if (parent != &main_bus_clk) + return -EINVAL; + + __raw_writel(reg, MXC_CCM_CBCMR); + + return 0; +} + +#define clk_nfc_set_parent NULL + +static unsigned long clk_nfc_get_rate(struct clk *clk) +{ + unsigned long rate; + u32 reg, div; + + reg = __raw_readl(MXC_CCM_CBCDR); + div = ((reg & MXC_CCM_CBCDR_NFC_PODF_MASK) >> + MXC_CCM_CBCDR_NFC_PODF_OFFSET) + 1; + rate = clk_get_rate(clk->parent) / div; + WARN_ON(rate == 0); + return rate; +} + +static unsigned long clk_nfc_round_rate(struct clk *clk, + unsigned long rate) +{ + u32 div; + unsigned long parent_rate = clk_get_rate(clk->parent); + + if (!rate) + return -EINVAL; + + div = parent_rate / rate; + + if (parent_rate % rate) + div++; + + if (div > 8) + return -EINVAL; + + return parent_rate / div; + +} + +static int clk_nfc_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div; + + div = clk_get_rate(clk->parent) / rate; + if (div == 0) + div++; + if (((clk_get_rate(clk->parent) / div) != rate) || (div > 8)) + return -EINVAL; + + reg = __raw_readl(MXC_CCM_CBCDR); + reg &= ~MXC_CCM_CBCDR_NFC_PODF_MASK; + reg |= (div - 1) << MXC_CCM_CBCDR_NFC_PODF_OFFSET; + __raw_writel(reg, MXC_CCM_CBCDR); + + while (__raw_readl(MXC_CCM_CDHIPR) & + MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY){ + } + + return 0; +} + +static unsigned long get_high_reference_clock_rate(struct clk *clk) +{ + return external_high_reference; +} + +static unsigned long get_low_reference_clock_rate(struct clk *clk) +{ + return external_low_reference; +} + +static unsigned long get_oscillator_reference_clock_rate(struct clk *clk) +{ + return oscillator_reference; +} + +static unsigned long get_ckih2_reference_clock_rate(struct clk *clk) +{ + return ckih2_reference; +} + +static unsigned long clk_emi_slow_get_rate(struct clk *clk) +{ + u32 reg, div; + + reg = __raw_readl(MXC_CCM_CBCDR); + div = ((reg & MXC_CCM_CBCDR_EMI_PODF_MASK) >> + MXC_CCM_CBCDR_EMI_PODF_OFFSET) + 1; + + return clk_get_rate(clk->parent) / div; +} + +static unsigned long _clk_ddr_hf_get_rate(struct clk *clk) +{ + unsigned long rate; + u32 reg, div; + + reg = __raw_readl(MXC_CCM_CBCDR); + div = ((reg & MXC_CCM_CBCDR_DDR_PODF_MASK) >> + MXC_CCM_CBCDR_DDR_PODF_OFFSET) + 1; + rate = clk_get_rate(clk->parent) / div; + + return rate; +} + +/* External high frequency clock */ +static struct clk ckih_clk = { + .get_rate = get_high_reference_clock_rate, +}; + +static struct clk ckih2_clk = { + .get_rate = get_ckih2_reference_clock_rate, +}; + +static struct clk osc_clk = { + .get_rate = get_oscillator_reference_clock_rate, +}; + +/* External low frequency (32kHz) clock */ +static struct clk ckil_clk = { + .get_rate = get_low_reference_clock_rate, +}; + +static struct clk pll1_main_clk = { + .parent = &osc_clk, + .get_rate = clk_pll_get_rate, + .enable = _clk_pll_enable, + .disable = _clk_pll_disable, +}; + +/* Clock tree block diagram (WIP): + * CCM: Clock Controller Module + * + * PLL output -> | + * | CCM Switcher -> CCM_CLK_ROOT_GEN -> + * PLL bypass -> | + * + */ + +/* PLL1 SW supplies to ARM core */ +static struct clk pll1_sw_clk = { + .parent = &pll1_main_clk, + .set_parent = _clk_pll1_sw_set_parent, + .get_rate = clk_pll1_sw_get_rate, +}; + +/* PLL2 SW supplies to AXI/AHB/IP buses */ +static struct clk pll2_sw_clk = { + .parent = &osc_clk, + .get_rate = clk_pll_get_rate, + .set_rate = _clk_pll_set_rate, + .set_parent = _clk_pll2_sw_set_parent, + .enable = _clk_pll_enable, + .disable = _clk_pll_disable, +}; + +/* PLL3 SW supplies to serial clocks like USB, SSI, etc. */ +static struct clk pll3_sw_clk = { + .parent = &osc_clk, + .set_rate = _clk_pll_set_rate, + .get_rate = clk_pll_get_rate, + .enable = _clk_pll_enable, + .disable = _clk_pll_disable, +}; + +/* PLL4 SW supplies to LVDS Display Bridge(LDB) */ +static struct clk mx53_pll4_sw_clk = { + .parent = &osc_clk, + .set_rate = _clk_pll_set_rate, + .enable = _clk_pll_enable, + .disable = _clk_pll_disable, +}; + +/* Low-power Audio Playback Mode clock */ +static struct clk lp_apm_clk = { + .parent = &osc_clk, + .set_parent = _clk_lp_apm_set_parent, +}; + +static struct clk periph_apm_clk = { + .parent = &pll1_sw_clk, + .set_parent = _clk_periph_apm_set_parent, +}; + +static struct clk cpu_clk = { + .parent = &pll1_sw_clk, + .get_rate = clk_cpu_get_rate, + .set_rate = clk_cpu_set_rate, +}; + +static struct clk ahb_clk = { + .parent = &main_bus_clk, + .get_rate = clk_ahb_get_rate, + .set_rate = _clk_ahb_set_rate, + .round_rate = _clk_ahb_round_rate, +}; + +static struct clk iim_clk = { + .parent = &ipg_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG15_OFFSET, +}; + +/* Main IP interface clock for access to registers */ +static struct clk ipg_clk = { + .parent = &ahb_clk, + .get_rate = clk_ipg_get_rate, +}; + +static struct clk ipg_perclk = { + .parent = &lp_apm_clk, + .get_rate = clk_ipg_per_get_rate, + .set_parent = _clk_ipg_per_set_parent, +}; + +static struct clk ahb_max_clk = { + .parent = &ahb_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG14_OFFSET, + .enable = _clk_max_enable, + .disable = _clk_max_disable, +}; + +static struct clk aips_tz1_clk = { + .parent = &ahb_clk, + .secondary = &ahb_max_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG12_OFFSET, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable_inwait, +}; + +static struct clk aips_tz2_clk = { + .parent = &ahb_clk, + .secondary = &ahb_max_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable_inwait, +}; + +static struct clk gpc_dvfs_clk = { + .enable_reg = MXC_CCM_CCGR5, + .enable_shift = MXC_CCM_CCGRx_CG12_OFFSET, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable, +}; + +static struct clk gpt_32k_clk = { + .id = 0, + .parent = &ckil_clk, +}; + +static struct clk dummy_clk = { + .id = 0, +}; + +static struct clk emi_slow_clk = { + .parent = &pll2_sw_clk, + .enable_reg = MXC_CCM_CCGR5, + .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable_inwait, + .get_rate = clk_emi_slow_get_rate, +}; + +static int clk_ipu_enable(struct clk *clk) +{ + u32 reg; + + _clk_ccgr_enable(clk); + + /* Enable handshake with IPU when certain clock rates are changed */ + reg = __raw_readl(MXC_CCM_CCDR); + reg &= ~MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, MXC_CCM_CCDR); + + /* Enable handshake with IPU when LPM is entered */ + reg = __raw_readl(MXC_CCM_CLPCR); + reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, MXC_CCM_CLPCR); + + return 0; +} + +static void clk_ipu_disable(struct clk *clk) +{ + u32 reg; + + _clk_ccgr_disable(clk); + + /* Disable handshake with IPU whe dividers are changed */ + reg = __raw_readl(MXC_CCM_CCDR); + reg |= MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, MXC_CCM_CCDR); + + /* Disable handshake with IPU when LPM is entered */ + reg = __raw_readl(MXC_CCM_CLPCR); + reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, MXC_CCM_CLPCR); +} + +static struct clk ahbmux1_clk = { + .parent = &ahb_clk, + .secondary = &ahb_max_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable_inwait, +}; + +static struct clk ipu_sec_clk = { + .parent = &emi_fast_clk, + .secondary = &ahbmux1_clk, +}; + +static struct clk ddr_hf_clk = { + .parent = &pll1_sw_clk, + .get_rate = _clk_ddr_hf_get_rate, +}; + +static struct clk ddr_clk = { + .parent = &ddr_hf_clk, +}; + +/* clock definitions for MIPI HSC unit which has been removed + * from documentation, but not from hardware + */ +static int _clk_hsc_enable(struct clk *clk) +{ + u32 reg; + + _clk_ccgr_enable(clk); + /* Handshake with IPU when certain clock rates are changed. */ + reg = __raw_readl(MXC_CCM_CCDR); + reg &= ~MXC_CCM_CCDR_HSC_HS_MASK; + __raw_writel(reg, MXC_CCM_CCDR); + + reg = __raw_readl(MXC_CCM_CLPCR); + reg &= ~MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS; + __raw_writel(reg, MXC_CCM_CLPCR); + + return 0; +} + +static void _clk_hsc_disable(struct clk *clk) +{ + u32 reg; + + _clk_ccgr_disable(clk); + /* No handshake with HSC as its not enabled. */ + reg = __raw_readl(MXC_CCM_CCDR); + reg |= MXC_CCM_CCDR_HSC_HS_MASK; + __raw_writel(reg, MXC_CCM_CCDR); + + reg = __raw_readl(MXC_CCM_CLPCR); + reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS; + __raw_writel(reg, MXC_CCM_CLPCR); +} + +static struct clk mipi_hsp_clk = { + .parent = &ipu_clk, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG6_OFFSET, + .enable = _clk_hsc_enable, + .disable = _clk_hsc_disable, + .secondary = &mipi_hsc1_clk, +}; + +#define DEFINE_CLOCK_CCGR(name, i, er, es, pfx, p, s) \ + static struct clk name = { \ + .id = i, \ + .enable_reg = er, \ + .enable_shift = es, \ + .get_rate = pfx##_get_rate, \ + .set_rate = pfx##_set_rate, \ + .round_rate = pfx##_round_rate, \ + .set_parent = pfx##_set_parent, \ + .enable = _clk_ccgr_enable, \ + .disable = _clk_ccgr_disable, \ + .parent = p, \ + .secondary = s, \ + } + +#define DEFINE_CLOCK_MAX(name, i, er, es, pfx, p, s) \ + static struct clk name = { \ + .id = i, \ + .enable_reg = er, \ + .enable_shift = es, \ + .get_rate = pfx##_get_rate, \ + .set_rate = pfx##_set_rate, \ + .set_parent = pfx##_set_parent, \ + .enable = _clk_max_enable, \ + .disable = _clk_max_disable, \ + .parent = p, \ + .secondary = s, \ + } + +#define CLK_GET_RATE(name, nr, bitsname) \ +static unsigned long clk_##name##_get_rate(struct clk *clk) \ +{ \ + u32 reg, pred, podf; \ + \ + reg = __raw_readl(MXC_CCM_CSCDR##nr); \ + pred = (reg & MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_MASK) \ + >> MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_OFFSET; \ + podf = (reg & MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_MASK) \ + >> MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_OFFSET; \ + \ + return DIV_ROUND_CLOSEST(clk_get_rate(clk->parent), \ + (pred + 1) * (podf + 1)); \ +} + +#define CLK_SET_PARENT(name, nr, bitsname) \ +static int clk_##name##_set_parent(struct clk *clk, struct clk *parent) \ +{ \ + u32 reg, mux; \ + \ + mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, \ + &pll3_sw_clk, &lp_apm_clk); \ + reg = __raw_readl(MXC_CCM_CSCMR##nr) & \ + ~MXC_CCM_CSCMR##nr##_##bitsname##_CLK_SEL_MASK; \ + reg |= mux << MXC_CCM_CSCMR##nr##_##bitsname##_CLK_SEL_OFFSET; \ + __raw_writel(reg, MXC_CCM_CSCMR##nr); \ + \ + return 0; \ +} + +#define CLK_SET_RATE(name, nr, bitsname) \ +static int clk_##name##_set_rate(struct clk *clk, unsigned long rate) \ +{ \ + u32 reg, div, parent_rate; \ + u32 pre = 0, post = 0; \ + \ + parent_rate = clk_get_rate(clk->parent); \ + div = parent_rate / rate; \ + \ + if ((parent_rate / div) != rate) \ + return -EINVAL; \ + \ + __calc_pre_post_dividers(div, &pre, &post, \ + (MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_MASK >> \ + MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_OFFSET) + 1, \ + (MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_MASK >> \ + MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_OFFSET) + 1);\ + \ + /* Set sdhc1 clock divider */ \ + reg = __raw_readl(MXC_CCM_CSCDR##nr) & \ + ~(MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_MASK \ + | MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_MASK); \ + reg |= (post - 1) << \ + MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_OFFSET; \ + reg |= (pre - 1) << \ + MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_OFFSET; \ + __raw_writel(reg, MXC_CCM_CSCDR##nr); \ + \ + return 0; \ +} + +/* UART */ +CLK_GET_RATE(uart, 1, UART) +CLK_SET_PARENT(uart, 1, UART) + +static struct clk uart_root_clk = { + .parent = &pll2_sw_clk, + .get_rate = clk_uart_get_rate, + .set_parent = clk_uart_set_parent, +}; + +/* USBOH3 */ +CLK_GET_RATE(usboh3, 1, USBOH3) +CLK_SET_PARENT(usboh3, 1, USBOH3) + +static struct clk usboh3_clk = { + .parent = &pll2_sw_clk, + .get_rate = clk_usboh3_get_rate, + .set_parent = clk_usboh3_set_parent, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable, + .enable_reg = MXC_CCM_CCGR2, + .enable_shift = MXC_CCM_CCGRx_CG14_OFFSET, +}; + +static struct clk usb_ahb_clk = { + .parent = &ipg_clk, + .enable = _clk_ccgr_enable, + .disable = _clk_ccgr_disable, + .enable_reg = MXC_CCM_CCGR2, + .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, +}; + +static int clk_usb_phy1_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; + + if (parent == &pll3_sw_clk) + reg |= 1 << MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET; + + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +static struct clk usb_phy1_clk = { + .parent = &pll3_sw_clk, + .set_parent = clk_usb_phy1_set_parent, + .enable = _clk_ccgr_enable, + .enable_reg = MXC_CCM_CCGR2, + .enable_shift = MXC_CCM_CCGRx_CG0_OFFSET, + .disable = _clk_ccgr_disable, +}; + +/* eCSPI */ +CLK_GET_RATE(ecspi, 2, CSPI) +CLK_SET_PARENT(ecspi, 1, CSPI) + +static struct clk ecspi_main_clk = { + .parent = &pll3_sw_clk, + .get_rate = clk_ecspi_get_rate, + .set_parent = clk_ecspi_set_parent, +}; + +/* eSDHC */ +CLK_GET_RATE(esdhc1, 1, ESDHC1_MSHC1) +CLK_SET_PARENT(esdhc1, 1, ESDHC1_MSHC1) +CLK_SET_RATE(esdhc1, 1, ESDHC1_MSHC1) + +/* mx51 specific */ +CLK_GET_RATE(esdhc2, 1, ESDHC2_MSHC2) +CLK_SET_PARENT(esdhc2, 1, ESDHC2_MSHC2) +CLK_SET_RATE(esdhc2, 1, ESDHC2_MSHC2) + +static int clk_esdhc3_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCMR1); + if (parent == &esdhc1_clk) + reg &= ~MXC_CCM_CSCMR1_ESDHC3_CLK_SEL; + else if (parent == &esdhc2_clk) + reg |= MXC_CCM_CSCMR1_ESDHC3_CLK_SEL; + else + return -EINVAL; + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +static int clk_esdhc4_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCMR1); + if (parent == &esdhc1_clk) + reg &= ~MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; + else if (parent == &esdhc2_clk) + reg |= MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; + else + return -EINVAL; + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +/* mx53 specific */ +static int clk_esdhc2_mx53_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCMR1); + if (parent == &esdhc1_clk) + reg &= ~MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL; + else if (parent == &esdhc3_mx53_clk) + reg |= MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL; + else + return -EINVAL; + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +CLK_GET_RATE(esdhc3_mx53, 1, ESDHC3_MX53) +CLK_SET_PARENT(esdhc3_mx53, 1, ESDHC3_MX53) +CLK_SET_RATE(esdhc3_mx53, 1, ESDHC3_MX53) + +static int clk_esdhc4_mx53_set_parent(struct clk *clk, struct clk *parent) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCMR1); + if (parent == &esdhc1_clk) + reg &= ~MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; + else if (parent == &esdhc3_mx53_clk) + reg |= MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; + else + return -EINVAL; + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +#define DEFINE_CLOCK_FULL(name, i, er, es, gr, sr, e, d, p, s) \ + static struct clk name = { \ + .id = i, \ + .enable_reg = er, \ + .enable_shift = es, \ + .get_rate = gr, \ + .set_rate = sr, \ + .enable = e, \ + .disable = d, \ + .parent = p, \ + .secondary = s, \ + } + +#define DEFINE_CLOCK(name, i, er, es, gr, sr, p, s) \ + DEFINE_CLOCK_FULL(name, i, er, es, gr, sr, _clk_ccgr_enable, _clk_ccgr_disable, p, s) + +/* Shared peripheral bus arbiter */ +DEFINE_CLOCK(spba_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG0_OFFSET, + NULL, NULL, &ipg_clk, NULL); + +/* UART */ +DEFINE_CLOCK(uart1_ipg_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG3_OFFSET, + NULL, NULL, &ipg_clk, &aips_tz1_clk); +DEFINE_CLOCK(uart2_ipg_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG5_OFFSET, + NULL, NULL, &ipg_clk, &aips_tz1_clk); +DEFINE_CLOCK(uart3_ipg_clk, 2, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG7_OFFSET, + NULL, NULL, &ipg_clk, &spba_clk); +DEFINE_CLOCK(uart4_ipg_clk, 3, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG4_OFFSET, + NULL, NULL, &ipg_clk, &spba_clk); +DEFINE_CLOCK(uart5_ipg_clk, 4, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG6_OFFSET, + NULL, NULL, &ipg_clk, &spba_clk); +DEFINE_CLOCK(uart1_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG4_OFFSET, + NULL, NULL, &uart_root_clk, &uart1_ipg_clk); +DEFINE_CLOCK(uart2_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG6_OFFSET, + NULL, NULL, &uart_root_clk, &uart2_ipg_clk); +DEFINE_CLOCK(uart3_clk, 2, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG8_OFFSET, + NULL, NULL, &uart_root_clk, &uart3_ipg_clk); +DEFINE_CLOCK(uart4_clk, 3, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG5_OFFSET, + NULL, NULL, &uart_root_clk, &uart4_ipg_clk); +DEFINE_CLOCK(uart5_clk, 4, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG7_OFFSET, + NULL, NULL, &uart_root_clk, &uart5_ipg_clk); + +/* GPT */ +DEFINE_CLOCK(gpt_ipg_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG10_OFFSET, + NULL, NULL, &ipg_clk, NULL); +DEFINE_CLOCK(gpt_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG9_OFFSET, + NULL, NULL, &ipg_clk, &gpt_ipg_clk); + +DEFINE_CLOCK(pwm1_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG6_OFFSET, + NULL, NULL, &ipg_clk, NULL); +DEFINE_CLOCK(pwm2_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG8_OFFSET, + NULL, NULL, &ipg_clk, NULL); + +/* I2C */ +DEFINE_CLOCK(i2c1_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG9_OFFSET, + NULL, NULL, &ipg_perclk, NULL); +DEFINE_CLOCK(i2c2_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG10_OFFSET, + NULL, NULL, &ipg_perclk, NULL); +DEFINE_CLOCK(hsi2c_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG11_OFFSET, + NULL, NULL, &ipg_clk, NULL); +DEFINE_CLOCK(i2c3_mx53_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG11_OFFSET, + NULL, NULL, &ipg_perclk, NULL); + +/* FEC */ +DEFINE_CLOCK(fec_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG12_OFFSET, + NULL, NULL, &ipg_clk, NULL); + +/* NFC */ +DEFINE_CLOCK_CCGR(nfc_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG10_OFFSET, + clk_nfc, &emi_slow_clk, NULL); + +/* SSI */ +DEFINE_CLOCK(ssi1_ipg_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG8_OFFSET, + NULL, NULL, &ipg_clk, NULL); +DEFINE_CLOCK(ssi1_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG9_OFFSET, + NULL, NULL, &pll3_sw_clk, &ssi1_ipg_clk); +DEFINE_CLOCK(ssi2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG10_OFFSET, + NULL, NULL, &ipg_clk, NULL); +DEFINE_CLOCK(ssi2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG11_OFFSET, + NULL, NULL, &pll3_sw_clk, &ssi2_ipg_clk); +DEFINE_CLOCK(ssi3_ipg_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG12_OFFSET, + NULL, NULL, &ipg_clk, NULL); +DEFINE_CLOCK(ssi3_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG13_OFFSET, + NULL, NULL, &pll3_sw_clk, &ssi3_ipg_clk); + +/* eCSPI */ +DEFINE_CLOCK_FULL(ecspi1_ipg_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG9_OFFSET, + NULL, NULL, _clk_ccgr_enable_inrun, _clk_ccgr_disable, + &ipg_clk, &spba_clk); +DEFINE_CLOCK(ecspi1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG10_OFFSET, + NULL, NULL, &ecspi_main_clk, &ecspi1_ipg_clk); +DEFINE_CLOCK_FULL(ecspi2_ipg_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG11_OFFSET, + NULL, NULL, _clk_ccgr_enable_inrun, _clk_ccgr_disable, + &ipg_clk, &aips_tz2_clk); +DEFINE_CLOCK(ecspi2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG12_OFFSET, + NULL, NULL, &ecspi_main_clk, &ecspi2_ipg_clk); + +/* CSPI */ +DEFINE_CLOCK(cspi_ipg_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG9_OFFSET, + NULL, NULL, &ipg_clk, &aips_tz2_clk); +DEFINE_CLOCK(cspi_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG13_OFFSET, + NULL, NULL, &ipg_clk, &cspi_ipg_clk); + +/* SDMA */ +DEFINE_CLOCK(sdma_clk, 1, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG15_OFFSET, + NULL, NULL, &ahb_clk, NULL); + +/* eSDHC */ +DEFINE_CLOCK_FULL(esdhc1_ipg_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG0_OFFSET, + NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); +DEFINE_CLOCK_MAX(esdhc1_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG1_OFFSET, + clk_esdhc1, &pll2_sw_clk, &esdhc1_ipg_clk); +DEFINE_CLOCK_FULL(esdhc2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG2_OFFSET, + NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); +DEFINE_CLOCK_FULL(esdhc3_ipg_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG4_OFFSET, + NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); +DEFINE_CLOCK_FULL(esdhc4_ipg_clk, 3, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG6_OFFSET, + NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); + +/* mx51 specific */ +DEFINE_CLOCK_MAX(esdhc2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG3_OFFSET, + clk_esdhc2, &pll2_sw_clk, &esdhc2_ipg_clk); + +static struct clk esdhc3_clk = { + .id = 2, + .parent = &esdhc1_clk, + .set_parent = clk_esdhc3_set_parent, + .enable_reg = MXC_CCM_CCGR3, + .enable_shift = MXC_CCM_CCGRx_CG5_OFFSET, + .enable = _clk_max_enable, + .disable = _clk_max_disable, + .secondary = &esdhc3_ipg_clk, +}; +static struct clk esdhc4_clk = { + .id = 3, + .parent = &esdhc1_clk, + .set_parent = clk_esdhc4_set_parent, + .enable_reg = MXC_CCM_CCGR3, + .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET, + .enable = _clk_max_enable, + .disable = _clk_max_disable, + .secondary = &esdhc4_ipg_clk, +}; + +/* mx53 specific */ +static struct clk esdhc2_mx53_clk = { + .id = 2, + .parent = &esdhc1_clk, + .set_parent = clk_esdhc2_mx53_set_parent, + .enable_reg = MXC_CCM_CCGR3, + .enable_shift = MXC_CCM_CCGRx_CG3_OFFSET, + .enable = _clk_max_enable, + .disable = _clk_max_disable, + .secondary = &esdhc3_ipg_clk, +}; + +DEFINE_CLOCK_MAX(esdhc3_mx53_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG5_OFFSET, + clk_esdhc3_mx53, &pll2_sw_clk, &esdhc2_ipg_clk); + +static struct clk esdhc4_mx53_clk = { + .id = 3, + .parent = &esdhc1_clk, + .set_parent = clk_esdhc4_mx53_set_parent, + .enable_reg = MXC_CCM_CCGR3, + .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET, + .enable = _clk_max_enable, + .disable = _clk_max_disable, + .secondary = &esdhc4_ipg_clk, +}; + +static struct clk sata_clk = { + .parent = &ipg_clk, + .enable = _clk_max_enable, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG1_OFFSET, + .disable = _clk_max_disable, +}; + +static struct clk ahci_phy_clk = { + .parent = &usb_phy1_clk, +}; + +static struct clk ahci_dma_clk = { + .parent = &ahb_clk, +}; + +DEFINE_CLOCK(mipi_esc_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG5_OFFSET, NULL, NULL, NULL, &pll2_sw_clk); +DEFINE_CLOCK(mipi_hsc2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG4_OFFSET, NULL, NULL, &mipi_esc_clk, &pll2_sw_clk); +DEFINE_CLOCK(mipi_hsc1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG3_OFFSET, NULL, NULL, &mipi_hsc2_clk, &pll2_sw_clk); + +/* IPU */ +DEFINE_CLOCK_FULL(ipu_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG5_OFFSET, + NULL, NULL, clk_ipu_enable, clk_ipu_disable, &ahb_clk, &ipu_sec_clk); + +DEFINE_CLOCK_FULL(emi_fast_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG7_OFFSET, + NULL, NULL, _clk_ccgr_enable, _clk_ccgr_disable_inwait, + &ddr_clk, NULL); + +DEFINE_CLOCK(ipu_di0_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG5_OFFSET, + NULL, NULL, &pll3_sw_clk, NULL); +DEFINE_CLOCK(ipu_di1_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG6_OFFSET, + NULL, NULL, &pll3_sw_clk, NULL); + +/* PATA */ +DEFINE_CLOCK(pata_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG0_OFFSET, + NULL, NULL, &ipg_clk, &spba_clk); + +#define _REGISTER_CLOCK(d, n, c) \ + { \ + .dev_id = d, \ + .con_id = n, \ + .clk = &c, \ + }, + +static struct clk_lookup mx51_lookups[] = { + /* i.mx51 has the i.mx21 type uart */ + _REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk) + _REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk) + _REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk) + _REGISTER_CLOCK(NULL, "gpt", gpt_clk) + /* i.mx51 has the i.mx27 type fec */ + _REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk) + _REGISTER_CLOCK("mxc_pwm.0", "pwm", pwm1_clk) + _REGISTER_CLOCK("mxc_pwm.1", "pwm", pwm2_clk) + _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) + _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) + _REGISTER_CLOCK("imx-i2c.2", NULL, hsi2c_clk) + _REGISTER_CLOCK("mxc-ehci.0", "usb", usboh3_clk) + _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_ahb_clk) + _REGISTER_CLOCK("mxc-ehci.0", "usb_phy1", usb_phy1_clk) + _REGISTER_CLOCK("mxc-ehci.1", "usb", usboh3_clk) + _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_ahb_clk) + _REGISTER_CLOCK("mxc-ehci.2", "usb", usboh3_clk) + _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_ahb_clk) + _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) + _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) + _REGISTER_CLOCK("imx-keypad", NULL, dummy_clk) + _REGISTER_CLOCK("mxc_nand", NULL, nfc_clk) + _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) + _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) + _REGISTER_CLOCK("imx-ssi.2", NULL, ssi3_clk) + /* i.mx51 has the i.mx35 type sdma */ + _REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk) + _REGISTER_CLOCK(NULL, "ckih", ckih_clk) + _REGISTER_CLOCK(NULL, "ckih2", ckih2_clk) + _REGISTER_CLOCK(NULL, "gpt_32k", gpt_32k_clk) + _REGISTER_CLOCK("imx51-ecspi.0", NULL, ecspi1_clk) + _REGISTER_CLOCK("imx51-ecspi.1", NULL, ecspi2_clk) + /* i.mx51 has the i.mx35 type cspi */ + _REGISTER_CLOCK("imx35-cspi.0", NULL, cspi_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx51.0", NULL, esdhc1_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx51.1", NULL, esdhc2_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx51.2", NULL, esdhc3_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx51.3", NULL, esdhc4_clk) + _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) + _REGISTER_CLOCK(NULL, "iim_clk", iim_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk) + _REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk) + _REGISTER_CLOCK(NULL, "mipi_hsp", mipi_hsp_clk) + _REGISTER_CLOCK("imx-ipuv3", NULL, ipu_clk) + _REGISTER_CLOCK("imx-ipuv3", "di0", ipu_di0_clk) + _REGISTER_CLOCK("imx-ipuv3", "di1", ipu_di1_clk) + _REGISTER_CLOCK(NULL, "gpc_dvfs", gpc_dvfs_clk) + _REGISTER_CLOCK("pata_imx", NULL, pata_clk) +}; + +static struct clk_lookup mx53_lookups[] = { + /* i.mx53 has the i.mx21 type uart */ + _REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk) + _REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk) + _REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk) + _REGISTER_CLOCK("imx21-uart.3", NULL, uart4_clk) + _REGISTER_CLOCK("imx21-uart.4", NULL, uart5_clk) + _REGISTER_CLOCK(NULL, "gpt", gpt_clk) + /* i.mx53 has the i.mx25 type fec */ + _REGISTER_CLOCK("imx25-fec.0", NULL, fec_clk) + _REGISTER_CLOCK(NULL, "iim_clk", iim_clk) + _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) + _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) + _REGISTER_CLOCK("imx-i2c.2", NULL, i2c3_mx53_clk) + /* i.mx53 has the i.mx51 type ecspi */ + _REGISTER_CLOCK("imx51-ecspi.0", NULL, ecspi1_clk) + _REGISTER_CLOCK("imx51-ecspi.1", NULL, ecspi2_clk) + /* i.mx53 has the i.mx25 type cspi */ + _REGISTER_CLOCK("imx35-cspi.0", NULL, cspi_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx53.0", NULL, esdhc1_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx53.1", NULL, esdhc2_mx53_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx53.2", NULL, esdhc3_mx53_clk) + _REGISTER_CLOCK("sdhci-esdhc-imx53.3", NULL, esdhc4_mx53_clk) + _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk) + _REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk) + /* i.mx53 has the i.mx35 type sdma */ + _REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk) + _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) + _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) + _REGISTER_CLOCK("imx-ssi.2", NULL, ssi3_clk) + _REGISTER_CLOCK("imx-keypad", NULL, dummy_clk) + _REGISTER_CLOCK("pata_imx", NULL, pata_clk) + _REGISTER_CLOCK("imx53-ahci.0", "ahci", sata_clk) + _REGISTER_CLOCK("imx53-ahci.0", "ahci_phy", ahci_phy_clk) + _REGISTER_CLOCK("imx53-ahci.0", "ahci_dma", ahci_dma_clk) +}; + +static void clk_tree_init(void) +{ + u32 reg; + + ipg_perclk.set_parent(&ipg_perclk, &lp_apm_clk); + + /* + * Initialise the IPG PER CLK dividers to 3. IPG_PER_CLK should be at + * 8MHz, its derived from lp_apm. + * + * FIXME: Verify if true for all boards + */ + reg = __raw_readl(MXC_CCM_CBCDR); + reg &= ~MXC_CCM_CBCDR_PERCLK_PRED1_MASK; + reg &= ~MXC_CCM_CBCDR_PERCLK_PRED2_MASK; + reg &= ~MXC_CCM_CBCDR_PERCLK_PODF_MASK; + reg |= (2 << MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET); + __raw_writel(reg, MXC_CCM_CBCDR); +} + +int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, + unsigned long ckih1, unsigned long ckih2) +{ + int i; + + external_low_reference = ckil; + external_high_reference = ckih1; + ckih2_reference = ckih2; + oscillator_reference = osc; + + for (i = 0; i < ARRAY_SIZE(mx51_lookups); i++) + clkdev_add(&mx51_lookups[i]); + + clk_tree_init(); + + clk_enable(&cpu_clk); + clk_enable(&main_bus_clk); + + clk_enable(&iim_clk); + imx_print_silicon_rev("i.MX51", mx51_revision()); + clk_disable(&iim_clk); + + /* move usb_phy_clk to 24MHz */ + clk_set_parent(&usb_phy1_clk, &osc_clk); + + /* set the usboh3_clk parent to pll2_sw_clk */ + clk_set_parent(&usboh3_clk, &pll2_sw_clk); + + /* Set SDHC parents to be PLL2 */ + clk_set_parent(&esdhc1_clk, &pll2_sw_clk); + clk_set_parent(&esdhc2_clk, &pll2_sw_clk); + + /* set SDHC root clock as 166.25MHZ*/ + clk_set_rate(&esdhc1_clk, 166250000); + clk_set_rate(&esdhc2_clk, 166250000); + + /* System timer */ + mxc_timer_init(&gpt_clk, MX51_IO_ADDRESS(MX51_GPT1_BASE_ADDR), + MX51_INT_GPT); + return 0; +} + +int __init mx53_clocks_init(unsigned long ckil, unsigned long osc, + unsigned long ckih1, unsigned long ckih2) +{ + int i; + + external_low_reference = ckil; + external_high_reference = ckih1; + ckih2_reference = ckih2; + oscillator_reference = osc; + + for (i = 0; i < ARRAY_SIZE(mx53_lookups); i++) + clkdev_add(&mx53_lookups[i]); + + clk_tree_init(); + + clk_set_parent(&uart_root_clk, &pll3_sw_clk); + clk_enable(&cpu_clk); + clk_enable(&main_bus_clk); + + clk_enable(&iim_clk); + imx_print_silicon_rev("i.MX53", mx53_revision()); + clk_disable(&iim_clk); + + /* Set SDHC parents to be PLL2 */ + clk_set_parent(&esdhc1_clk, &pll2_sw_clk); + clk_set_parent(&esdhc3_mx53_clk, &pll2_sw_clk); + + /* set SDHC root clock as 200MHZ*/ + clk_set_rate(&esdhc1_clk, 200000000); + clk_set_rate(&esdhc3_mx53_clk, 200000000); + + /* System timer */ + mxc_timer_init(&gpt_clk, MX53_IO_ADDRESS(MX53_GPT1_BASE_ADDR), + MX53_INT_GPT); + return 0; +} + +static void __init clk_get_freq_dt(unsigned long *ckil, unsigned long *osc, + unsigned long *ckih1, unsigned long *ckih2) +{ + struct device_node *np; + + /* retrieve the freqency of fixed clocks from device tree */ + for_each_compatible_node(np, NULL, "fixed-clock") { + u32 rate; + if (of_property_read_u32(np, "clock-frequency", &rate)) + continue; + + if (of_device_is_compatible(np, "fsl,imx-ckil")) + *ckil = rate; + else if (of_device_is_compatible(np, "fsl,imx-osc")) + *osc = rate; + else if (of_device_is_compatible(np, "fsl,imx-ckih1")) + *ckih1 = rate; + else if (of_device_is_compatible(np, "fsl,imx-ckih2")) + *ckih2 = rate; + } +} + +int __init mx51_clocks_init_dt(void) +{ + unsigned long ckil, osc, ckih1, ckih2; + + clk_get_freq_dt(&ckil, &osc, &ckih1, &ckih2); + return mx51_clocks_init(ckil, osc, ckih1, ckih2); +} + +int __init mx53_clocks_init_dt(void) +{ + unsigned long ckil, osc, ckih1, ckih2; + + clk_get_freq_dt(&ckil, &osc, &ckih1, &ckih2); + return mx53_clocks_init(ckil, osc, ckih1, ckih2); +} diff --git a/arch/arm/mach-imx/cpu-imx5.c b/arch/arm/mach-imx/cpu-imx5.c new file mode 100644 index 0000000..5c53282 --- /dev/null +++ b/arch/arm/mach-imx/cpu-imx5.c @@ -0,0 +1,186 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * This file contains the CPU initialization code. + */ + +#include +#include +#include +#include +#include +#include + +static int mx5_cpu_rev = -1; + +#define IIM_SREV 0x24 +#define MX50_HW_ADADIG_DIGPROG 0xB0 + +static int get_mx51_srev(void) +{ + void __iomem *iim_base = MX51_IO_ADDRESS(MX51_IIM_BASE_ADDR); + u32 rev = readl(iim_base + IIM_SREV) & 0xff; + + switch (rev) { + case 0x0: + return IMX_CHIP_REVISION_2_0; + case 0x10: + return IMX_CHIP_REVISION_3_0; + default: + return IMX_CHIP_REVISION_UNKNOWN; + } +} + +/* + * Returns: + * the silicon revision of the cpu + * -EINVAL - not a mx51 + */ +int mx51_revision(void) +{ + if (!cpu_is_mx51()) + return -EINVAL; + + if (mx5_cpu_rev == -1) + mx5_cpu_rev = get_mx51_srev(); + + return mx5_cpu_rev; +} +EXPORT_SYMBOL(mx51_revision); + +#ifdef CONFIG_NEON + +/* + * All versions of the silicon before Rev. 3 have broken NEON implementations. + * Dependent on link order - so the assumption is that vfp_init is called + * before us. + */ +static int __init mx51_neon_fixup(void) +{ + if (!cpu_is_mx51()) + return 0; + + if (mx51_revision() < IMX_CHIP_REVISION_3_0 && (elf_hwcap & HWCAP_NEON)) { + elf_hwcap &= ~HWCAP_NEON; + pr_info("Turning off NEON support, detected broken NEON implementation\n"); + } + return 0; +} + +late_initcall(mx51_neon_fixup); +#endif + +static int get_mx53_srev(void) +{ + void __iomem *iim_base = MX51_IO_ADDRESS(MX53_IIM_BASE_ADDR); + u32 rev = readl(iim_base + IIM_SREV) & 0xff; + + switch (rev) { + case 0x0: + return IMX_CHIP_REVISION_1_0; + case 0x2: + return IMX_CHIP_REVISION_2_0; + case 0x3: + return IMX_CHIP_REVISION_2_1; + default: + return IMX_CHIP_REVISION_UNKNOWN; + } +} + +/* + * Returns: + * the silicon revision of the cpu + * -EINVAL - not a mx53 + */ +int mx53_revision(void) +{ + if (!cpu_is_mx53()) + return -EINVAL; + + if (mx5_cpu_rev == -1) + mx5_cpu_rev = get_mx53_srev(); + + return mx5_cpu_rev; +} +EXPORT_SYMBOL(mx53_revision); + +static int get_mx50_srev(void) +{ + void __iomem *anatop = ioremap(MX50_ANATOP_BASE_ADDR, SZ_8K); + u32 rev; + + if (!anatop) { + mx5_cpu_rev = -EINVAL; + return 0; + } + + rev = readl(anatop + MX50_HW_ADADIG_DIGPROG); + rev &= 0xff; + + iounmap(anatop); + if (rev == 0x0) + return IMX_CHIP_REVISION_1_0; + else if (rev == 0x1) + return IMX_CHIP_REVISION_1_1; + return 0; +} + +/* + * Returns: + * the silicon revision of the cpu + * -EINVAL - not a mx50 + */ +int mx50_revision(void) +{ + if (!cpu_is_mx50()) + return -EINVAL; + + if (mx5_cpu_rev == -1) + mx5_cpu_rev = get_mx50_srev(); + + return mx5_cpu_rev; +} +EXPORT_SYMBOL(mx50_revision); + +static int __init post_cpu_init(void) +{ + unsigned int reg; + void __iomem *base; + + if (cpu_is_mx51() || cpu_is_mx53()) { + if (cpu_is_mx51()) + base = MX51_IO_ADDRESS(MX51_AIPS1_BASE_ADDR); + else + base = MX53_IO_ADDRESS(MX53_AIPS1_BASE_ADDR); + + __raw_writel(0x0, base + 0x40); + __raw_writel(0x0, base + 0x44); + __raw_writel(0x0, base + 0x48); + __raw_writel(0x0, base + 0x4C); + reg = __raw_readl(base + 0x50) & 0x00FFFFFF; + __raw_writel(reg, base + 0x50); + + if (cpu_is_mx51()) + base = MX51_IO_ADDRESS(MX51_AIPS2_BASE_ADDR); + else + base = MX53_IO_ADDRESS(MX53_AIPS2_BASE_ADDR); + + __raw_writel(0x0, base + 0x40); + __raw_writel(0x0, base + 0x44); + __raw_writel(0x0, base + 0x48); + __raw_writel(0x0, base + 0x4C); + reg = __raw_readl(base + 0x50) & 0x00FFFFFF; + __raw_writel(reg, base + 0x50); + } + + return 0; +} + +postcore_initcall(post_cpu_init); diff --git a/arch/arm/mach-imx/cpu_op-mx51.c b/arch/arm/mach-imx/cpu_op-mx51.c new file mode 100644 index 0000000..9d34c3d --- /dev/null +++ b/arch/arm/mach-imx/cpu_op-mx51.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include + +static struct cpu_op mx51_cpu_op[] = { + { + .cpu_rate = 160000000,}, + { + .cpu_rate = 800000000,}, +}; + +struct cpu_op *mx51_get_cpu_op(int *op) +{ + *op = ARRAY_SIZE(mx51_cpu_op); + return mx51_cpu_op; +} diff --git a/arch/arm/mach-imx/cpu_op-mx51.h b/arch/arm/mach-imx/cpu_op-mx51.h new file mode 100644 index 0000000..97477fe --- /dev/null +++ b/arch/arm/mach-imx/cpu_op-mx51.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +extern struct cpu_op *mx51_get_cpu_op(int *op); diff --git a/arch/arm/mach-imx/crm-regs-imx5.h b/arch/arm/mach-imx/crm-regs-imx5.h new file mode 100644 index 0000000..5e11ba7 --- /dev/null +++ b/arch/arm/mach-imx/crm-regs-imx5.h @@ -0,0 +1,600 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef __ARCH_ARM_MACH_MX51_CRM_REGS_H__ +#define __ARCH_ARM_MACH_MX51_CRM_REGS_H__ + +#define MX51_CCM_BASE MX51_IO_ADDRESS(MX51_CCM_BASE_ADDR) +#define MX51_DPLL1_BASE MX51_IO_ADDRESS(MX51_PLL1_BASE_ADDR) +#define MX51_DPLL2_BASE MX51_IO_ADDRESS(MX51_PLL2_BASE_ADDR) +#define MX51_DPLL3_BASE MX51_IO_ADDRESS(MX51_PLL3_BASE_ADDR) +#define MX51_CORTEXA8_BASE MX51_IO_ADDRESS(MX51_ARM_BASE_ADDR) +#define MX51_GPC_BASE MX51_IO_ADDRESS(MX51_GPC_BASE_ADDR) + +/*MX53*/ +#define MX53_CCM_BASE MX53_IO_ADDRESS(MX53_CCM_BASE_ADDR) +#define MX53_DPLL1_BASE MX53_IO_ADDRESS(MX53_PLL1_BASE_ADDR) +#define MX53_DPLL2_BASE MX53_IO_ADDRESS(MX53_PLL2_BASE_ADDR) +#define MX53_DPLL3_BASE MX53_IO_ADDRESS(MX53_PLL3_BASE_ADDR) +#define MX53_DPLL4_BASE MX53_IO_ADDRESS(MX53_PLL3_BASE_ADDR) + +/* PLL Register Offsets */ +#define MXC_PLL_DP_CTL 0x00 +#define MXC_PLL_DP_CONFIG 0x04 +#define MXC_PLL_DP_OP 0x08 +#define MXC_PLL_DP_MFD 0x0C +#define MXC_PLL_DP_MFN 0x10 +#define MXC_PLL_DP_MFNMINUS 0x14 +#define MXC_PLL_DP_MFNPLUS 0x18 +#define MXC_PLL_DP_HFS_OP 0x1C +#define MXC_PLL_DP_HFS_MFD 0x20 +#define MXC_PLL_DP_HFS_MFN 0x24 +#define MXC_PLL_DP_MFN_TOGC 0x28 +#define MXC_PLL_DP_DESTAT 0x2c + +/* PLL Register Bit definitions */ +#define MXC_PLL_DP_CTL_MUL_CTRL 0x2000 +#define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000 +#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET 12 +#define MXC_PLL_DP_CTL_ADE 0x800 +#define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400 +#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK (3 << 8) +#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET 8 +#define MXC_PLL_DP_CTL_HFSM 0x80 +#define MXC_PLL_DP_CTL_PRE 0x40 +#define MXC_PLL_DP_CTL_UPEN 0x20 +#define MXC_PLL_DP_CTL_RST 0x10 +#define MXC_PLL_DP_CTL_RCP 0x8 +#define MXC_PLL_DP_CTL_PLM 0x4 +#define MXC_PLL_DP_CTL_BRM0 0x2 +#define MXC_PLL_DP_CTL_LRF 0x1 + +#define MXC_PLL_DP_CONFIG_BIST 0x8 +#define MXC_PLL_DP_CONFIG_SJC_CE 0x4 +#define MXC_PLL_DP_CONFIG_AREN 0x2 +#define MXC_PLL_DP_CONFIG_LDREQ 0x1 + +#define MXC_PLL_DP_OP_MFI_OFFSET 4 +#define MXC_PLL_DP_OP_MFI_MASK (0xF << 4) +#define MXC_PLL_DP_OP_PDF_OFFSET 0 +#define MXC_PLL_DP_OP_PDF_MASK 0xF + +#define MXC_PLL_DP_MFD_OFFSET 0 +#define MXC_PLL_DP_MFD_MASK 0x07FFFFFF + +#define MXC_PLL_DP_MFN_OFFSET 0x0 +#define MXC_PLL_DP_MFN_MASK 0x07FFFFFF + +#define MXC_PLL_DP_MFN_TOGC_TOG_DIS (1 << 17) +#define MXC_PLL_DP_MFN_TOGC_TOG_EN (1 << 16) +#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET 0x0 +#define MXC_PLL_DP_MFN_TOGC_CNT_MASK 0xFFFF + +#define MXC_PLL_DP_DESTAT_TOG_SEL (1 << 31) +#define MXC_PLL_DP_DESTAT_MFN 0x07FFFFFF + +/* Register addresses of CCM*/ +#define MXC_CCM_CCR (MX51_CCM_BASE + 0x00) +#define MXC_CCM_CCDR (MX51_CCM_BASE + 0x04) +#define MXC_CCM_CSR (MX51_CCM_BASE + 0x08) +#define MXC_CCM_CCSR (MX51_CCM_BASE + 0x0C) +#define MXC_CCM_CACRR (MX51_CCM_BASE + 0x10) +#define MXC_CCM_CBCDR (MX51_CCM_BASE + 0x14) +#define MXC_CCM_CBCMR (MX51_CCM_BASE + 0x18) +#define MXC_CCM_CSCMR1 (MX51_CCM_BASE + 0x1C) +#define MXC_CCM_CSCMR2 (MX51_CCM_BASE + 0x20) +#define MXC_CCM_CSCDR1 (MX51_CCM_BASE + 0x24) +#define MXC_CCM_CS1CDR (MX51_CCM_BASE + 0x28) +#define MXC_CCM_CS2CDR (MX51_CCM_BASE + 0x2C) +#define MXC_CCM_CDCDR (MX51_CCM_BASE + 0x30) +#define MXC_CCM_CHSCDR (MX51_CCM_BASE + 0x34) +#define MXC_CCM_CSCDR2 (MX51_CCM_BASE + 0x38) +#define MXC_CCM_CSCDR3 (MX51_CCM_BASE + 0x3C) +#define MXC_CCM_CSCDR4 (MX51_CCM_BASE + 0x40) +#define MXC_CCM_CWDR (MX51_CCM_BASE + 0x44) +#define MXC_CCM_CDHIPR (MX51_CCM_BASE + 0x48) +#define MXC_CCM_CDCR (MX51_CCM_BASE + 0x4C) +#define MXC_CCM_CTOR (MX51_CCM_BASE + 0x50) +#define MXC_CCM_CLPCR (MX51_CCM_BASE + 0x54) +#define MXC_CCM_CISR (MX51_CCM_BASE + 0x58) +#define MXC_CCM_CIMR (MX51_CCM_BASE + 0x5C) +#define MXC_CCM_CCOSR (MX51_CCM_BASE + 0x60) +#define MXC_CCM_CGPR (MX51_CCM_BASE + 0x64) +#define MXC_CCM_CCGR0 (MX51_CCM_BASE + 0x68) +#define MXC_CCM_CCGR1 (MX51_CCM_BASE + 0x6C) +#define MXC_CCM_CCGR2 (MX51_CCM_BASE + 0x70) +#define MXC_CCM_CCGR3 (MX51_CCM_BASE + 0x74) +#define MXC_CCM_CCGR4 (MX51_CCM_BASE + 0x78) +#define MXC_CCM_CCGR5 (MX51_CCM_BASE + 0x7C) +#define MXC_CCM_CCGR6 (MX51_CCM_BASE + 0x80) +#define MXC_CCM_CCGR7 (MX51_CCM_BASE + 0x84) + +#define MXC_CCM_CMEOR (MX51_CCM_BASE + 0x84) + +/* Define the bits in register CCR */ +#define MXC_CCM_CCR_COSC_EN (1 << 12) +#define MXC_CCM_CCR_FPM_MULT_MASK (1 << 11) +#define MXC_CCM_CCR_CAMP2_EN (1 << 10) +#define MXC_CCM_CCR_CAMP1_EN (1 << 9) +#define MXC_CCM_CCR_FPM_EN (1 << 8) +#define MXC_CCM_CCR_OSCNT_OFFSET (0) +#define MXC_CCM_CCR_OSCNT_MASK (0xFF) + +/* Define the bits in register CCDR */ +#define MXC_CCM_CCDR_HSC_HS_MASK (0x1 << 18) +#define MXC_CCM_CCDR_IPU_HS_MASK (0x1 << 17) +#define MXC_CCM_CCDR_EMI_HS_MASK (0x1 << 16) + +/* Define the bits in register CSR */ +#define MXC_CCM_CSR_COSR_READY (1 << 5) +#define MXC_CCM_CSR_LVS_VALUE (1 << 4) +#define MXC_CCM_CSR_CAMP2_READY (1 << 3) +#define MXC_CCM_CSR_CAMP1_READY (1 << 2) +#define MXC_CCM_CSR_FPM_READY (1 << 1) +#define MXC_CCM_CSR_REF_EN_B (1 << 0) + +/* Define the bits in register CCSR */ +#define MXC_CCM_CCSR_LP_APM_SEL (0x1 << 9) +#define MXC_CCM_CCSR_STEP_SEL_OFFSET (7) +#define MXC_CCM_CCSR_STEP_SEL_MASK (0x3 << 7) +#define MXC_CCM_CCSR_STEP_SEL_LP_APM 0 +#define MXC_CCM_CCSR_STEP_SEL_PLL1_BYPASS 1 /* Only when JTAG connected? */ +#define MXC_CCM_CCSR_STEP_SEL_PLL2_DIVIDED 2 +#define MXC_CCM_CCSR_STEP_SEL_PLL3_DIVIDED 3 +#define MXC_CCM_CCSR_PLL2_PODF_OFFSET (5) +#define MXC_CCM_CCSR_PLL2_PODF_MASK (0x3 << 5) +#define MXC_CCM_CCSR_PLL3_PODF_OFFSET (3) +#define MXC_CCM_CCSR_PLL3_PODF_MASK (0x3 << 3) +#define MXC_CCM_CCSR_PLL1_SW_CLK_SEL (1 << 2) /* 0: pll1_main_clk, + 1: step_clk */ +#define MXC_CCM_CCSR_PLL2_SW_CLK_SEL (1 << 1) +#define MXC_CCM_CCSR_PLL3_SW_CLK_SEL (1 << 0) + +/* Define the bits in register CACRR */ +#define MXC_CCM_CACRR_ARM_PODF_OFFSET (0) +#define MXC_CCM_CACRR_ARM_PODF_MASK (0x7) + +/* Define the bits in register CBCDR */ +#define MXC_CCM_CBCDR_EMI_CLK_SEL (0x1 << 26) +#define MXC_CCM_CBCDR_PERIPH_CLK_SEL (0x1 << 25) +#define MXC_CCM_CBCDR_DDR_HF_SEL_OFFSET (30) +#define MXC_CCM_CBCDR_DDR_HF_SEL (0x1 << 30) +#define MXC_CCM_CBCDR_DDR_PODF_OFFSET (27) +#define MXC_CCM_CBCDR_DDR_PODF_MASK (0x7 << 27) +#define MXC_CCM_CBCDR_EMI_PODF_OFFSET (22) +#define MXC_CCM_CBCDR_EMI_PODF_MASK (0x7 << 22) +#define MXC_CCM_CBCDR_AXI_B_PODF_OFFSET (19) +#define MXC_CCM_CBCDR_AXI_B_PODF_MASK (0x7 << 19) +#define MXC_CCM_CBCDR_AXI_A_PODF_OFFSET (16) +#define MXC_CCM_CBCDR_AXI_A_PODF_MASK (0x7 << 16) +#define MXC_CCM_CBCDR_NFC_PODF_OFFSET (13) +#define MXC_CCM_CBCDR_NFC_PODF_MASK (0x7 << 13) +#define MXC_CCM_CBCDR_AHB_PODF_OFFSET (10) +#define MXC_CCM_CBCDR_AHB_PODF_MASK (0x7 << 10) +#define MXC_CCM_CBCDR_IPG_PODF_OFFSET (8) +#define MXC_CCM_CBCDR_IPG_PODF_MASK (0x3 << 8) +#define MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET (6) +#define MXC_CCM_CBCDR_PERCLK_PRED1_MASK (0x3 << 6) +#define MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET (3) +#define MXC_CCM_CBCDR_PERCLK_PRED2_MASK (0x7 << 3) +#define MXC_CCM_CBCDR_PERCLK_PODF_OFFSET (0) +#define MXC_CCM_CBCDR_PERCLK_PODF_MASK (0x7) + +/* Define the bits in register CBCMR */ +#define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_OFFSET (14) +#define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_MASK (0x3 << 14) +#define MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET (12) +#define MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK (0x3 << 12) +#define MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET (10) +#define MXC_CCM_CBCMR_DDR_CLK_SEL_MASK (0x3 << 10) +#define MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_OFFSET (8) +#define MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_MASK (0x3 << 8) +#define MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_OFFSET (6) +#define MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_MASK (0x3 << 6) +#define MXC_CCM_CBCMR_GPU_CLK_SEL_OFFSET (4) +#define MXC_CCM_CBCMR_GPU_CLK_SEL_MASK (0x3 << 4) +#define MXC_CCM_CBCMR_GPU2D_CLK_SEL_OFFSET (14) +#define MXC_CCM_CBCMR_GPU2D_CLK_SEL_MASK (0x3 << 14) +#define MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL (0x1 << 1) +#define MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL (0x1 << 0) + +/* Define the bits in register CSCMR1 */ +#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET (30) +#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK (0x3 << 30) +#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET (28) +#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK (0x3 << 28) +#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET (26) +#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL (0x1 << 26) +#define MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET (24) +#define MXC_CCM_CSCMR1_UART_CLK_SEL_MASK (0x3 << 24) +#define MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET (22) +#define MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK (0x3 << 22) +#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET (20) +#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK (0x3 << 20) +#define MXC_CCM_CSCMR1_ESDHC3_CLK_SEL (0x1 << 19) +#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL (0x1 << 19) +#define MXC_CCM_CSCMR1_ESDHC4_CLK_SEL (0x1 << 18) +#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET (16) +#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK (0x3 << 16) +#define MXC_CCM_CSCMR1_ESDHC3_MX53_CLK_SEL_OFFSET (16) +#define MXC_CCM_CSCMR1_ESDHC3_MX53_CLK_SEL_MASK (0x3 << 16) +#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET (14) +#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK (0x3 << 14) +#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET (12) +#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK (0x3 << 12) +#define MXC_CCM_CSCMR1_SSI3_CLK_SEL (0x1 << 11) +#define MXC_CCM_CSCMR1_VPU_RCLK_SEL (0x1 << 10) +#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_OFFSET (8) +#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_MASK (0x3 << 8) +#define MXC_CCM_CSCMR1_TVE_CLK_SEL (0x1 << 7) +#define MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL (0x1 << 6) +#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET (4) +#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK (0x3 << 4) +#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL_OFFSET (2) +#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL_MASK (0x3 << 2) +#define MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL (0x1 << 1) +#define MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL (0x1) + +/* Define the bits in register CSCMR2 */ +#define MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(n) (26+n*3) +#define MXC_CCM_CSCMR2_DI_CLK_SEL_MASK(n) (0x7 << (26+n*3)) +#define MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_OFFSET (24) +#define MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_MASK (0x3 << 24) +#define MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_OFFSET (22) +#define MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_MASK (0x3 << 22) +#define MXC_CCM_CSCMR2_ESC_CLK_SEL_OFFSET (20) +#define MXC_CCM_CSCMR2_ESC_CLK_SEL_MASK (0x3 << 20) +#define MXC_CCM_CSCMR2_HSC2_CLK_SEL_OFFSET (18) +#define MXC_CCM_CSCMR2_HSC2_CLK_SEL_MASK (0x3 << 18) +#define MXC_CCM_CSCMR2_HSC1_CLK_SEL_OFFSET (16) +#define MXC_CCM_CSCMR2_HSC1_CLK_SEL_MASK (0x3 << 16) +#define MXC_CCM_CSCMR2_HSI2C_CLK_SEL_OFFSET (14) +#define MXC_CCM_CSCMR2_HSI2C_CLK_SEL_MASK (0x3 << 14) +#define MXC_CCM_CSCMR2_FIRI_CLK_SEL_OFFSET (12) +#define MXC_CCM_CSCMR2_FIRI_CLK_SEL_MASK (0x3 << 12) +#define MXC_CCM_CSCMR2_SIM_CLK_SEL_OFFSET (10) +#define MXC_CCM_CSCMR2_SIM_CLK_SEL_MASK (0x3 << 10) +#define MXC_CCM_CSCMR2_SLIMBUS_COM (0x1 << 9) +#define MXC_CCM_CSCMR2_SLIMBUS_CLK_SEL_OFFSET (6) +#define MXC_CCM_CSCMR2_SLIMBUS_CLK_SEL_MASK (0x7 << 6) +#define MXC_CCM_CSCMR2_SPDIF1_COM (1 << 5) +#define MXC_CCM_CSCMR2_SPDIF0_COM (1 << 4) +#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_OFFSET (2) +#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_MASK (0x3 << 2) +#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_OFFSET (0) +#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_MASK (0x3) + +/* Define the bits in register CSCDR1 */ +#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_OFFSET (22) +#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK (0x7 << 22) +#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET (19) +#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK (0x7 << 19) +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PRED_OFFSET (22) +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PRED_MASK (0x7 << 22) +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PODF_OFFSET (19) +#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PODF_MASK (0x7 << 19) +#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET (16) +#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK (0x7 << 16) +#define MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET (14) +#define MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK (0x3 << 14) +#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_OFFSET (11) +#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_MASK (0x7 << 11) +#define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET (8) +#define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK (0x7 << 8) +#define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET (6) +#define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK (0x3 << 6) +#define MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET (3) +#define MXC_CCM_CSCDR1_UART_CLK_PRED_MASK (0x7 << 3) +#define MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET (0) +#define MXC_CCM_CSCDR1_UART_CLK_PODF_MASK (0x7) + +/* Define the bits in register CS1CDR and CS2CDR */ +#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_OFFSET (22) +#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_MASK (0x7 << 22) +#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_OFFSET (16) +#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_MASK (0x3F << 16) +#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET (6) +#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK (0x7 << 6) +#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET (0) +#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK (0x3F) + +#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_OFFSET (22) +#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_MASK (0x7 << 22) +#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_OFFSET (16) +#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_MASK (0x3F << 16) +#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET (6) +#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK (0x7 << 6) +#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET (0) +#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK (0x3F) + +/* Define the bits in register CDCDR */ +#define MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET (28) +#define MXC_CCM_CDCDR_TVE_CLK_PRED_MASK (0x7 << 28) +#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET (25) +#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK (0x7 << 25) +#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET (19) +#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK (0x3F << 19) +#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_OFFSET (16) +#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK (0x7 << 16) +#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_OFFSET (9) +#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK (0x3F << 9) +#define MXC_CCM_CDCDR_DI_CLK_PRED_OFFSET (6) +#define MXC_CCM_CDCDR_DI_CLK_PRED_MASK (0x7 << 6) +#define MXC_CCM_CDCDR_USB_PHY_PRED_OFFSET (3) +#define MXC_CCM_CDCDR_USB_PHY_PRED_MASK (0x7 << 3) +#define MXC_CCM_CDCDR_USB_PHY_PODF_OFFSET (0) +#define MXC_CCM_CDCDR_USB_PHY_PODF_MASK (0x7) + +/* Define the bits in register CHSCCDR */ +#define MXC_CCM_CHSCCDR_ESC_CLK_PRED_OFFSET (12) +#define MXC_CCM_CHSCCDR_ESC_CLK_PRED_MASK (0x7 << 12) +#define MXC_CCM_CHSCCDR_ESC_CLK_PODF_OFFSET (6) +#define MXC_CCM_CHSCCDR_ESC_CLK_PODF_MASK (0x3F << 6) +#define MXC_CCM_CHSCCDR_HSC2_CLK_PODF_OFFSET (3) +#define MXC_CCM_CHSCCDR_HSC2_CLK_PODF_MASK (0x7 << 3) +#define MXC_CCM_CHSCCDR_HSC1_CLK_PODF_OFFSET (0) +#define MXC_CCM_CHSCCDR_HSC1_CLK_PODF_MASK (0x7) + +/* Define the bits in register CSCDR2 */ +#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET (25) +#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK (0x7 << 25) +#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET (19) +#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK (0x3F << 19) +#define MXC_CCM_CSCDR2_SIM_CLK_PRED_OFFSET (16) +#define MXC_CCM_CSCDR2_SIM_CLK_PRED_MASK (0x7 << 16) +#define MXC_CCM_CSCDR2_SIM_CLK_PODF_OFFSET (9) +#define MXC_CCM_CSCDR2_SIM_CLK_PODF_MASK (0x3F << 9) +#define MXC_CCM_CSCDR2_SLIMBUS_CLK_PRED_OFFSET (6) +#define MXC_CCM_CSCDR2_SLIMBUS_PRED_MASK (0x7 << 6) +#define MXC_CCM_CSCDR2_SLIMBUS_PODF_OFFSET (0) +#define MXC_CCM_CSCDR2_SLIMBUS_PODF_MASK (0x3F) + +/* Define the bits in register CSCDR3 */ +#define MXC_CCM_CSCDR3_HSI2C_CLK_PRED_OFFSET (16) +#define MXC_CCM_CSCDR3_HSI2C_CLK_PRED_MASK (0x7 << 16) +#define MXC_CCM_CSCDR3_HSI2C_CLK_PODF_OFFSET (9) +#define MXC_CCM_CSCDR3_HSI2C_CLK_PODF_MASK (0x3F << 9) +#define MXC_CCM_CSCDR3_FIRI_CLK_PRED_OFFSET (6) +#define MXC_CCM_CSCDR3_FIRI_CLK_PRED_MASK (0x7 << 6) +#define MXC_CCM_CSCDR3_FIRI_CLK_PODF_OFFSET (0) +#define MXC_CCM_CSCDR3_FIRI_CLK_PODF_MASK (0x3F) + +/* Define the bits in register CSCDR4 */ +#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_OFFSET (16) +#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_MASK (0x7 << 16) +#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_OFFSET (9) +#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_MASK (0x3F << 9) +#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_OFFSET (6) +#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_MASK (0x7 << 6) +#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_OFFSET (0) +#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_MASK (0x3F) + +/* Define the bits in register CDHIPR */ +#define MXC_CCM_CDHIPR_ARM_PODF_BUSY (1 << 16) +#define MXC_CCM_CDHIPR_DDR_HF_CLK_SEL_BUSY (1 << 8) +#define MXC_CCM_CDHIPR_DDR_PODF_BUSY (1 << 7) +#define MXC_CCM_CDHIPR_EMI_CLK_SEL_BUSY (1 << 6) +#define MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY (1 << 5) +#define MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY (1 << 4) +#define MXC_CCM_CDHIPR_AHB_PODF_BUSY (1 << 3) +#define MXC_CCM_CDHIPR_EMI_PODF_BUSY (1 << 2) +#define MXC_CCM_CDHIPR_AXI_B_PODF_BUSY (1 << 1) +#define MXC_CCM_CDHIPR_AXI_A_PODF_BUSY (1 << 0) + +/* Define the bits in register CDCR */ +#define MXC_CCM_CDCR_ARM_FREQ_SHIFT_DIVIDER (0x1 << 2) +#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_OFFSET (0) +#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK (0x3) + +/* Define the bits in register CLPCR */ +#define MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS (0x1 << 23) +#define MXC_CCM_CLPCR_BYPASS_SCC_LPM_HS (0x1 << 22) +#define MX51_CCM_CLPCR_BYPASS_MAX_LPM_HS (0x1 << 21) +#define MX53_CCM_CLPCR_BYPASS_MAX_LPM_HS (0x1 << 25) +#define MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS (0x1 << 20) +#define MXC_CCM_CLPCR_BYPASS_EMI_LPM_HS (0x1 << 19) +#define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS (0x1 << 18) +#define MXC_CCM_CLPCR_BYPASS_RTIC_LPM_HS (0x1 << 17) +#define MXC_CCM_CLPCR_BYPASS_RNGC_LPM_HS (0x1 << 16) +#define MXC_CCM_CLPCR_COSC_PWRDOWN (0x1 << 11) +#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET (9) +#define MXC_CCM_CLPCR_STBY_COUNT_MASK (0x3 << 9) +#define MXC_CCM_CLPCR_VSTBY (0x1 << 8) +#define MXC_CCM_CLPCR_DIS_REF_OSC (0x1 << 7) +#define MXC_CCM_CLPCR_SBYOS (0x1 << 6) +#define MXC_CCM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) +#define MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET (3) +#define MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK (0x3 << 3) +#define MXC_CCM_CLPCR_LPM_OFFSET (0) +#define MXC_CCM_CLPCR_LPM_MASK (0x3) + +/* Define the bits in register CISR */ +#define MXC_CCM_CISR_ARM_PODF_LOADED (0x1 << 25) +#define MXC_CCM_CISR_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21) +#define MXC_CCM_CISR_AHB_PODF_LOADED (0x1 << 20) +#define MXC_CCM_CISR_EMI_PODF_LOADED (0x1 << 19) +#define MXC_CCM_CISR_AXI_B_PODF_LOADED (0x1 << 18) +#define MXC_CCM_CISR_AXI_A_PODF_LOADED (0x1 << 17) +#define MXC_CCM_CISR_DIVIDER_LOADED (0x1 << 16) +#define MXC_CCM_CISR_COSC_READY (0x1 << 6) +#define MXC_CCM_CISR_CKIH2_READY (0x1 << 5) +#define MXC_CCM_CISR_CKIH_READY (0x1 << 4) +#define MXC_CCM_CISR_FPM_READY (0x1 << 3) +#define MXC_CCM_CISR_LRF_PLL3 (0x1 << 2) +#define MXC_CCM_CISR_LRF_PLL2 (0x1 << 1) +#define MXC_CCM_CISR_LRF_PLL1 (0x1) + +/* Define the bits in register CIMR */ +#define MXC_CCM_CIMR_MASK_ARM_PODF_LOADED (0x1 << 25) +#define MXC_CCM_CIMR_MASK_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21) +#define MXC_CCM_CIMR_MASK_EMI_PODF_LOADED (0x1 << 20) +#define MXC_CCM_CIMR_MASK_AXI_C_PODF_LOADED (0x1 << 19) +#define MXC_CCM_CIMR_MASK_AXI_B_PODF_LOADED (0x1 << 18) +#define MXC_CCM_CIMR_MASK_AXI_A_PODF_LOADED (0x1 << 17) +#define MXC_CCM_CIMR_MASK_DIVIDER_LOADED (0x1 << 16) +#define MXC_CCM_CIMR_MASK_COSC_READY (0x1 << 5) +#define MXC_CCM_CIMR_MASK_CKIH_READY (0x1 << 4) +#define MXC_CCM_CIMR_MASK_FPM_READY (0x1 << 3) +#define MXC_CCM_CIMR_MASK_LRF_PLL3 (0x1 << 2) +#define MXC_CCM_CIMR_MASK_LRF_PLL2 (0x1 << 1) +#define MXC_CCM_CIMR_MASK_LRF_PLL1 (0x1) + +/* Define the bits in register CCOSR */ +#define MXC_CCM_CCOSR_CKO2_EN_OFFSET (0x1 << 24) +#define MXC_CCM_CCOSR_CKO2_DIV_OFFSET (21) +#define MXC_CCM_CCOSR_CKO2_DIV_MASK (0x7 << 21) +#define MXC_CCM_CCOSR_CKO2_SEL_OFFSET (16) +#define MXC_CCM_CCOSR_CKO2_SEL_MASK (0x1F << 16) +#define MXC_CCM_CCOSR_CKOL_EN (0x1 << 7) +#define MXC_CCM_CCOSR_CKOL_DIV_OFFSET (4) +#define MXC_CCM_CCOSR_CKOL_DIV_MASK (0x7 << 4) +#define MXC_CCM_CCOSR_CKOL_SEL_OFFSET (0) +#define MXC_CCM_CCOSR_CKOL_SEL_MASK (0xF) + +/* Define the bits in registers CGPR */ +#define MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE (0x1 << 4) +#define MXC_CCM_CGPR_FPM_SEL (0x1 << 3) +#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_OFFSET (0) +#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_MASK (0x7) + +/* Define the bits in registers CCGRx */ +#define MXC_CCM_CCGRx_CG_MASK 0x3 +#define MXC_CCM_CCGRx_MOD_OFF 0x0 +#define MXC_CCM_CCGRx_MOD_ON 0x3 +#define MXC_CCM_CCGRx_MOD_IDLE 0x1 + +#define MXC_CCM_CCGRx_CG15_MASK (0x3 << 30) +#define MXC_CCM_CCGRx_CG14_MASK (0x3 << 28) +#define MXC_CCM_CCGRx_CG13_MASK (0x3 << 26) +#define MXC_CCM_CCGRx_CG12_MASK (0x3 << 24) +#define MXC_CCM_CCGRx_CG11_MASK (0x3 << 22) +#define MXC_CCM_CCGRx_CG10_MASK (0x3 << 20) +#define MXC_CCM_CCGRx_CG9_MASK (0x3 << 18) +#define MXC_CCM_CCGRx_CG8_MASK (0x3 << 16) +#define MXC_CCM_CCGRx_CG5_MASK (0x3 << 10) +#define MXC_CCM_CCGRx_CG4_MASK (0x3 << 8) +#define MXC_CCM_CCGRx_CG3_MASK (0x3 << 6) +#define MXC_CCM_CCGRx_CG2_MASK (0x3 << 4) +#define MXC_CCM_CCGRx_CG1_MASK (0x3 << 2) +#define MXC_CCM_CCGRx_CG0_MASK (0x3 << 0) + +#define MXC_CCM_CCGRx_CG15_OFFSET 30 +#define MXC_CCM_CCGRx_CG14_OFFSET 28 +#define MXC_CCM_CCGRx_CG13_OFFSET 26 +#define MXC_CCM_CCGRx_CG12_OFFSET 24 +#define MXC_CCM_CCGRx_CG11_OFFSET 22 +#define MXC_CCM_CCGRx_CG10_OFFSET 20 +#define MXC_CCM_CCGRx_CG9_OFFSET 18 +#define MXC_CCM_CCGRx_CG8_OFFSET 16 +#define MXC_CCM_CCGRx_CG7_OFFSET 14 +#define MXC_CCM_CCGRx_CG6_OFFSET 12 +#define MXC_CCM_CCGRx_CG5_OFFSET 10 +#define MXC_CCM_CCGRx_CG4_OFFSET 8 +#define MXC_CCM_CCGRx_CG3_OFFSET 6 +#define MXC_CCM_CCGRx_CG2_OFFSET 4 +#define MXC_CCM_CCGRx_CG1_OFFSET 2 +#define MXC_CCM_CCGRx_CG0_OFFSET 0 + +#define MXC_DPTC_LP_BASE (MX51_GPC_BASE + 0x80) +#define MXC_DPTC_GP_BASE (MX51_GPC_BASE + 0x100) +#define MXC_DVFS_CORE_BASE (MX51_GPC_BASE + 0x180) +#define MXC_DPTC_PER_BASE (MX51_GPC_BASE + 0x1C0) +#define MXC_PGC_IPU_BASE (MX51_GPC_BASE + 0x220) +#define MXC_PGC_VPU_BASE (MX51_GPC_BASE + 0x240) +#define MXC_PGC_GPU_BASE (MX51_GPC_BASE + 0x260) +#define MXC_SRPG_NEON_BASE (MX51_GPC_BASE + 0x280) +#define MXC_SRPG_ARM_BASE (MX51_GPC_BASE + 0x2A0) +#define MXC_SRPG_EMPGC0_BASE (MX51_GPC_BASE + 0x2C0) +#define MXC_SRPG_EMPGC1_BASE (MX51_GPC_BASE + 0x2D0) +#define MXC_SRPG_MEGAMIX_BASE (MX51_GPC_BASE + 0x2E0) +#define MXC_SRPG_EMI_BASE (MX51_GPC_BASE + 0x300) + +/* CORTEXA8 platform */ +#define MXC_CORTEXA8_PLAT_PVID (MX51_CORTEXA8_BASE + 0x0) +#define MXC_CORTEXA8_PLAT_GPC (MX51_CORTEXA8_BASE + 0x4) +#define MXC_CORTEXA8_PLAT_PIC (MX51_CORTEXA8_BASE + 0x8) +#define MXC_CORTEXA8_PLAT_LPC (MX51_CORTEXA8_BASE + 0xC) +#define MXC_CORTEXA8_PLAT_NEON_LPC (MX51_CORTEXA8_BASE + 0x10) +#define MXC_CORTEXA8_PLAT_ICGC (MX51_CORTEXA8_BASE + 0x14) +#define MXC_CORTEXA8_PLAT_AMC (MX51_CORTEXA8_BASE + 0x18) +#define MXC_CORTEXA8_PLAT_NMC (MX51_CORTEXA8_BASE + 0x20) +#define MXC_CORTEXA8_PLAT_NMS (MX51_CORTEXA8_BASE + 0x24) + +/* DVFS CORE */ +#define MXC_DVFSTHRS (MXC_DVFS_CORE_BASE + 0x00) +#define MXC_DVFSCOUN (MXC_DVFS_CORE_BASE + 0x04) +#define MXC_DVFSSIG1 (MXC_DVFS_CORE_BASE + 0x08) +#define MXC_DVFSSIG0 (MXC_DVFS_CORE_BASE + 0x0C) +#define MXC_DVFSGPC0 (MXC_DVFS_CORE_BASE + 0x10) +#define MXC_DVFSGPC1 (MXC_DVFS_CORE_BASE + 0x14) +#define MXC_DVFSGPBT (MXC_DVFS_CORE_BASE + 0x18) +#define MXC_DVFSEMAC (MXC_DVFS_CORE_BASE + 0x1C) +#define MXC_DVFSCNTR (MXC_DVFS_CORE_BASE + 0x20) +#define MXC_DVFSLTR0_0 (MXC_DVFS_CORE_BASE + 0x24) +#define MXC_DVFSLTR0_1 (MXC_DVFS_CORE_BASE + 0x28) +#define MXC_DVFSLTR1_0 (MXC_DVFS_CORE_BASE + 0x2C) +#define MXC_DVFSLTR1_1 (MXC_DVFS_CORE_BASE + 0x30) +#define MXC_DVFSPT0 (MXC_DVFS_CORE_BASE + 0x34) +#define MXC_DVFSPT1 (MXC_DVFS_CORE_BASE + 0x38) +#define MXC_DVFSPT2 (MXC_DVFS_CORE_BASE + 0x3C) +#define MXC_DVFSPT3 (MXC_DVFS_CORE_BASE + 0x40) + +/* GPC */ +#define MXC_GPC_CNTR (MX51_GPC_BASE + 0x0) +#define MXC_GPC_PGR (MX51_GPC_BASE + 0x4) +#define MXC_GPC_VCR (MX51_GPC_BASE + 0x8) +#define MXC_GPC_ALL_PU (MX51_GPC_BASE + 0xC) +#define MXC_GPC_NEON (MX51_GPC_BASE + 0x10) +#define MXC_GPC_PGR_ARMPG_OFFSET 8 +#define MXC_GPC_PGR_ARMPG_MASK (3 << 8) + +/* PGC */ +#define MXC_PGC_IPU_PGCR (MXC_PGC_IPU_BASE + 0x0) +#define MXC_PGC_IPU_PGSR (MXC_PGC_IPU_BASE + 0xC) +#define MXC_PGC_VPU_PGCR (MXC_PGC_VPU_BASE + 0x0) +#define MXC_PGC_VPU_PGSR (MXC_PGC_VPU_BASE + 0xC) +#define MXC_PGC_GPU_PGCR (MXC_PGC_GPU_BASE + 0x0) +#define MXC_PGC_GPU_PGSR (MXC_PGC_GPU_BASE + 0xC) + +#define MXC_PGCR_PCR 1 +#define MXC_SRPGCR_PCR 1 +#define MXC_EMPGCR_PCR 1 +#define MXC_PGSR_PSR 1 + + +#define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0) +#define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1) + +/* SRPG */ +#define MXC_SRPG_NEON_SRPGCR (MXC_SRPG_NEON_BASE + 0x0) +#define MXC_SRPG_NEON_PUPSCR (MXC_SRPG_NEON_BASE + 0x4) +#define MXC_SRPG_NEON_PDNSCR (MXC_SRPG_NEON_BASE + 0x8) + +#define MXC_SRPG_ARM_SRPGCR (MXC_SRPG_ARM_BASE + 0x0) +#define MXC_SRPG_ARM_PUPSCR (MXC_SRPG_ARM_BASE + 0x4) +#define MXC_SRPG_ARM_PDNSCR (MXC_SRPG_ARM_BASE + 0x8) + +#define MXC_SRPG_EMPGC0_SRPGCR (MXC_SRPG_EMPGC0_BASE + 0x0) +#define MXC_SRPG_EMPGC0_PUPSCR (MXC_SRPG_EMPGC0_BASE + 0x4) +#define MXC_SRPG_EMPGC0_PDNSCR (MXC_SRPG_EMPGC0_BASE + 0x8) + +#define MXC_SRPG_EMPGC1_SRPGCR (MXC_SRPG_EMPGC1_BASE + 0x0) +#define MXC_SRPG_EMPGC1_PUPSCR (MXC_SRPG_EMPGC1_BASE + 0x4) +#define MXC_SRPG_EMPGC1_PDNSCR (MXC_SRPG_EMPGC1_BASE + 0x8) + +#define MXC_SRPG_MEGAMIX_SRPGCR (MXC_SRPG_MEGAMIX_BASE + 0x0) +#define MXC_SRPG_MEGAMIX_PUPSCR (MXC_SRPG_MEGAMIX_BASE + 0x4) +#define MXC_SRPG_MEGAMIX_PDNSCR (MXC_SRPG_MEGAMIX_BASE + 0x8) + +#define MXC_SRPGC_EMI_SRPGCR (MXC_SRPGC_EMI_BASE + 0x0) +#define MXC_SRPGC_EMI_PUPSCR (MXC_SRPGC_EMI_BASE + 0x4) +#define MXC_SRPGC_EMI_PDNSCR (MXC_SRPGC_EMI_BASE + 0x8) + +#endif /* __ARCH_ARM_MACH_MX51_CRM_REGS_H__ */ diff --git a/arch/arm/mach-imx/devices-imx50.h b/arch/arm/mach-imx/devices-imx50.h new file mode 100644 index 0000000..7216667 --- /dev/null +++ b/arch/arm/mach-imx/devices-imx50.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +extern const struct imx_imx_uart_1irq_data imx50_imx_uart_data[]; +#define imx50_add_imx_uart(id, pdata) \ + imx_add_imx_uart_1irq(&imx50_imx_uart_data[id], pdata) + +extern const struct imx_fec_data imx50_fec_data; +#define imx50_add_fec(pdata) \ + imx_add_fec(&imx50_fec_data, pdata) + +extern const struct imx_imx_i2c_data imx50_imx_i2c_data[]; +#define imx50_add_imx_i2c(id, pdata) \ + imx_add_imx_i2c(&imx50_imx_i2c_data[id], pdata) diff --git a/arch/arm/mach-imx/devices-imx51.h b/arch/arm/mach-imx/devices-imx51.h new file mode 100644 index 0000000..af488bc --- /dev/null +++ b/arch/arm/mach-imx/devices-imx51.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Pengutronix + * Uwe Kleine-Koenig + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#include +#include + +extern const struct imx_fec_data imx51_fec_data; +#define imx51_add_fec(pdata) \ + imx_add_fec(&imx51_fec_data, pdata) + +extern const struct imx_fsl_usb2_udc_data imx51_fsl_usb2_udc_data; +#define imx51_add_fsl_usb2_udc(pdata) \ + imx_add_fsl_usb2_udc(&imx51_fsl_usb2_udc_data, pdata) + +extern const struct imx_imx_i2c_data imx51_imx_i2c_data[]; +#define imx51_add_imx_i2c(id, pdata) \ + imx_add_imx_i2c(&imx51_imx_i2c_data[id], pdata) +#define imx51_add_hsi2c(pdata) \ + imx51_add_imx_i2c(2, pdata) + +extern const struct imx_imx_ssi_data imx51_imx_ssi_data[]; +#define imx51_add_imx_ssi(id, pdata) \ + imx_add_imx_ssi(&imx51_imx_ssi_data[id], pdata) + +extern const struct imx_imx_uart_1irq_data imx51_imx_uart_data[]; +#define imx51_add_imx_uart(id, pdata) \ + imx_add_imx_uart_1irq(&imx51_imx_uart_data[id], pdata) + +extern const struct imx_mxc_ehci_data imx51_mxc_ehci_otg_data; +#define imx51_add_mxc_ehci_otg(pdata) \ + imx_add_mxc_ehci(&imx51_mxc_ehci_otg_data, pdata) +extern const struct imx_mxc_ehci_data imx51_mxc_ehci_hs_data[]; +#define imx51_add_mxc_ehci_hs(id, pdata) \ + imx_add_mxc_ehci(&imx51_mxc_ehci_hs_data[id - 1], pdata) + +extern const struct imx_mxc_nand_data imx51_mxc_nand_data; +#define imx51_add_mxc_nand(pdata) \ + imx_add_mxc_nand(&imx51_mxc_nand_data, pdata) + +extern const struct imx_sdhci_esdhc_imx_data imx51_sdhci_esdhc_imx_data[]; +#define imx51_add_sdhci_esdhc_imx(id, pdata) \ + imx_add_sdhci_esdhc_imx(&imx51_sdhci_esdhc_imx_data[id], pdata) + +extern const struct imx_spi_imx_data imx51_cspi_data; +#define imx51_add_cspi(pdata) \ + imx_add_spi_imx(&imx51_cspi_data, pdata) + +extern const struct imx_spi_imx_data imx51_ecspi_data[]; +#define imx51_add_ecspi(id, pdata) \ + imx_add_spi_imx(&imx51_ecspi_data[id], pdata) + +extern const struct imx_imx2_wdt_data imx51_imx2_wdt_data[]; +#define imx51_add_imx2_wdt(id, pdata) \ + imx_add_imx2_wdt(&imx51_imx2_wdt_data[id]) + +extern const struct imx_mxc_pwm_data imx51_mxc_pwm_data[]; +#define imx51_add_mxc_pwm(id) \ + imx_add_mxc_pwm(&imx51_mxc_pwm_data[id]) + +extern const struct imx_imx_keypad_data imx51_imx_keypad_data; +#define imx51_add_imx_keypad(pdata) \ + imx_add_imx_keypad(&imx51_imx_keypad_data, pdata) + +extern const struct imx_pata_imx_data imx51_pata_imx_data; +#define imx51_add_pata_imx() \ + imx_add_pata_imx(&imx51_pata_imx_data) diff --git a/arch/arm/mach-imx/devices-imx53.h b/arch/arm/mach-imx/devices-imx53.h new file mode 100644 index 0000000..6e1e5d1 --- /dev/null +++ b/arch/arm/mach-imx/devices-imx53.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Yong Shen. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#include +#include + +extern const struct imx_fec_data imx53_fec_data; +#define imx53_add_fec(pdata) \ + imx_add_fec(&imx53_fec_data, pdata) + +extern const struct imx_imx_uart_1irq_data imx53_imx_uart_data[]; +#define imx53_add_imx_uart(id, pdata) \ + imx_add_imx_uart_1irq(&imx53_imx_uart_data[id], pdata) + + +extern const struct imx_imx_i2c_data imx53_imx_i2c_data[]; +#define imx53_add_imx_i2c(id, pdata) \ + imx_add_imx_i2c(&imx53_imx_i2c_data[id], pdata) + +extern const struct imx_sdhci_esdhc_imx_data imx53_sdhci_esdhc_imx_data[]; +#define imx53_add_sdhci_esdhc_imx(id, pdata) \ + imx_add_sdhci_esdhc_imx(&imx53_sdhci_esdhc_imx_data[id], pdata) + +extern const struct imx_spi_imx_data imx53_ecspi_data[]; +#define imx53_add_ecspi(id, pdata) \ + imx_add_spi_imx(&imx53_ecspi_data[id], pdata) + +extern const struct imx_imx2_wdt_data imx53_imx2_wdt_data[]; +#define imx53_add_imx2_wdt(id, pdata) \ + imx_add_imx2_wdt(&imx53_imx2_wdt_data[id]) + +extern const struct imx_imx_ssi_data imx53_imx_ssi_data[]; +#define imx53_add_imx_ssi(id, pdata) \ + imx_add_imx_ssi(&imx53_imx_ssi_data[id], pdata) + +extern const struct imx_imx_keypad_data imx53_imx_keypad_data; +#define imx53_add_imx_keypad(pdata) \ + imx_add_imx_keypad(&imx53_imx_keypad_data, pdata) + +extern const struct imx_pata_imx_data imx53_pata_imx_data; +#define imx53_add_pata_imx() \ + imx_add_pata_imx(&imx53_pata_imx_data) + +extern struct platform_device *__init imx53_add_ahci_imx(void); diff --git a/arch/arm/mach-imx/efika.h b/arch/arm/mach-imx/efika.h new file mode 100644 index 0000000..014aa98 --- /dev/null +++ b/arch/arm/mach-imx/efika.h @@ -0,0 +1,10 @@ +#ifndef _EFIKA_H +#define _EFIKA_H + +#define EFIKA_WLAN_EN IMX_GPIO_NR(2, 16) +#define EFIKA_WLAN_RESET IMX_GPIO_NR(2, 10) +#define EFIKA_USB_PHY_RESET IMX_GPIO_NR(2, 9) + +void __init efika_board_common_init(void); + +#endif diff --git a/arch/arm/mach-imx/ehci-imx5.c b/arch/arm/mach-imx/ehci-imx5.c new file mode 100644 index 0000000..c17fa13 --- /dev/null +++ b/arch/arm/mach-imx/ehci-imx5.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2009 Daniel Mack + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include + +#include +#include + +#define MXC_OTG_OFFSET 0 +#define MXC_H1_OFFSET 0x200 +#define MXC_H2_OFFSET 0x400 + +/* USB_CTRL */ +#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */ +#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */ +#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */ +#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */ +#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */ + +/* USB_PHY_CTRL_FUNC */ +#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */ +#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */ + +/* USBH2CTRL */ +#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) +#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) +#define MXC_H2_UCTRL_H2PM_BIT (1 << 4) + +#define MXC_USBCMD_OFFSET 0x140 + +/* USBCMD */ +#define MXC_UCMD_ITC_NO_THRESHOLD_MASK (~(0xff << 16)) /* Interrupt Threshold Control */ + +int mx51_initialize_usb_hw(int port, unsigned int flags) +{ + unsigned int v; + void __iomem *usb_base; + void __iomem *usbotg_base; + void __iomem *usbother_base; + int ret = 0; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) { + printk(KERN_ERR "%s(): ioremap failed\n", __func__); + return -ENOMEM; + } + + switch (port) { + case 0: /* OTG port */ + usbotg_base = usb_base + MXC_OTG_OFFSET; + break; + case 1: /* Host 1 port */ + usbotg_base = usb_base + MXC_H1_OFFSET; + break; + case 2: /* Host 2 port */ + usbotg_base = usb_base + MXC_H2_OFFSET; + break; + default: + printk(KERN_ERR"%s no such port %d\n", __func__, port); + ret = -ENOENT; + goto error; + } + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + switch (port) { + case 0: /*OTG port */ + if (flags & MXC_EHCI_INTERNAL_PHY) { + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + + if (flags & MXC_EHCI_POWER_PINS_ENABLED) { + /* OC/USBPWR is not used */ + v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; + } else { + /* OC/USBPWR is used */ + v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; + } + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + + v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); + if (flags & MXC_EHCI_WAKEUP_ENABLED) + v |= MXC_OTG_UCTRL_OWIE_BIT;/* OTG wakeup enable */ + else + v &= ~MXC_OTG_UCTRL_OWIE_BIT;/* OTG wakeup disable */ + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v |= MXC_OTG_UCTRL_OPM_BIT; + else + v &= ~MXC_OTG_UCTRL_OPM_BIT; + __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + } + break; + case 1: /* Host 1 */ + /*Host ULPI */ + v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); + if (flags & MXC_EHCI_WAKEUP_ENABLED) { + /* HOST1 wakeup/ULPI intr enable */ + v |= (MXC_H1_UCTRL_H1WIE_BIT | MXC_H1_UCTRL_H1UIE_BIT); + } else { + /* HOST1 wakeup/ULPI intr disable */ + v &= ~(MXC_H1_UCTRL_H1WIE_BIT | MXC_H1_UCTRL_H1UIE_BIT); + } + + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used*/ + else + v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used*/ + __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H1_OC_DIS_BIT; /* OC is not used */ + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + + v = __raw_readl(usbotg_base + MXC_USBCMD_OFFSET); + if (flags & MXC_EHCI_ITC_NO_THRESHOLD) + /* Interrupt Threshold Control:Immediate (no threshold) */ + v &= MXC_UCMD_ITC_NO_THRESHOLD_MASK; + __raw_writel(v, usbotg_base + MXC_USBCMD_OFFSET); + break; + case 2: /* Host 2 ULPI */ + v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); + if (flags & MXC_EHCI_WAKEUP_ENABLED) { + /* HOST1 wakeup/ULPI intr enable */ + v |= (MXC_H2_UCTRL_H2WIE_BIT | MXC_H2_UCTRL_H2UIE_BIT); + } else { + /* HOST1 wakeup/ULPI intr disable */ + v &= ~(MXC_H2_UCTRL_H2WIE_BIT | MXC_H2_UCTRL_H2UIE_BIT); + } + + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used*/ + else + v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used*/ + __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); + break; + } + +error: + iounmap(usb_base); + return ret; +} + diff --git a/arch/arm/mach-imx/eukrea_mbimx51-baseboard.c b/arch/arm/mach-imx/eukrea_mbimx51-baseboard.c new file mode 100644 index 0000000..a6a3ab8 --- /dev/null +++ b/arch/arm/mach-imx/eukrea_mbimx51-baseboard.c @@ -0,0 +1,206 @@ +/* + * + * Copyright (C) 2010 Eric Bénard + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "devices-imx51.h" + +#define MBIMX51_TSC2007_GPIO IMX_GPIO_NR(3, 30) +#define MBIMX51_LED0 IMX_GPIO_NR(3, 5) +#define MBIMX51_LED1 IMX_GPIO_NR(3, 6) +#define MBIMX51_LED2 IMX_GPIO_NR(3, 7) +#define MBIMX51_LED3 IMX_GPIO_NR(3, 8) + +static const struct gpio_led mbimx51_leds[] __initconst = { + { + .name = "led0", + .default_trigger = "heartbeat", + .active_low = 1, + .gpio = MBIMX51_LED0, + }, + { + .name = "led1", + .default_trigger = "nand-disk", + .active_low = 1, + .gpio = MBIMX51_LED1, + }, + { + .name = "led2", + .default_trigger = "mmc0", + .active_low = 1, + .gpio = MBIMX51_LED2, + }, + { + .name = "led3", + .default_trigger = "default-on", + .active_low = 1, + .gpio = MBIMX51_LED3, + }, +}; + +static const struct gpio_led_platform_data mbimx51_leds_info __initconst = { + .leds = mbimx51_leds, + .num_leds = ARRAY_SIZE(mbimx51_leds), +}; + +static iomux_v3_cfg_t mbimx51_pads[] = { + /* UART2 */ + MX51_PAD_UART2_RXD__UART2_RXD, + MX51_PAD_UART2_TXD__UART2_TXD, + + /* UART3 */ + MX51_PAD_UART3_RXD__UART3_RXD, + MX51_PAD_UART3_TXD__UART3_TXD, + MX51_PAD_KEY_COL4__UART3_RTS, + MX51_PAD_KEY_COL5__UART3_CTS, + + /* TSC2007 IRQ */ + MX51_PAD_NANDF_D10__GPIO3_30, + + /* LEDS */ + MX51_PAD_DISPB2_SER_DIN__GPIO3_5, + MX51_PAD_DISPB2_SER_DIO__GPIO3_6, + MX51_PAD_DISPB2_SER_CLK__GPIO3_7, + MX51_PAD_DISPB2_SER_RS__GPIO3_8, + + /* KPP */ + MX51_PAD_KEY_ROW0__KEY_ROW0, + MX51_PAD_KEY_ROW1__KEY_ROW1, + MX51_PAD_KEY_ROW2__KEY_ROW2, + MX51_PAD_KEY_ROW3__KEY_ROW3, + MX51_PAD_KEY_COL0__KEY_COL0, + MX51_PAD_KEY_COL1__KEY_COL1, + MX51_PAD_KEY_COL2__KEY_COL2, + MX51_PAD_KEY_COL3__KEY_COL3, + + /* SD 1 */ + MX51_PAD_SD1_CMD__SD1_CMD, + MX51_PAD_SD1_CLK__SD1_CLK, + MX51_PAD_SD1_DATA0__SD1_DATA0, + MX51_PAD_SD1_DATA1__SD1_DATA1, + MX51_PAD_SD1_DATA2__SD1_DATA2, + MX51_PAD_SD1_DATA3__SD1_DATA3, + + /* SD 2 */ + MX51_PAD_SD2_CMD__SD2_CMD, + MX51_PAD_SD2_CLK__SD2_CLK, + MX51_PAD_SD2_DATA0__SD2_DATA0, + MX51_PAD_SD2_DATA1__SD2_DATA1, + MX51_PAD_SD2_DATA2__SD2_DATA2, + MX51_PAD_SD2_DATA3__SD2_DATA3, +}; + +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static int mbimx51_keymap[] = { + KEY(0, 0, KEY_1), + KEY(0, 1, KEY_2), + KEY(0, 2, KEY_3), + KEY(0, 3, KEY_UP), + + KEY(1, 0, KEY_4), + KEY(1, 1, KEY_5), + KEY(1, 2, KEY_6), + KEY(1, 3, KEY_LEFT), + + KEY(2, 0, KEY_7), + KEY(2, 1, KEY_8), + KEY(2, 2, KEY_9), + KEY(2, 3, KEY_RIGHT), + + KEY(3, 0, KEY_0), + KEY(3, 1, KEY_DOWN), + KEY(3, 2, KEY_ESC), + KEY(3, 3, KEY_ENTER), +}; + +static const struct matrix_keymap_data mbimx51_map_data __initconst = { + .keymap = mbimx51_keymap, + .keymap_size = ARRAY_SIZE(mbimx51_keymap), +}; + +static int tsc2007_get_pendown_state(void) +{ + return !gpio_get_value(MBIMX51_TSC2007_GPIO); +} + +struct tsc2007_platform_data tsc2007_data = { + .model = 2007, + .x_plate_ohms = 180, + .get_pendown_state = tsc2007_get_pendown_state, +}; + +static struct i2c_board_info mbimx51_i2c_devices[] = { + { + I2C_BOARD_INFO("tsc2007", 0x49), + .irq = IMX_GPIO_TO_IRQ(MBIMX51_TSC2007_GPIO), + .platform_data = &tsc2007_data, + }, { + I2C_BOARD_INFO("tlv320aic23", 0x1a), + }, +}; + +/* + * baseboard initialization. + */ +void __init eukrea_mbimx51_baseboard_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mbimx51_pads, + ARRAY_SIZE(mbimx51_pads)); + + imx51_add_imx_uart(1, NULL); + imx51_add_imx_uart(2, &uart_pdata); + + gpio_request(MBIMX51_LED0, "LED0"); + gpio_direction_output(MBIMX51_LED0, 1); + gpio_free(MBIMX51_LED0); + gpio_request(MBIMX51_LED1, "LED1"); + gpio_direction_output(MBIMX51_LED1, 1); + gpio_free(MBIMX51_LED1); + gpio_request(MBIMX51_LED2, "LED2"); + gpio_direction_output(MBIMX51_LED2, 1); + gpio_free(MBIMX51_LED2); + gpio_request(MBIMX51_LED3, "LED3"); + gpio_direction_output(MBIMX51_LED3, 1); + gpio_free(MBIMX51_LED3); + + gpio_led_register_device(-1, &mbimx51_leds_info); + + imx51_add_imx_keypad(&mbimx51_map_data); + + gpio_request(MBIMX51_TSC2007_GPIO, "tsc2007_irq"); + gpio_direction_input(MBIMX51_TSC2007_GPIO); + irq_set_irq_type(gpio_to_irq(MBIMX51_TSC2007_GPIO), + IRQF_TRIGGER_FALLING); + i2c_register_board_info(1, mbimx51_i2c_devices, + ARRAY_SIZE(mbimx51_i2c_devices)); + + imx51_add_sdhci_esdhc_imx(0, NULL); + imx51_add_sdhci_esdhc_imx(1, NULL); +} diff --git a/arch/arm/mach-imx/eukrea_mbimxsd-baseboard.c b/arch/arm/mach-imx/eukrea_mbimxsd-baseboard.c new file mode 100644 index 0000000..d817fc8 --- /dev/null +++ b/arch/arm/mach-imx/eukrea_mbimxsd-baseboard.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 Eric Benard - eric@eukrea.com + * + * Based on pcm970-baseboard.c which is : + * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + * + * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" + +static iomux_v3_cfg_t eukrea_mbimxsd_pads[] = { + /* LED */ + MX51_PAD_NANDF_D10__GPIO3_30, + /* SWITCH */ + NEW_PAD_CTRL(MX51_PAD_NANDF_D9__GPIO3_31, PAD_CTL_PUS_22K_UP | + PAD_CTL_PKE | PAD_CTL_SRE_FAST | + PAD_CTL_DSE_HIGH | PAD_CTL_PUE | PAD_CTL_HYS), + /* UART2 */ + MX51_PAD_UART2_RXD__UART2_RXD, + MX51_PAD_UART2_TXD__UART2_TXD, + /* UART 3 */ + MX51_PAD_UART3_RXD__UART3_RXD, + MX51_PAD_UART3_TXD__UART3_TXD, + MX51_PAD_KEY_COL4__UART3_RTS, + MX51_PAD_KEY_COL5__UART3_CTS, + /* SD */ + MX51_PAD_SD1_CMD__SD1_CMD, + MX51_PAD_SD1_CLK__SD1_CLK, + MX51_PAD_SD1_DATA0__SD1_DATA0, + MX51_PAD_SD1_DATA1__SD1_DATA1, + MX51_PAD_SD1_DATA2__SD1_DATA2, + MX51_PAD_SD1_DATA3__SD1_DATA3, + /* SD1 CD */ + NEW_PAD_CTRL(MX51_PAD_GPIO1_0__SD1_CD, PAD_CTL_PUS_22K_UP | + PAD_CTL_PKE | PAD_CTL_SRE_FAST | + PAD_CTL_DSE_HIGH | PAD_CTL_PUE | PAD_CTL_HYS), +}; + +#define GPIO_LED1 IMX_GPIO_NR(3, 30) +#define GPIO_SWITCH1 IMX_GPIO_NR(3, 31) + +static const struct gpio_led eukrea_mbimxsd_leds[] __initconst = { + { + .name = "led1", + .default_trigger = "heartbeat", + .active_low = 1, + .gpio = GPIO_LED1, + }, +}; + +static const struct gpio_led_platform_data + eukrea_mbimxsd_led_info __initconst = { + .leds = eukrea_mbimxsd_leds, + .num_leds = ARRAY_SIZE(eukrea_mbimxsd_leds), +}; + +static struct gpio_keys_button eukrea_mbimxsd_gpio_buttons[] = { + { + .gpio = GPIO_SWITCH1, + .code = BTN_0, + .desc = "BP1", + .active_low = 1, + .wakeup = 1, + }, +}; + +static const struct gpio_keys_platform_data + eukrea_mbimxsd_button_data __initconst = { + .buttons = eukrea_mbimxsd_gpio_buttons, + .nbuttons = ARRAY_SIZE(eukrea_mbimxsd_gpio_buttons), +}; + +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct i2c_board_info eukrea_mbimxsd_i2c_devices[] = { + { + I2C_BOARD_INFO("tlv320aic23", 0x1a), + }, +}; + +/* + * system init for baseboard usage. Will be called by cpuimx51sd init. + * + * Add platform devices present on this baseboard and init + * them from CPU side as far as required to use them later on + */ +void __init eukrea_mbimxsd51_baseboard_init(void) +{ + if (mxc_iomux_v3_setup_multiple_pads(eukrea_mbimxsd_pads, + ARRAY_SIZE(eukrea_mbimxsd_pads))) + printk(KERN_ERR "error setting mbimxsd pads !\n"); + + imx51_add_imx_uart(1, NULL); + imx51_add_imx_uart(2, &uart_pdata); + + imx51_add_sdhci_esdhc_imx(0, NULL); + + gpio_request(GPIO_LED1, "LED1"); + gpio_direction_output(GPIO_LED1, 1); + gpio_free(GPIO_LED1); + + gpio_request(GPIO_SWITCH1, "SWITCH1"); + gpio_direction_input(GPIO_SWITCH1); + gpio_free(GPIO_SWITCH1); + + i2c_register_board_info(0, eukrea_mbimxsd_i2c_devices, + ARRAY_SIZE(eukrea_mbimxsd_i2c_devices)); + + gpio_led_register_device(-1, &eukrea_mbimxsd_led_info); + imx_add_gpio_keys(&eukrea_mbimxsd_button_data); +} diff --git a/arch/arm/mach-imx/imx51-dt.c b/arch/arm/mach-imx/imx51-dt.c new file mode 100644 index 0000000..ccc6158 --- /dev/null +++ b/arch/arm/mach-imx/imx51-dt.c @@ -0,0 +1,116 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Lookup table for attaching a specific name and platform_data pointer to + * devices as they get created by of_platform_populate(). Ideally this table + * would not exist, but the current clock implementation depends on some devices + * having a specific name. + */ +static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = { + OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART1_BASE_ADDR, "imx21-uart.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART2_BASE_ADDR, "imx21-uart.1", NULL), + OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART3_BASE_ADDR, "imx21-uart.2", NULL), + OF_DEV_AUXDATA("fsl,imx51-fec", MX51_FEC_BASE_ADDR, "imx27-fec.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx51.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx51.1", NULL), + OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx51.2", NULL), + OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx51.3", NULL), + OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), + OF_DEV_AUXDATA("fsl,imx51-cspi", MX51_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx51-sdma", MX51_SDMA_BASE_ADDR, "imx35-sdma", NULL), + OF_DEV_AUXDATA("fsl,imx51-wdt", MX51_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), + { /* sentinel */ } +}; + +static void __init imx51_tzic_add_irq_domain(struct device_node *np, + struct device_node *interrupt_parent) +{ + irq_domain_add_simple(np, 0); +} + +static void __init imx51_gpio_add_irq_domain(struct device_node *np, + struct device_node *interrupt_parent) +{ + static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS - + 32 * 4; /* imx51 gets 4 gpio ports */ + + irq_domain_add_simple(np, gpio_irq_base); + gpio_irq_base += 32; +} + +static const struct of_device_id imx51_irq_match[] __initconst = { + { .compatible = "fsl,imx51-tzic", .data = imx51_tzic_add_irq_domain, }, + { .compatible = "fsl,imx51-gpio", .data = imx51_gpio_add_irq_domain, }, + { /* sentinel */ } +}; + +static const struct of_device_id imx51_iomuxc_of_match[] __initconst = { + { .compatible = "fsl,imx51-iomuxc-babbage", .data = imx51_babbage_common_init, }, + { /* sentinel */ } +}; + +static void __init imx51_dt_init(void) +{ + struct device_node *node; + const struct of_device_id *of_id; + void (*func)(void); + + of_irq_init(imx51_irq_match); + + node = of_find_matching_node(NULL, imx51_iomuxc_of_match); + if (node) { + of_id = of_match_node(imx51_iomuxc_of_match, node); + func = of_id->data; + func(); + of_node_put(node); + } + + of_platform_populate(NULL, of_default_bus_match_table, + imx51_auxdata_lookup, NULL); +} + +static void __init imx51_timer_init(void) +{ + mx51_clocks_init_dt(); +} + +static struct sys_timer imx51_timer = { + .init = imx51_timer_init, +}; + +static const char *imx51_dt_board_compat[] __initdata = { + "fsl,imx51-babbage", + NULL +}; + +DT_MACHINE_START(IMX51_DT, "Freescale i.MX51 (Device Tree Support)") + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .timer = &imx51_timer, + .init_machine = imx51_dt_init, + .dt_compat = imx51_dt_board_compat, +MACHINE_END diff --git a/arch/arm/mach-imx/imx53-dt.c b/arch/arm/mach-imx/imx53-dt.c new file mode 100644 index 0000000..ccaa0b8 --- /dev/null +++ b/arch/arm/mach-imx/imx53-dt.c @@ -0,0 +1,126 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Lookup table for attaching a specific name and platform_data pointer to + * devices as they get created by of_platform_populate(). Ideally this table + * would not exist, but the current clock implementation depends on some devices + * having a specific name. + */ +static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = { + OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART1_BASE_ADDR, "imx21-uart.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART2_BASE_ADDR, "imx21-uart.1", NULL), + OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART3_BASE_ADDR, "imx21-uart.2", NULL), + OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART4_BASE_ADDR, "imx21-uart.3", NULL), + OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART5_BASE_ADDR, "imx21-uart.4", NULL), + OF_DEV_AUXDATA("fsl,imx53-fec", MX53_FEC_BASE_ADDR, "imx25-fec.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx53.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx53.1", NULL), + OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx53.2", NULL), + OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx53.3", NULL), + OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), + OF_DEV_AUXDATA("fsl,imx53-cspi", MX53_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx-i2c.2", NULL), + OF_DEV_AUXDATA("fsl,imx53-sdma", MX53_SDMA_BASE_ADDR, "imx35-sdma", NULL), + OF_DEV_AUXDATA("fsl,imx53-wdt", MX53_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), + { /* sentinel */ } +}; + +static void __init imx53_tzic_add_irq_domain(struct device_node *np, + struct device_node *interrupt_parent) +{ + irq_domain_add_simple(np, 0); +} + +static void __init imx53_gpio_add_irq_domain(struct device_node *np, + struct device_node *interrupt_parent) +{ + static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS - + 32 * 7; /* imx53 gets 7 gpio ports */ + + irq_domain_add_simple(np, gpio_irq_base); + gpio_irq_base += 32; +} + +static const struct of_device_id imx53_irq_match[] __initconst = { + { .compatible = "fsl,imx53-tzic", .data = imx53_tzic_add_irq_domain, }, + { .compatible = "fsl,imx53-gpio", .data = imx53_gpio_add_irq_domain, }, + { /* sentinel */ } +}; + +static const struct of_device_id imx53_iomuxc_of_match[] __initconst = { + { .compatible = "fsl,imx53-iomuxc-ard", .data = imx53_ard_common_init, }, + { .compatible = "fsl,imx53-iomuxc-evk", .data = imx53_evk_common_init, }, + { .compatible = "fsl,imx53-iomuxc-qsb", .data = imx53_qsb_common_init, }, + { .compatible = "fsl,imx53-iomuxc-smd", .data = imx53_smd_common_init, }, + { /* sentinel */ } +}; + +static void __init imx53_dt_init(void) +{ + struct device_node *node; + const struct of_device_id *of_id; + void (*func)(void); + + of_irq_init(imx53_irq_match); + + node = of_find_matching_node(NULL, imx53_iomuxc_of_match); + if (node) { + of_id = of_match_node(imx53_iomuxc_of_match, node); + func = of_id->data; + func(); + of_node_put(node); + } + + of_platform_populate(NULL, of_default_bus_match_table, + imx53_auxdata_lookup, NULL); +} + +static void __init imx53_timer_init(void) +{ + mx53_clocks_init_dt(); +} + +static struct sys_timer imx53_timer = { + .init = imx53_timer_init, +}; + +static const char *imx53_dt_board_compat[] __initdata = { + "fsl,imx53-ard", + "fsl,imx53-evk", + "fsl,imx53-qsb", + "fsl,imx53-smd", + NULL +}; + +DT_MACHINE_START(IMX53_DT, "Freescale i.MX53 (Device Tree Support)") + .map_io = mx53_map_io, + .init_early = imx53_init_early, + .init_irq = mx53_init_irq, + .handle_irq = imx53_handle_irq, + .timer = &imx53_timer, + .init_machine = imx53_dt_init, + .dt_compat = imx53_dt_board_compat, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-cpuimx51.c b/arch/arm/mach-imx/mach-cpuimx51.c new file mode 100644 index 0000000..1fc1103 --- /dev/null +++ b/arch/arm/mach-imx/mach-cpuimx51.c @@ -0,0 +1,300 @@ +/* + * + * Copyright (C) 2010 Eric Bénard + * + * based on board-mx51_babbage.c which is + * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009-2010 Amit Kucheria + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" + +#define CPUIMX51_USBH1_STP IMX_GPIO_NR(1, 27) +#define CPUIMX51_QUARTA_GPIO IMX_GPIO_NR(3, 28) +#define CPUIMX51_QUARTB_GPIO IMX_GPIO_NR(3, 25) +#define CPUIMX51_QUARTC_GPIO IMX_GPIO_NR(3, 26) +#define CPUIMX51_QUARTD_GPIO IMX_GPIO_NR(3, 27) +#define CPUIMX51_QUART_XTAL 14745600 +#define CPUIMX51_QUART_REGSHIFT 17 + +/* USB_CTRL_1 */ +#define MX51_USB_CTRL_1_OFFSET 0x10 +#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) + +#define MX51_USB_PLLDIV_12_MHZ 0x00 +#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 +#define MX51_USB_PLL_DIV_24_MHZ 0x02 + +static struct plat_serial8250_port serial_platform_data[] = { + { + .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x400000), + .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTA_GPIO), + .irqflags = IRQF_TRIGGER_HIGH, + .uartclk = CPUIMX51_QUART_XTAL, + .regshift = CPUIMX51_QUART_REGSHIFT, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x800000), + .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTB_GPIO), + .irqflags = IRQF_TRIGGER_HIGH, + .uartclk = CPUIMX51_QUART_XTAL, + .regshift = CPUIMX51_QUART_REGSHIFT, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x1000000), + .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTC_GPIO), + .irqflags = IRQF_TRIGGER_HIGH, + .uartclk = CPUIMX51_QUART_XTAL, + .regshift = CPUIMX51_QUART_REGSHIFT, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x2000000), + .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTD_GPIO), + .irqflags = IRQF_TRIGGER_HIGH, + .uartclk = CPUIMX51_QUART_XTAL, + .regshift = CPUIMX51_QUART_REGSHIFT, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + } +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +static struct platform_device *devices[] __initdata = { + &serial_device, +}; + +static iomux_v3_cfg_t eukrea_cpuimx51_pads[] = { + /* UART1 */ + MX51_PAD_UART1_RXD__UART1_RXD, + MX51_PAD_UART1_TXD__UART1_TXD, + MX51_PAD_UART1_RTS__UART1_RTS, + MX51_PAD_UART1_CTS__UART1_CTS, + + /* I2C2 */ + MX51_PAD_GPIO1_2__I2C2_SCL, + MX51_PAD_GPIO1_3__I2C2_SDA, + MX51_PAD_NANDF_D10__GPIO3_30, + + /* QUART IRQ */ + MX51_PAD_NANDF_D15__GPIO3_25, + MX51_PAD_NANDF_D14__GPIO3_26, + MX51_PAD_NANDF_D13__GPIO3_27, + MX51_PAD_NANDF_D12__GPIO3_28, + + /* USB HOST1 */ + MX51_PAD_USBH1_CLK__USBH1_CLK, + MX51_PAD_USBH1_DIR__USBH1_DIR, + MX51_PAD_USBH1_NXT__USBH1_NXT, + MX51_PAD_USBH1_DATA0__USBH1_DATA0, + MX51_PAD_USBH1_DATA1__USBH1_DATA1, + MX51_PAD_USBH1_DATA2__USBH1_DATA2, + MX51_PAD_USBH1_DATA3__USBH1_DATA3, + MX51_PAD_USBH1_DATA4__USBH1_DATA4, + MX51_PAD_USBH1_DATA5__USBH1_DATA5, + MX51_PAD_USBH1_DATA6__USBH1_DATA6, + MX51_PAD_USBH1_DATA7__USBH1_DATA7, + MX51_PAD_USBH1_STP__USBH1_STP, +}; + +static const struct mxc_nand_platform_data + eukrea_cpuimx51_nand_board_info __initconst = { + .width = 1, + .hw_ecc = 1, + .flash_bbt = 1, +}; + +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static const +struct imxi2c_platform_data eukrea_cpuimx51_i2c_data __initconst = { + .bitrate = 100000, +}; + +static struct i2c_board_info eukrea_cpuimx51_i2c_devices[] = { + { + I2C_BOARD_INFO("pcf8563", 0x51), + }, +}; + +/* This function is board specific as the bit mask for the plldiv will also +be different for other Freescale SoCs, thus a common bitmask is not +possible and cannot get place in /plat-mxc/ehci.c.*/ +static int initialize_otg_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + /* Set the PHY clock to 19.2MHz */ + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; + v |= MX51_USB_PLL_DIV_19_2_MHZ; + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(0, MXC_EHCI_INTERNAL_PHY); +} + +static int initialize_usbh1_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + /* The clock for the USBH1 ULPI port will come externally from the PHY. */ + v = __raw_readl(usbother_base + MX51_USB_CTRL_1_OFFSET); + __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + MX51_USB_CTRL_1_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(1, MXC_EHCI_POWER_PINS_ENABLED | + MXC_EHCI_ITC_NO_THRESHOLD); +} + +static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { + .init = initialize_otg_port, + .portsc = MXC_EHCI_UTMI_16BIT, +}; + +static const struct fsl_usb2_platform_data usb_pdata __initconst = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_UTMI_WIDE, +}; + +static const struct mxc_usbh_platform_data usbh1_config __initconst = { + .init = initialize_usbh1_port, + .portsc = MXC_EHCI_MODE_ULPI, +}; + +static int otg_mode_host; + +static int __init eukrea_cpuimx51_otg_mode(char *options) +{ + if (!strcmp(options, "host")) + otg_mode_host = 1; + else if (!strcmp(options, "device")) + otg_mode_host = 0; + else + pr_info("otg_mode neither \"host\" nor \"device\". " + "Defaulting to device\n"); + return 0; +} +__setup("otg_mode=", eukrea_cpuimx51_otg_mode); + +/* + * Board specific initialization. + */ +static void __init eukrea_cpuimx51_init(void) +{ + imx51_soc_init(); + + mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx51_pads, + ARRAY_SIZE(eukrea_cpuimx51_pads)); + + imx51_add_imx_uart(0, &uart_pdata); + imx51_add_mxc_nand(&eukrea_cpuimx51_nand_board_info); + + gpio_request(CPUIMX51_QUARTA_GPIO, "quarta_irq"); + gpio_direction_input(CPUIMX51_QUARTA_GPIO); + gpio_free(CPUIMX51_QUARTA_GPIO); + gpio_request(CPUIMX51_QUARTB_GPIO, "quartb_irq"); + gpio_direction_input(CPUIMX51_QUARTB_GPIO); + gpio_free(CPUIMX51_QUARTB_GPIO); + gpio_request(CPUIMX51_QUARTC_GPIO, "quartc_irq"); + gpio_direction_input(CPUIMX51_QUARTC_GPIO); + gpio_free(CPUIMX51_QUARTC_GPIO); + gpio_request(CPUIMX51_QUARTD_GPIO, "quartd_irq"); + gpio_direction_input(CPUIMX51_QUARTD_GPIO); + gpio_free(CPUIMX51_QUARTD_GPIO); + + imx51_add_fec(NULL); + platform_add_devices(devices, ARRAY_SIZE(devices)); + + imx51_add_imx_i2c(1, &eukrea_cpuimx51_i2c_data); + i2c_register_board_info(1, eukrea_cpuimx51_i2c_devices, + ARRAY_SIZE(eukrea_cpuimx51_i2c_devices)); + + if (otg_mode_host) + imx51_add_mxc_ehci_otg(&dr_utmi_config); + else { + initialize_otg_port(NULL); + imx51_add_fsl_usb2_udc(&usb_pdata); + } + imx51_add_mxc_ehci_hs(1, &usbh1_config); + +#ifdef CONFIG_MACH_EUKREA_MBIMX51_BASEBOARD + eukrea_mbimx51_baseboard_init(); +#endif +} + +static void __init eukrea_cpuimx51_timer_init(void) +{ + mx51_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mxc_timer = { + .init = eukrea_cpuimx51_timer_init, +}; + +MACHINE_START(EUKREA_CPUIMX51, "Eukrea CPUIMX51 Module") + /* Maintainer: Eric Bénard */ + .atag_offset = 0x100, + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .timer = &mxc_timer, + .init_machine = eukrea_cpuimx51_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-cpuimx51sd.c b/arch/arm/mach-imx/mach-cpuimx51sd.c new file mode 100644 index 0000000..52a11c1 --- /dev/null +++ b/arch/arm/mach-imx/mach-cpuimx51sd.c @@ -0,0 +1,338 @@ +/* + * + * Copyright (C) 2010 Eric Bénard + * + * based on board-mx51_babbage.c which is + * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009-2010 Amit Kucheria + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" +#include "cpu_op-mx51.h" + +#define USBH1_RST IMX_GPIO_NR(2, 28) +#define ETH_RST IMX_GPIO_NR(2, 31) +#define TSC2007_IRQGPIO IMX_GPIO_NR(3, 12) +#define CAN_IRQGPIO IMX_GPIO_NR(1, 1) +#define CAN_RST IMX_GPIO_NR(4, 15) +#define CAN_NCS IMX_GPIO_NR(4, 24) +#define CAN_RXOBF IMX_GPIO_NR(1, 4) +#define CAN_RX1BF IMX_GPIO_NR(1, 6) +#define CAN_TXORTS IMX_GPIO_NR(1, 7) +#define CAN_TX1RTS IMX_GPIO_NR(1, 8) +#define CAN_TX2RTS IMX_GPIO_NR(1, 9) +#define I2C_SCL IMX_GPIO_NR(4, 16) +#define I2C_SDA IMX_GPIO_NR(4, 17) + +/* USB_CTRL_1 */ +#define MX51_USB_CTRL_1_OFFSET 0x10 +#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) + +#define MX51_USB_PLLDIV_12_MHZ 0x00 +#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 +#define MX51_USB_PLL_DIV_24_MHZ 0x02 + +static iomux_v3_cfg_t eukrea_cpuimx51sd_pads[] = { + /* UART1 */ + MX51_PAD_UART1_RXD__UART1_RXD, + MX51_PAD_UART1_TXD__UART1_TXD, + MX51_PAD_UART1_RTS__UART1_RTS, + MX51_PAD_UART1_CTS__UART1_CTS, + + /* USB HOST1 */ + MX51_PAD_USBH1_CLK__USBH1_CLK, + MX51_PAD_USBH1_DIR__USBH1_DIR, + MX51_PAD_USBH1_NXT__USBH1_NXT, + MX51_PAD_USBH1_DATA0__USBH1_DATA0, + MX51_PAD_USBH1_DATA1__USBH1_DATA1, + MX51_PAD_USBH1_DATA2__USBH1_DATA2, + MX51_PAD_USBH1_DATA3__USBH1_DATA3, + MX51_PAD_USBH1_DATA4__USBH1_DATA4, + MX51_PAD_USBH1_DATA5__USBH1_DATA5, + MX51_PAD_USBH1_DATA6__USBH1_DATA6, + MX51_PAD_USBH1_DATA7__USBH1_DATA7, + MX51_PAD_USBH1_STP__USBH1_STP, + MX51_PAD_EIM_CS3__GPIO2_28, /* PHY nRESET */ + + /* FEC */ + MX51_PAD_EIM_DTACK__GPIO2_31, /* PHY nRESET */ + + /* HSI2C */ + MX51_PAD_I2C1_CLK__GPIO4_16, + MX51_PAD_I2C1_DAT__GPIO4_17, + + /* CAN */ + MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, + MX51_PAD_CSPI1_MISO__ECSPI1_MISO, + MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, + MX51_PAD_CSPI1_SS0__GPIO4_24, /* nCS */ + MX51_PAD_CSI2_PIXCLK__GPIO4_15, /* nReset */ + MX51_PAD_GPIO1_1__GPIO1_1, /* IRQ */ + MX51_PAD_GPIO1_4__GPIO1_4, /* Control signals */ + MX51_PAD_GPIO1_6__GPIO1_6, + MX51_PAD_GPIO1_7__GPIO1_7, + MX51_PAD_GPIO1_8__GPIO1_8, + MX51_PAD_GPIO1_9__GPIO1_9, + + /* Touchscreen */ + /* IRQ */ + NEW_PAD_CTRL(MX51_PAD_GPIO_NAND__GPIO_NAND, PAD_CTL_PUS_22K_UP | + PAD_CTL_PKE | PAD_CTL_SRE_FAST | + PAD_CTL_DSE_HIGH | PAD_CTL_PUE | PAD_CTL_HYS), +}; + +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct tsc2007_platform_data tsc2007_info = { + .model = 2007, + .x_plate_ohms = 180, +}; + +static struct i2c_board_info eukrea_cpuimx51sd_i2c_devices[] = { + { + I2C_BOARD_INFO("pcf8563", 0x51), + }, { + I2C_BOARD_INFO("tsc2007", 0x49), + .type = "tsc2007", + .platform_data = &tsc2007_info, + .irq = IMX_GPIO_TO_IRQ(TSC2007_IRQGPIO), + }, +}; + +static const struct mxc_nand_platform_data + eukrea_cpuimx51sd_nand_board_info __initconst = { + .width = 1, + .hw_ecc = 1, + .flash_bbt = 1, +}; + +/* This function is board specific as the bit mask for the plldiv will also +be different for other Freescale SoCs, thus a common bitmask is not +possible and cannot get place in /plat-mxc/ehci.c.*/ +static int initialize_otg_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + /* Set the PHY clock to 19.2MHz */ + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; + v |= MX51_USB_PLL_DIV_19_2_MHZ; + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(0, MXC_EHCI_INTERNAL_PHY); +} + +static int initialize_usbh1_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + /* The clock for the USBH1 ULPI port will come from the PHY. */ + v = __raw_readl(usbother_base + MX51_USB_CTRL_1_OFFSET); + __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, + usbother_base + MX51_USB_CTRL_1_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(1, MXC_EHCI_POWER_PINS_ENABLED | + MXC_EHCI_ITC_NO_THRESHOLD); +} + +static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { + .init = initialize_otg_port, + .portsc = MXC_EHCI_UTMI_16BIT, +}; + +static const struct fsl_usb2_platform_data usb_pdata __initconst = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_UTMI_WIDE, +}; + +static const struct mxc_usbh_platform_data usbh1_config __initconst = { + .init = initialize_usbh1_port, + .portsc = MXC_EHCI_MODE_ULPI, +}; + +static int otg_mode_host; + +static int __init eukrea_cpuimx51sd_otg_mode(char *options) +{ + if (!strcmp(options, "host")) + otg_mode_host = 1; + else if (!strcmp(options, "device")) + otg_mode_host = 0; + else + pr_info("otg_mode neither \"host\" nor \"device\". " + "Defaulting to device\n"); + return 0; +} +__setup("otg_mode=", eukrea_cpuimx51sd_otg_mode); + +static struct i2c_gpio_platform_data pdata = { + .sda_pin = I2C_SDA, + .sda_is_open_drain = 0, + .scl_pin = I2C_SCL, + .scl_is_open_drain = 0, + .udelay = 2, +}; + +static struct platform_device hsi2c_gpio_device = { + .name = "i2c-gpio", + .id = 0, + .dev.platform_data = &pdata, +}; + +static struct mcp251x_platform_data mcp251x_info = { + .oscillator_frequency = 24E6, +}; + +static struct spi_board_info cpuimx51sd_spi_device[] = { + { + .modalias = "mcp2515", + .max_speed_hz = 10000000, + .bus_num = 0, + .mode = SPI_MODE_0, + .chip_select = 0, + .platform_data = &mcp251x_info, + .irq = IMX_GPIO_TO_IRQ(CAN_IRQGPIO) + }, +}; + +static int cpuimx51sd_spi1_cs[] = { + CAN_NCS, +}; + +static const struct spi_imx_master cpuimx51sd_ecspi1_pdata __initconst = { + .chipselect = cpuimx51sd_spi1_cs, + .num_chipselect = ARRAY_SIZE(cpuimx51sd_spi1_cs), +}; + +static struct platform_device *platform_devices[] __initdata = { + &hsi2c_gpio_device, +}; + +static void __init eukrea_cpuimx51sd_init(void) +{ + imx51_soc_init(); + + mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx51sd_pads, + ARRAY_SIZE(eukrea_cpuimx51sd_pads)); + +#if defined(CONFIG_CPU_FREQ_IMX) + get_cpu_op = mx51_get_cpu_op; +#endif + + imx51_add_imx_uart(0, &uart_pdata); + imx51_add_mxc_nand(&eukrea_cpuimx51sd_nand_board_info); + + gpio_request(ETH_RST, "eth_rst"); + gpio_set_value(ETH_RST, 1); + imx51_add_fec(NULL); + + gpio_request(CAN_IRQGPIO, "can_irq"); + gpio_direction_input(CAN_IRQGPIO); + gpio_free(CAN_IRQGPIO); + gpio_request(CAN_NCS, "can_ncs"); + gpio_direction_output(CAN_NCS, 1); + gpio_free(CAN_NCS); + gpio_request(CAN_RST, "can_rst"); + gpio_direction_output(CAN_RST, 0); + msleep(20); + gpio_set_value(CAN_RST, 1); + imx51_add_ecspi(0, &cpuimx51sd_ecspi1_pdata); + spi_register_board_info(cpuimx51sd_spi_device, + ARRAY_SIZE(cpuimx51sd_spi_device)); + + gpio_request(TSC2007_IRQGPIO, "tsc2007_irq"); + gpio_direction_input(TSC2007_IRQGPIO); + gpio_free(TSC2007_IRQGPIO); + + i2c_register_board_info(0, eukrea_cpuimx51sd_i2c_devices, + ARRAY_SIZE(eukrea_cpuimx51sd_i2c_devices)); + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); + + if (otg_mode_host) + imx51_add_mxc_ehci_otg(&dr_utmi_config); + else { + initialize_otg_port(NULL); + imx51_add_fsl_usb2_udc(&usb_pdata); + } + + gpio_request(USBH1_RST, "usb_rst"); + gpio_direction_output(USBH1_RST, 0); + msleep(20); + gpio_set_value(USBH1_RST, 1); + imx51_add_mxc_ehci_hs(1, &usbh1_config); + +#ifdef CONFIG_MACH_EUKREA_MBIMXSD51_BASEBOARD + eukrea_mbimxsd51_baseboard_init(); +#endif +} + +static void __init eukrea_cpuimx51sd_timer_init(void) +{ + mx51_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mxc_timer = { + .init = eukrea_cpuimx51sd_timer_init, +}; + +MACHINE_START(EUKREA_CPUIMX51SD, "Eukrea CPUIMX51SD") + /* Maintainer: Eric Bénard */ + .atag_offset = 0x100, + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .timer = &mxc_timer, + .init_machine = eukrea_cpuimx51sd_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx50_rdp.c b/arch/arm/mach-imx/mach-mx50_rdp.c new file mode 100644 index 0000000..fc3621d --- /dev/null +++ b/arch/arm/mach-imx/mach-mx50_rdp.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "devices-imx50.h" + +#define FEC_EN IMX_GPIO_NR(6, 23) +#define FEC_RESET_B IMX_GPIO_NR(4, 12) + +static iomux_v3_cfg_t mx50_rdp_pads[] __initdata = { + /* SD1 */ + MX50_PAD_ECSPI2_SS0__GPIO_4_19, + MX50_PAD_EIM_CRE__GPIO_1_27, + MX50_PAD_SD1_CMD__SD1_CMD, + + MX50_PAD_SD1_CLK__SD1_CLK, + MX50_PAD_SD1_D0__SD1_D0, + MX50_PAD_SD1_D1__SD1_D1, + MX50_PAD_SD1_D2__SD1_D2, + MX50_PAD_SD1_D3__SD1_D3, + + /* SD2 */ + MX50_PAD_SD2_CD__GPIO_5_17, + MX50_PAD_SD2_WP__GPIO_5_16, + MX50_PAD_SD2_CMD__SD2_CMD, + MX50_PAD_SD2_CLK__SD2_CLK, + MX50_PAD_SD2_D0__SD2_D0, + MX50_PAD_SD2_D1__SD2_D1, + MX50_PAD_SD2_D2__SD2_D2, + MX50_PAD_SD2_D3__SD2_D3, + MX50_PAD_SD2_D4__SD2_D4, + MX50_PAD_SD2_D5__SD2_D5, + MX50_PAD_SD2_D6__SD2_D6, + MX50_PAD_SD2_D7__SD2_D7, + + /* SD3 */ + MX50_PAD_SD3_CMD__SD3_CMD, + MX50_PAD_SD3_CLK__SD3_CLK, + MX50_PAD_SD3_D0__SD3_D0, + MX50_PAD_SD3_D1__SD3_D1, + MX50_PAD_SD3_D2__SD3_D2, + MX50_PAD_SD3_D3__SD3_D3, + MX50_PAD_SD3_D4__SD3_D4, + MX50_PAD_SD3_D5__SD3_D5, + MX50_PAD_SD3_D6__SD3_D6, + MX50_PAD_SD3_D7__SD3_D7, + + /* PWR_INT */ + MX50_PAD_ECSPI2_MISO__GPIO_4_18, + + /* UART pad setting */ + MX50_PAD_UART1_TXD__UART1_TXD, + MX50_PAD_UART1_RXD__UART1_RXD, + MX50_PAD_UART1_RTS__UART1_RTS, + MX50_PAD_UART2_TXD__UART2_TXD, + MX50_PAD_UART2_RXD__UART2_RXD, + MX50_PAD_UART2_CTS__UART2_CTS, + MX50_PAD_UART2_RTS__UART2_RTS, + + MX50_PAD_I2C1_SCL__I2C1_SCL, + MX50_PAD_I2C1_SDA__I2C1_SDA, + MX50_PAD_I2C2_SCL__I2C2_SCL, + MX50_PAD_I2C2_SDA__I2C2_SDA, + + MX50_PAD_EPITO__USBH1_PWR, + /* Need to comment below line if + * one needs to debug owire. + */ + MX50_PAD_OWIRE__USBH1_OC, + /* using gpio to control otg pwr */ + MX50_PAD_PWM2__GPIO_6_25, + MX50_PAD_I2C3_SCL__USBOTG_OC, + + MX50_PAD_SSI_RXC__FEC_MDIO, + MX50_PAD_SSI_RXFS__FEC_MDC, + MX50_PAD_DISP_D0__FEC_TXCLK, + MX50_PAD_DISP_D1__FEC_RX_ER, + MX50_PAD_DISP_D2__FEC_RX_DV, + MX50_PAD_DISP_D3__FEC_RXD1, + MX50_PAD_DISP_D4__FEC_RXD0, + MX50_PAD_DISP_D5__FEC_TX_EN, + MX50_PAD_DISP_D6__FEC_TXD1, + MX50_PAD_DISP_D7__FEC_TXD0, + MX50_PAD_I2C3_SDA__GPIO_6_23, + MX50_PAD_ECSPI1_SCLK__GPIO_4_12, + + MX50_PAD_CSPI_SS0__CSPI_SS0, + MX50_PAD_ECSPI1_MOSI__CSPI_SS1, + MX50_PAD_CSPI_MOSI__CSPI_MOSI, + MX50_PAD_CSPI_MISO__CSPI_MISO, + + /* SGTL500_OSC_EN */ + MX50_PAD_UART1_CTS__GPIO_6_8, + + /* SGTL_AMP_SHDN */ + MX50_PAD_UART3_RXD__GPIO_6_15, + + /* Keypad */ + MX50_PAD_KEY_COL0__KEY_COL0, + MX50_PAD_KEY_ROW0__KEY_ROW0, + MX50_PAD_KEY_COL1__KEY_COL1, + MX50_PAD_KEY_ROW1__KEY_ROW1, + MX50_PAD_KEY_COL2__KEY_COL2, + MX50_PAD_KEY_ROW2__KEY_ROW2, + MX50_PAD_KEY_COL3__KEY_COL3, + MX50_PAD_KEY_ROW3__KEY_ROW3, + MX50_PAD_EIM_DA0__KEY_COL4, + MX50_PAD_EIM_DA1__KEY_ROW4, + MX50_PAD_EIM_DA2__KEY_COL5, + MX50_PAD_EIM_DA3__KEY_ROW5, + MX50_PAD_EIM_DA4__KEY_COL6, + MX50_PAD_EIM_DA5__KEY_ROW6, + MX50_PAD_EIM_DA6__KEY_COL7, + MX50_PAD_EIM_DA7__KEY_ROW7, + /*EIM pads */ + MX50_PAD_EIM_DA8__GPIO_1_8, + MX50_PAD_EIM_DA9__GPIO_1_9, + MX50_PAD_EIM_DA10__GPIO_1_10, + MX50_PAD_EIM_DA11__GPIO_1_11, + MX50_PAD_EIM_DA12__GPIO_1_12, + MX50_PAD_EIM_DA13__GPIO_1_13, + MX50_PAD_EIM_DA14__GPIO_1_14, + MX50_PAD_EIM_DA15__GPIO_1_15, + MX50_PAD_EIM_CS2__GPIO_1_16, + MX50_PAD_EIM_CS1__GPIO_1_17, + MX50_PAD_EIM_CS0__GPIO_1_18, + MX50_PAD_EIM_EB0__GPIO_1_19, + MX50_PAD_EIM_EB1__GPIO_1_20, + MX50_PAD_EIM_WAIT__GPIO_1_21, + MX50_PAD_EIM_BCLK__GPIO_1_22, + MX50_PAD_EIM_RDY__GPIO_1_23, + MX50_PAD_EIM_OE__GPIO_1_24, +}; + +/* Serial ports */ +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static const struct fec_platform_data fec_data __initconst = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +static inline void mx50_rdp_fec_reset(void) +{ + gpio_request(FEC_EN, "fec-en"); + gpio_direction_output(FEC_EN, 0); + gpio_request(FEC_RESET_B, "fec-reset_b"); + gpio_direction_output(FEC_RESET_B, 0); + msleep(1); + gpio_set_value(FEC_RESET_B, 1); +} + +static const struct imxi2c_platform_data i2c_data __initconst = { + .bitrate = 100000, +}; + +/* + * Board specific initialization. + */ +static void __init mx50_rdp_board_init(void) +{ + imx50_soc_init(); + + mxc_iomux_v3_setup_multiple_pads(mx50_rdp_pads, + ARRAY_SIZE(mx50_rdp_pads)); + + imx50_add_imx_uart(0, &uart_pdata); + imx50_add_imx_uart(1, &uart_pdata); + mx50_rdp_fec_reset(); + imx50_add_fec(&fec_data); + imx50_add_imx_i2c(0, &i2c_data); + imx50_add_imx_i2c(1, &i2c_data); + imx50_add_imx_i2c(2, &i2c_data); +} + +static void __init mx50_rdp_timer_init(void) +{ + mx50_clocks_init(32768, 24000000, 22579200); +} + +static struct sys_timer mx50_rdp_timer = { + .init = mx50_rdp_timer_init, +}; + +MACHINE_START(MX50_RDP, "Freescale MX50 Reference Design Platform") + .map_io = mx50_map_io, + .init_early = imx50_init_early, + .init_irq = mx50_init_irq, + .handle_irq = imx50_handle_irq, + .timer = &mx50_rdp_timer, + .init_machine = mx50_rdp_board_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx51_3ds.c b/arch/arm/mach-imx/mach-mx51_3ds.c new file mode 100644 index 0000000..0578390 --- /dev/null +++ b/arch/arm/mach-imx/mach-mx51_3ds.c @@ -0,0 +1,178 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2010 Jason Wang + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" + +#define EXPIO_PARENT_INT gpio_to_irq(IMX_GPIO_NR(1, 6)) +#define MX51_3DS_ECSPI2_CS (GPIO_PORTC + 28) + +static iomux_v3_cfg_t mx51_3ds_pads[] = { + /* UART1 */ + MX51_PAD_UART1_RXD__UART1_RXD, + MX51_PAD_UART1_TXD__UART1_TXD, + MX51_PAD_UART1_RTS__UART1_RTS, + MX51_PAD_UART1_CTS__UART1_CTS, + + /* UART2 */ + MX51_PAD_UART2_RXD__UART2_RXD, + MX51_PAD_UART2_TXD__UART2_TXD, + MX51_PAD_EIM_D25__UART2_CTS, + MX51_PAD_EIM_D26__UART2_RTS, + + /* UART3 */ + MX51_PAD_UART3_RXD__UART3_RXD, + MX51_PAD_UART3_TXD__UART3_TXD, + MX51_PAD_EIM_D24__UART3_CTS, + MX51_PAD_EIM_D27__UART3_RTS, + + /* CPLD PARENT IRQ PIN */ + MX51_PAD_GPIO1_6__GPIO1_6, + + /* KPP */ + MX51_PAD_KEY_ROW0__KEY_ROW0, + MX51_PAD_KEY_ROW1__KEY_ROW1, + MX51_PAD_KEY_ROW2__KEY_ROW2, + MX51_PAD_KEY_ROW3__KEY_ROW3, + MX51_PAD_KEY_COL0__KEY_COL0, + MX51_PAD_KEY_COL1__KEY_COL1, + MX51_PAD_KEY_COL2__KEY_COL2, + MX51_PAD_KEY_COL3__KEY_COL3, + MX51_PAD_KEY_COL4__KEY_COL4, + MX51_PAD_KEY_COL5__KEY_COL5, + + /* eCSPI2 */ + MX51_PAD_NANDF_RB2__ECSPI2_SCLK, + MX51_PAD_NANDF_RB3__ECSPI2_MISO, + MX51_PAD_NANDF_D15__ECSPI2_MOSI, + MX51_PAD_NANDF_D12__GPIO3_28, +}; + +/* Serial ports */ +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static int mx51_3ds_board_keymap[] = { + KEY(0, 0, KEY_1), + KEY(0, 1, KEY_2), + KEY(0, 2, KEY_3), + KEY(0, 3, KEY_F1), + KEY(0, 4, KEY_UP), + KEY(0, 5, KEY_F2), + + KEY(1, 0, KEY_4), + KEY(1, 1, KEY_5), + KEY(1, 2, KEY_6), + KEY(1, 3, KEY_LEFT), + KEY(1, 4, KEY_SELECT), + KEY(1, 5, KEY_RIGHT), + + KEY(2, 0, KEY_7), + KEY(2, 1, KEY_8), + KEY(2, 2, KEY_9), + KEY(2, 3, KEY_F3), + KEY(2, 4, KEY_DOWN), + KEY(2, 5, KEY_F4), + + KEY(3, 0, KEY_0), + KEY(3, 1, KEY_OK), + KEY(3, 2, KEY_ESC), + KEY(3, 3, KEY_ENTER), + KEY(3, 4, KEY_MENU), + KEY(3, 5, KEY_BACK) +}; + +static const struct matrix_keymap_data mx51_3ds_map_data __initconst = { + .keymap = mx51_3ds_board_keymap, + .keymap_size = ARRAY_SIZE(mx51_3ds_board_keymap), +}; + +static int mx51_3ds_spi2_cs[] = { + MXC_SPI_CS(0), + MX51_3DS_ECSPI2_CS, +}; + +static const struct spi_imx_master mx51_3ds_ecspi2_pdata __initconst = { + .chipselect = mx51_3ds_spi2_cs, + .num_chipselect = ARRAY_SIZE(mx51_3ds_spi2_cs), +}; + +static struct spi_board_info mx51_3ds_spi_nor_device[] = { + { + .modalias = "m25p80", + .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 1, + .chip_select = 1, + .mode = SPI_MODE_0, + .platform_data = NULL,}, +}; + +/* + * Board specific initialization. + */ +static void __init mx51_3ds_init(void) +{ + imx51_soc_init(); + + mxc_iomux_v3_setup_multiple_pads(mx51_3ds_pads, + ARRAY_SIZE(mx51_3ds_pads)); + + imx51_add_imx_uart(0, &uart_pdata); + imx51_add_imx_uart(1, &uart_pdata); + imx51_add_imx_uart(2, &uart_pdata); + + imx51_add_ecspi(1, &mx51_3ds_ecspi2_pdata); + spi_register_board_info(mx51_3ds_spi_nor_device, + ARRAY_SIZE(mx51_3ds_spi_nor_device)); + + if (mxc_expio_init(MX51_CS5_BASE_ADDR, EXPIO_PARENT_INT)) + printk(KERN_WARNING "Init of the debugboard failed, all " + "devices on the board are unusable.\n"); + + imx51_add_sdhci_esdhc_imx(0, NULL); + imx51_add_imx_keypad(&mx51_3ds_map_data); + imx51_add_imx2_wdt(0, NULL); +} + +static void __init mx51_3ds_timer_init(void) +{ + mx51_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mx51_3ds_timer = { + .init = mx51_3ds_timer_init, +}; + +MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board") + /* Maintainer: Freescale Semiconductor, Inc. */ + .atag_offset = 0x100, + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .timer = &mx51_3ds_timer, + .init_machine = mx51_3ds_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx51_babbage.c b/arch/arm/mach-imx/mach-mx51_babbage.c new file mode 100644 index 0000000..5c83760 --- /dev/null +++ b/arch/arm/mach-imx/mach-mx51_babbage.c @@ -0,0 +1,429 @@ +/* + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009-2010 Amit Kucheria + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" +#include "cpu_op-mx51.h" + +#define BABBAGE_USB_HUB_RESET IMX_GPIO_NR(1, 7) +#define BABBAGE_USBH1_STP IMX_GPIO_NR(1, 27) +#define BABBAGE_USB_PHY_RESET IMX_GPIO_NR(2, 5) +#define BABBAGE_FEC_PHY_RESET IMX_GPIO_NR(2, 14) +#define BABBAGE_POWER_KEY IMX_GPIO_NR(2, 21) +#define BABBAGE_ECSPI1_CS0 IMX_GPIO_NR(4, 24) +#define BABBAGE_ECSPI1_CS1 IMX_GPIO_NR(4, 25) +#define BABBAGE_SD2_CD IMX_GPIO_NR(1, 6) +#define BABBAGE_SD2_WP IMX_GPIO_NR(1, 5) + +/* USB_CTRL_1 */ +#define MX51_USB_CTRL_1_OFFSET 0x10 +#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) + +#define MX51_USB_PLLDIV_12_MHZ 0x00 +#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 +#define MX51_USB_PLL_DIV_24_MHZ 0x02 + +static struct gpio_keys_button babbage_buttons[] = { + { + .gpio = BABBAGE_POWER_KEY, + .code = BTN_0, + .desc = "PWR", + .active_low = 1, + .wakeup = 1, + }, +}; + +static const struct gpio_keys_platform_data imx_button_data __initconst = { + .buttons = babbage_buttons, + .nbuttons = ARRAY_SIZE(babbage_buttons), +}; + +static iomux_v3_cfg_t mx51babbage_pads[] = { + /* UART1 */ + MX51_PAD_UART1_RXD__UART1_RXD, + MX51_PAD_UART1_TXD__UART1_TXD, + MX51_PAD_UART1_RTS__UART1_RTS, + MX51_PAD_UART1_CTS__UART1_CTS, + + /* UART2 */ + MX51_PAD_UART2_RXD__UART2_RXD, + MX51_PAD_UART2_TXD__UART2_TXD, + + /* UART3 */ + MX51_PAD_EIM_D25__UART3_RXD, + MX51_PAD_EIM_D26__UART3_TXD, + MX51_PAD_EIM_D27__UART3_RTS, + MX51_PAD_EIM_D24__UART3_CTS, + + /* I2C1 */ + MX51_PAD_EIM_D16__I2C1_SDA, + MX51_PAD_EIM_D19__I2C1_SCL, + + /* I2C2 */ + MX51_PAD_KEY_COL4__I2C2_SCL, + MX51_PAD_KEY_COL5__I2C2_SDA, + + /* HSI2C */ + MX51_PAD_I2C1_CLK__I2C1_CLK, + MX51_PAD_I2C1_DAT__I2C1_DAT, + + /* USB HOST1 */ + MX51_PAD_USBH1_CLK__USBH1_CLK, + MX51_PAD_USBH1_DIR__USBH1_DIR, + MX51_PAD_USBH1_NXT__USBH1_NXT, + MX51_PAD_USBH1_DATA0__USBH1_DATA0, + MX51_PAD_USBH1_DATA1__USBH1_DATA1, + MX51_PAD_USBH1_DATA2__USBH1_DATA2, + MX51_PAD_USBH1_DATA3__USBH1_DATA3, + MX51_PAD_USBH1_DATA4__USBH1_DATA4, + MX51_PAD_USBH1_DATA5__USBH1_DATA5, + MX51_PAD_USBH1_DATA6__USBH1_DATA6, + MX51_PAD_USBH1_DATA7__USBH1_DATA7, + + /* USB HUB reset line*/ + MX51_PAD_GPIO1_7__GPIO1_7, + + /* USB PHY reset line */ + MX51_PAD_EIM_D21__GPIO2_5, + + /* FEC */ + MX51_PAD_EIM_EB2__FEC_MDIO, + MX51_PAD_EIM_EB3__FEC_RDATA1, + MX51_PAD_EIM_CS2__FEC_RDATA2, + MX51_PAD_EIM_CS3__FEC_RDATA3, + MX51_PAD_EIM_CS4__FEC_RX_ER, + MX51_PAD_EIM_CS5__FEC_CRS, + MX51_PAD_NANDF_RB2__FEC_COL, + MX51_PAD_NANDF_RB3__FEC_RX_CLK, + MX51_PAD_NANDF_D9__FEC_RDATA0, + MX51_PAD_NANDF_D8__FEC_TDATA0, + MX51_PAD_NANDF_CS2__FEC_TX_ER, + MX51_PAD_NANDF_CS3__FEC_MDC, + MX51_PAD_NANDF_CS4__FEC_TDATA1, + MX51_PAD_NANDF_CS5__FEC_TDATA2, + MX51_PAD_NANDF_CS6__FEC_TDATA3, + MX51_PAD_NANDF_CS7__FEC_TX_EN, + MX51_PAD_NANDF_RDY_INT__FEC_TX_CLK, + + /* FEC PHY reset line */ + MX51_PAD_EIM_A20__GPIO2_14, + + /* SD 1 */ + MX51_PAD_SD1_CMD__SD1_CMD, + MX51_PAD_SD1_CLK__SD1_CLK, + MX51_PAD_SD1_DATA0__SD1_DATA0, + MX51_PAD_SD1_DATA1__SD1_DATA1, + MX51_PAD_SD1_DATA2__SD1_DATA2, + MX51_PAD_SD1_DATA3__SD1_DATA3, + /* CD/WP from controller */ + MX51_PAD_GPIO1_0__SD1_CD, + MX51_PAD_GPIO1_1__SD1_WP, + + /* SD 2 */ + MX51_PAD_SD2_CMD__SD2_CMD, + MX51_PAD_SD2_CLK__SD2_CLK, + MX51_PAD_SD2_DATA0__SD2_DATA0, + MX51_PAD_SD2_DATA1__SD2_DATA1, + MX51_PAD_SD2_DATA2__SD2_DATA2, + MX51_PAD_SD2_DATA3__SD2_DATA3, + /* CD/WP gpio */ + MX51_PAD_GPIO1_6__GPIO1_6, + MX51_PAD_GPIO1_5__GPIO1_5, + + /* eCSPI1 */ + MX51_PAD_CSPI1_MISO__ECSPI1_MISO, + MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, + MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, + MX51_PAD_CSPI1_SS0__GPIO4_24, + MX51_PAD_CSPI1_SS1__GPIO4_25, +}; + +/* Serial ports */ +static const struct imxuart_platform_data uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static const struct imxi2c_platform_data babbage_i2c_data __initconst = { + .bitrate = 100000, +}; + +static const struct imxi2c_platform_data babbage_hsi2c_data __initconst = { + .bitrate = 400000, +}; + +static struct gpio mx51_babbage_usbh1_gpios[] = { + { BABBAGE_USBH1_STP, GPIOF_OUT_INIT_LOW, "usbh1_stp" }, + { BABBAGE_USB_PHY_RESET, GPIOF_OUT_INIT_LOW, "usbh1_phy_reset" }, +}; + +static int gpio_usbh1_active(void) +{ + iomux_v3_cfg_t usbh1stp_gpio = MX51_PAD_USBH1_STP__GPIO1_27; + int ret; + + /* Set USBH1_STP to GPIO and toggle it */ + mxc_iomux_v3_setup_pad(usbh1stp_gpio); + ret = gpio_request_array(mx51_babbage_usbh1_gpios, + ARRAY_SIZE(mx51_babbage_usbh1_gpios)); + + if (ret) { + pr_debug("failed to get USBH1 pins: %d\n", ret); + return ret; + } + + msleep(100); + gpio_set_value(BABBAGE_USBH1_STP, 1); + gpio_set_value(BABBAGE_USB_PHY_RESET, 1); + gpio_free_array(mx51_babbage_usbh1_gpios, + ARRAY_SIZE(mx51_babbage_usbh1_gpios)); + return 0; +} + +static inline void babbage_usbhub_reset(void) +{ + int ret; + + /* Reset USB hub */ + ret = gpio_request_one(BABBAGE_USB_HUB_RESET, + GPIOF_OUT_INIT_LOW, "GPIO1_7"); + if (ret) { + printk(KERN_ERR"failed to get GPIO_USB_HUB_RESET: %d\n", ret); + return; + } + + msleep(2); + /* Deassert reset */ + gpio_set_value(BABBAGE_USB_HUB_RESET, 1); +} + +static inline void babbage_fec_reset(void) +{ + int ret; + + /* reset FEC PHY */ + ret = gpio_request_one(BABBAGE_FEC_PHY_RESET, + GPIOF_OUT_INIT_LOW, "fec-phy-reset"); + if (ret) { + printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); + return; + } + msleep(1); + gpio_set_value(BABBAGE_FEC_PHY_RESET, 1); +} + +/* This function is board specific as the bit mask for the plldiv will also +be different for other Freescale SoCs, thus a common bitmask is not +possible and cannot get place in /plat-mxc/ehci.c.*/ +static int initialize_otg_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + /* Set the PHY clock to 19.2MHz */ + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; + v |= MX51_USB_PLL_DIV_19_2_MHZ; + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(0, MXC_EHCI_INTERNAL_PHY); +} + +static int initialize_usbh1_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + + /* The clock for the USBH1 ULPI port will come externally from the PHY. */ + v = __raw_readl(usbother_base + MX51_USB_CTRL_1_OFFSET); + __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + MX51_USB_CTRL_1_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(1, MXC_EHCI_POWER_PINS_ENABLED | + MXC_EHCI_ITC_NO_THRESHOLD); +} + +static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { + .init = initialize_otg_port, + .portsc = MXC_EHCI_UTMI_16BIT, +}; + +static const struct fsl_usb2_platform_data usb_pdata __initconst = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_UTMI_WIDE, +}; + +static const struct mxc_usbh_platform_data usbh1_config __initconst = { + .init = initialize_usbh1_port, + .portsc = MXC_EHCI_MODE_ULPI, +}; + +static int otg_mode_host; + +static int __init babbage_otg_mode(char *options) +{ + if (!strcmp(options, "host")) + otg_mode_host = 1; + else if (!strcmp(options, "device")) + otg_mode_host = 0; + else + pr_info("otg_mode neither \"host\" nor \"device\". " + "Defaulting to device\n"); + return 0; +} +__setup("otg_mode=", babbage_otg_mode); + +static struct spi_board_info mx51_babbage_spi_board_info[] __initdata = { + { + .modalias = "mtd_dataflash", + .max_speed_hz = 25000000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + .platform_data = NULL, + }, +}; + +static int mx51_babbage_spi_cs[] = { + BABBAGE_ECSPI1_CS0, + BABBAGE_ECSPI1_CS1, +}; + +static const struct spi_imx_master mx51_babbage_spi_pdata __initconst = { + .chipselect = mx51_babbage_spi_cs, + .num_chipselect = ARRAY_SIZE(mx51_babbage_spi_cs), +}; + +static const struct esdhc_platform_data mx51_babbage_sd1_data __initconst = { + .cd_type = ESDHC_CD_CONTROLLER, + .wp_type = ESDHC_WP_CONTROLLER, +}; + +static const struct esdhc_platform_data mx51_babbage_sd2_data __initconst = { + .cd_gpio = BABBAGE_SD2_CD, + .wp_gpio = BABBAGE_SD2_WP, + .cd_type = ESDHC_CD_GPIO, + .wp_type = ESDHC_WP_GPIO, +}; + +void __init imx51_babbage_common_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx51babbage_pads, + ARRAY_SIZE(mx51babbage_pads)); +} + +/* + * Board specific initialization. + */ +static void __init mx51_babbage_init(void) +{ + iomux_v3_cfg_t usbh1stp = MX51_PAD_USBH1_STP__USBH1_STP; + iomux_v3_cfg_t power_key = NEW_PAD_CTRL(MX51_PAD_EIM_A27__GPIO2_21, + PAD_CTL_SRE_FAST | PAD_CTL_DSE_HIGH | PAD_CTL_PUS_100K_UP); + + imx51_soc_init(); + +#if defined(CONFIG_CPU_FREQ_IMX) + get_cpu_op = mx51_get_cpu_op; +#endif + imx51_babbage_common_init(); + + imx51_add_imx_uart(0, &uart_pdata); + imx51_add_imx_uart(1, NULL); + imx51_add_imx_uart(2, &uart_pdata); + + babbage_fec_reset(); + imx51_add_fec(NULL); + + /* Set the PAD settings for the pwr key. */ + mxc_iomux_v3_setup_pad(power_key); + imx_add_gpio_keys(&imx_button_data); + + imx51_add_imx_i2c(0, &babbage_i2c_data); + imx51_add_imx_i2c(1, &babbage_i2c_data); + imx51_add_hsi2c(&babbage_hsi2c_data); + + if (otg_mode_host) + imx51_add_mxc_ehci_otg(&dr_utmi_config); + else { + initialize_otg_port(NULL); + imx51_add_fsl_usb2_udc(&usb_pdata); + } + + gpio_usbh1_active(); + imx51_add_mxc_ehci_hs(1, &usbh1_config); + /* setback USBH1_STP to be function */ + mxc_iomux_v3_setup_pad(usbh1stp); + babbage_usbhub_reset(); + + imx51_add_sdhci_esdhc_imx(0, &mx51_babbage_sd1_data); + imx51_add_sdhci_esdhc_imx(1, &mx51_babbage_sd2_data); + + spi_register_board_info(mx51_babbage_spi_board_info, + ARRAY_SIZE(mx51_babbage_spi_board_info)); + imx51_add_ecspi(0, &mx51_babbage_spi_pdata); + imx51_add_imx2_wdt(0, NULL); +} + +static void __init mx51_babbage_timer_init(void) +{ + mx51_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mx51_babbage_timer = { + .init = mx51_babbage_timer_init, +}; + +MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board") + /* Maintainer: Amit Kucheria */ + .atag_offset = 0x100, + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .timer = &mx51_babbage_timer, + .init_machine = mx51_babbage_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx51_efikamx.c b/arch/arm/mach-imx/mach-mx51_efikamx.c new file mode 100644 index 0000000..a9e4866 --- /dev/null +++ b/arch/arm/mach-imx/mach-mx51_efikamx.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010 Linaro Limited + * + * based on code from the following + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2010 Pegatron Corporation. All Rights Reserved. + * Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" +#include "efika.h" + +#define EFIKAMX_PCBID0 IMX_GPIO_NR(3, 16) +#define EFIKAMX_PCBID1 IMX_GPIO_NR(3, 17) +#define EFIKAMX_PCBID2 IMX_GPIO_NR(3, 11) + +#define EFIKAMX_BLUE_LED IMX_GPIO_NR(3, 13) +#define EFIKAMX_GREEN_LED IMX_GPIO_NR(3, 14) +#define EFIKAMX_RED_LED IMX_GPIO_NR(3, 15) + +#define EFIKAMX_POWER_KEY IMX_GPIO_NR(2, 31) + +/* board 1.1 doesn't have same reset gpio */ +#define EFIKAMX_RESET1_1 IMX_GPIO_NR(3, 2) +#define EFIKAMX_RESET IMX_GPIO_NR(1, 4) + +#define EFIKAMX_POWEROFF IMX_GPIO_NR(4, 13) + +#define EFIKAMX_PMIC IMX_GPIO_NR(1, 6) + +/* the pci ids pin have pull up. they're driven low according to board id */ +#define MX51_PAD_PCBID0 IOMUX_PAD(0x518, 0x130, 3, 0x0, 0, PAD_CTL_PUS_100K_UP) +#define MX51_PAD_PCBID1 IOMUX_PAD(0x51C, 0x134, 3, 0x0, 0, PAD_CTL_PUS_100K_UP) +#define MX51_PAD_PCBID2 IOMUX_PAD(0x504, 0x128, 3, 0x0, 0, PAD_CTL_PUS_100K_UP) +#define MX51_PAD_PWRKEY IOMUX_PAD(0x48c, 0x0f8, 1, 0x0, 0, PAD_CTL_PUS_100K_UP | PAD_CTL_PKE) + +static iomux_v3_cfg_t mx51efikamx_pads[] = { + /* board id */ + MX51_PAD_PCBID0, + MX51_PAD_PCBID1, + MX51_PAD_PCBID2, + + /* leds */ + MX51_PAD_CSI1_D9__GPIO3_13, + MX51_PAD_CSI1_VSYNC__GPIO3_14, + MX51_PAD_CSI1_HSYNC__GPIO3_15, + + /* power key */ + MX51_PAD_PWRKEY, + + /* reset */ + MX51_PAD_DI1_PIN13__GPIO3_2, + MX51_PAD_GPIO1_4__GPIO1_4, + + /* power off */ + MX51_PAD_CSI2_VSYNC__GPIO4_13, +}; + +/* PCBID2 PCBID1 PCBID0 STATE + 1 1 1 ER1:rev1.1 + 1 1 0 ER2:rev1.2 + 1 0 1 ER3:rev1.3 + 1 0 0 ER4:rev1.4 +*/ +static void __init mx51_efikamx_board_id(void) +{ + int id; + + /* things are taking time to settle */ + msleep(150); + + gpio_request(EFIKAMX_PCBID0, "pcbid0"); + gpio_direction_input(EFIKAMX_PCBID0); + gpio_request(EFIKAMX_PCBID1, "pcbid1"); + gpio_direction_input(EFIKAMX_PCBID1); + gpio_request(EFIKAMX_PCBID2, "pcbid2"); + gpio_direction_input(EFIKAMX_PCBID2); + + id = gpio_get_value(EFIKAMX_PCBID0) ? 1 : 0; + id |= (gpio_get_value(EFIKAMX_PCBID1) ? 1 : 0) << 1; + id |= (gpio_get_value(EFIKAMX_PCBID2) ? 1 : 0) << 2; + + switch (id) { + case 7: + system_rev = 0x11; + break; + case 6: + system_rev = 0x12; + break; + case 5: + system_rev = 0x13; + break; + case 4: + system_rev = 0x14; + break; + default: + system_rev = 0x10; + break; + } + + if ((system_rev == 0x10) + || (system_rev == 0x12) + || (system_rev == 0x14)) { + printk(KERN_WARNING + "EfikaMX: Unsupported board revision 1.%u!\n", + system_rev & 0xf); + } +} + +static struct gpio_led mx51_efikamx_leds[] __initdata = { + { + .name = "efikamx:green", + .default_trigger = "default-on", + .gpio = EFIKAMX_GREEN_LED, + }, + { + .name = "efikamx:red", + .default_trigger = "ide-disk", + .gpio = EFIKAMX_RED_LED, + }, + { + .name = "efikamx:blue", + .default_trigger = "mmc0", + .gpio = EFIKAMX_BLUE_LED, + }, +}; + +static const struct gpio_led_platform_data + mx51_efikamx_leds_data __initconst = { + .leds = mx51_efikamx_leds, + .num_leds = ARRAY_SIZE(mx51_efikamx_leds), +}; + +static struct esdhc_platform_data sd_pdata = { + .cd_type = ESDHC_CD_CONTROLLER, + .wp_type = ESDHC_WP_CONTROLLER, +}; + +static struct gpio_keys_button mx51_efikamx_powerkey[] = { + { + .code = KEY_POWER, + .gpio = EFIKAMX_POWER_KEY, + .type = EV_PWR, + .desc = "Power Button (CM)", + .wakeup = 1, + .debounce_interval = 10, /* ms */ + }, +}; + +static const struct gpio_keys_platform_data mx51_efikamx_powerkey_data __initconst = { + .buttons = mx51_efikamx_powerkey, + .nbuttons = ARRAY_SIZE(mx51_efikamx_powerkey), +}; + +void mx51_efikamx_reset(void) +{ + if (system_rev == 0x11) + gpio_direction_output(EFIKAMX_RESET1_1, 0); + else + gpio_direction_output(EFIKAMX_RESET, 0); +} + +static struct regulator *pwgt1, *pwgt2, *coincell; + +static void mx51_efikamx_power_off(void) +{ + if (!IS_ERR(coincell)) + regulator_disable(coincell); + + if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { + regulator_disable(pwgt2); + regulator_disable(pwgt1); + } + gpio_direction_output(EFIKAMX_POWEROFF, 1); +} + +static int __init mx51_efikamx_power_init(void) +{ + if (machine_is_mx51_efikamx()) { + pwgt1 = regulator_get(NULL, "pwgt1"); + pwgt2 = regulator_get(NULL, "pwgt2"); + if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { + regulator_enable(pwgt1); + regulator_enable(pwgt2); + } + gpio_request(EFIKAMX_POWEROFF, "poweroff"); + pm_power_off = mx51_efikamx_power_off; + + /* enable coincell charger. maybe need a small power driver ? */ + coincell = regulator_get(NULL, "coincell"); + if (!IS_ERR(coincell)) { + regulator_set_voltage(coincell, 3000000, 3000000); + regulator_enable(coincell); + } + + regulator_has_full_constraints(); + } + + return 0; +} +late_initcall(mx51_efikamx_power_init); + +static void __init mx51_efikamx_init(void) +{ + imx51_soc_init(); + + mxc_iomux_v3_setup_multiple_pads(mx51efikamx_pads, + ARRAY_SIZE(mx51efikamx_pads)); + efika_board_common_init(); + + mx51_efikamx_board_id(); + + /* on < 1.2 boards both SD controllers are used */ + if (system_rev < 0x12) { + imx51_add_sdhci_esdhc_imx(0, NULL); + imx51_add_sdhci_esdhc_imx(1, &sd_pdata); + mx51_efikamx_leds[2].default_trigger = "mmc1"; + } else + imx51_add_sdhci_esdhc_imx(0, &sd_pdata); + + gpio_led_register_device(-1, &mx51_efikamx_leds_data); + imx_add_gpio_keys(&mx51_efikamx_powerkey_data); + + if (system_rev == 0x11) { + gpio_request(EFIKAMX_RESET1_1, "reset"); + gpio_direction_output(EFIKAMX_RESET1_1, 1); + } else { + gpio_request(EFIKAMX_RESET, "reset"); + gpio_direction_output(EFIKAMX_RESET, 1); + } + + /* + * enable wifi by default only on mx + * sb and mx have same wlan pin but the value to enable it are + * different :/ + */ + gpio_request(EFIKA_WLAN_EN, "wlan_en"); + gpio_direction_output(EFIKA_WLAN_EN, 0); + msleep(10); + + gpio_request(EFIKA_WLAN_RESET, "wlan_rst"); + gpio_direction_output(EFIKA_WLAN_RESET, 0); + msleep(10); + gpio_set_value(EFIKA_WLAN_RESET, 1); +} + +static void __init mx51_efikamx_timer_init(void) +{ + mx51_clocks_init(32768, 24000000, 22579200, 24576000); +} + +static struct sys_timer mx51_efikamx_timer = { + .init = mx51_efikamx_timer_init, +}; + +MACHINE_START(MX51_EFIKAMX, "Genesi EfikaMX nettop") + /* Maintainer: Amit Kucheria */ + .atag_offset = 0x100, + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .timer = &mx51_efikamx_timer, + .init_machine = mx51_efikamx_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx51_efikasb.c b/arch/arm/mach-imx/mach-mx51_efikasb.c new file mode 100644 index 0000000..38c4a3e --- /dev/null +++ b/arch/arm/mach-imx/mach-mx51_efikasb.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) Arnaud Patard + * + * based on code from the following + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2010 Pegatron Corporation. All Rights Reserved. + * Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" +#include "efika.h" + +#define EFIKASB_USBH2_STP IMX_GPIO_NR(2, 20) +#define EFIKASB_GREEN_LED IMX_GPIO_NR(1, 3) +#define EFIKASB_WHITE_LED IMX_GPIO_NR(2, 25) +#define EFIKASB_PCBID0 IMX_GPIO_NR(2, 28) +#define EFIKASB_PCBID1 IMX_GPIO_NR(2, 29) +#define EFIKASB_PWRKEY IMX_GPIO_NR(2, 31) +#define EFIKASB_LID IMX_GPIO_NR(3, 14) +#define EFIKASB_POWEROFF IMX_GPIO_NR(4, 13) +#define EFIKASB_RFKILL IMX_GPIO_NR(3, 1) + +#define MX51_PAD_PWRKEY IOMUX_PAD(0x48c, 0x0f8, 1, 0x0, 0, PAD_CTL_PUS_100K_UP | PAD_CTL_PKE) +#define MX51_PAD_SD1_CD IOMUX_PAD(0x47c, 0x0e8, 1, __NA_, 0, MX51_ESDHC_PAD_CTRL) + +static iomux_v3_cfg_t mx51efikasb_pads[] = { + /* USB HOST2 */ + MX51_PAD_EIM_D16__USBH2_DATA0, + MX51_PAD_EIM_D17__USBH2_DATA1, + MX51_PAD_EIM_D18__USBH2_DATA2, + MX51_PAD_EIM_D19__USBH2_DATA3, + MX51_PAD_EIM_D20__USBH2_DATA4, + MX51_PAD_EIM_D21__USBH2_DATA5, + MX51_PAD_EIM_D22__USBH2_DATA6, + MX51_PAD_EIM_D23__USBH2_DATA7, + MX51_PAD_EIM_A24__USBH2_CLK, + MX51_PAD_EIM_A25__USBH2_DIR, + MX51_PAD_EIM_A26__USBH2_STP, + MX51_PAD_EIM_A27__USBH2_NXT, + + /* leds */ + MX51_PAD_EIM_CS0__GPIO2_25, + MX51_PAD_GPIO1_3__GPIO1_3, + + /* pcb id */ + MX51_PAD_EIM_CS3__GPIO2_28, + MX51_PAD_EIM_CS4__GPIO2_29, + + /* lid */ + MX51_PAD_CSI1_VSYNC__GPIO3_14, + + /* power key*/ + MX51_PAD_PWRKEY, + + /* wifi/bt button */ + MX51_PAD_DI1_PIN12__GPIO3_1, + + /* power off */ + MX51_PAD_CSI2_VSYNC__GPIO4_13, + + /* wdog reset */ + MX51_PAD_GPIO1_4__WDOG1_WDOG_B, + + /* BT */ + MX51_PAD_EIM_A17__GPIO2_11, + + MX51_PAD_SD1_CD, +}; + +static int initialize_usbh2_port(struct platform_device *pdev) +{ + iomux_v3_cfg_t usbh2stp = MX51_PAD_EIM_A26__USBH2_STP; + iomux_v3_cfg_t usbh2gpio = MX51_PAD_EIM_A26__GPIO2_20; + + mxc_iomux_v3_setup_pad(usbh2gpio); + gpio_request(EFIKASB_USBH2_STP, "usbh2_stp"); + gpio_direction_output(EFIKASB_USBH2_STP, 0); + msleep(1); + gpio_set_value(EFIKASB_USBH2_STP, 1); + msleep(1); + + gpio_free(EFIKASB_USBH2_STP); + mxc_iomux_v3_setup_pad(usbh2stp); + + mdelay(10); + + return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_ITC_NO_THRESHOLD); +} + +static struct mxc_usbh_platform_data usbh2_config __initdata = { + .init = initialize_usbh2_port, + .portsc = MXC_EHCI_MODE_ULPI, +}; + +static void __init mx51_efikasb_usb(void) +{ + usbh2_config.otg = imx_otg_ulpi_create(ULPI_OTG_DRVVBUS | + ULPI_OTG_DRVVBUS_EXT | ULPI_OTG_EXTVBUSIND); + if (usbh2_config.otg) + imx51_add_mxc_ehci_hs(2, &usbh2_config); +} + +static const struct gpio_led mx51_efikasb_leds[] __initconst = { + { + .name = "efikasb:green", + .default_trigger = "default-on", + .gpio = EFIKASB_GREEN_LED, + .active_low = 1, + }, + { + .name = "efikasb:white", + .default_trigger = "caps", + .gpio = EFIKASB_WHITE_LED, + }, +}; + +static const struct gpio_led_platform_data + mx51_efikasb_leds_data __initconst = { + .leds = mx51_efikasb_leds, + .num_leds = ARRAY_SIZE(mx51_efikasb_leds), +}; + +static struct gpio_keys_button mx51_efikasb_keys[] = { + { + .code = KEY_POWER, + .gpio = EFIKASB_PWRKEY, + .type = EV_KEY, + .desc = "Power Button", + .wakeup = 1, + .active_low = 1, + }, + { + .code = SW_LID, + .gpio = EFIKASB_LID, + .type = EV_SW, + .desc = "Lid Switch", + .active_low = 1, + }, + { + .code = KEY_RFKILL, + .gpio = EFIKASB_RFKILL, + .type = EV_KEY, + .desc = "rfkill", + .active_low = 1, + }, +}; + +static const struct gpio_keys_platform_data mx51_efikasb_keys_data __initconst = { + .buttons = mx51_efikasb_keys, + .nbuttons = ARRAY_SIZE(mx51_efikasb_keys), +}; + +static struct esdhc_platform_data sd0_pdata = { +#define EFIKASB_SD1_CD IMX_GPIO_NR(2, 27) + .cd_gpio = EFIKASB_SD1_CD, + .cd_type = ESDHC_CD_GPIO, + .wp_type = ESDHC_WP_CONTROLLER, +}; + +static struct esdhc_platform_data sd1_pdata = { + .cd_type = ESDHC_CD_CONTROLLER, + .wp_type = ESDHC_WP_CONTROLLER, +}; + +static struct regulator *pwgt1, *pwgt2; + +static void mx51_efikasb_power_off(void) +{ + gpio_set_value(EFIKA_USB_PHY_RESET, 0); + + if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { + regulator_disable(pwgt2); + regulator_disable(pwgt1); + } + gpio_direction_output(EFIKASB_POWEROFF, 1); +} + +static int __init mx51_efikasb_power_init(void) +{ + if (machine_is_mx51_efikasb()) { + pwgt1 = regulator_get(NULL, "pwgt1"); + pwgt2 = regulator_get(NULL, "pwgt2"); + if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { + regulator_enable(pwgt1); + regulator_enable(pwgt2); + } + gpio_request(EFIKASB_POWEROFF, "poweroff"); + pm_power_off = mx51_efikasb_power_off; + + regulator_has_full_constraints(); + } + + return 0; +} +late_initcall(mx51_efikasb_power_init); + +/* 01 R1.3 board + 10 R2.0 board */ +static void __init mx51_efikasb_board_id(void) +{ + int id; + + gpio_request(EFIKASB_PCBID0, "pcb id0"); + gpio_direction_input(EFIKASB_PCBID0); + gpio_request(EFIKASB_PCBID1, "pcb id1"); + gpio_direction_input(EFIKASB_PCBID1); + + id = gpio_get_value(EFIKASB_PCBID0) ? 1 : 0; + id |= (gpio_get_value(EFIKASB_PCBID1) ? 1 : 0) << 1; + + switch (id) { + default: + break; + case 1: + system_rev = 0x13; + break; + case 2: + system_rev = 0x20; + break; + } +} + +static void __init efikasb_board_init(void) +{ + imx51_soc_init(); + + mxc_iomux_v3_setup_multiple_pads(mx51efikasb_pads, + ARRAY_SIZE(mx51efikasb_pads)); + efika_board_common_init(); + + mx51_efikasb_board_id(); + mx51_efikasb_usb(); + imx51_add_sdhci_esdhc_imx(0, &sd0_pdata); + imx51_add_sdhci_esdhc_imx(1, &sd1_pdata); + + gpio_led_register_device(-1, &mx51_efikasb_leds_data); + imx_add_gpio_keys(&mx51_efikasb_keys_data); +} + +static void __init mx51_efikasb_timer_init(void) +{ + mx51_clocks_init(32768, 24000000, 22579200, 24576000); +} + +static struct sys_timer mx51_efikasb_timer = { + .init = mx51_efikasb_timer_init, +}; + +MACHINE_START(MX51_EFIKASB, "Genesi Efika Smartbook") + .atag_offset = 0x100, + .map_io = mx51_map_io, + .init_early = imx51_init_early, + .init_irq = mx51_init_irq, + .handle_irq = imx51_handle_irq, + .init_machine = efikasb_board_init, + .timer = &mx51_efikasb_timer, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx53_ard.c b/arch/arm/mach-imx/mach-mx53_ard.c new file mode 100644 index 0000000..b88a2bc --- /dev/null +++ b/arch/arm/mach-imx/mach-mx53_ard.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "devices-imx53.h" + +#define ARD_ETHERNET_INT_B IMX_GPIO_NR(2, 31) +#define ARD_SD1_CD IMX_GPIO_NR(1, 1) +#define ARD_SD1_WP IMX_GPIO_NR(1, 9) +#define ARD_I2CPORTEXP_B IMX_GPIO_NR(2, 3) +#define ARD_VOLUMEDOWN IMX_GPIO_NR(4, 0) +#define ARD_HOME IMX_GPIO_NR(5, 10) +#define ARD_BACK IMX_GPIO_NR(5, 11) +#define ARD_PROG IMX_GPIO_NR(5, 12) +#define ARD_VOLUMEUP IMX_GPIO_NR(5, 13) + +static iomux_v3_cfg_t mx53_ard_pads[] = { + /* UART1 */ + MX53_PAD_PATA_DIOW__UART1_TXD_MUX, + MX53_PAD_PATA_DMACK__UART1_RXD_MUX, + /* WEIM for CS1 */ + MX53_PAD_EIM_EB3__GPIO2_31, /* ETHERNET_INT_B */ + MX53_PAD_EIM_D16__EMI_WEIM_D_16, + MX53_PAD_EIM_D17__EMI_WEIM_D_17, + MX53_PAD_EIM_D18__EMI_WEIM_D_18, + MX53_PAD_EIM_D19__EMI_WEIM_D_19, + MX53_PAD_EIM_D20__EMI_WEIM_D_20, + MX53_PAD_EIM_D21__EMI_WEIM_D_21, + MX53_PAD_EIM_D22__EMI_WEIM_D_22, + MX53_PAD_EIM_D23__EMI_WEIM_D_23, + MX53_PAD_EIM_D24__EMI_WEIM_D_24, + MX53_PAD_EIM_D25__EMI_WEIM_D_25, + MX53_PAD_EIM_D26__EMI_WEIM_D_26, + MX53_PAD_EIM_D27__EMI_WEIM_D_27, + MX53_PAD_EIM_D28__EMI_WEIM_D_28, + MX53_PAD_EIM_D29__EMI_WEIM_D_29, + MX53_PAD_EIM_D30__EMI_WEIM_D_30, + MX53_PAD_EIM_D31__EMI_WEIM_D_31, + MX53_PAD_EIM_DA0__EMI_NAND_WEIM_DA_0, + MX53_PAD_EIM_DA1__EMI_NAND_WEIM_DA_1, + MX53_PAD_EIM_DA2__EMI_NAND_WEIM_DA_2, + MX53_PAD_EIM_DA3__EMI_NAND_WEIM_DA_3, + MX53_PAD_EIM_DA4__EMI_NAND_WEIM_DA_4, + MX53_PAD_EIM_DA5__EMI_NAND_WEIM_DA_5, + MX53_PAD_EIM_DA6__EMI_NAND_WEIM_DA_6, + MX53_PAD_EIM_OE__EMI_WEIM_OE, + MX53_PAD_EIM_RW__EMI_WEIM_RW, + MX53_PAD_EIM_CS1__EMI_WEIM_CS_1, + /* SDHC1 */ + MX53_PAD_SD1_CMD__ESDHC1_CMD, + MX53_PAD_SD1_CLK__ESDHC1_CLK, + MX53_PAD_SD1_DATA0__ESDHC1_DAT0, + MX53_PAD_SD1_DATA1__ESDHC1_DAT1, + MX53_PAD_SD1_DATA2__ESDHC1_DAT2, + MX53_PAD_SD1_DATA3__ESDHC1_DAT3, + MX53_PAD_PATA_DATA8__ESDHC1_DAT4, + MX53_PAD_PATA_DATA9__ESDHC1_DAT5, + MX53_PAD_PATA_DATA10__ESDHC1_DAT6, + MX53_PAD_PATA_DATA11__ESDHC1_DAT7, + MX53_PAD_GPIO_1__GPIO1_1, + MX53_PAD_GPIO_9__GPIO1_9, + /* I2C2 */ + MX53_PAD_EIM_EB2__I2C2_SCL, + MX53_PAD_KEY_ROW3__I2C2_SDA, + /* I2C3 */ + MX53_PAD_GPIO_3__I2C3_SCL, + MX53_PAD_GPIO_16__I2C3_SDA, + /* GPIO */ + MX53_PAD_DISP0_DAT16__GPIO5_10, /* home */ + MX53_PAD_DISP0_DAT17__GPIO5_11, /* back */ + MX53_PAD_DISP0_DAT18__GPIO5_12, /* prog */ + MX53_PAD_DISP0_DAT19__GPIO5_13, /* vol up */ + MX53_PAD_GPIO_10__GPIO4_0, /* vol down */ +}; + +#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake) \ +{ \ + .gpio = gpio_num, \ + .type = EV_KEY, \ + .code = ev_code, \ + .active_low = act_low, \ + .desc = "btn " descr, \ + .wakeup = wake, \ +} + +static struct gpio_keys_button ard_buttons[] = { + GPIO_BUTTON(ARD_HOME, KEY_HOME, 1, "home", 0), + GPIO_BUTTON(ARD_BACK, KEY_BACK, 1, "back", 0), + GPIO_BUTTON(ARD_PROG, KEY_PROGRAM, 1, "program", 0), + GPIO_BUTTON(ARD_VOLUMEUP, KEY_VOLUMEUP, 1, "volume-up", 0), + GPIO_BUTTON(ARD_VOLUMEDOWN, KEY_VOLUMEDOWN, 1, "volume-down", 0), +}; + +static const struct gpio_keys_platform_data ard_button_data __initconst = { + .buttons = ard_buttons, + .nbuttons = ARRAY_SIZE(ard_buttons), +}; + +static struct resource ard_smsc911x_resources[] = { + { + .start = MX53_CS1_64MB_BASE_ADDR, + .end = MX53_CS1_64MB_BASE_ADDR + SZ_32M - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = IMX_GPIO_TO_IRQ(ARD_ETHERNET_INT_B), + .end = IMX_GPIO_TO_IRQ(ARD_ETHERNET_INT_B), + .flags = IORESOURCE_IRQ, + }, +}; + +struct smsc911x_platform_config ard_smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_32BIT, +}; + +static struct platform_device ard_smsc_lan9220_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(ard_smsc911x_resources), + .resource = ard_smsc911x_resources, + .dev = { + .platform_data = &ard_smsc911x_config, + }, +}; + +static const struct esdhc_platform_data mx53_ard_sd1_data __initconst = { + .cd_gpio = ARD_SD1_CD, + .wp_gpio = ARD_SD1_WP, +}; + +static struct imxi2c_platform_data mx53_ard_i2c2_data = { + .bitrate = 50000, +}; + +static struct imxi2c_platform_data mx53_ard_i2c3_data = { + .bitrate = 400000, +}; + +static void __init mx53_ard_io_init(void) +{ + gpio_request(ARD_ETHERNET_INT_B, "eth-int-b"); + gpio_direction_input(ARD_ETHERNET_INT_B); + + gpio_request(ARD_I2CPORTEXP_B, "i2cptexp-rst"); + gpio_direction_output(ARD_I2CPORTEXP_B, 1); +} + +/* Config CS1 settings for ethernet controller */ +static int weim_cs_config(void) +{ + u32 reg; + void __iomem *weim_base, *iomuxc_base; + + weim_base = ioremap(MX53_WEIM_BASE_ADDR, SZ_4K); + if (!weim_base) + return -ENOMEM; + + iomuxc_base = ioremap(MX53_IOMUXC_BASE_ADDR, SZ_4K); + if (!iomuxc_base) + return -ENOMEM; + + /* CS1 timings for LAN9220 */ + writel(0x20001, (weim_base + 0x18)); + writel(0x0, (weim_base + 0x1C)); + writel(0x16000202, (weim_base + 0x20)); + writel(0x00000002, (weim_base + 0x24)); + writel(0x16002082, (weim_base + 0x28)); + writel(0x00000000, (weim_base + 0x2C)); + writel(0x00000000, (weim_base + 0x90)); + + /* specify 64 MB on CS1 and CS0 on GPR1 */ + reg = readl(iomuxc_base + 0x4); + reg &= ~0x3F; + reg |= 0x1B; + writel(reg, (iomuxc_base + 0x4)); + + iounmap(iomuxc_base); + iounmap(weim_base); + + return 0; +} + +void __init imx53_ard_common_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx53_ard_pads, + ARRAY_SIZE(mx53_ard_pads)); + weim_cs_config(); +} + +static struct platform_device *devices[] __initdata = { + &ard_smsc_lan9220_device, +}; + +static void __init mx53_ard_board_init(void) +{ + imx53_soc_init(); + imx53_add_imx_uart(0, NULL); + + imx53_ard_common_init(); + mx53_ard_io_init(); + platform_add_devices(devices, ARRAY_SIZE(devices)); + + imx53_add_sdhci_esdhc_imx(0, &mx53_ard_sd1_data); + imx53_add_imx2_wdt(0, NULL); + imx53_add_imx_i2c(1, &mx53_ard_i2c2_data); + imx53_add_imx_i2c(2, &mx53_ard_i2c3_data); + imx_add_gpio_keys(&ard_button_data); + imx53_add_ahci_imx(); +} + +static void __init mx53_ard_timer_init(void) +{ + mx53_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mx53_ard_timer = { + .init = mx53_ard_timer_init, +}; + +MACHINE_START(MX53_ARD, "Freescale MX53 ARD Board") + .map_io = mx53_map_io, + .init_early = imx53_init_early, + .init_irq = mx53_init_irq, + .handle_irq = imx53_handle_irq, + .timer = &mx53_ard_timer, + .init_machine = mx53_ard_board_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx53_evk.c b/arch/arm/mach-imx/mach-mx53_evk.c new file mode 100644 index 0000000..c69413d --- /dev/null +++ b/arch/arm/mach-imx/mach-mx53_evk.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2010 Yong Shen. + */ + +/* + * 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. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MX53_EVK_FEC_PHY_RST IMX_GPIO_NR(7, 6) +#define EVK_ECSPI1_CS0 IMX_GPIO_NR(2, 30) +#define EVK_ECSPI1_CS1 IMX_GPIO_NR(3, 19) +#define MX53EVK_LED IMX_GPIO_NR(7, 7) + +#include "devices-imx53.h" + +static iomux_v3_cfg_t mx53_evk_pads[] = { + MX53_PAD_CSI0_DAT10__UART1_TXD_MUX, + MX53_PAD_CSI0_DAT11__UART1_RXD_MUX, + + MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX, + MX53_PAD_PATA_DMARQ__UART2_TXD_MUX, + MX53_PAD_PATA_DIOR__UART2_RTS, + MX53_PAD_PATA_INTRQ__UART2_CTS, + + MX53_PAD_PATA_CS_0__UART3_TXD_MUX, + MX53_PAD_PATA_CS_1__UART3_RXD_MUX, + + MX53_PAD_EIM_D16__ECSPI1_SCLK, + MX53_PAD_EIM_D17__ECSPI1_MISO, + MX53_PAD_EIM_D18__ECSPI1_MOSI, + + /* ecspi chip select lines */ + MX53_PAD_EIM_EB2__GPIO2_30, + MX53_PAD_EIM_D19__GPIO3_19, + /* LED */ + MX53_PAD_PATA_DA_1__GPIO7_7, +}; + +static const struct imxuart_platform_data mx53_evk_uart_pdata __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static const struct gpio_led mx53evk_leds[] __initconst = { + { + .name = "green", + .default_trigger = "heartbeat", + .gpio = MX53EVK_LED, + }, +}; + +static const struct gpio_led_platform_data mx53evk_leds_data __initconst = { + .leds = mx53evk_leds, + .num_leds = ARRAY_SIZE(mx53evk_leds), +}; + +static inline void mx53_evk_init_uart(void) +{ + imx53_add_imx_uart(0, NULL); + imx53_add_imx_uart(1, &mx53_evk_uart_pdata); + imx53_add_imx_uart(2, NULL); +} + +static const struct imxi2c_platform_data mx53_evk_i2c_data __initconst = { + .bitrate = 100000, +}; + +static inline void mx53_evk_fec_reset(void) +{ + int ret; + + /* reset FEC PHY */ + ret = gpio_request_one(MX53_EVK_FEC_PHY_RST, GPIOF_OUT_INIT_LOW, + "fec-phy-reset"); + if (ret) { + printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); + return; + } + msleep(1); + gpio_set_value(MX53_EVK_FEC_PHY_RST, 1); +} + +static struct fec_platform_data mx53_evk_fec_pdata = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +static struct spi_board_info mx53_evk_spi_board_info[] __initdata = { + { + .modalias = "mtd_dataflash", + .max_speed_hz = 25000000, + .bus_num = 0, + .chip_select = 1, + .mode = SPI_MODE_0, + .platform_data = NULL, + }, +}; + +static int mx53_evk_spi_cs[] = { + EVK_ECSPI1_CS0, + EVK_ECSPI1_CS1, +}; + +static const struct spi_imx_master mx53_evk_spi_data __initconst = { + .chipselect = mx53_evk_spi_cs, + .num_chipselect = ARRAY_SIZE(mx53_evk_spi_cs), +}; + +void __init imx53_evk_common_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx53_evk_pads, + ARRAY_SIZE(mx53_evk_pads)); +} + +static void __init mx53_evk_board_init(void) +{ + imx53_soc_init(); + imx53_evk_common_init(); + + mx53_evk_init_uart(); + mx53_evk_fec_reset(); + imx53_add_fec(&mx53_evk_fec_pdata); + + imx53_add_imx_i2c(0, &mx53_evk_i2c_data); + imx53_add_imx_i2c(1, &mx53_evk_i2c_data); + + imx53_add_sdhci_esdhc_imx(0, NULL); + imx53_add_sdhci_esdhc_imx(1, NULL); + + spi_register_board_info(mx53_evk_spi_board_info, + ARRAY_SIZE(mx53_evk_spi_board_info)); + imx53_add_ecspi(0, &mx53_evk_spi_data); + imx53_add_imx2_wdt(0, NULL); + gpio_led_register_device(-1, &mx53evk_leds_data); +} + +static void __init mx53_evk_timer_init(void) +{ + mx53_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mx53_evk_timer = { + .init = mx53_evk_timer_init, +}; + +MACHINE_START(MX53_EVK, "Freescale MX53 EVK Board") + .map_io = mx53_map_io, + .init_early = imx53_init_early, + .init_irq = mx53_init_irq, + .handle_irq = imx53_handle_irq, + .timer = &mx53_evk_timer, + .init_machine = mx53_evk_board_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx53_loco.c b/arch/arm/mach-imx/mach-mx53_loco.c new file mode 100644 index 0000000..e64a8f7 --- /dev/null +++ b/arch/arm/mach-imx/mach-mx53_loco.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "devices-imx53.h" + +#define MX53_LOCO_POWER IMX_GPIO_NR(1, 8) +#define MX53_LOCO_UI1 IMX_GPIO_NR(2, 14) +#define MX53_LOCO_UI2 IMX_GPIO_NR(2, 15) +#define LOCO_FEC_PHY_RST IMX_GPIO_NR(7, 6) +#define LOCO_LED IMX_GPIO_NR(7, 7) +#define LOCO_SD3_CD IMX_GPIO_NR(3, 11) +#define LOCO_SD3_WP IMX_GPIO_NR(3, 12) +#define LOCO_SD1_CD IMX_GPIO_NR(3, 13) +#define LOCO_ACCEL_EN IMX_GPIO_NR(6, 14) + +static iomux_v3_cfg_t mx53_loco_pads[] = { + /* FEC */ + MX53_PAD_FEC_MDC__FEC_MDC, + MX53_PAD_FEC_MDIO__FEC_MDIO, + MX53_PAD_FEC_REF_CLK__FEC_TX_CLK, + MX53_PAD_FEC_RX_ER__FEC_RX_ER, + MX53_PAD_FEC_CRS_DV__FEC_RX_DV, + MX53_PAD_FEC_RXD1__FEC_RDATA_1, + MX53_PAD_FEC_RXD0__FEC_RDATA_0, + MX53_PAD_FEC_TX_EN__FEC_TX_EN, + MX53_PAD_FEC_TXD1__FEC_TDATA_1, + MX53_PAD_FEC_TXD0__FEC_TDATA_0, + /* FEC_nRST */ + MX53_PAD_PATA_DA_0__GPIO7_6, + /* FEC_nINT */ + MX53_PAD_PATA_DATA4__GPIO2_4, + /* AUDMUX5 */ + MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC, + MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD, + MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS, + MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD, + /* I2C1 */ + MX53_PAD_CSI0_DAT8__I2C1_SDA, + MX53_PAD_CSI0_DAT9__I2C1_SCL, + MX53_PAD_NANDF_CS1__GPIO6_14, /* Accelerometer Enable */ + /* I2C2 */ + MX53_PAD_KEY_COL3__I2C2_SCL, + MX53_PAD_KEY_ROW3__I2C2_SDA, + /* SD1 */ + MX53_PAD_SD1_CMD__ESDHC1_CMD, + MX53_PAD_SD1_CLK__ESDHC1_CLK, + MX53_PAD_SD1_DATA0__ESDHC1_DAT0, + MX53_PAD_SD1_DATA1__ESDHC1_DAT1, + MX53_PAD_SD1_DATA2__ESDHC1_DAT2, + MX53_PAD_SD1_DATA3__ESDHC1_DAT3, + /* SD1_CD */ + MX53_PAD_EIM_DA13__GPIO3_13, + /* SD3 */ + MX53_PAD_PATA_DATA8__ESDHC3_DAT0, + MX53_PAD_PATA_DATA9__ESDHC3_DAT1, + MX53_PAD_PATA_DATA10__ESDHC3_DAT2, + MX53_PAD_PATA_DATA11__ESDHC3_DAT3, + MX53_PAD_PATA_DATA0__ESDHC3_DAT4, + MX53_PAD_PATA_DATA1__ESDHC3_DAT5, + MX53_PAD_PATA_DATA2__ESDHC3_DAT6, + MX53_PAD_PATA_DATA3__ESDHC3_DAT7, + MX53_PAD_PATA_IORDY__ESDHC3_CLK, + MX53_PAD_PATA_RESET_B__ESDHC3_CMD, + /* SD3_CD */ + MX53_PAD_EIM_DA11__GPIO3_11, + /* SD3_WP */ + MX53_PAD_EIM_DA12__GPIO3_12, + /* VGA */ + MX53_PAD_EIM_OE__IPU_DI1_PIN7, + MX53_PAD_EIM_RW__IPU_DI1_PIN8, + /* DISPLB */ + MX53_PAD_EIM_D20__IPU_SER_DISP0_CS, + MX53_PAD_EIM_D21__IPU_DISPB0_SER_CLK, + MX53_PAD_EIM_D22__IPU_DISPB0_SER_DIN, + MX53_PAD_EIM_D23__IPU_DI0_D0_CS, + /* DISP0_POWER_EN */ + MX53_PAD_EIM_D24__GPIO3_24, + /* DISP0 DET INT */ + MX53_PAD_EIM_D31__GPIO3_31, + /* LVDS */ + MX53_PAD_LVDS0_TX3_P__LDB_LVDS0_TX3, + MX53_PAD_LVDS0_CLK_P__LDB_LVDS0_CLK, + MX53_PAD_LVDS0_TX2_P__LDB_LVDS0_TX2, + MX53_PAD_LVDS0_TX1_P__LDB_LVDS0_TX1, + MX53_PAD_LVDS0_TX0_P__LDB_LVDS0_TX0, + MX53_PAD_LVDS1_TX3_P__LDB_LVDS1_TX3, + MX53_PAD_LVDS1_TX2_P__LDB_LVDS1_TX2, + MX53_PAD_LVDS1_CLK_P__LDB_LVDS1_CLK, + MX53_PAD_LVDS1_TX1_P__LDB_LVDS1_TX1, + MX53_PAD_LVDS1_TX0_P__LDB_LVDS1_TX0, + /* I2C1 */ + MX53_PAD_CSI0_DAT8__I2C1_SDA, + MX53_PAD_CSI0_DAT9__I2C1_SCL, + /* UART1 */ + MX53_PAD_CSI0_DAT10__UART1_TXD_MUX, + MX53_PAD_CSI0_DAT11__UART1_RXD_MUX, + /* CSI0 */ + MX53_PAD_CSI0_DAT12__IPU_CSI0_D_12, + MX53_PAD_CSI0_DAT13__IPU_CSI0_D_13, + MX53_PAD_CSI0_DAT14__IPU_CSI0_D_14, + MX53_PAD_CSI0_DAT15__IPU_CSI0_D_15, + MX53_PAD_CSI0_DAT16__IPU_CSI0_D_16, + MX53_PAD_CSI0_DAT17__IPU_CSI0_D_17, + MX53_PAD_CSI0_DAT18__IPU_CSI0_D_18, + MX53_PAD_CSI0_DAT19__IPU_CSI0_D_19, + MX53_PAD_CSI0_VSYNC__IPU_CSI0_VSYNC, + MX53_PAD_CSI0_MCLK__IPU_CSI0_HSYNC, + MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK, + /* DISPLAY */ + MX53_PAD_DI0_DISP_CLK__IPU_DI0_DISP_CLK, + MX53_PAD_DI0_PIN15__IPU_DI0_PIN15, + MX53_PAD_DI0_PIN2__IPU_DI0_PIN2, + MX53_PAD_DI0_PIN3__IPU_DI0_PIN3, + MX53_PAD_DISP0_DAT0__IPU_DISP0_DAT_0, + MX53_PAD_DISP0_DAT1__IPU_DISP0_DAT_1, + MX53_PAD_DISP0_DAT2__IPU_DISP0_DAT_2, + MX53_PAD_DISP0_DAT3__IPU_DISP0_DAT_3, + MX53_PAD_DISP0_DAT4__IPU_DISP0_DAT_4, + MX53_PAD_DISP0_DAT5__IPU_DISP0_DAT_5, + MX53_PAD_DISP0_DAT6__IPU_DISP0_DAT_6, + MX53_PAD_DISP0_DAT7__IPU_DISP0_DAT_7, + MX53_PAD_DISP0_DAT8__IPU_DISP0_DAT_8, + MX53_PAD_DISP0_DAT9__IPU_DISP0_DAT_9, + MX53_PAD_DISP0_DAT10__IPU_DISP0_DAT_10, + MX53_PAD_DISP0_DAT11__IPU_DISP0_DAT_11, + MX53_PAD_DISP0_DAT12__IPU_DISP0_DAT_12, + MX53_PAD_DISP0_DAT13__IPU_DISP0_DAT_13, + MX53_PAD_DISP0_DAT14__IPU_DISP0_DAT_14, + MX53_PAD_DISP0_DAT15__IPU_DISP0_DAT_15, + MX53_PAD_DISP0_DAT16__IPU_DISP0_DAT_16, + MX53_PAD_DISP0_DAT17__IPU_DISP0_DAT_17, + MX53_PAD_DISP0_DAT18__IPU_DISP0_DAT_18, + MX53_PAD_DISP0_DAT19__IPU_DISP0_DAT_19, + MX53_PAD_DISP0_DAT20__IPU_DISP0_DAT_20, + MX53_PAD_DISP0_DAT21__IPU_DISP0_DAT_21, + MX53_PAD_DISP0_DAT22__IPU_DISP0_DAT_22, + MX53_PAD_DISP0_DAT23__IPU_DISP0_DAT_23, + /* Audio CLK*/ + MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK, + /* PWM */ + MX53_PAD_GPIO_1__PWM2_PWMO, + /* SPDIF */ + MX53_PAD_GPIO_7__SPDIF_PLOCK, + MX53_PAD_GPIO_17__SPDIF_OUT1, + /* GPIO */ + MX53_PAD_PATA_DA_1__GPIO7_7, /* LED */ + MX53_PAD_PATA_DA_2__GPIO7_8, + MX53_PAD_PATA_DATA5__GPIO2_5, + MX53_PAD_PATA_DATA6__GPIO2_6, + MX53_PAD_PATA_DATA14__GPIO2_14, + MX53_PAD_PATA_DATA15__GPIO2_15, + MX53_PAD_PATA_INTRQ__GPIO7_2, + MX53_PAD_EIM_WAIT__GPIO5_0, + MX53_PAD_NANDF_WP_B__GPIO6_9, + MX53_PAD_NANDF_RB0__GPIO6_10, + MX53_PAD_NANDF_CS1__GPIO6_14, + MX53_PAD_NANDF_CS2__GPIO6_15, + MX53_PAD_NANDF_CS3__GPIO6_16, + MX53_PAD_GPIO_5__GPIO1_5, + MX53_PAD_GPIO_16__GPIO7_11, + MX53_PAD_GPIO_8__GPIO1_8, +}; + +#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake) \ +{ \ + .gpio = gpio_num, \ + .type = EV_KEY, \ + .code = ev_code, \ + .active_low = act_low, \ + .desc = "btn " descr, \ + .wakeup = wake, \ +} + +static struct gpio_keys_button loco_buttons[] = { + GPIO_BUTTON(MX53_LOCO_POWER, KEY_POWER, 1, "power", 0), + GPIO_BUTTON(MX53_LOCO_UI1, KEY_VOLUMEUP, 1, "volume-up", 0), + GPIO_BUTTON(MX53_LOCO_UI2, KEY_VOLUMEDOWN, 1, "volume-down", 0), +}; + +static const struct gpio_keys_platform_data loco_button_data __initconst = { + .buttons = loco_buttons, + .nbuttons = ARRAY_SIZE(loco_buttons), +}; + +static const struct esdhc_platform_data mx53_loco_sd1_data __initconst = { + .cd_gpio = LOCO_SD1_CD, + .cd_type = ESDHC_CD_GPIO, + .wp_type = ESDHC_WP_NONE, +}; + +static const struct esdhc_platform_data mx53_loco_sd3_data __initconst = { + .cd_gpio = LOCO_SD3_CD, + .wp_gpio = LOCO_SD3_WP, + .cd_type = ESDHC_CD_GPIO, + .wp_type = ESDHC_WP_GPIO, +}; + +static inline void mx53_loco_fec_reset(void) +{ + int ret; + + /* reset FEC PHY */ + ret = gpio_request(LOCO_FEC_PHY_RST, "fec-phy-reset"); + if (ret) { + printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); + return; + } + gpio_direction_output(LOCO_FEC_PHY_RST, 0); + msleep(1); + gpio_set_value(LOCO_FEC_PHY_RST, 1); +} + +static struct fec_platform_data mx53_loco_fec_data = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +static const struct imxi2c_platform_data mx53_loco_i2c_data __initconst = { + .bitrate = 100000, +}; + +static const struct gpio_led mx53loco_leds[] __initconst = { + { + .name = "green", + .default_trigger = "heartbeat", + .gpio = LOCO_LED, + }, +}; + +static const struct gpio_led_platform_data mx53loco_leds_data __initconst = { + .leds = mx53loco_leds, + .num_leds = ARRAY_SIZE(mx53loco_leds), +}; + +void __init imx53_qsb_common_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads, + ARRAY_SIZE(mx53_loco_pads)); +} + +static struct i2c_board_info mx53loco_i2c_devices[] = { + { + I2C_BOARD_INFO("mma8450", 0x1C), + }, +}; + +static void __init mx53_loco_board_init(void) +{ + int ret; + imx53_soc_init(); + imx53_qsb_common_init(); + + imx53_add_imx_uart(0, NULL); + mx53_loco_fec_reset(); + imx53_add_fec(&mx53_loco_fec_data); + imx53_add_imx2_wdt(0, NULL); + + ret = gpio_request_one(LOCO_ACCEL_EN, GPIOF_OUT_INIT_HIGH, "accel_en"); + if (ret) + pr_err("Cannot request ACCEL_EN pin: %d\n", ret); + + i2c_register_board_info(0, mx53loco_i2c_devices, + ARRAY_SIZE(mx53loco_i2c_devices)); + imx53_add_imx_i2c(0, &mx53_loco_i2c_data); + imx53_add_imx_i2c(1, &mx53_loco_i2c_data); + imx53_add_sdhci_esdhc_imx(0, &mx53_loco_sd1_data); + imx53_add_sdhci_esdhc_imx(2, &mx53_loco_sd3_data); + imx_add_gpio_keys(&loco_button_data); + gpio_led_register_device(-1, &mx53loco_leds_data); + imx53_add_ahci_imx(); +} + +static void __init mx53_loco_timer_init(void) +{ + mx53_clocks_init(32768, 24000000, 0, 0); +} + +static struct sys_timer mx53_loco_timer = { + .init = mx53_loco_timer_init, +}; + +MACHINE_START(MX53_LOCO, "Freescale MX53 LOCO Board") + .map_io = mx53_map_io, + .init_early = imx53_init_early, + .init_irq = mx53_init_irq, + .handle_irq = imx53_handle_irq, + .timer = &mx53_loco_timer, + .init_machine = mx53_loco_board_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mach-mx53_smd.c b/arch/arm/mach-imx/mach-mx53_smd.c new file mode 100644 index 0000000..d498573 --- /dev/null +++ b/arch/arm/mach-imx/mach-mx53_smd.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "devices-imx53.h" + +#define SMD_FEC_PHY_RST IMX_GPIO_NR(7, 6) +#define MX53_SMD_SATA_PWR_EN IMX_GPIO_NR(3, 3) + +static iomux_v3_cfg_t mx53_smd_pads[] = { + MX53_PAD_CSI0_DAT10__UART1_TXD_MUX, + MX53_PAD_CSI0_DAT11__UART1_RXD_MUX, + + MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX, + MX53_PAD_PATA_DMARQ__UART2_TXD_MUX, + + MX53_PAD_PATA_CS_0__UART3_TXD_MUX, + MX53_PAD_PATA_CS_1__UART3_RXD_MUX, + MX53_PAD_PATA_DA_1__UART3_CTS, + MX53_PAD_PATA_DA_2__UART3_RTS, + /* I2C1 */ + MX53_PAD_CSI0_DAT8__I2C1_SDA, + MX53_PAD_CSI0_DAT9__I2C1_SCL, + /* SD1 */ + MX53_PAD_SD1_CMD__ESDHC1_CMD, + MX53_PAD_SD1_CLK__ESDHC1_CLK, + MX53_PAD_SD1_DATA0__ESDHC1_DAT0, + MX53_PAD_SD1_DATA1__ESDHC1_DAT1, + MX53_PAD_SD1_DATA2__ESDHC1_DAT2, + MX53_PAD_SD1_DATA3__ESDHC1_DAT3, + /* SD2 */ + MX53_PAD_SD2_CMD__ESDHC2_CMD, + MX53_PAD_SD2_CLK__ESDHC2_CLK, + MX53_PAD_SD2_DATA0__ESDHC2_DAT0, + MX53_PAD_SD2_DATA1__ESDHC2_DAT1, + MX53_PAD_SD2_DATA2__ESDHC2_DAT2, + MX53_PAD_SD2_DATA3__ESDHC2_DAT3, + /* SD3 */ + MX53_PAD_PATA_DATA8__ESDHC3_DAT0, + MX53_PAD_PATA_DATA9__ESDHC3_DAT1, + MX53_PAD_PATA_DATA10__ESDHC3_DAT2, + MX53_PAD_PATA_DATA11__ESDHC3_DAT3, + MX53_PAD_PATA_DATA0__ESDHC3_DAT4, + MX53_PAD_PATA_DATA1__ESDHC3_DAT5, + MX53_PAD_PATA_DATA2__ESDHC3_DAT6, + MX53_PAD_PATA_DATA3__ESDHC3_DAT7, + MX53_PAD_PATA_IORDY__ESDHC3_CLK, + MX53_PAD_PATA_RESET_B__ESDHC3_CMD, +}; + +static const struct imxuart_platform_data mx53_smd_uart_data __initconst = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static inline void mx53_smd_init_uart(void) +{ + imx53_add_imx_uart(0, NULL); + imx53_add_imx_uart(1, NULL); + imx53_add_imx_uart(2, &mx53_smd_uart_data); +} + +static inline void mx53_smd_fec_reset(void) +{ + int ret; + + /* reset FEC PHY */ + ret = gpio_request(SMD_FEC_PHY_RST, "fec-phy-reset"); + if (ret) { + printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); + return; + } + gpio_direction_output(SMD_FEC_PHY_RST, 0); + msleep(1); + gpio_set_value(SMD_FEC_PHY_RST, 1); +} + +static struct fec_platform_data mx53_smd_fec_data = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +static const struct imxi2c_platform_data mx53_smd_i2c_data __initconst = { + .bitrate = 100000, +}; + +static inline void mx53_smd_ahci_pwr_on(void) +{ + int ret; + + /* Enable SATA PWR */ + ret = gpio_request_one(MX53_SMD_SATA_PWR_EN, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "ahci-sata-pwr"); + if (ret) { + pr_err("failed to enable SATA_PWR_EN: %d\n", ret); + return; + } +} + +void __init imx53_smd_common_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx53_smd_pads, + ARRAY_SIZE(mx53_smd_pads)); +} + +static void __init mx53_smd_board_init(void) +{ + imx53_soc_init(); + imx53_smd_common_init(); + + mx53_smd_init_uart(); + mx53_smd_fec_reset(); + imx53_add_fec(&mx53_smd_fec_data); + imx53_add_imx2_wdt(0, NULL); + imx53_add_imx_i2c(0, &mx53_smd_i2c_data); + imx53_add_sdhci_esdhc_imx(0, NULL); + imx53_add_sdhci_esdhc_imx(1, NULL); + imx53_add_sdhci_esdhc_imx(2, NULL); + mx53_smd_ahci_pwr_on(); + imx53_add_ahci_imx(); +} + +static void __init mx53_smd_timer_init(void) +{ + mx53_clocks_init(32768, 24000000, 22579200, 0); +} + +static struct sys_timer mx53_smd_timer = { + .init = mx53_smd_timer_init, +}; + +MACHINE_START(MX53_SMD, "Freescale MX53 SMD Board") + .map_io = mx53_map_io, + .init_early = imx53_init_early, + .init_irq = mx53_init_irq, + .handle_irq = imx53_handle_irq, + .timer = &mx53_smd_timer, + .init_machine = mx53_smd_board_init, +MACHINE_END diff --git a/arch/arm/mach-imx/mm-imx5.c b/arch/arm/mach-imx/mm-imx5.c new file mode 100644 index 0000000..26eacc9 --- /dev/null +++ b/arch/arm/mach-imx/mm-imx5.c @@ -0,0 +1,190 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * Create static mapping between physical to virtual memory. + */ + +#include +#include + +#include + +#include +#include +#include +#include + +static void imx5_idle(void) +{ + mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); +} + +/* + * Define the MX50 memory map. + */ +static struct map_desc mx50_io_desc[] __initdata = { + imx_map_entry(MX50, TZIC, MT_DEVICE), + imx_map_entry(MX50, SPBA0, MT_DEVICE), + imx_map_entry(MX50, AIPS1, MT_DEVICE), + imx_map_entry(MX50, AIPS2, MT_DEVICE), +}; + +/* + * Define the MX51 memory map. + */ +static struct map_desc mx51_io_desc[] __initdata = { + imx_map_entry(MX51, TZIC, MT_DEVICE), + imx_map_entry(MX51, IRAM, MT_DEVICE), + imx_map_entry(MX51, AIPS1, MT_DEVICE), + imx_map_entry(MX51, SPBA0, MT_DEVICE), + imx_map_entry(MX51, AIPS2, MT_DEVICE), +}; + +/* + * Define the MX53 memory map. + */ +static struct map_desc mx53_io_desc[] __initdata = { + imx_map_entry(MX53, TZIC, MT_DEVICE), + imx_map_entry(MX53, AIPS1, MT_DEVICE), + imx_map_entry(MX53, SPBA0, MT_DEVICE), + imx_map_entry(MX53, AIPS2, MT_DEVICE), +}; + +/* + * This function initializes the memory map. It is called during the + * system startup to create static physical to virtual memory mappings + * for the IO modules. + */ +void __init mx50_map_io(void) +{ + iotable_init(mx50_io_desc, ARRAY_SIZE(mx50_io_desc)); +} + +void __init mx51_map_io(void) +{ + iotable_init(mx51_io_desc, ARRAY_SIZE(mx51_io_desc)); +} + +void __init mx53_map_io(void) +{ + iotable_init(mx53_io_desc, ARRAY_SIZE(mx53_io_desc)); +} + +void __init imx50_init_early(void) +{ + mxc_set_cpu_type(MXC_CPU_MX50); + mxc_iomux_v3_init(MX50_IO_ADDRESS(MX50_IOMUXC_BASE_ADDR)); + mxc_arch_reset_init(MX50_IO_ADDRESS(MX50_WDOG_BASE_ADDR)); +} + +void __init imx51_init_early(void) +{ + mxc_set_cpu_type(MXC_CPU_MX51); + mxc_iomux_v3_init(MX51_IO_ADDRESS(MX51_IOMUXC_BASE_ADDR)); + mxc_arch_reset_init(MX51_IO_ADDRESS(MX51_WDOG1_BASE_ADDR)); + imx_idle = imx5_idle; +} + +void __init imx53_init_early(void) +{ + mxc_set_cpu_type(MXC_CPU_MX53); + mxc_iomux_v3_init(MX53_IO_ADDRESS(MX53_IOMUXC_BASE_ADDR)); + mxc_arch_reset_init(MX53_IO_ADDRESS(MX53_WDOG1_BASE_ADDR)); +} + +void __init mx50_init_irq(void) +{ + tzic_init_irq(MX50_IO_ADDRESS(MX50_TZIC_BASE_ADDR)); +} + +void __init mx51_init_irq(void) +{ + tzic_init_irq(MX51_IO_ADDRESS(MX51_TZIC_BASE_ADDR)); +} + +void __init mx53_init_irq(void) +{ + tzic_init_irq(MX53_IO_ADDRESS(MX53_TZIC_BASE_ADDR)); +} + +static struct sdma_script_start_addrs imx51_sdma_script __initdata = { + .ap_2_ap_addr = 642, + .uart_2_mcu_addr = 817, + .mcu_2_app_addr = 747, + .mcu_2_shp_addr = 961, + .ata_2_mcu_addr = 1473, + .mcu_2_ata_addr = 1392, + .app_2_per_addr = 1033, + .app_2_mcu_addr = 683, + .shp_2_per_addr = 1251, + .shp_2_mcu_addr = 892, +}; + +static struct sdma_platform_data imx51_sdma_pdata __initdata = { + .fw_name = "sdma-imx51.bin", + .script_addrs = &imx51_sdma_script, +}; + +static struct sdma_script_start_addrs imx53_sdma_script __initdata = { + .ap_2_ap_addr = 642, + .app_2_mcu_addr = 683, + .mcu_2_app_addr = 747, + .uart_2_mcu_addr = 817, + .shp_2_mcu_addr = 891, + .mcu_2_shp_addr = 960, + .uartsh_2_mcu_addr = 1032, + .spdif_2_mcu_addr = 1100, + .mcu_2_spdif_addr = 1134, + .firi_2_mcu_addr = 1193, + .mcu_2_firi_addr = 1290, +}; + +static struct sdma_platform_data imx53_sdma_pdata __initdata = { + .fw_name = "sdma-imx53.bin", + .script_addrs = &imx53_sdma_script, +}; + +void __init imx50_soc_init(void) +{ + /* i.mx50 has the i.mx31 type gpio */ + mxc_register_gpio("imx31-gpio", 0, MX50_GPIO1_BASE_ADDR, SZ_16K, MX50_INT_GPIO1_LOW, MX50_INT_GPIO1_HIGH); + mxc_register_gpio("imx31-gpio", 1, MX50_GPIO2_BASE_ADDR, SZ_16K, MX50_INT_GPIO2_LOW, MX50_INT_GPIO2_HIGH); + mxc_register_gpio("imx31-gpio", 2, MX50_GPIO3_BASE_ADDR, SZ_16K, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH); + mxc_register_gpio("imx31-gpio", 3, MX50_GPIO4_BASE_ADDR, SZ_16K, MX50_INT_GPIO4_LOW, MX50_INT_GPIO4_HIGH); + mxc_register_gpio("imx31-gpio", 4, MX50_GPIO5_BASE_ADDR, SZ_16K, MX50_INT_GPIO5_LOW, MX50_INT_GPIO5_HIGH); + mxc_register_gpio("imx31-gpio", 5, MX50_GPIO6_BASE_ADDR, SZ_16K, MX50_INT_GPIO6_LOW, MX50_INT_GPIO6_HIGH); +} + +void __init imx51_soc_init(void) +{ + /* i.mx51 has the i.mx31 type gpio */ + mxc_register_gpio("imx31-gpio", 0, MX51_GPIO1_BASE_ADDR, SZ_16K, MX51_INT_GPIO1_LOW, MX51_INT_GPIO1_HIGH); + mxc_register_gpio("imx31-gpio", 1, MX51_GPIO2_BASE_ADDR, SZ_16K, MX51_INT_GPIO2_LOW, MX51_INT_GPIO2_HIGH); + mxc_register_gpio("imx31-gpio", 2, MX51_GPIO3_BASE_ADDR, SZ_16K, MX51_INT_GPIO3_LOW, MX51_INT_GPIO3_HIGH); + mxc_register_gpio("imx31-gpio", 3, MX51_GPIO4_BASE_ADDR, SZ_16K, MX51_INT_GPIO4_LOW, MX51_INT_GPIO4_HIGH); + + /* i.mx51 has the i.mx35 type sdma */ + imx_add_imx_sdma("imx35-sdma", MX51_SDMA_BASE_ADDR, MX51_INT_SDMA, &imx51_sdma_pdata); +} + +void __init imx53_soc_init(void) +{ + /* i.mx53 has the i.mx31 type gpio */ + mxc_register_gpio("imx31-gpio", 0, MX53_GPIO1_BASE_ADDR, SZ_16K, MX53_INT_GPIO1_LOW, MX53_INT_GPIO1_HIGH); + mxc_register_gpio("imx31-gpio", 1, MX53_GPIO2_BASE_ADDR, SZ_16K, MX53_INT_GPIO2_LOW, MX53_INT_GPIO2_HIGH); + mxc_register_gpio("imx31-gpio", 2, MX53_GPIO3_BASE_ADDR, SZ_16K, MX53_INT_GPIO3_LOW, MX53_INT_GPIO3_HIGH); + mxc_register_gpio("imx31-gpio", 3, MX53_GPIO4_BASE_ADDR, SZ_16K, MX53_INT_GPIO4_LOW, MX53_INT_GPIO4_HIGH); + mxc_register_gpio("imx31-gpio", 4, MX53_GPIO5_BASE_ADDR, SZ_16K, MX53_INT_GPIO5_LOW, MX53_INT_GPIO5_HIGH); + mxc_register_gpio("imx31-gpio", 5, MX53_GPIO6_BASE_ADDR, SZ_16K, MX53_INT_GPIO6_LOW, MX53_INT_GPIO6_HIGH); + mxc_register_gpio("imx31-gpio", 6, MX53_GPIO7_BASE_ADDR, SZ_16K, MX53_INT_GPIO7_LOW, MX53_INT_GPIO7_HIGH); + + /* i.mx53 has the i.mx35 type sdma */ + imx_add_imx_sdma("imx35-sdma", MX53_SDMA_BASE_ADDR, MX53_INT_SDMA, &imx53_sdma_pdata); +} diff --git a/arch/arm/mach-imx/mx51_efika.c b/arch/arm/mach-imx/mx51_efika.c new file mode 100644 index 0000000..ec6ca91 --- /dev/null +++ b/arch/arm/mach-imx/mx51_efika.c @@ -0,0 +1,632 @@ +/* + * based on code from the following + * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2010 Pegatron Corporation. All Rights Reserved. + * Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "devices-imx51.h" +#include "efika.h" +#include "cpu_op-mx51.h" + +#define MX51_USB_CTRL_1_OFFSET 0x10 +#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) +#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 + +#define EFIKAMX_USB_HUB_RESET IMX_GPIO_NR(1, 5) +#define EFIKAMX_USBH1_STP IMX_GPIO_NR(1, 27) + +#define EFIKAMX_SPI_CS0 IMX_GPIO_NR(4, 24) +#define EFIKAMX_SPI_CS1 IMX_GPIO_NR(4, 25) + +#define EFIKAMX_PMIC IMX_GPIO_NR(1, 6) + +static iomux_v3_cfg_t mx51efika_pads[] = { + /* UART1 */ + MX51_PAD_UART1_RXD__UART1_RXD, + MX51_PAD_UART1_TXD__UART1_TXD, + MX51_PAD_UART1_RTS__UART1_RTS, + MX51_PAD_UART1_CTS__UART1_CTS, + + /* SD 1 */ + MX51_PAD_SD1_CMD__SD1_CMD, + MX51_PAD_SD1_CLK__SD1_CLK, + MX51_PAD_SD1_DATA0__SD1_DATA0, + MX51_PAD_SD1_DATA1__SD1_DATA1, + MX51_PAD_SD1_DATA2__SD1_DATA2, + MX51_PAD_SD1_DATA3__SD1_DATA3, + + /* SD 2 */ + MX51_PAD_SD2_CMD__SD2_CMD, + MX51_PAD_SD2_CLK__SD2_CLK, + MX51_PAD_SD2_DATA0__SD2_DATA0, + MX51_PAD_SD2_DATA1__SD2_DATA1, + MX51_PAD_SD2_DATA2__SD2_DATA2, + MX51_PAD_SD2_DATA3__SD2_DATA3, + + /* SD/MMC WP/CD */ + MX51_PAD_GPIO1_0__SD1_CD, + MX51_PAD_GPIO1_1__SD1_WP, + MX51_PAD_GPIO1_7__SD2_WP, + MX51_PAD_GPIO1_8__SD2_CD, + + /* spi */ + MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, + MX51_PAD_CSPI1_MISO__ECSPI1_MISO, + MX51_PAD_CSPI1_SS0__GPIO4_24, + MX51_PAD_CSPI1_SS1__GPIO4_25, + MX51_PAD_CSPI1_RDY__ECSPI1_RDY, + MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, + MX51_PAD_GPIO1_6__GPIO1_6, + + /* USB HOST1 */ + MX51_PAD_USBH1_CLK__USBH1_CLK, + MX51_PAD_USBH1_DIR__USBH1_DIR, + MX51_PAD_USBH1_NXT__USBH1_NXT, + MX51_PAD_USBH1_DATA0__USBH1_DATA0, + MX51_PAD_USBH1_DATA1__USBH1_DATA1, + MX51_PAD_USBH1_DATA2__USBH1_DATA2, + MX51_PAD_USBH1_DATA3__USBH1_DATA3, + MX51_PAD_USBH1_DATA4__USBH1_DATA4, + MX51_PAD_USBH1_DATA5__USBH1_DATA5, + MX51_PAD_USBH1_DATA6__USBH1_DATA6, + MX51_PAD_USBH1_DATA7__USBH1_DATA7, + + /* USB HUB RESET */ + MX51_PAD_GPIO1_5__GPIO1_5, + + /* WLAN */ + MX51_PAD_EIM_A22__GPIO2_16, + MX51_PAD_EIM_A16__GPIO2_10, + + /* USB PHY RESET */ + MX51_PAD_EIM_D27__GPIO2_9, +}; + +/* Serial ports */ +static const struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +/* This function is board specific as the bit mask for the plldiv will also + * be different for other Freescale SoCs, thus a common bitmask is not + * possible and cannot get place in /plat-mxc/ehci.c. + */ +static int initialize_otg_port(struct platform_device *pdev) +{ + u32 v; + void __iomem *usb_base; + void __iomem *usbother_base; + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + if (!usb_base) + return -ENOMEM; + usbother_base = (void __iomem *)(usb_base + MX5_USBOTHER_REGS_OFFSET); + + /* Set the PHY clock to 19.2MHz */ + v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; + v |= MX51_USB_PLL_DIV_19_2_MHZ; + __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); + iounmap(usb_base); + + mdelay(10); + + return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_INTERNAL_PHY); +} + +static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { + .init = initialize_otg_port, + .portsc = MXC_EHCI_UTMI_16BIT, +}; + +static int initialize_usbh1_port(struct platform_device *pdev) +{ + iomux_v3_cfg_t usbh1stp = MX51_PAD_USBH1_STP__USBH1_STP; + iomux_v3_cfg_t usbh1gpio = MX51_PAD_USBH1_STP__GPIO1_27; + u32 v; + void __iomem *usb_base; + void __iomem *socregs_base; + + mxc_iomux_v3_setup_pad(usbh1gpio); + gpio_request(EFIKAMX_USBH1_STP, "usbh1_stp"); + gpio_direction_output(EFIKAMX_USBH1_STP, 0); + msleep(1); + gpio_set_value(EFIKAMX_USBH1_STP, 1); + msleep(1); + + usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); + socregs_base = (void __iomem *)(usb_base + MX5_USBOTHER_REGS_OFFSET); + + /* The clock for the USBH1 ULPI port will come externally */ + /* from the PHY. */ + v = __raw_readl(socregs_base + MX51_USB_CTRL_1_OFFSET); + __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, + socregs_base + MX51_USB_CTRL_1_OFFSET); + + iounmap(usb_base); + + gpio_free(EFIKAMX_USBH1_STP); + mxc_iomux_v3_setup_pad(usbh1stp); + + mdelay(10); + + return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_ITC_NO_THRESHOLD); +} + +static struct mxc_usbh_platform_data usbh1_config __initdata = { + .init = initialize_usbh1_port, + .portsc = MXC_EHCI_MODE_ULPI, +}; + +static void mx51_efika_hubreset(void) +{ + gpio_request(EFIKAMX_USB_HUB_RESET, "usb_hub_rst"); + gpio_direction_output(EFIKAMX_USB_HUB_RESET, 1); + msleep(1); + gpio_set_value(EFIKAMX_USB_HUB_RESET, 0); + msleep(1); + gpio_set_value(EFIKAMX_USB_HUB_RESET, 1); +} + +static void __init mx51_efika_usb(void) +{ + mx51_efika_hubreset(); + + /* pulling it low, means no USB at all... */ + gpio_request(EFIKA_USB_PHY_RESET, "usb_phy_reset"); + gpio_direction_output(EFIKA_USB_PHY_RESET, 0); + msleep(1); + gpio_set_value(EFIKA_USB_PHY_RESET, 1); + + usbh1_config.otg = imx_otg_ulpi_create(ULPI_OTG_DRVVBUS | + ULPI_OTG_DRVVBUS_EXT | ULPI_OTG_EXTVBUSIND); + + imx51_add_mxc_ehci_otg(&dr_utmi_config); + if (usbh1_config.otg) + imx51_add_mxc_ehci_hs(1, &usbh1_config); +} + +static struct mtd_partition mx51_efika_spi_nor_partitions[] = { + { + .name = "u-boot", + .offset = 0, + .size = SZ_256K, + }, + { + .name = "config", + .offset = MTDPART_OFS_APPEND, + .size = SZ_64K, + }, +}; + +static struct flash_platform_data mx51_efika_spi_flash_data = { + .name = "spi_flash", + .parts = mx51_efika_spi_nor_partitions, + .nr_parts = ARRAY_SIZE(mx51_efika_spi_nor_partitions), + .type = "sst25vf032b", +}; + +static struct regulator_consumer_supply sw1_consumers[] = { + { + .supply = "cpu_vcc", + } +}; + +static struct regulator_consumer_supply vdig_consumers[] = { + /* sgtl5000 */ + REGULATOR_SUPPLY("VDDA", "1-000a"), + REGULATOR_SUPPLY("VDDD", "1-000a"), +}; + +static struct regulator_consumer_supply vvideo_consumers[] = { + /* sgtl5000 */ + REGULATOR_SUPPLY("VDDIO", "1-000a"), +}; + +static struct regulator_consumer_supply vsd_consumers[] = { + REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx51.0"), + REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx51.1"), +}; + +static struct regulator_consumer_supply pwgt1_consumer[] = { + { + .supply = "pwgt1", + } +}; + +static struct regulator_consumer_supply pwgt2_consumer[] = { + { + .supply = "pwgt2", + } +}; + +static struct regulator_consumer_supply coincell_consumer[] = { + { + .supply = "coincell", + } +}; + +static struct regulator_init_data sw1_init = { + .constraints = { + .name = "SW1", + .min_uV = 600000, + .max_uV = 1375000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .valid_modes_mask = 0, + .always_on = 1, + .boot_on = 1, + .state_mem = { + .uV = 850000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(sw1_consumers), + .consumer_supplies = sw1_consumers, +}; + +static struct regulator_init_data sw2_init = { + .constraints = { + .name = "SW2", + .min_uV = 900000, + .max_uV = 1850000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .always_on = 1, + .boot_on = 1, + .state_mem = { + .uV = 950000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + } +}; + +static struct regulator_init_data sw3_init = { + .constraints = { + .name = "SW3", + .min_uV = 1100000, + .max_uV = 1850000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .always_on = 1, + .boot_on = 1, + } +}; + +static struct regulator_init_data sw4_init = { + .constraints = { + .name = "SW4", + .min_uV = 1100000, + .max_uV = 1850000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .always_on = 1, + .boot_on = 1, + } +}; + +static struct regulator_init_data viohi_init = { + .constraints = { + .name = "VIOHI", + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data vusb_init = { + .constraints = { + .name = "VUSB", + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data swbst_init = { + .constraints = { + .name = "SWBST", + } +}; + +static struct regulator_init_data vdig_init = { + .constraints = { + .name = "VDIG", + .min_uV = 1050000, + .max_uV = 1800000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vdig_consumers), + .consumer_supplies = vdig_consumers, +}; + +static struct regulator_init_data vpll_init = { + .constraints = { + .name = "VPLL", + .min_uV = 1050000, + .max_uV = 1800000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data vusb2_init = { + .constraints = { + .name = "VUSB2", + .min_uV = 2400000, + .max_uV = 2775000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data vvideo_init = { + .constraints = { + .name = "VVIDEO", + .min_uV = 2775000, + .max_uV = 2775000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vvideo_consumers), + .consumer_supplies = vvideo_consumers, +}; + +static struct regulator_init_data vaudio_init = { + .constraints = { + .name = "VAUDIO", + .min_uV = 2300000, + .max_uV = 3000000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + } +}; + +static struct regulator_init_data vsd_init = { + .constraints = { + .name = "VSD", + .min_uV = 1800000, + .max_uV = 3150000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vsd_consumers), + .consumer_supplies = vsd_consumers, +}; + +static struct regulator_init_data vcam_init = { + .constraints = { + .name = "VCAM", + .min_uV = 2500000, + .max_uV = 3000000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, + .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, + .boot_on = 1, + } +}; + +static struct regulator_init_data vgen1_init = { + .constraints = { + .name = "VGEN1", + .min_uV = 1200000, + .max_uV = 3150000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data vgen2_init = { + .constraints = { + .name = "VGEN2", + .min_uV = 1200000, + .max_uV = 3150000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data vgen3_init = { + .constraints = { + .name = "VGEN3", + .min_uV = 1800000, + .max_uV = 2900000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .always_on = 1, + } +}; + +static struct regulator_init_data gpo1_init = { + .constraints = { + .name = "GPO1", + } +}; + +static struct regulator_init_data gpo2_init = { + .constraints = { + .name = "GPO2", + } +}; + +static struct regulator_init_data gpo3_init = { + .constraints = { + .name = "GPO3", + } +}; + +static struct regulator_init_data gpo4_init = { + .constraints = { + .name = "GPO4", + } +}; + +static struct regulator_init_data pwgt1_init = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(pwgt1_consumer), + .consumer_supplies = pwgt1_consumer, +}; + +static struct regulator_init_data pwgt2_init = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(pwgt2_consumer), + .consumer_supplies = pwgt2_consumer, +}; + +static struct regulator_init_data vcoincell_init = { + .constraints = { + .name = "COINCELL", + .min_uV = 3000000, + .max_uV = 3000000, + .valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(coincell_consumer), + .consumer_supplies = coincell_consumer, +}; + +static struct mc13xxx_regulator_init_data mx51_efika_regulators[] = { + { .id = MC13892_SW1, .init_data = &sw1_init }, + { .id = MC13892_SW2, .init_data = &sw2_init }, + { .id = MC13892_SW3, .init_data = &sw3_init }, + { .id = MC13892_SW4, .init_data = &sw4_init }, + { .id = MC13892_SWBST, .init_data = &swbst_init }, + { .id = MC13892_VIOHI, .init_data = &viohi_init }, + { .id = MC13892_VPLL, .init_data = &vpll_init }, + { .id = MC13892_VDIG, .init_data = &vdig_init }, + { .id = MC13892_VSD, .init_data = &vsd_init }, + { .id = MC13892_VUSB2, .init_data = &vusb2_init }, + { .id = MC13892_VVIDEO, .init_data = &vvideo_init }, + { .id = MC13892_VAUDIO, .init_data = &vaudio_init }, + { .id = MC13892_VCAM, .init_data = &vcam_init }, + { .id = MC13892_VGEN1, .init_data = &vgen1_init }, + { .id = MC13892_VGEN2, .init_data = &vgen2_init }, + { .id = MC13892_VGEN3, .init_data = &vgen3_init }, + { .id = MC13892_VUSB, .init_data = &vusb_init }, + { .id = MC13892_GPO1, .init_data = &gpo1_init }, + { .id = MC13892_GPO2, .init_data = &gpo2_init }, + { .id = MC13892_GPO3, .init_data = &gpo3_init }, + { .id = MC13892_GPO4, .init_data = &gpo4_init }, + { .id = MC13892_PWGT1SPI, .init_data = &pwgt1_init }, + { .id = MC13892_PWGT2SPI, .init_data = &pwgt2_init }, + { .id = MC13892_VCOINCELL, .init_data = &vcoincell_init }, +}; + +static struct mc13xxx_platform_data mx51_efika_mc13892_data = { + .flags = MC13XXX_USE_RTC, + .regulators = { + .num_regulators = ARRAY_SIZE(mx51_efika_regulators), + .regulators = mx51_efika_regulators, + }, +}; + +static struct spi_board_info mx51_efika_spi_board_info[] __initdata = { + { + .modalias = "m25p80", + .max_speed_hz = 25000000, + .bus_num = 0, + .chip_select = 1, + .platform_data = &mx51_efika_spi_flash_data, + .irq = -1, + }, + { + .modalias = "mc13892", + .max_speed_hz = 1000000, + .bus_num = 0, + .chip_select = 0, + .platform_data = &mx51_efika_mc13892_data, + .irq = IMX_GPIO_TO_IRQ(EFIKAMX_PMIC), + }, +}; + +static int mx51_efika_spi_cs[] = { + EFIKAMX_SPI_CS0, + EFIKAMX_SPI_CS1, +}; + +static const struct spi_imx_master mx51_efika_spi_pdata __initconst = { + .chipselect = mx51_efika_spi_cs, + .num_chipselect = ARRAY_SIZE(mx51_efika_spi_cs), +}; + +void __init efika_board_common_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx51efika_pads, + ARRAY_SIZE(mx51efika_pads)); + imx51_add_imx_uart(0, &uart_pdata); + mx51_efika_usb(); + + /* FIXME: comes from original code. check this. */ + if (mx51_revision() < IMX_CHIP_REVISION_2_0) + sw2_init.constraints.state_mem.uV = 1100000; + else if (mx51_revision() == IMX_CHIP_REVISION_2_0) { + sw2_init.constraints.state_mem.uV = 1250000; + sw1_init.constraints.state_mem.uV = 1000000; + } + if (machine_is_mx51_efikasb()) + vgen1_init.constraints.max_uV = 1200000; + + gpio_request(EFIKAMX_PMIC, "pmic irq"); + gpio_direction_input(EFIKAMX_PMIC); + spi_register_board_info(mx51_efika_spi_board_info, + ARRAY_SIZE(mx51_efika_spi_board_info)); + imx51_add_ecspi(0, &mx51_efika_spi_pdata); + + imx51_add_pata_imx(); + +#if defined(CONFIG_CPU_FREQ_IMX) + get_cpu_op = mx51_get_cpu_op; +#endif +} diff --git a/arch/arm/mach-imx/pm-imx5.c b/arch/arm/mach-imx/pm-imx5.c new file mode 100644 index 0000000..3d57b5d --- /dev/null +++ b/arch/arm/mach-imx/pm-imx5.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "crm-regs-imx5.h" + +static struct clk *gpc_dvfs_clk; + +/* + * set cpu low power mode before WFI instruction. This function is called + * mx5 because it can be used for mx50, mx51, and mx53. + */ +void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) +{ + u32 plat_lpc, arm_srpgcr, ccm_clpcr; + u32 empgc0, empgc1; + int stop_mode = 0; + + /* always allow platform to issue a deep sleep mode request */ + plat_lpc = __raw_readl(MXC_CORTEXA8_PLAT_LPC) & + ~(MXC_CORTEXA8_PLAT_LPC_DSM); + ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK); + arm_srpgcr = __raw_readl(MXC_SRPG_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR); + empgc0 = __raw_readl(MXC_SRPG_EMPGC0_SRPGCR) & ~(MXC_SRPGCR_PCR); + empgc1 = __raw_readl(MXC_SRPG_EMPGC1_SRPGCR) & ~(MXC_SRPGCR_PCR); + + switch (mode) { + case WAIT_CLOCKED: + break; + case WAIT_UNCLOCKED: + ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; + break; + case WAIT_UNCLOCKED_POWER_OFF: + case STOP_POWER_OFF: + plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM + | MXC_CORTEXA8_PLAT_LPC_DBG_DSM; + if (mode == WAIT_UNCLOCKED_POWER_OFF) { + ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; + ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; + ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; + stop_mode = 0; + } else { + ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; + ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; + ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; + ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; + stop_mode = 1; + } + arm_srpgcr |= MXC_SRPGCR_PCR; + + if (tzic_enable_wake(1) != 0) + return; + break; + case STOP_POWER_ON: + ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; + break; + default: + printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); + return; + } + + __raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC); + __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); + __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR); + + /* Enable NEON SRPG for all but MX50TO1.0. */ + if (mx50_revision() != IMX_CHIP_REVISION_1_0) + __raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR); + + if (stop_mode) { + empgc0 |= MXC_SRPGCR_PCR; + empgc1 |= MXC_SRPGCR_PCR; + + __raw_writel(empgc0, MXC_SRPG_EMPGC0_SRPGCR); + __raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR); + } +} + +static int mx5_suspend_prepare(void) +{ + return clk_enable(gpc_dvfs_clk); +} + +static int mx5_suspend_enter(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_MEM: + mx5_cpu_lp_set(STOP_POWER_OFF); + break; + case PM_SUSPEND_STANDBY: + mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); + break; + default: + return -EINVAL; + } + + if (state == PM_SUSPEND_MEM) { + local_flush_tlb_all(); + flush_cache_all(); + + /*clear the EMPGC0/1 bits */ + __raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR); + __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); + } + cpu_do_idle(); + return 0; +} + +static void mx5_suspend_finish(void) +{ + clk_disable(gpc_dvfs_clk); +} + +static int mx5_pm_valid(suspend_state_t state) +{ + return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX); +} + +static const struct platform_suspend_ops mx5_suspend_ops = { + .valid = mx5_pm_valid, + .prepare = mx5_suspend_prepare, + .enter = mx5_suspend_enter, + .finish = mx5_suspend_finish, +}; + +static int __init mx5_pm_init(void) +{ + if (!cpu_is_mx51() && !cpu_is_mx53()) + return 0; + + if (gpc_dvfs_clk == NULL) + gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); + + if (!IS_ERR(gpc_dvfs_clk)) { + if (cpu_is_mx51()) + suspend_set_ops(&mx5_suspend_ops); + } else + return -EPERM; + + return 0; +} +device_initcall(mx5_pm_init); diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig deleted file mode 100644 index af0c212..0000000 --- a/arch/arm/mach-mx5/Kconfig +++ /dev/null @@ -1,244 +0,0 @@ -if ARCH_MX5 - -# ARCH_MX5/50/53 are left to mark places where prevent multi-soc in single -# image. So for most time, SOC_IMX50/51/53 should be used. - -config ARCH_MX51 - bool - -config ARCH_MX50 - bool - -config ARCH_MX53 - bool - -config SOC_IMX50 - bool - select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 - select MXC_TZIC - select ARCH_MXC_IOMUX_V3 - select ARCH_MXC_AUDMUX_V2 - select ARCH_HAS_CPUFREQ - select ARCH_MX50 - -config SOC_IMX51 - bool - select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 - select MXC_TZIC - select ARCH_MXC_IOMUX_V3 - select ARCH_MXC_AUDMUX_V2 - select ARCH_HAS_CPUFREQ - select ARCH_MX51 - -config SOC_IMX53 - bool - select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 - select MXC_TZIC - select ARCH_MXC_IOMUX_V3 - select ARCH_MX53 - -#comment "i.MX50 machines:" - -config MACH_MX50_RDP - bool "Support MX50 reference design platform" - depends on BROKEN - select SOC_IMX50 - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_SPI_IMX - help - Include support for MX50 reference design platform (RDP) board. This - includes specific configurations for the board and its peripherals. - -comment "i.MX51 machines:" - -config MACH_IMX51_DT - bool "Support i.MX51 platforms from device tree" - select SOC_IMX51 - select USE_OF - select MACH_MX51_BABBAGE - help - Include support for Freescale i.MX51 based platforms - using the device tree for discovery - -config MACH_MX51_BABBAGE - bool "Support MX51 BABBAGE platforms" - select SOC_IMX51 - select IMX_HAVE_PLATFORM_FSL_USB2_UDC - select IMX_HAVE_PLATFORM_IMX2_WDT - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_MXC_EHCI - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_SPI_IMX - help - Include support for MX51 Babbage platform, also known as MX51EVK in - u-boot. This includes specific configurations for the board and its - peripherals. - -config MACH_MX51_3DS - bool "Support MX51PDK (3DS)" - select SOC_IMX51 - select IMX_HAVE_PLATFORM_IMX2_WDT - select IMX_HAVE_PLATFORM_IMX_KEYPAD - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_SPI_IMX - select MXC_DEBUG_BOARD - help - Include support for MX51PDK (3DS) platform. This includes specific - configurations for the board and its peripherals. - -config MACH_EUKREA_CPUIMX51 - bool "Support Eukrea CPUIMX51 module" - select SOC_IMX51 - select IMX_HAVE_PLATFORM_FSL_USB2_UDC - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_MXC_EHCI - select IMX_HAVE_PLATFORM_MXC_NAND - select IMX_HAVE_PLATFORM_SPI_IMX - help - Include support for Eukrea CPUIMX51 platform. This includes - specific configurations for the module and its peripherals. - -choice - prompt "Baseboard" - depends on MACH_EUKREA_CPUIMX51 - default MACH_EUKREA_MBIMX51_BASEBOARD - -config MACH_EUKREA_MBIMX51_BASEBOARD - prompt "Eukrea MBIMX51 development board" - bool - select IMX_HAVE_PLATFORM_IMX_KEYPAD - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select LEDS_GPIO_REGISTER - help - This adds board specific devices that can be found on Eukrea's - MBIMX51 evaluation board. - -endchoice - -config MACH_EUKREA_CPUIMX51SD - bool "Support Eukrea CPUIMX51SD module" - select SOC_IMX51 - select IMX_HAVE_PLATFORM_FSL_USB2_UDC - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_MXC_EHCI - select IMX_HAVE_PLATFORM_MXC_NAND - select IMX_HAVE_PLATFORM_SPI_IMX - help - Include support for Eukrea CPUIMX51SD platform. This includes - specific configurations for the module and its peripherals. - -choice - prompt "Baseboard" - depends on MACH_EUKREA_CPUIMX51SD - default MACH_EUKREA_MBIMXSD51_BASEBOARD - -config MACH_EUKREA_MBIMXSD51_BASEBOARD - prompt "Eukrea MBIMXSD development board" - bool - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select LEDS_GPIO_REGISTER - help - This adds board specific devices that can be found on Eukrea's - MBIMXSD evaluation board. - -endchoice - -config MX51_EFIKA_COMMON - bool - select SOC_IMX51 - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_MXC_EHCI - select IMX_HAVE_PLATFORM_PATA_IMX - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_SPI_IMX - select MXC_ULPI if USB_ULPI - -config MACH_MX51_EFIKAMX - bool "Support MX51 Genesi Efika MX nettop" - select LEDS_GPIO_REGISTER - select MX51_EFIKA_COMMON - help - Include support for Genesi Efika MX nettop. This includes specific - configurations for the board and its peripherals. - -config MACH_MX51_EFIKASB - bool "Support MX51 Genesi Efika Smartbook" - select LEDS_GPIO_REGISTER - select MX51_EFIKA_COMMON - help - Include support for Genesi Efika Smartbook. This includes specific - configurations for the board and its peripherals. - -comment "i.MX53 machines:" - -config MACH_IMX53_DT - bool "Support i.MX53 platforms from device tree" - select SOC_IMX53 - select USE_OF - select MACH_MX53_ARD - select MACH_MX53_EVK - select MACH_MX53_LOCO - select MACH_MX53_SMD - help - Include support for Freescale i.MX53 based platforms - using the device tree for discovery - -config MACH_MX53_EVK - bool "Support MX53 EVK platforms" - select SOC_IMX53 - select IMX_HAVE_PLATFORM_IMX2_WDT - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_SPI_IMX - select LEDS_GPIO_REGISTER - help - Include support for MX53 EVK platform. This includes specific - configurations for the board and its peripherals. - -config MACH_MX53_SMD - bool "Support MX53 SMD platforms" - select SOC_IMX53 - select IMX_HAVE_PLATFORM_IMX2_WDT - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - help - Include support for MX53 SMD platform. This includes specific - configurations for the board and its peripherals. - -config MACH_MX53_LOCO - bool "Support MX53 LOCO platforms" - select SOC_IMX53 - select IMX_HAVE_PLATFORM_IMX2_WDT - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_GPIO_KEYS - select LEDS_GPIO_REGISTER - help - Include support for MX53 LOCO platform. This includes specific - configurations for the board and its peripherals. - -config MACH_MX53_ARD - bool "Support MX53 ARD platforms" - select SOC_IMX53 - select IMX_HAVE_PLATFORM_IMX2_WDT - select IMX_HAVE_PLATFORM_IMX_I2C - select IMX_HAVE_PLATFORM_IMX_UART - select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX - select IMX_HAVE_PLATFORM_GPIO_KEYS - help - Include support for MX53 ARD platform. This includes specific - configurations for the board and its peripherals. - -endif diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile deleted file mode 100644 index 0fc6080..0000000 --- a/arch/arm/mach-mx5/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# -# Makefile for the linux kernel. -# - -# Object file lists. -obj-y := cpu.o mm.o clock-mx51-mx53.o ehci.o system.o - -obj-$(CONFIG_PM) += pm-imx5.o -obj-$(CONFIG_CPU_FREQ_IMX) += cpu_op-mx51.o -obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o -obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o -obj-$(CONFIG_MACH_MX53_EVK) += board-mx53_evk.o -obj-$(CONFIG_MACH_MX53_SMD) += board-mx53_smd.o -obj-$(CONFIG_MACH_MX53_LOCO) += board-mx53_loco.o -obj-$(CONFIG_MACH_MX53_ARD) += board-mx53_ard.o -obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o -obj-$(CONFIG_MACH_EUKREA_MBIMX51_BASEBOARD) += eukrea_mbimx51-baseboard.o -obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += board-cpuimx51sd.o -obj-$(CONFIG_MACH_EUKREA_MBIMXSD51_BASEBOARD) += eukrea_mbimxsd-baseboard.o -obj-$(CONFIG_MX51_EFIKA_COMMON) += mx51_efika.o -obj-$(CONFIG_MACH_MX51_EFIKAMX) += board-mx51_efikamx.o -obj-$(CONFIG_MACH_MX51_EFIKASB) += board-mx51_efikasb.o -obj-$(CONFIG_MACH_MX50_RDP) += board-mx50_rdp.o - -obj-$(CONFIG_MACH_IMX51_DT) += imx51-dt.o -obj-$(CONFIG_MACH_IMX53_DT) += imx53-dt.o diff --git a/arch/arm/mach-mx5/Makefile.boot b/arch/arm/mach-mx5/Makefile.boot deleted file mode 100644 index ca207ca..0000000 --- a/arch/arm/mach-mx5/Makefile.boot +++ /dev/null @@ -1,9 +0,0 @@ - zreladdr-$(CONFIG_ARCH_MX50) += 0x70008000 -params_phys-$(CONFIG_ARCH_MX50) := 0x70000100 -initrd_phys-$(CONFIG_ARCH_MX50) := 0x70800000 - zreladdr-$(CONFIG_ARCH_MX51) += 0x90008000 -params_phys-$(CONFIG_ARCH_MX51) := 0x90000100 -initrd_phys-$(CONFIG_ARCH_MX51) := 0x90800000 - zreladdr-$(CONFIG_ARCH_MX53) += 0x70008000 -params_phys-$(CONFIG_ARCH_MX53) := 0x70000100 -initrd_phys-$(CONFIG_ARCH_MX53) := 0x70800000 diff --git a/arch/arm/mach-mx5/board-cpuimx51.c b/arch/arm/mach-mx5/board-cpuimx51.c deleted file mode 100644 index 1fc1103..0000000 --- a/arch/arm/mach-mx5/board-cpuimx51.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * - * Copyright (C) 2010 Eric Bénard - * - * based on board-mx51_babbage.c which is - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2009-2010 Amit Kucheria - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" - -#define CPUIMX51_USBH1_STP IMX_GPIO_NR(1, 27) -#define CPUIMX51_QUARTA_GPIO IMX_GPIO_NR(3, 28) -#define CPUIMX51_QUARTB_GPIO IMX_GPIO_NR(3, 25) -#define CPUIMX51_QUARTC_GPIO IMX_GPIO_NR(3, 26) -#define CPUIMX51_QUARTD_GPIO IMX_GPIO_NR(3, 27) -#define CPUIMX51_QUART_XTAL 14745600 -#define CPUIMX51_QUART_REGSHIFT 17 - -/* USB_CTRL_1 */ -#define MX51_USB_CTRL_1_OFFSET 0x10 -#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) - -#define MX51_USB_PLLDIV_12_MHZ 0x00 -#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 -#define MX51_USB_PLL_DIV_24_MHZ 0x02 - -static struct plat_serial8250_port serial_platform_data[] = { - { - .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x400000), - .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTA_GPIO), - .irqflags = IRQF_TRIGGER_HIGH, - .uartclk = CPUIMX51_QUART_XTAL, - .regshift = CPUIMX51_QUART_REGSHIFT, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x800000), - .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTB_GPIO), - .irqflags = IRQF_TRIGGER_HIGH, - .uartclk = CPUIMX51_QUART_XTAL, - .regshift = CPUIMX51_QUART_REGSHIFT, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x1000000), - .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTC_GPIO), - .irqflags = IRQF_TRIGGER_HIGH, - .uartclk = CPUIMX51_QUART_XTAL, - .regshift = CPUIMX51_QUART_REGSHIFT, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - .mapbase = (unsigned long)(MX51_CS1_BASE_ADDR + 0x2000000), - .irq = IMX_GPIO_TO_IRQ(CPUIMX51_QUARTD_GPIO), - .irqflags = IRQF_TRIGGER_HIGH, - .uartclk = CPUIMX51_QUART_XTAL, - .regshift = CPUIMX51_QUART_REGSHIFT, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - } -}; - -static struct platform_device serial_device = { - .name = "serial8250", - .id = 0, - .dev = { - .platform_data = serial_platform_data, - }, -}; - -static struct platform_device *devices[] __initdata = { - &serial_device, -}; - -static iomux_v3_cfg_t eukrea_cpuimx51_pads[] = { - /* UART1 */ - MX51_PAD_UART1_RXD__UART1_RXD, - MX51_PAD_UART1_TXD__UART1_TXD, - MX51_PAD_UART1_RTS__UART1_RTS, - MX51_PAD_UART1_CTS__UART1_CTS, - - /* I2C2 */ - MX51_PAD_GPIO1_2__I2C2_SCL, - MX51_PAD_GPIO1_3__I2C2_SDA, - MX51_PAD_NANDF_D10__GPIO3_30, - - /* QUART IRQ */ - MX51_PAD_NANDF_D15__GPIO3_25, - MX51_PAD_NANDF_D14__GPIO3_26, - MX51_PAD_NANDF_D13__GPIO3_27, - MX51_PAD_NANDF_D12__GPIO3_28, - - /* USB HOST1 */ - MX51_PAD_USBH1_CLK__USBH1_CLK, - MX51_PAD_USBH1_DIR__USBH1_DIR, - MX51_PAD_USBH1_NXT__USBH1_NXT, - MX51_PAD_USBH1_DATA0__USBH1_DATA0, - MX51_PAD_USBH1_DATA1__USBH1_DATA1, - MX51_PAD_USBH1_DATA2__USBH1_DATA2, - MX51_PAD_USBH1_DATA3__USBH1_DATA3, - MX51_PAD_USBH1_DATA4__USBH1_DATA4, - MX51_PAD_USBH1_DATA5__USBH1_DATA5, - MX51_PAD_USBH1_DATA6__USBH1_DATA6, - MX51_PAD_USBH1_DATA7__USBH1_DATA7, - MX51_PAD_USBH1_STP__USBH1_STP, -}; - -static const struct mxc_nand_platform_data - eukrea_cpuimx51_nand_board_info __initconst = { - .width = 1, - .hw_ecc = 1, - .flash_bbt = 1, -}; - -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static const -struct imxi2c_platform_data eukrea_cpuimx51_i2c_data __initconst = { - .bitrate = 100000, -}; - -static struct i2c_board_info eukrea_cpuimx51_i2c_devices[] = { - { - I2C_BOARD_INFO("pcf8563", 0x51), - }, -}; - -/* This function is board specific as the bit mask for the plldiv will also -be different for other Freescale SoCs, thus a common bitmask is not -possible and cannot get place in /plat-mxc/ehci.c.*/ -static int initialize_otg_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - /* Set the PHY clock to 19.2MHz */ - v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; - v |= MX51_USB_PLL_DIV_19_2_MHZ; - __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(0, MXC_EHCI_INTERNAL_PHY); -} - -static int initialize_usbh1_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - /* The clock for the USBH1 ULPI port will come externally from the PHY. */ - v = __raw_readl(usbother_base + MX51_USB_CTRL_1_OFFSET); - __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + MX51_USB_CTRL_1_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(1, MXC_EHCI_POWER_PINS_ENABLED | - MXC_EHCI_ITC_NO_THRESHOLD); -} - -static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { - .init = initialize_otg_port, - .portsc = MXC_EHCI_UTMI_16BIT, -}; - -static const struct fsl_usb2_platform_data usb_pdata __initconst = { - .operating_mode = FSL_USB2_DR_DEVICE, - .phy_mode = FSL_USB2_PHY_UTMI_WIDE, -}; - -static const struct mxc_usbh_platform_data usbh1_config __initconst = { - .init = initialize_usbh1_port, - .portsc = MXC_EHCI_MODE_ULPI, -}; - -static int otg_mode_host; - -static int __init eukrea_cpuimx51_otg_mode(char *options) -{ - if (!strcmp(options, "host")) - otg_mode_host = 1; - else if (!strcmp(options, "device")) - otg_mode_host = 0; - else - pr_info("otg_mode neither \"host\" nor \"device\". " - "Defaulting to device\n"); - return 0; -} -__setup("otg_mode=", eukrea_cpuimx51_otg_mode); - -/* - * Board specific initialization. - */ -static void __init eukrea_cpuimx51_init(void) -{ - imx51_soc_init(); - - mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx51_pads, - ARRAY_SIZE(eukrea_cpuimx51_pads)); - - imx51_add_imx_uart(0, &uart_pdata); - imx51_add_mxc_nand(&eukrea_cpuimx51_nand_board_info); - - gpio_request(CPUIMX51_QUARTA_GPIO, "quarta_irq"); - gpio_direction_input(CPUIMX51_QUARTA_GPIO); - gpio_free(CPUIMX51_QUARTA_GPIO); - gpio_request(CPUIMX51_QUARTB_GPIO, "quartb_irq"); - gpio_direction_input(CPUIMX51_QUARTB_GPIO); - gpio_free(CPUIMX51_QUARTB_GPIO); - gpio_request(CPUIMX51_QUARTC_GPIO, "quartc_irq"); - gpio_direction_input(CPUIMX51_QUARTC_GPIO); - gpio_free(CPUIMX51_QUARTC_GPIO); - gpio_request(CPUIMX51_QUARTD_GPIO, "quartd_irq"); - gpio_direction_input(CPUIMX51_QUARTD_GPIO); - gpio_free(CPUIMX51_QUARTD_GPIO); - - imx51_add_fec(NULL); - platform_add_devices(devices, ARRAY_SIZE(devices)); - - imx51_add_imx_i2c(1, &eukrea_cpuimx51_i2c_data); - i2c_register_board_info(1, eukrea_cpuimx51_i2c_devices, - ARRAY_SIZE(eukrea_cpuimx51_i2c_devices)); - - if (otg_mode_host) - imx51_add_mxc_ehci_otg(&dr_utmi_config); - else { - initialize_otg_port(NULL); - imx51_add_fsl_usb2_udc(&usb_pdata); - } - imx51_add_mxc_ehci_hs(1, &usbh1_config); - -#ifdef CONFIG_MACH_EUKREA_MBIMX51_BASEBOARD - eukrea_mbimx51_baseboard_init(); -#endif -} - -static void __init eukrea_cpuimx51_timer_init(void) -{ - mx51_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mxc_timer = { - .init = eukrea_cpuimx51_timer_init, -}; - -MACHINE_START(EUKREA_CPUIMX51, "Eukrea CPUIMX51 Module") - /* Maintainer: Eric Bénard */ - .atag_offset = 0x100, - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .timer = &mxc_timer, - .init_machine = eukrea_cpuimx51_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-cpuimx51sd.c b/arch/arm/mach-mx5/board-cpuimx51sd.c deleted file mode 100644 index 52a11c1..0000000 --- a/arch/arm/mach-mx5/board-cpuimx51sd.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * - * Copyright (C) 2010 Eric Bénard - * - * based on board-mx51_babbage.c which is - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2009-2010 Amit Kucheria - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" -#include "cpu_op-mx51.h" - -#define USBH1_RST IMX_GPIO_NR(2, 28) -#define ETH_RST IMX_GPIO_NR(2, 31) -#define TSC2007_IRQGPIO IMX_GPIO_NR(3, 12) -#define CAN_IRQGPIO IMX_GPIO_NR(1, 1) -#define CAN_RST IMX_GPIO_NR(4, 15) -#define CAN_NCS IMX_GPIO_NR(4, 24) -#define CAN_RXOBF IMX_GPIO_NR(1, 4) -#define CAN_RX1BF IMX_GPIO_NR(1, 6) -#define CAN_TXORTS IMX_GPIO_NR(1, 7) -#define CAN_TX1RTS IMX_GPIO_NR(1, 8) -#define CAN_TX2RTS IMX_GPIO_NR(1, 9) -#define I2C_SCL IMX_GPIO_NR(4, 16) -#define I2C_SDA IMX_GPIO_NR(4, 17) - -/* USB_CTRL_1 */ -#define MX51_USB_CTRL_1_OFFSET 0x10 -#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) - -#define MX51_USB_PLLDIV_12_MHZ 0x00 -#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 -#define MX51_USB_PLL_DIV_24_MHZ 0x02 - -static iomux_v3_cfg_t eukrea_cpuimx51sd_pads[] = { - /* UART1 */ - MX51_PAD_UART1_RXD__UART1_RXD, - MX51_PAD_UART1_TXD__UART1_TXD, - MX51_PAD_UART1_RTS__UART1_RTS, - MX51_PAD_UART1_CTS__UART1_CTS, - - /* USB HOST1 */ - MX51_PAD_USBH1_CLK__USBH1_CLK, - MX51_PAD_USBH1_DIR__USBH1_DIR, - MX51_PAD_USBH1_NXT__USBH1_NXT, - MX51_PAD_USBH1_DATA0__USBH1_DATA0, - MX51_PAD_USBH1_DATA1__USBH1_DATA1, - MX51_PAD_USBH1_DATA2__USBH1_DATA2, - MX51_PAD_USBH1_DATA3__USBH1_DATA3, - MX51_PAD_USBH1_DATA4__USBH1_DATA4, - MX51_PAD_USBH1_DATA5__USBH1_DATA5, - MX51_PAD_USBH1_DATA6__USBH1_DATA6, - MX51_PAD_USBH1_DATA7__USBH1_DATA7, - MX51_PAD_USBH1_STP__USBH1_STP, - MX51_PAD_EIM_CS3__GPIO2_28, /* PHY nRESET */ - - /* FEC */ - MX51_PAD_EIM_DTACK__GPIO2_31, /* PHY nRESET */ - - /* HSI2C */ - MX51_PAD_I2C1_CLK__GPIO4_16, - MX51_PAD_I2C1_DAT__GPIO4_17, - - /* CAN */ - MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, - MX51_PAD_CSPI1_MISO__ECSPI1_MISO, - MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, - MX51_PAD_CSPI1_SS0__GPIO4_24, /* nCS */ - MX51_PAD_CSI2_PIXCLK__GPIO4_15, /* nReset */ - MX51_PAD_GPIO1_1__GPIO1_1, /* IRQ */ - MX51_PAD_GPIO1_4__GPIO1_4, /* Control signals */ - MX51_PAD_GPIO1_6__GPIO1_6, - MX51_PAD_GPIO1_7__GPIO1_7, - MX51_PAD_GPIO1_8__GPIO1_8, - MX51_PAD_GPIO1_9__GPIO1_9, - - /* Touchscreen */ - /* IRQ */ - NEW_PAD_CTRL(MX51_PAD_GPIO_NAND__GPIO_NAND, PAD_CTL_PUS_22K_UP | - PAD_CTL_PKE | PAD_CTL_SRE_FAST | - PAD_CTL_DSE_HIGH | PAD_CTL_PUE | PAD_CTL_HYS), -}; - -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct tsc2007_platform_data tsc2007_info = { - .model = 2007, - .x_plate_ohms = 180, -}; - -static struct i2c_board_info eukrea_cpuimx51sd_i2c_devices[] = { - { - I2C_BOARD_INFO("pcf8563", 0x51), - }, { - I2C_BOARD_INFO("tsc2007", 0x49), - .type = "tsc2007", - .platform_data = &tsc2007_info, - .irq = IMX_GPIO_TO_IRQ(TSC2007_IRQGPIO), - }, -}; - -static const struct mxc_nand_platform_data - eukrea_cpuimx51sd_nand_board_info __initconst = { - .width = 1, - .hw_ecc = 1, - .flash_bbt = 1, -}; - -/* This function is board specific as the bit mask for the plldiv will also -be different for other Freescale SoCs, thus a common bitmask is not -possible and cannot get place in /plat-mxc/ehci.c.*/ -static int initialize_otg_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - /* Set the PHY clock to 19.2MHz */ - v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; - v |= MX51_USB_PLL_DIV_19_2_MHZ; - __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(0, MXC_EHCI_INTERNAL_PHY); -} - -static int initialize_usbh1_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - /* The clock for the USBH1 ULPI port will come from the PHY. */ - v = __raw_readl(usbother_base + MX51_USB_CTRL_1_OFFSET); - __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, - usbother_base + MX51_USB_CTRL_1_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(1, MXC_EHCI_POWER_PINS_ENABLED | - MXC_EHCI_ITC_NO_THRESHOLD); -} - -static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { - .init = initialize_otg_port, - .portsc = MXC_EHCI_UTMI_16BIT, -}; - -static const struct fsl_usb2_platform_data usb_pdata __initconst = { - .operating_mode = FSL_USB2_DR_DEVICE, - .phy_mode = FSL_USB2_PHY_UTMI_WIDE, -}; - -static const struct mxc_usbh_platform_data usbh1_config __initconst = { - .init = initialize_usbh1_port, - .portsc = MXC_EHCI_MODE_ULPI, -}; - -static int otg_mode_host; - -static int __init eukrea_cpuimx51sd_otg_mode(char *options) -{ - if (!strcmp(options, "host")) - otg_mode_host = 1; - else if (!strcmp(options, "device")) - otg_mode_host = 0; - else - pr_info("otg_mode neither \"host\" nor \"device\". " - "Defaulting to device\n"); - return 0; -} -__setup("otg_mode=", eukrea_cpuimx51sd_otg_mode); - -static struct i2c_gpio_platform_data pdata = { - .sda_pin = I2C_SDA, - .sda_is_open_drain = 0, - .scl_pin = I2C_SCL, - .scl_is_open_drain = 0, - .udelay = 2, -}; - -static struct platform_device hsi2c_gpio_device = { - .name = "i2c-gpio", - .id = 0, - .dev.platform_data = &pdata, -}; - -static struct mcp251x_platform_data mcp251x_info = { - .oscillator_frequency = 24E6, -}; - -static struct spi_board_info cpuimx51sd_spi_device[] = { - { - .modalias = "mcp2515", - .max_speed_hz = 10000000, - .bus_num = 0, - .mode = SPI_MODE_0, - .chip_select = 0, - .platform_data = &mcp251x_info, - .irq = IMX_GPIO_TO_IRQ(CAN_IRQGPIO) - }, -}; - -static int cpuimx51sd_spi1_cs[] = { - CAN_NCS, -}; - -static const struct spi_imx_master cpuimx51sd_ecspi1_pdata __initconst = { - .chipselect = cpuimx51sd_spi1_cs, - .num_chipselect = ARRAY_SIZE(cpuimx51sd_spi1_cs), -}; - -static struct platform_device *platform_devices[] __initdata = { - &hsi2c_gpio_device, -}; - -static void __init eukrea_cpuimx51sd_init(void) -{ - imx51_soc_init(); - - mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx51sd_pads, - ARRAY_SIZE(eukrea_cpuimx51sd_pads)); - -#if defined(CONFIG_CPU_FREQ_IMX) - get_cpu_op = mx51_get_cpu_op; -#endif - - imx51_add_imx_uart(0, &uart_pdata); - imx51_add_mxc_nand(&eukrea_cpuimx51sd_nand_board_info); - - gpio_request(ETH_RST, "eth_rst"); - gpio_set_value(ETH_RST, 1); - imx51_add_fec(NULL); - - gpio_request(CAN_IRQGPIO, "can_irq"); - gpio_direction_input(CAN_IRQGPIO); - gpio_free(CAN_IRQGPIO); - gpio_request(CAN_NCS, "can_ncs"); - gpio_direction_output(CAN_NCS, 1); - gpio_free(CAN_NCS); - gpio_request(CAN_RST, "can_rst"); - gpio_direction_output(CAN_RST, 0); - msleep(20); - gpio_set_value(CAN_RST, 1); - imx51_add_ecspi(0, &cpuimx51sd_ecspi1_pdata); - spi_register_board_info(cpuimx51sd_spi_device, - ARRAY_SIZE(cpuimx51sd_spi_device)); - - gpio_request(TSC2007_IRQGPIO, "tsc2007_irq"); - gpio_direction_input(TSC2007_IRQGPIO); - gpio_free(TSC2007_IRQGPIO); - - i2c_register_board_info(0, eukrea_cpuimx51sd_i2c_devices, - ARRAY_SIZE(eukrea_cpuimx51sd_i2c_devices)); - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); - - if (otg_mode_host) - imx51_add_mxc_ehci_otg(&dr_utmi_config); - else { - initialize_otg_port(NULL); - imx51_add_fsl_usb2_udc(&usb_pdata); - } - - gpio_request(USBH1_RST, "usb_rst"); - gpio_direction_output(USBH1_RST, 0); - msleep(20); - gpio_set_value(USBH1_RST, 1); - imx51_add_mxc_ehci_hs(1, &usbh1_config); - -#ifdef CONFIG_MACH_EUKREA_MBIMXSD51_BASEBOARD - eukrea_mbimxsd51_baseboard_init(); -#endif -} - -static void __init eukrea_cpuimx51sd_timer_init(void) -{ - mx51_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mxc_timer = { - .init = eukrea_cpuimx51sd_timer_init, -}; - -MACHINE_START(EUKREA_CPUIMX51SD, "Eukrea CPUIMX51SD") - /* Maintainer: Eric Bénard */ - .atag_offset = 0x100, - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .timer = &mxc_timer, - .init_machine = eukrea_cpuimx51sd_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx50_rdp.c b/arch/arm/mach-mx5/board-mx50_rdp.c deleted file mode 100644 index fc3621d..0000000 --- a/arch/arm/mach-mx5/board-mx50_rdp.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * 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. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "devices-imx50.h" - -#define FEC_EN IMX_GPIO_NR(6, 23) -#define FEC_RESET_B IMX_GPIO_NR(4, 12) - -static iomux_v3_cfg_t mx50_rdp_pads[] __initdata = { - /* SD1 */ - MX50_PAD_ECSPI2_SS0__GPIO_4_19, - MX50_PAD_EIM_CRE__GPIO_1_27, - MX50_PAD_SD1_CMD__SD1_CMD, - - MX50_PAD_SD1_CLK__SD1_CLK, - MX50_PAD_SD1_D0__SD1_D0, - MX50_PAD_SD1_D1__SD1_D1, - MX50_PAD_SD1_D2__SD1_D2, - MX50_PAD_SD1_D3__SD1_D3, - - /* SD2 */ - MX50_PAD_SD2_CD__GPIO_5_17, - MX50_PAD_SD2_WP__GPIO_5_16, - MX50_PAD_SD2_CMD__SD2_CMD, - MX50_PAD_SD2_CLK__SD2_CLK, - MX50_PAD_SD2_D0__SD2_D0, - MX50_PAD_SD2_D1__SD2_D1, - MX50_PAD_SD2_D2__SD2_D2, - MX50_PAD_SD2_D3__SD2_D3, - MX50_PAD_SD2_D4__SD2_D4, - MX50_PAD_SD2_D5__SD2_D5, - MX50_PAD_SD2_D6__SD2_D6, - MX50_PAD_SD2_D7__SD2_D7, - - /* SD3 */ - MX50_PAD_SD3_CMD__SD3_CMD, - MX50_PAD_SD3_CLK__SD3_CLK, - MX50_PAD_SD3_D0__SD3_D0, - MX50_PAD_SD3_D1__SD3_D1, - MX50_PAD_SD3_D2__SD3_D2, - MX50_PAD_SD3_D3__SD3_D3, - MX50_PAD_SD3_D4__SD3_D4, - MX50_PAD_SD3_D5__SD3_D5, - MX50_PAD_SD3_D6__SD3_D6, - MX50_PAD_SD3_D7__SD3_D7, - - /* PWR_INT */ - MX50_PAD_ECSPI2_MISO__GPIO_4_18, - - /* UART pad setting */ - MX50_PAD_UART1_TXD__UART1_TXD, - MX50_PAD_UART1_RXD__UART1_RXD, - MX50_PAD_UART1_RTS__UART1_RTS, - MX50_PAD_UART2_TXD__UART2_TXD, - MX50_PAD_UART2_RXD__UART2_RXD, - MX50_PAD_UART2_CTS__UART2_CTS, - MX50_PAD_UART2_RTS__UART2_RTS, - - MX50_PAD_I2C1_SCL__I2C1_SCL, - MX50_PAD_I2C1_SDA__I2C1_SDA, - MX50_PAD_I2C2_SCL__I2C2_SCL, - MX50_PAD_I2C2_SDA__I2C2_SDA, - - MX50_PAD_EPITO__USBH1_PWR, - /* Need to comment below line if - * one needs to debug owire. - */ - MX50_PAD_OWIRE__USBH1_OC, - /* using gpio to control otg pwr */ - MX50_PAD_PWM2__GPIO_6_25, - MX50_PAD_I2C3_SCL__USBOTG_OC, - - MX50_PAD_SSI_RXC__FEC_MDIO, - MX50_PAD_SSI_RXFS__FEC_MDC, - MX50_PAD_DISP_D0__FEC_TXCLK, - MX50_PAD_DISP_D1__FEC_RX_ER, - MX50_PAD_DISP_D2__FEC_RX_DV, - MX50_PAD_DISP_D3__FEC_RXD1, - MX50_PAD_DISP_D4__FEC_RXD0, - MX50_PAD_DISP_D5__FEC_TX_EN, - MX50_PAD_DISP_D6__FEC_TXD1, - MX50_PAD_DISP_D7__FEC_TXD0, - MX50_PAD_I2C3_SDA__GPIO_6_23, - MX50_PAD_ECSPI1_SCLK__GPIO_4_12, - - MX50_PAD_CSPI_SS0__CSPI_SS0, - MX50_PAD_ECSPI1_MOSI__CSPI_SS1, - MX50_PAD_CSPI_MOSI__CSPI_MOSI, - MX50_PAD_CSPI_MISO__CSPI_MISO, - - /* SGTL500_OSC_EN */ - MX50_PAD_UART1_CTS__GPIO_6_8, - - /* SGTL_AMP_SHDN */ - MX50_PAD_UART3_RXD__GPIO_6_15, - - /* Keypad */ - MX50_PAD_KEY_COL0__KEY_COL0, - MX50_PAD_KEY_ROW0__KEY_ROW0, - MX50_PAD_KEY_COL1__KEY_COL1, - MX50_PAD_KEY_ROW1__KEY_ROW1, - MX50_PAD_KEY_COL2__KEY_COL2, - MX50_PAD_KEY_ROW2__KEY_ROW2, - MX50_PAD_KEY_COL3__KEY_COL3, - MX50_PAD_KEY_ROW3__KEY_ROW3, - MX50_PAD_EIM_DA0__KEY_COL4, - MX50_PAD_EIM_DA1__KEY_ROW4, - MX50_PAD_EIM_DA2__KEY_COL5, - MX50_PAD_EIM_DA3__KEY_ROW5, - MX50_PAD_EIM_DA4__KEY_COL6, - MX50_PAD_EIM_DA5__KEY_ROW6, - MX50_PAD_EIM_DA6__KEY_COL7, - MX50_PAD_EIM_DA7__KEY_ROW7, - /*EIM pads */ - MX50_PAD_EIM_DA8__GPIO_1_8, - MX50_PAD_EIM_DA9__GPIO_1_9, - MX50_PAD_EIM_DA10__GPIO_1_10, - MX50_PAD_EIM_DA11__GPIO_1_11, - MX50_PAD_EIM_DA12__GPIO_1_12, - MX50_PAD_EIM_DA13__GPIO_1_13, - MX50_PAD_EIM_DA14__GPIO_1_14, - MX50_PAD_EIM_DA15__GPIO_1_15, - MX50_PAD_EIM_CS2__GPIO_1_16, - MX50_PAD_EIM_CS1__GPIO_1_17, - MX50_PAD_EIM_CS0__GPIO_1_18, - MX50_PAD_EIM_EB0__GPIO_1_19, - MX50_PAD_EIM_EB1__GPIO_1_20, - MX50_PAD_EIM_WAIT__GPIO_1_21, - MX50_PAD_EIM_BCLK__GPIO_1_22, - MX50_PAD_EIM_RDY__GPIO_1_23, - MX50_PAD_EIM_OE__GPIO_1_24, -}; - -/* Serial ports */ -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static const struct fec_platform_data fec_data __initconst = { - .phy = PHY_INTERFACE_MODE_RMII, -}; - -static inline void mx50_rdp_fec_reset(void) -{ - gpio_request(FEC_EN, "fec-en"); - gpio_direction_output(FEC_EN, 0); - gpio_request(FEC_RESET_B, "fec-reset_b"); - gpio_direction_output(FEC_RESET_B, 0); - msleep(1); - gpio_set_value(FEC_RESET_B, 1); -} - -static const struct imxi2c_platform_data i2c_data __initconst = { - .bitrate = 100000, -}; - -/* - * Board specific initialization. - */ -static void __init mx50_rdp_board_init(void) -{ - imx50_soc_init(); - - mxc_iomux_v3_setup_multiple_pads(mx50_rdp_pads, - ARRAY_SIZE(mx50_rdp_pads)); - - imx50_add_imx_uart(0, &uart_pdata); - imx50_add_imx_uart(1, &uart_pdata); - mx50_rdp_fec_reset(); - imx50_add_fec(&fec_data); - imx50_add_imx_i2c(0, &i2c_data); - imx50_add_imx_i2c(1, &i2c_data); - imx50_add_imx_i2c(2, &i2c_data); -} - -static void __init mx50_rdp_timer_init(void) -{ - mx50_clocks_init(32768, 24000000, 22579200); -} - -static struct sys_timer mx50_rdp_timer = { - .init = mx50_rdp_timer_init, -}; - -MACHINE_START(MX50_RDP, "Freescale MX50 Reference Design Platform") - .map_io = mx50_map_io, - .init_early = imx50_init_early, - .init_irq = mx50_init_irq, - .handle_irq = imx50_handle_irq, - .timer = &mx50_rdp_timer, - .init_machine = mx50_rdp_board_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx51_3ds.c b/arch/arm/mach-mx5/board-mx51_3ds.c deleted file mode 100644 index 0578390..0000000 --- a/arch/arm/mach-mx5/board-mx51_3ds.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2010 Jason Wang - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" - -#define EXPIO_PARENT_INT gpio_to_irq(IMX_GPIO_NR(1, 6)) -#define MX51_3DS_ECSPI2_CS (GPIO_PORTC + 28) - -static iomux_v3_cfg_t mx51_3ds_pads[] = { - /* UART1 */ - MX51_PAD_UART1_RXD__UART1_RXD, - MX51_PAD_UART1_TXD__UART1_TXD, - MX51_PAD_UART1_RTS__UART1_RTS, - MX51_PAD_UART1_CTS__UART1_CTS, - - /* UART2 */ - MX51_PAD_UART2_RXD__UART2_RXD, - MX51_PAD_UART2_TXD__UART2_TXD, - MX51_PAD_EIM_D25__UART2_CTS, - MX51_PAD_EIM_D26__UART2_RTS, - - /* UART3 */ - MX51_PAD_UART3_RXD__UART3_RXD, - MX51_PAD_UART3_TXD__UART3_TXD, - MX51_PAD_EIM_D24__UART3_CTS, - MX51_PAD_EIM_D27__UART3_RTS, - - /* CPLD PARENT IRQ PIN */ - MX51_PAD_GPIO1_6__GPIO1_6, - - /* KPP */ - MX51_PAD_KEY_ROW0__KEY_ROW0, - MX51_PAD_KEY_ROW1__KEY_ROW1, - MX51_PAD_KEY_ROW2__KEY_ROW2, - MX51_PAD_KEY_ROW3__KEY_ROW3, - MX51_PAD_KEY_COL0__KEY_COL0, - MX51_PAD_KEY_COL1__KEY_COL1, - MX51_PAD_KEY_COL2__KEY_COL2, - MX51_PAD_KEY_COL3__KEY_COL3, - MX51_PAD_KEY_COL4__KEY_COL4, - MX51_PAD_KEY_COL5__KEY_COL5, - - /* eCSPI2 */ - MX51_PAD_NANDF_RB2__ECSPI2_SCLK, - MX51_PAD_NANDF_RB3__ECSPI2_MISO, - MX51_PAD_NANDF_D15__ECSPI2_MOSI, - MX51_PAD_NANDF_D12__GPIO3_28, -}; - -/* Serial ports */ -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static int mx51_3ds_board_keymap[] = { - KEY(0, 0, KEY_1), - KEY(0, 1, KEY_2), - KEY(0, 2, KEY_3), - KEY(0, 3, KEY_F1), - KEY(0, 4, KEY_UP), - KEY(0, 5, KEY_F2), - - KEY(1, 0, KEY_4), - KEY(1, 1, KEY_5), - KEY(1, 2, KEY_6), - KEY(1, 3, KEY_LEFT), - KEY(1, 4, KEY_SELECT), - KEY(1, 5, KEY_RIGHT), - - KEY(2, 0, KEY_7), - KEY(2, 1, KEY_8), - KEY(2, 2, KEY_9), - KEY(2, 3, KEY_F3), - KEY(2, 4, KEY_DOWN), - KEY(2, 5, KEY_F4), - - KEY(3, 0, KEY_0), - KEY(3, 1, KEY_OK), - KEY(3, 2, KEY_ESC), - KEY(3, 3, KEY_ENTER), - KEY(3, 4, KEY_MENU), - KEY(3, 5, KEY_BACK) -}; - -static const struct matrix_keymap_data mx51_3ds_map_data __initconst = { - .keymap = mx51_3ds_board_keymap, - .keymap_size = ARRAY_SIZE(mx51_3ds_board_keymap), -}; - -static int mx51_3ds_spi2_cs[] = { - MXC_SPI_CS(0), - MX51_3DS_ECSPI2_CS, -}; - -static const struct spi_imx_master mx51_3ds_ecspi2_pdata __initconst = { - .chipselect = mx51_3ds_spi2_cs, - .num_chipselect = ARRAY_SIZE(mx51_3ds_spi2_cs), -}; - -static struct spi_board_info mx51_3ds_spi_nor_device[] = { - { - .modalias = "m25p80", - .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */ - .bus_num = 1, - .chip_select = 1, - .mode = SPI_MODE_0, - .platform_data = NULL,}, -}; - -/* - * Board specific initialization. - */ -static void __init mx51_3ds_init(void) -{ - imx51_soc_init(); - - mxc_iomux_v3_setup_multiple_pads(mx51_3ds_pads, - ARRAY_SIZE(mx51_3ds_pads)); - - imx51_add_imx_uart(0, &uart_pdata); - imx51_add_imx_uart(1, &uart_pdata); - imx51_add_imx_uart(2, &uart_pdata); - - imx51_add_ecspi(1, &mx51_3ds_ecspi2_pdata); - spi_register_board_info(mx51_3ds_spi_nor_device, - ARRAY_SIZE(mx51_3ds_spi_nor_device)); - - if (mxc_expio_init(MX51_CS5_BASE_ADDR, EXPIO_PARENT_INT)) - printk(KERN_WARNING "Init of the debugboard failed, all " - "devices on the board are unusable.\n"); - - imx51_add_sdhci_esdhc_imx(0, NULL); - imx51_add_imx_keypad(&mx51_3ds_map_data); - imx51_add_imx2_wdt(0, NULL); -} - -static void __init mx51_3ds_timer_init(void) -{ - mx51_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mx51_3ds_timer = { - .init = mx51_3ds_timer_init, -}; - -MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board") - /* Maintainer: Freescale Semiconductor, Inc. */ - .atag_offset = 0x100, - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .timer = &mx51_3ds_timer, - .init_machine = mx51_3ds_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c deleted file mode 100644 index 5c83760..0000000 --- a/arch/arm/mach-mx5/board-mx51_babbage.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2009-2010 Amit Kucheria - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" -#include "cpu_op-mx51.h" - -#define BABBAGE_USB_HUB_RESET IMX_GPIO_NR(1, 7) -#define BABBAGE_USBH1_STP IMX_GPIO_NR(1, 27) -#define BABBAGE_USB_PHY_RESET IMX_GPIO_NR(2, 5) -#define BABBAGE_FEC_PHY_RESET IMX_GPIO_NR(2, 14) -#define BABBAGE_POWER_KEY IMX_GPIO_NR(2, 21) -#define BABBAGE_ECSPI1_CS0 IMX_GPIO_NR(4, 24) -#define BABBAGE_ECSPI1_CS1 IMX_GPIO_NR(4, 25) -#define BABBAGE_SD2_CD IMX_GPIO_NR(1, 6) -#define BABBAGE_SD2_WP IMX_GPIO_NR(1, 5) - -/* USB_CTRL_1 */ -#define MX51_USB_CTRL_1_OFFSET 0x10 -#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) - -#define MX51_USB_PLLDIV_12_MHZ 0x00 -#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 -#define MX51_USB_PLL_DIV_24_MHZ 0x02 - -static struct gpio_keys_button babbage_buttons[] = { - { - .gpio = BABBAGE_POWER_KEY, - .code = BTN_0, - .desc = "PWR", - .active_low = 1, - .wakeup = 1, - }, -}; - -static const struct gpio_keys_platform_data imx_button_data __initconst = { - .buttons = babbage_buttons, - .nbuttons = ARRAY_SIZE(babbage_buttons), -}; - -static iomux_v3_cfg_t mx51babbage_pads[] = { - /* UART1 */ - MX51_PAD_UART1_RXD__UART1_RXD, - MX51_PAD_UART1_TXD__UART1_TXD, - MX51_PAD_UART1_RTS__UART1_RTS, - MX51_PAD_UART1_CTS__UART1_CTS, - - /* UART2 */ - MX51_PAD_UART2_RXD__UART2_RXD, - MX51_PAD_UART2_TXD__UART2_TXD, - - /* UART3 */ - MX51_PAD_EIM_D25__UART3_RXD, - MX51_PAD_EIM_D26__UART3_TXD, - MX51_PAD_EIM_D27__UART3_RTS, - MX51_PAD_EIM_D24__UART3_CTS, - - /* I2C1 */ - MX51_PAD_EIM_D16__I2C1_SDA, - MX51_PAD_EIM_D19__I2C1_SCL, - - /* I2C2 */ - MX51_PAD_KEY_COL4__I2C2_SCL, - MX51_PAD_KEY_COL5__I2C2_SDA, - - /* HSI2C */ - MX51_PAD_I2C1_CLK__I2C1_CLK, - MX51_PAD_I2C1_DAT__I2C1_DAT, - - /* USB HOST1 */ - MX51_PAD_USBH1_CLK__USBH1_CLK, - MX51_PAD_USBH1_DIR__USBH1_DIR, - MX51_PAD_USBH1_NXT__USBH1_NXT, - MX51_PAD_USBH1_DATA0__USBH1_DATA0, - MX51_PAD_USBH1_DATA1__USBH1_DATA1, - MX51_PAD_USBH1_DATA2__USBH1_DATA2, - MX51_PAD_USBH1_DATA3__USBH1_DATA3, - MX51_PAD_USBH1_DATA4__USBH1_DATA4, - MX51_PAD_USBH1_DATA5__USBH1_DATA5, - MX51_PAD_USBH1_DATA6__USBH1_DATA6, - MX51_PAD_USBH1_DATA7__USBH1_DATA7, - - /* USB HUB reset line*/ - MX51_PAD_GPIO1_7__GPIO1_7, - - /* USB PHY reset line */ - MX51_PAD_EIM_D21__GPIO2_5, - - /* FEC */ - MX51_PAD_EIM_EB2__FEC_MDIO, - MX51_PAD_EIM_EB3__FEC_RDATA1, - MX51_PAD_EIM_CS2__FEC_RDATA2, - MX51_PAD_EIM_CS3__FEC_RDATA3, - MX51_PAD_EIM_CS4__FEC_RX_ER, - MX51_PAD_EIM_CS5__FEC_CRS, - MX51_PAD_NANDF_RB2__FEC_COL, - MX51_PAD_NANDF_RB3__FEC_RX_CLK, - MX51_PAD_NANDF_D9__FEC_RDATA0, - MX51_PAD_NANDF_D8__FEC_TDATA0, - MX51_PAD_NANDF_CS2__FEC_TX_ER, - MX51_PAD_NANDF_CS3__FEC_MDC, - MX51_PAD_NANDF_CS4__FEC_TDATA1, - MX51_PAD_NANDF_CS5__FEC_TDATA2, - MX51_PAD_NANDF_CS6__FEC_TDATA3, - MX51_PAD_NANDF_CS7__FEC_TX_EN, - MX51_PAD_NANDF_RDY_INT__FEC_TX_CLK, - - /* FEC PHY reset line */ - MX51_PAD_EIM_A20__GPIO2_14, - - /* SD 1 */ - MX51_PAD_SD1_CMD__SD1_CMD, - MX51_PAD_SD1_CLK__SD1_CLK, - MX51_PAD_SD1_DATA0__SD1_DATA0, - MX51_PAD_SD1_DATA1__SD1_DATA1, - MX51_PAD_SD1_DATA2__SD1_DATA2, - MX51_PAD_SD1_DATA3__SD1_DATA3, - /* CD/WP from controller */ - MX51_PAD_GPIO1_0__SD1_CD, - MX51_PAD_GPIO1_1__SD1_WP, - - /* SD 2 */ - MX51_PAD_SD2_CMD__SD2_CMD, - MX51_PAD_SD2_CLK__SD2_CLK, - MX51_PAD_SD2_DATA0__SD2_DATA0, - MX51_PAD_SD2_DATA1__SD2_DATA1, - MX51_PAD_SD2_DATA2__SD2_DATA2, - MX51_PAD_SD2_DATA3__SD2_DATA3, - /* CD/WP gpio */ - MX51_PAD_GPIO1_6__GPIO1_6, - MX51_PAD_GPIO1_5__GPIO1_5, - - /* eCSPI1 */ - MX51_PAD_CSPI1_MISO__ECSPI1_MISO, - MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, - MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, - MX51_PAD_CSPI1_SS0__GPIO4_24, - MX51_PAD_CSPI1_SS1__GPIO4_25, -}; - -/* Serial ports */ -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static const struct imxi2c_platform_data babbage_i2c_data __initconst = { - .bitrate = 100000, -}; - -static const struct imxi2c_platform_data babbage_hsi2c_data __initconst = { - .bitrate = 400000, -}; - -static struct gpio mx51_babbage_usbh1_gpios[] = { - { BABBAGE_USBH1_STP, GPIOF_OUT_INIT_LOW, "usbh1_stp" }, - { BABBAGE_USB_PHY_RESET, GPIOF_OUT_INIT_LOW, "usbh1_phy_reset" }, -}; - -static int gpio_usbh1_active(void) -{ - iomux_v3_cfg_t usbh1stp_gpio = MX51_PAD_USBH1_STP__GPIO1_27; - int ret; - - /* Set USBH1_STP to GPIO and toggle it */ - mxc_iomux_v3_setup_pad(usbh1stp_gpio); - ret = gpio_request_array(mx51_babbage_usbh1_gpios, - ARRAY_SIZE(mx51_babbage_usbh1_gpios)); - - if (ret) { - pr_debug("failed to get USBH1 pins: %d\n", ret); - return ret; - } - - msleep(100); - gpio_set_value(BABBAGE_USBH1_STP, 1); - gpio_set_value(BABBAGE_USB_PHY_RESET, 1); - gpio_free_array(mx51_babbage_usbh1_gpios, - ARRAY_SIZE(mx51_babbage_usbh1_gpios)); - return 0; -} - -static inline void babbage_usbhub_reset(void) -{ - int ret; - - /* Reset USB hub */ - ret = gpio_request_one(BABBAGE_USB_HUB_RESET, - GPIOF_OUT_INIT_LOW, "GPIO1_7"); - if (ret) { - printk(KERN_ERR"failed to get GPIO_USB_HUB_RESET: %d\n", ret); - return; - } - - msleep(2); - /* Deassert reset */ - gpio_set_value(BABBAGE_USB_HUB_RESET, 1); -} - -static inline void babbage_fec_reset(void) -{ - int ret; - - /* reset FEC PHY */ - ret = gpio_request_one(BABBAGE_FEC_PHY_RESET, - GPIOF_OUT_INIT_LOW, "fec-phy-reset"); - if (ret) { - printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); - return; - } - msleep(1); - gpio_set_value(BABBAGE_FEC_PHY_RESET, 1); -} - -/* This function is board specific as the bit mask for the plldiv will also -be different for other Freescale SoCs, thus a common bitmask is not -possible and cannot get place in /plat-mxc/ehci.c.*/ -static int initialize_otg_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - /* Set the PHY clock to 19.2MHz */ - v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; - v |= MX51_USB_PLL_DIV_19_2_MHZ; - __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(0, MXC_EHCI_INTERNAL_PHY); -} - -static int initialize_usbh1_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - /* The clock for the USBH1 ULPI port will come externally from the PHY. */ - v = __raw_readl(usbother_base + MX51_USB_CTRL_1_OFFSET); - __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + MX51_USB_CTRL_1_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(1, MXC_EHCI_POWER_PINS_ENABLED | - MXC_EHCI_ITC_NO_THRESHOLD); -} - -static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { - .init = initialize_otg_port, - .portsc = MXC_EHCI_UTMI_16BIT, -}; - -static const struct fsl_usb2_platform_data usb_pdata __initconst = { - .operating_mode = FSL_USB2_DR_DEVICE, - .phy_mode = FSL_USB2_PHY_UTMI_WIDE, -}; - -static const struct mxc_usbh_platform_data usbh1_config __initconst = { - .init = initialize_usbh1_port, - .portsc = MXC_EHCI_MODE_ULPI, -}; - -static int otg_mode_host; - -static int __init babbage_otg_mode(char *options) -{ - if (!strcmp(options, "host")) - otg_mode_host = 1; - else if (!strcmp(options, "device")) - otg_mode_host = 0; - else - pr_info("otg_mode neither \"host\" nor \"device\". " - "Defaulting to device\n"); - return 0; -} -__setup("otg_mode=", babbage_otg_mode); - -static struct spi_board_info mx51_babbage_spi_board_info[] __initdata = { - { - .modalias = "mtd_dataflash", - .max_speed_hz = 25000000, - .bus_num = 0, - .chip_select = 1, - .mode = SPI_MODE_0, - .platform_data = NULL, - }, -}; - -static int mx51_babbage_spi_cs[] = { - BABBAGE_ECSPI1_CS0, - BABBAGE_ECSPI1_CS1, -}; - -static const struct spi_imx_master mx51_babbage_spi_pdata __initconst = { - .chipselect = mx51_babbage_spi_cs, - .num_chipselect = ARRAY_SIZE(mx51_babbage_spi_cs), -}; - -static const struct esdhc_platform_data mx51_babbage_sd1_data __initconst = { - .cd_type = ESDHC_CD_CONTROLLER, - .wp_type = ESDHC_WP_CONTROLLER, -}; - -static const struct esdhc_platform_data mx51_babbage_sd2_data __initconst = { - .cd_gpio = BABBAGE_SD2_CD, - .wp_gpio = BABBAGE_SD2_WP, - .cd_type = ESDHC_CD_GPIO, - .wp_type = ESDHC_WP_GPIO, -}; - -void __init imx51_babbage_common_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx51babbage_pads, - ARRAY_SIZE(mx51babbage_pads)); -} - -/* - * Board specific initialization. - */ -static void __init mx51_babbage_init(void) -{ - iomux_v3_cfg_t usbh1stp = MX51_PAD_USBH1_STP__USBH1_STP; - iomux_v3_cfg_t power_key = NEW_PAD_CTRL(MX51_PAD_EIM_A27__GPIO2_21, - PAD_CTL_SRE_FAST | PAD_CTL_DSE_HIGH | PAD_CTL_PUS_100K_UP); - - imx51_soc_init(); - -#if defined(CONFIG_CPU_FREQ_IMX) - get_cpu_op = mx51_get_cpu_op; -#endif - imx51_babbage_common_init(); - - imx51_add_imx_uart(0, &uart_pdata); - imx51_add_imx_uart(1, NULL); - imx51_add_imx_uart(2, &uart_pdata); - - babbage_fec_reset(); - imx51_add_fec(NULL); - - /* Set the PAD settings for the pwr key. */ - mxc_iomux_v3_setup_pad(power_key); - imx_add_gpio_keys(&imx_button_data); - - imx51_add_imx_i2c(0, &babbage_i2c_data); - imx51_add_imx_i2c(1, &babbage_i2c_data); - imx51_add_hsi2c(&babbage_hsi2c_data); - - if (otg_mode_host) - imx51_add_mxc_ehci_otg(&dr_utmi_config); - else { - initialize_otg_port(NULL); - imx51_add_fsl_usb2_udc(&usb_pdata); - } - - gpio_usbh1_active(); - imx51_add_mxc_ehci_hs(1, &usbh1_config); - /* setback USBH1_STP to be function */ - mxc_iomux_v3_setup_pad(usbh1stp); - babbage_usbhub_reset(); - - imx51_add_sdhci_esdhc_imx(0, &mx51_babbage_sd1_data); - imx51_add_sdhci_esdhc_imx(1, &mx51_babbage_sd2_data); - - spi_register_board_info(mx51_babbage_spi_board_info, - ARRAY_SIZE(mx51_babbage_spi_board_info)); - imx51_add_ecspi(0, &mx51_babbage_spi_pdata); - imx51_add_imx2_wdt(0, NULL); -} - -static void __init mx51_babbage_timer_init(void) -{ - mx51_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mx51_babbage_timer = { - .init = mx51_babbage_timer_init, -}; - -MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board") - /* Maintainer: Amit Kucheria */ - .atag_offset = 0x100, - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .timer = &mx51_babbage_timer, - .init_machine = mx51_babbage_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx51_efikamx.c b/arch/arm/mach-mx5/board-mx51_efikamx.c deleted file mode 100644 index a9e4866..0000000 --- a/arch/arm/mach-mx5/board-mx51_efikamx.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2010 Linaro Limited - * - * based on code from the following - * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2009-2010 Pegatron Corporation. All Rights Reserved. - * Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" -#include "efika.h" - -#define EFIKAMX_PCBID0 IMX_GPIO_NR(3, 16) -#define EFIKAMX_PCBID1 IMX_GPIO_NR(3, 17) -#define EFIKAMX_PCBID2 IMX_GPIO_NR(3, 11) - -#define EFIKAMX_BLUE_LED IMX_GPIO_NR(3, 13) -#define EFIKAMX_GREEN_LED IMX_GPIO_NR(3, 14) -#define EFIKAMX_RED_LED IMX_GPIO_NR(3, 15) - -#define EFIKAMX_POWER_KEY IMX_GPIO_NR(2, 31) - -/* board 1.1 doesn't have same reset gpio */ -#define EFIKAMX_RESET1_1 IMX_GPIO_NR(3, 2) -#define EFIKAMX_RESET IMX_GPIO_NR(1, 4) - -#define EFIKAMX_POWEROFF IMX_GPIO_NR(4, 13) - -#define EFIKAMX_PMIC IMX_GPIO_NR(1, 6) - -/* the pci ids pin have pull up. they're driven low according to board id */ -#define MX51_PAD_PCBID0 IOMUX_PAD(0x518, 0x130, 3, 0x0, 0, PAD_CTL_PUS_100K_UP) -#define MX51_PAD_PCBID1 IOMUX_PAD(0x51C, 0x134, 3, 0x0, 0, PAD_CTL_PUS_100K_UP) -#define MX51_PAD_PCBID2 IOMUX_PAD(0x504, 0x128, 3, 0x0, 0, PAD_CTL_PUS_100K_UP) -#define MX51_PAD_PWRKEY IOMUX_PAD(0x48c, 0x0f8, 1, 0x0, 0, PAD_CTL_PUS_100K_UP | PAD_CTL_PKE) - -static iomux_v3_cfg_t mx51efikamx_pads[] = { - /* board id */ - MX51_PAD_PCBID0, - MX51_PAD_PCBID1, - MX51_PAD_PCBID2, - - /* leds */ - MX51_PAD_CSI1_D9__GPIO3_13, - MX51_PAD_CSI1_VSYNC__GPIO3_14, - MX51_PAD_CSI1_HSYNC__GPIO3_15, - - /* power key */ - MX51_PAD_PWRKEY, - - /* reset */ - MX51_PAD_DI1_PIN13__GPIO3_2, - MX51_PAD_GPIO1_4__GPIO1_4, - - /* power off */ - MX51_PAD_CSI2_VSYNC__GPIO4_13, -}; - -/* PCBID2 PCBID1 PCBID0 STATE - 1 1 1 ER1:rev1.1 - 1 1 0 ER2:rev1.2 - 1 0 1 ER3:rev1.3 - 1 0 0 ER4:rev1.4 -*/ -static void __init mx51_efikamx_board_id(void) -{ - int id; - - /* things are taking time to settle */ - msleep(150); - - gpio_request(EFIKAMX_PCBID0, "pcbid0"); - gpio_direction_input(EFIKAMX_PCBID0); - gpio_request(EFIKAMX_PCBID1, "pcbid1"); - gpio_direction_input(EFIKAMX_PCBID1); - gpio_request(EFIKAMX_PCBID2, "pcbid2"); - gpio_direction_input(EFIKAMX_PCBID2); - - id = gpio_get_value(EFIKAMX_PCBID0) ? 1 : 0; - id |= (gpio_get_value(EFIKAMX_PCBID1) ? 1 : 0) << 1; - id |= (gpio_get_value(EFIKAMX_PCBID2) ? 1 : 0) << 2; - - switch (id) { - case 7: - system_rev = 0x11; - break; - case 6: - system_rev = 0x12; - break; - case 5: - system_rev = 0x13; - break; - case 4: - system_rev = 0x14; - break; - default: - system_rev = 0x10; - break; - } - - if ((system_rev == 0x10) - || (system_rev == 0x12) - || (system_rev == 0x14)) { - printk(KERN_WARNING - "EfikaMX: Unsupported board revision 1.%u!\n", - system_rev & 0xf); - } -} - -static struct gpio_led mx51_efikamx_leds[] __initdata = { - { - .name = "efikamx:green", - .default_trigger = "default-on", - .gpio = EFIKAMX_GREEN_LED, - }, - { - .name = "efikamx:red", - .default_trigger = "ide-disk", - .gpio = EFIKAMX_RED_LED, - }, - { - .name = "efikamx:blue", - .default_trigger = "mmc0", - .gpio = EFIKAMX_BLUE_LED, - }, -}; - -static const struct gpio_led_platform_data - mx51_efikamx_leds_data __initconst = { - .leds = mx51_efikamx_leds, - .num_leds = ARRAY_SIZE(mx51_efikamx_leds), -}; - -static struct esdhc_platform_data sd_pdata = { - .cd_type = ESDHC_CD_CONTROLLER, - .wp_type = ESDHC_WP_CONTROLLER, -}; - -static struct gpio_keys_button mx51_efikamx_powerkey[] = { - { - .code = KEY_POWER, - .gpio = EFIKAMX_POWER_KEY, - .type = EV_PWR, - .desc = "Power Button (CM)", - .wakeup = 1, - .debounce_interval = 10, /* ms */ - }, -}; - -static const struct gpio_keys_platform_data mx51_efikamx_powerkey_data __initconst = { - .buttons = mx51_efikamx_powerkey, - .nbuttons = ARRAY_SIZE(mx51_efikamx_powerkey), -}; - -void mx51_efikamx_reset(void) -{ - if (system_rev == 0x11) - gpio_direction_output(EFIKAMX_RESET1_1, 0); - else - gpio_direction_output(EFIKAMX_RESET, 0); -} - -static struct regulator *pwgt1, *pwgt2, *coincell; - -static void mx51_efikamx_power_off(void) -{ - if (!IS_ERR(coincell)) - regulator_disable(coincell); - - if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { - regulator_disable(pwgt2); - regulator_disable(pwgt1); - } - gpio_direction_output(EFIKAMX_POWEROFF, 1); -} - -static int __init mx51_efikamx_power_init(void) -{ - if (machine_is_mx51_efikamx()) { - pwgt1 = regulator_get(NULL, "pwgt1"); - pwgt2 = regulator_get(NULL, "pwgt2"); - if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { - regulator_enable(pwgt1); - regulator_enable(pwgt2); - } - gpio_request(EFIKAMX_POWEROFF, "poweroff"); - pm_power_off = mx51_efikamx_power_off; - - /* enable coincell charger. maybe need a small power driver ? */ - coincell = regulator_get(NULL, "coincell"); - if (!IS_ERR(coincell)) { - regulator_set_voltage(coincell, 3000000, 3000000); - regulator_enable(coincell); - } - - regulator_has_full_constraints(); - } - - return 0; -} -late_initcall(mx51_efikamx_power_init); - -static void __init mx51_efikamx_init(void) -{ - imx51_soc_init(); - - mxc_iomux_v3_setup_multiple_pads(mx51efikamx_pads, - ARRAY_SIZE(mx51efikamx_pads)); - efika_board_common_init(); - - mx51_efikamx_board_id(); - - /* on < 1.2 boards both SD controllers are used */ - if (system_rev < 0x12) { - imx51_add_sdhci_esdhc_imx(0, NULL); - imx51_add_sdhci_esdhc_imx(1, &sd_pdata); - mx51_efikamx_leds[2].default_trigger = "mmc1"; - } else - imx51_add_sdhci_esdhc_imx(0, &sd_pdata); - - gpio_led_register_device(-1, &mx51_efikamx_leds_data); - imx_add_gpio_keys(&mx51_efikamx_powerkey_data); - - if (system_rev == 0x11) { - gpio_request(EFIKAMX_RESET1_1, "reset"); - gpio_direction_output(EFIKAMX_RESET1_1, 1); - } else { - gpio_request(EFIKAMX_RESET, "reset"); - gpio_direction_output(EFIKAMX_RESET, 1); - } - - /* - * enable wifi by default only on mx - * sb and mx have same wlan pin but the value to enable it are - * different :/ - */ - gpio_request(EFIKA_WLAN_EN, "wlan_en"); - gpio_direction_output(EFIKA_WLAN_EN, 0); - msleep(10); - - gpio_request(EFIKA_WLAN_RESET, "wlan_rst"); - gpio_direction_output(EFIKA_WLAN_RESET, 0); - msleep(10); - gpio_set_value(EFIKA_WLAN_RESET, 1); -} - -static void __init mx51_efikamx_timer_init(void) -{ - mx51_clocks_init(32768, 24000000, 22579200, 24576000); -} - -static struct sys_timer mx51_efikamx_timer = { - .init = mx51_efikamx_timer_init, -}; - -MACHINE_START(MX51_EFIKAMX, "Genesi EfikaMX nettop") - /* Maintainer: Amit Kucheria */ - .atag_offset = 0x100, - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .timer = &mx51_efikamx_timer, - .init_machine = mx51_efikamx_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx51_efikasb.c b/arch/arm/mach-mx5/board-mx51_efikasb.c deleted file mode 100644 index 38c4a3e..0000000 --- a/arch/arm/mach-mx5/board-mx51_efikasb.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) Arnaud Patard - * - * based on code from the following - * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2009-2010 Pegatron Corporation. All Rights Reserved. - * Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" -#include "efika.h" - -#define EFIKASB_USBH2_STP IMX_GPIO_NR(2, 20) -#define EFIKASB_GREEN_LED IMX_GPIO_NR(1, 3) -#define EFIKASB_WHITE_LED IMX_GPIO_NR(2, 25) -#define EFIKASB_PCBID0 IMX_GPIO_NR(2, 28) -#define EFIKASB_PCBID1 IMX_GPIO_NR(2, 29) -#define EFIKASB_PWRKEY IMX_GPIO_NR(2, 31) -#define EFIKASB_LID IMX_GPIO_NR(3, 14) -#define EFIKASB_POWEROFF IMX_GPIO_NR(4, 13) -#define EFIKASB_RFKILL IMX_GPIO_NR(3, 1) - -#define MX51_PAD_PWRKEY IOMUX_PAD(0x48c, 0x0f8, 1, 0x0, 0, PAD_CTL_PUS_100K_UP | PAD_CTL_PKE) -#define MX51_PAD_SD1_CD IOMUX_PAD(0x47c, 0x0e8, 1, __NA_, 0, MX51_ESDHC_PAD_CTRL) - -static iomux_v3_cfg_t mx51efikasb_pads[] = { - /* USB HOST2 */ - MX51_PAD_EIM_D16__USBH2_DATA0, - MX51_PAD_EIM_D17__USBH2_DATA1, - MX51_PAD_EIM_D18__USBH2_DATA2, - MX51_PAD_EIM_D19__USBH2_DATA3, - MX51_PAD_EIM_D20__USBH2_DATA4, - MX51_PAD_EIM_D21__USBH2_DATA5, - MX51_PAD_EIM_D22__USBH2_DATA6, - MX51_PAD_EIM_D23__USBH2_DATA7, - MX51_PAD_EIM_A24__USBH2_CLK, - MX51_PAD_EIM_A25__USBH2_DIR, - MX51_PAD_EIM_A26__USBH2_STP, - MX51_PAD_EIM_A27__USBH2_NXT, - - /* leds */ - MX51_PAD_EIM_CS0__GPIO2_25, - MX51_PAD_GPIO1_3__GPIO1_3, - - /* pcb id */ - MX51_PAD_EIM_CS3__GPIO2_28, - MX51_PAD_EIM_CS4__GPIO2_29, - - /* lid */ - MX51_PAD_CSI1_VSYNC__GPIO3_14, - - /* power key*/ - MX51_PAD_PWRKEY, - - /* wifi/bt button */ - MX51_PAD_DI1_PIN12__GPIO3_1, - - /* power off */ - MX51_PAD_CSI2_VSYNC__GPIO4_13, - - /* wdog reset */ - MX51_PAD_GPIO1_4__WDOG1_WDOG_B, - - /* BT */ - MX51_PAD_EIM_A17__GPIO2_11, - - MX51_PAD_SD1_CD, -}; - -static int initialize_usbh2_port(struct platform_device *pdev) -{ - iomux_v3_cfg_t usbh2stp = MX51_PAD_EIM_A26__USBH2_STP; - iomux_v3_cfg_t usbh2gpio = MX51_PAD_EIM_A26__GPIO2_20; - - mxc_iomux_v3_setup_pad(usbh2gpio); - gpio_request(EFIKASB_USBH2_STP, "usbh2_stp"); - gpio_direction_output(EFIKASB_USBH2_STP, 0); - msleep(1); - gpio_set_value(EFIKASB_USBH2_STP, 1); - msleep(1); - - gpio_free(EFIKASB_USBH2_STP); - mxc_iomux_v3_setup_pad(usbh2stp); - - mdelay(10); - - return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_ITC_NO_THRESHOLD); -} - -static struct mxc_usbh_platform_data usbh2_config __initdata = { - .init = initialize_usbh2_port, - .portsc = MXC_EHCI_MODE_ULPI, -}; - -static void __init mx51_efikasb_usb(void) -{ - usbh2_config.otg = imx_otg_ulpi_create(ULPI_OTG_DRVVBUS | - ULPI_OTG_DRVVBUS_EXT | ULPI_OTG_EXTVBUSIND); - if (usbh2_config.otg) - imx51_add_mxc_ehci_hs(2, &usbh2_config); -} - -static const struct gpio_led mx51_efikasb_leds[] __initconst = { - { - .name = "efikasb:green", - .default_trigger = "default-on", - .gpio = EFIKASB_GREEN_LED, - .active_low = 1, - }, - { - .name = "efikasb:white", - .default_trigger = "caps", - .gpio = EFIKASB_WHITE_LED, - }, -}; - -static const struct gpio_led_platform_data - mx51_efikasb_leds_data __initconst = { - .leds = mx51_efikasb_leds, - .num_leds = ARRAY_SIZE(mx51_efikasb_leds), -}; - -static struct gpio_keys_button mx51_efikasb_keys[] = { - { - .code = KEY_POWER, - .gpio = EFIKASB_PWRKEY, - .type = EV_KEY, - .desc = "Power Button", - .wakeup = 1, - .active_low = 1, - }, - { - .code = SW_LID, - .gpio = EFIKASB_LID, - .type = EV_SW, - .desc = "Lid Switch", - .active_low = 1, - }, - { - .code = KEY_RFKILL, - .gpio = EFIKASB_RFKILL, - .type = EV_KEY, - .desc = "rfkill", - .active_low = 1, - }, -}; - -static const struct gpio_keys_platform_data mx51_efikasb_keys_data __initconst = { - .buttons = mx51_efikasb_keys, - .nbuttons = ARRAY_SIZE(mx51_efikasb_keys), -}; - -static struct esdhc_platform_data sd0_pdata = { -#define EFIKASB_SD1_CD IMX_GPIO_NR(2, 27) - .cd_gpio = EFIKASB_SD1_CD, - .cd_type = ESDHC_CD_GPIO, - .wp_type = ESDHC_WP_CONTROLLER, -}; - -static struct esdhc_platform_data sd1_pdata = { - .cd_type = ESDHC_CD_CONTROLLER, - .wp_type = ESDHC_WP_CONTROLLER, -}; - -static struct regulator *pwgt1, *pwgt2; - -static void mx51_efikasb_power_off(void) -{ - gpio_set_value(EFIKA_USB_PHY_RESET, 0); - - if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { - regulator_disable(pwgt2); - regulator_disable(pwgt1); - } - gpio_direction_output(EFIKASB_POWEROFF, 1); -} - -static int __init mx51_efikasb_power_init(void) -{ - if (machine_is_mx51_efikasb()) { - pwgt1 = regulator_get(NULL, "pwgt1"); - pwgt2 = regulator_get(NULL, "pwgt2"); - if (!IS_ERR(pwgt1) && !IS_ERR(pwgt2)) { - regulator_enable(pwgt1); - regulator_enable(pwgt2); - } - gpio_request(EFIKASB_POWEROFF, "poweroff"); - pm_power_off = mx51_efikasb_power_off; - - regulator_has_full_constraints(); - } - - return 0; -} -late_initcall(mx51_efikasb_power_init); - -/* 01 R1.3 board - 10 R2.0 board */ -static void __init mx51_efikasb_board_id(void) -{ - int id; - - gpio_request(EFIKASB_PCBID0, "pcb id0"); - gpio_direction_input(EFIKASB_PCBID0); - gpio_request(EFIKASB_PCBID1, "pcb id1"); - gpio_direction_input(EFIKASB_PCBID1); - - id = gpio_get_value(EFIKASB_PCBID0) ? 1 : 0; - id |= (gpio_get_value(EFIKASB_PCBID1) ? 1 : 0) << 1; - - switch (id) { - default: - break; - case 1: - system_rev = 0x13; - break; - case 2: - system_rev = 0x20; - break; - } -} - -static void __init efikasb_board_init(void) -{ - imx51_soc_init(); - - mxc_iomux_v3_setup_multiple_pads(mx51efikasb_pads, - ARRAY_SIZE(mx51efikasb_pads)); - efika_board_common_init(); - - mx51_efikasb_board_id(); - mx51_efikasb_usb(); - imx51_add_sdhci_esdhc_imx(0, &sd0_pdata); - imx51_add_sdhci_esdhc_imx(1, &sd1_pdata); - - gpio_led_register_device(-1, &mx51_efikasb_leds_data); - imx_add_gpio_keys(&mx51_efikasb_keys_data); -} - -static void __init mx51_efikasb_timer_init(void) -{ - mx51_clocks_init(32768, 24000000, 22579200, 24576000); -} - -static struct sys_timer mx51_efikasb_timer = { - .init = mx51_efikasb_timer_init, -}; - -MACHINE_START(MX51_EFIKASB, "Genesi Efika Smartbook") - .atag_offset = 0x100, - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .init_machine = efikasb_board_init, - .timer = &mx51_efikasb_timer, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx53_ard.c b/arch/arm/mach-mx5/board-mx53_ard.c deleted file mode 100644 index b88a2bc..0000000 --- a/arch/arm/mach-mx5/board-mx53_ard.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * 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. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "devices-imx53.h" - -#define ARD_ETHERNET_INT_B IMX_GPIO_NR(2, 31) -#define ARD_SD1_CD IMX_GPIO_NR(1, 1) -#define ARD_SD1_WP IMX_GPIO_NR(1, 9) -#define ARD_I2CPORTEXP_B IMX_GPIO_NR(2, 3) -#define ARD_VOLUMEDOWN IMX_GPIO_NR(4, 0) -#define ARD_HOME IMX_GPIO_NR(5, 10) -#define ARD_BACK IMX_GPIO_NR(5, 11) -#define ARD_PROG IMX_GPIO_NR(5, 12) -#define ARD_VOLUMEUP IMX_GPIO_NR(5, 13) - -static iomux_v3_cfg_t mx53_ard_pads[] = { - /* UART1 */ - MX53_PAD_PATA_DIOW__UART1_TXD_MUX, - MX53_PAD_PATA_DMACK__UART1_RXD_MUX, - /* WEIM for CS1 */ - MX53_PAD_EIM_EB3__GPIO2_31, /* ETHERNET_INT_B */ - MX53_PAD_EIM_D16__EMI_WEIM_D_16, - MX53_PAD_EIM_D17__EMI_WEIM_D_17, - MX53_PAD_EIM_D18__EMI_WEIM_D_18, - MX53_PAD_EIM_D19__EMI_WEIM_D_19, - MX53_PAD_EIM_D20__EMI_WEIM_D_20, - MX53_PAD_EIM_D21__EMI_WEIM_D_21, - MX53_PAD_EIM_D22__EMI_WEIM_D_22, - MX53_PAD_EIM_D23__EMI_WEIM_D_23, - MX53_PAD_EIM_D24__EMI_WEIM_D_24, - MX53_PAD_EIM_D25__EMI_WEIM_D_25, - MX53_PAD_EIM_D26__EMI_WEIM_D_26, - MX53_PAD_EIM_D27__EMI_WEIM_D_27, - MX53_PAD_EIM_D28__EMI_WEIM_D_28, - MX53_PAD_EIM_D29__EMI_WEIM_D_29, - MX53_PAD_EIM_D30__EMI_WEIM_D_30, - MX53_PAD_EIM_D31__EMI_WEIM_D_31, - MX53_PAD_EIM_DA0__EMI_NAND_WEIM_DA_0, - MX53_PAD_EIM_DA1__EMI_NAND_WEIM_DA_1, - MX53_PAD_EIM_DA2__EMI_NAND_WEIM_DA_2, - MX53_PAD_EIM_DA3__EMI_NAND_WEIM_DA_3, - MX53_PAD_EIM_DA4__EMI_NAND_WEIM_DA_4, - MX53_PAD_EIM_DA5__EMI_NAND_WEIM_DA_5, - MX53_PAD_EIM_DA6__EMI_NAND_WEIM_DA_6, - MX53_PAD_EIM_OE__EMI_WEIM_OE, - MX53_PAD_EIM_RW__EMI_WEIM_RW, - MX53_PAD_EIM_CS1__EMI_WEIM_CS_1, - /* SDHC1 */ - MX53_PAD_SD1_CMD__ESDHC1_CMD, - MX53_PAD_SD1_CLK__ESDHC1_CLK, - MX53_PAD_SD1_DATA0__ESDHC1_DAT0, - MX53_PAD_SD1_DATA1__ESDHC1_DAT1, - MX53_PAD_SD1_DATA2__ESDHC1_DAT2, - MX53_PAD_SD1_DATA3__ESDHC1_DAT3, - MX53_PAD_PATA_DATA8__ESDHC1_DAT4, - MX53_PAD_PATA_DATA9__ESDHC1_DAT5, - MX53_PAD_PATA_DATA10__ESDHC1_DAT6, - MX53_PAD_PATA_DATA11__ESDHC1_DAT7, - MX53_PAD_GPIO_1__GPIO1_1, - MX53_PAD_GPIO_9__GPIO1_9, - /* I2C2 */ - MX53_PAD_EIM_EB2__I2C2_SCL, - MX53_PAD_KEY_ROW3__I2C2_SDA, - /* I2C3 */ - MX53_PAD_GPIO_3__I2C3_SCL, - MX53_PAD_GPIO_16__I2C3_SDA, - /* GPIO */ - MX53_PAD_DISP0_DAT16__GPIO5_10, /* home */ - MX53_PAD_DISP0_DAT17__GPIO5_11, /* back */ - MX53_PAD_DISP0_DAT18__GPIO5_12, /* prog */ - MX53_PAD_DISP0_DAT19__GPIO5_13, /* vol up */ - MX53_PAD_GPIO_10__GPIO4_0, /* vol down */ -}; - -#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake) \ -{ \ - .gpio = gpio_num, \ - .type = EV_KEY, \ - .code = ev_code, \ - .active_low = act_low, \ - .desc = "btn " descr, \ - .wakeup = wake, \ -} - -static struct gpio_keys_button ard_buttons[] = { - GPIO_BUTTON(ARD_HOME, KEY_HOME, 1, "home", 0), - GPIO_BUTTON(ARD_BACK, KEY_BACK, 1, "back", 0), - GPIO_BUTTON(ARD_PROG, KEY_PROGRAM, 1, "program", 0), - GPIO_BUTTON(ARD_VOLUMEUP, KEY_VOLUMEUP, 1, "volume-up", 0), - GPIO_BUTTON(ARD_VOLUMEDOWN, KEY_VOLUMEDOWN, 1, "volume-down", 0), -}; - -static const struct gpio_keys_platform_data ard_button_data __initconst = { - .buttons = ard_buttons, - .nbuttons = ARRAY_SIZE(ard_buttons), -}; - -static struct resource ard_smsc911x_resources[] = { - { - .start = MX53_CS1_64MB_BASE_ADDR, - .end = MX53_CS1_64MB_BASE_ADDR + SZ_32M - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = IMX_GPIO_TO_IRQ(ARD_ETHERNET_INT_B), - .end = IMX_GPIO_TO_IRQ(ARD_ETHERNET_INT_B), - .flags = IORESOURCE_IRQ, - }, -}; - -struct smsc911x_platform_config ard_smsc911x_config = { - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, - .flags = SMSC911X_USE_32BIT, -}; - -static struct platform_device ard_smsc_lan9220_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(ard_smsc911x_resources), - .resource = ard_smsc911x_resources, - .dev = { - .platform_data = &ard_smsc911x_config, - }, -}; - -static const struct esdhc_platform_data mx53_ard_sd1_data __initconst = { - .cd_gpio = ARD_SD1_CD, - .wp_gpio = ARD_SD1_WP, -}; - -static struct imxi2c_platform_data mx53_ard_i2c2_data = { - .bitrate = 50000, -}; - -static struct imxi2c_platform_data mx53_ard_i2c3_data = { - .bitrate = 400000, -}; - -static void __init mx53_ard_io_init(void) -{ - gpio_request(ARD_ETHERNET_INT_B, "eth-int-b"); - gpio_direction_input(ARD_ETHERNET_INT_B); - - gpio_request(ARD_I2CPORTEXP_B, "i2cptexp-rst"); - gpio_direction_output(ARD_I2CPORTEXP_B, 1); -} - -/* Config CS1 settings for ethernet controller */ -static int weim_cs_config(void) -{ - u32 reg; - void __iomem *weim_base, *iomuxc_base; - - weim_base = ioremap(MX53_WEIM_BASE_ADDR, SZ_4K); - if (!weim_base) - return -ENOMEM; - - iomuxc_base = ioremap(MX53_IOMUXC_BASE_ADDR, SZ_4K); - if (!iomuxc_base) - return -ENOMEM; - - /* CS1 timings for LAN9220 */ - writel(0x20001, (weim_base + 0x18)); - writel(0x0, (weim_base + 0x1C)); - writel(0x16000202, (weim_base + 0x20)); - writel(0x00000002, (weim_base + 0x24)); - writel(0x16002082, (weim_base + 0x28)); - writel(0x00000000, (weim_base + 0x2C)); - writel(0x00000000, (weim_base + 0x90)); - - /* specify 64 MB on CS1 and CS0 on GPR1 */ - reg = readl(iomuxc_base + 0x4); - reg &= ~0x3F; - reg |= 0x1B; - writel(reg, (iomuxc_base + 0x4)); - - iounmap(iomuxc_base); - iounmap(weim_base); - - return 0; -} - -void __init imx53_ard_common_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx53_ard_pads, - ARRAY_SIZE(mx53_ard_pads)); - weim_cs_config(); -} - -static struct platform_device *devices[] __initdata = { - &ard_smsc_lan9220_device, -}; - -static void __init mx53_ard_board_init(void) -{ - imx53_soc_init(); - imx53_add_imx_uart(0, NULL); - - imx53_ard_common_init(); - mx53_ard_io_init(); - platform_add_devices(devices, ARRAY_SIZE(devices)); - - imx53_add_sdhci_esdhc_imx(0, &mx53_ard_sd1_data); - imx53_add_imx2_wdt(0, NULL); - imx53_add_imx_i2c(1, &mx53_ard_i2c2_data); - imx53_add_imx_i2c(2, &mx53_ard_i2c3_data); - imx_add_gpio_keys(&ard_button_data); - imx53_add_ahci_imx(); -} - -static void __init mx53_ard_timer_init(void) -{ - mx53_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mx53_ard_timer = { - .init = mx53_ard_timer_init, -}; - -MACHINE_START(MX53_ARD, "Freescale MX53 ARD Board") - .map_io = mx53_map_io, - .init_early = imx53_init_early, - .init_irq = mx53_init_irq, - .handle_irq = imx53_handle_irq, - .timer = &mx53_ard_timer, - .init_machine = mx53_ard_board_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx53_evk.c b/arch/arm/mach-mx5/board-mx53_evk.c deleted file mode 100644 index c69413d..0000000 --- a/arch/arm/mach-mx5/board-mx53_evk.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2010 Yong Shen. - */ - -/* - * 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. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MX53_EVK_FEC_PHY_RST IMX_GPIO_NR(7, 6) -#define EVK_ECSPI1_CS0 IMX_GPIO_NR(2, 30) -#define EVK_ECSPI1_CS1 IMX_GPIO_NR(3, 19) -#define MX53EVK_LED IMX_GPIO_NR(7, 7) - -#include "devices-imx53.h" - -static iomux_v3_cfg_t mx53_evk_pads[] = { - MX53_PAD_CSI0_DAT10__UART1_TXD_MUX, - MX53_PAD_CSI0_DAT11__UART1_RXD_MUX, - - MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX, - MX53_PAD_PATA_DMARQ__UART2_TXD_MUX, - MX53_PAD_PATA_DIOR__UART2_RTS, - MX53_PAD_PATA_INTRQ__UART2_CTS, - - MX53_PAD_PATA_CS_0__UART3_TXD_MUX, - MX53_PAD_PATA_CS_1__UART3_RXD_MUX, - - MX53_PAD_EIM_D16__ECSPI1_SCLK, - MX53_PAD_EIM_D17__ECSPI1_MISO, - MX53_PAD_EIM_D18__ECSPI1_MOSI, - - /* ecspi chip select lines */ - MX53_PAD_EIM_EB2__GPIO2_30, - MX53_PAD_EIM_D19__GPIO3_19, - /* LED */ - MX53_PAD_PATA_DA_1__GPIO7_7, -}; - -static const struct imxuart_platform_data mx53_evk_uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static const struct gpio_led mx53evk_leds[] __initconst = { - { - .name = "green", - .default_trigger = "heartbeat", - .gpio = MX53EVK_LED, - }, -}; - -static const struct gpio_led_platform_data mx53evk_leds_data __initconst = { - .leds = mx53evk_leds, - .num_leds = ARRAY_SIZE(mx53evk_leds), -}; - -static inline void mx53_evk_init_uart(void) -{ - imx53_add_imx_uart(0, NULL); - imx53_add_imx_uart(1, &mx53_evk_uart_pdata); - imx53_add_imx_uart(2, NULL); -} - -static const struct imxi2c_platform_data mx53_evk_i2c_data __initconst = { - .bitrate = 100000, -}; - -static inline void mx53_evk_fec_reset(void) -{ - int ret; - - /* reset FEC PHY */ - ret = gpio_request_one(MX53_EVK_FEC_PHY_RST, GPIOF_OUT_INIT_LOW, - "fec-phy-reset"); - if (ret) { - printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); - return; - } - msleep(1); - gpio_set_value(MX53_EVK_FEC_PHY_RST, 1); -} - -static struct fec_platform_data mx53_evk_fec_pdata = { - .phy = PHY_INTERFACE_MODE_RMII, -}; - -static struct spi_board_info mx53_evk_spi_board_info[] __initdata = { - { - .modalias = "mtd_dataflash", - .max_speed_hz = 25000000, - .bus_num = 0, - .chip_select = 1, - .mode = SPI_MODE_0, - .platform_data = NULL, - }, -}; - -static int mx53_evk_spi_cs[] = { - EVK_ECSPI1_CS0, - EVK_ECSPI1_CS1, -}; - -static const struct spi_imx_master mx53_evk_spi_data __initconst = { - .chipselect = mx53_evk_spi_cs, - .num_chipselect = ARRAY_SIZE(mx53_evk_spi_cs), -}; - -void __init imx53_evk_common_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx53_evk_pads, - ARRAY_SIZE(mx53_evk_pads)); -} - -static void __init mx53_evk_board_init(void) -{ - imx53_soc_init(); - imx53_evk_common_init(); - - mx53_evk_init_uart(); - mx53_evk_fec_reset(); - imx53_add_fec(&mx53_evk_fec_pdata); - - imx53_add_imx_i2c(0, &mx53_evk_i2c_data); - imx53_add_imx_i2c(1, &mx53_evk_i2c_data); - - imx53_add_sdhci_esdhc_imx(0, NULL); - imx53_add_sdhci_esdhc_imx(1, NULL); - - spi_register_board_info(mx53_evk_spi_board_info, - ARRAY_SIZE(mx53_evk_spi_board_info)); - imx53_add_ecspi(0, &mx53_evk_spi_data); - imx53_add_imx2_wdt(0, NULL); - gpio_led_register_device(-1, &mx53evk_leds_data); -} - -static void __init mx53_evk_timer_init(void) -{ - mx53_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mx53_evk_timer = { - .init = mx53_evk_timer_init, -}; - -MACHINE_START(MX53_EVK, "Freescale MX53 EVK Board") - .map_io = mx53_map_io, - .init_early = imx53_init_early, - .init_irq = mx53_init_irq, - .handle_irq = imx53_handle_irq, - .timer = &mx53_evk_timer, - .init_machine = mx53_evk_board_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx53_loco.c b/arch/arm/mach-mx5/board-mx53_loco.c deleted file mode 100644 index e64a8f7..0000000 --- a/arch/arm/mach-mx5/board-mx53_loco.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * 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. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "devices-imx53.h" - -#define MX53_LOCO_POWER IMX_GPIO_NR(1, 8) -#define MX53_LOCO_UI1 IMX_GPIO_NR(2, 14) -#define MX53_LOCO_UI2 IMX_GPIO_NR(2, 15) -#define LOCO_FEC_PHY_RST IMX_GPIO_NR(7, 6) -#define LOCO_LED IMX_GPIO_NR(7, 7) -#define LOCO_SD3_CD IMX_GPIO_NR(3, 11) -#define LOCO_SD3_WP IMX_GPIO_NR(3, 12) -#define LOCO_SD1_CD IMX_GPIO_NR(3, 13) -#define LOCO_ACCEL_EN IMX_GPIO_NR(6, 14) - -static iomux_v3_cfg_t mx53_loco_pads[] = { - /* FEC */ - MX53_PAD_FEC_MDC__FEC_MDC, - MX53_PAD_FEC_MDIO__FEC_MDIO, - MX53_PAD_FEC_REF_CLK__FEC_TX_CLK, - MX53_PAD_FEC_RX_ER__FEC_RX_ER, - MX53_PAD_FEC_CRS_DV__FEC_RX_DV, - MX53_PAD_FEC_RXD1__FEC_RDATA_1, - MX53_PAD_FEC_RXD0__FEC_RDATA_0, - MX53_PAD_FEC_TX_EN__FEC_TX_EN, - MX53_PAD_FEC_TXD1__FEC_TDATA_1, - MX53_PAD_FEC_TXD0__FEC_TDATA_0, - /* FEC_nRST */ - MX53_PAD_PATA_DA_0__GPIO7_6, - /* FEC_nINT */ - MX53_PAD_PATA_DATA4__GPIO2_4, - /* AUDMUX5 */ - MX53_PAD_KEY_COL0__AUDMUX_AUD5_TXC, - MX53_PAD_KEY_ROW0__AUDMUX_AUD5_TXD, - MX53_PAD_KEY_COL1__AUDMUX_AUD5_TXFS, - MX53_PAD_KEY_ROW1__AUDMUX_AUD5_RXD, - /* I2C1 */ - MX53_PAD_CSI0_DAT8__I2C1_SDA, - MX53_PAD_CSI0_DAT9__I2C1_SCL, - MX53_PAD_NANDF_CS1__GPIO6_14, /* Accelerometer Enable */ - /* I2C2 */ - MX53_PAD_KEY_COL3__I2C2_SCL, - MX53_PAD_KEY_ROW3__I2C2_SDA, - /* SD1 */ - MX53_PAD_SD1_CMD__ESDHC1_CMD, - MX53_PAD_SD1_CLK__ESDHC1_CLK, - MX53_PAD_SD1_DATA0__ESDHC1_DAT0, - MX53_PAD_SD1_DATA1__ESDHC1_DAT1, - MX53_PAD_SD1_DATA2__ESDHC1_DAT2, - MX53_PAD_SD1_DATA3__ESDHC1_DAT3, - /* SD1_CD */ - MX53_PAD_EIM_DA13__GPIO3_13, - /* SD3 */ - MX53_PAD_PATA_DATA8__ESDHC3_DAT0, - MX53_PAD_PATA_DATA9__ESDHC3_DAT1, - MX53_PAD_PATA_DATA10__ESDHC3_DAT2, - MX53_PAD_PATA_DATA11__ESDHC3_DAT3, - MX53_PAD_PATA_DATA0__ESDHC3_DAT4, - MX53_PAD_PATA_DATA1__ESDHC3_DAT5, - MX53_PAD_PATA_DATA2__ESDHC3_DAT6, - MX53_PAD_PATA_DATA3__ESDHC3_DAT7, - MX53_PAD_PATA_IORDY__ESDHC3_CLK, - MX53_PAD_PATA_RESET_B__ESDHC3_CMD, - /* SD3_CD */ - MX53_PAD_EIM_DA11__GPIO3_11, - /* SD3_WP */ - MX53_PAD_EIM_DA12__GPIO3_12, - /* VGA */ - MX53_PAD_EIM_OE__IPU_DI1_PIN7, - MX53_PAD_EIM_RW__IPU_DI1_PIN8, - /* DISPLB */ - MX53_PAD_EIM_D20__IPU_SER_DISP0_CS, - MX53_PAD_EIM_D21__IPU_DISPB0_SER_CLK, - MX53_PAD_EIM_D22__IPU_DISPB0_SER_DIN, - MX53_PAD_EIM_D23__IPU_DI0_D0_CS, - /* DISP0_POWER_EN */ - MX53_PAD_EIM_D24__GPIO3_24, - /* DISP0 DET INT */ - MX53_PAD_EIM_D31__GPIO3_31, - /* LVDS */ - MX53_PAD_LVDS0_TX3_P__LDB_LVDS0_TX3, - MX53_PAD_LVDS0_CLK_P__LDB_LVDS0_CLK, - MX53_PAD_LVDS0_TX2_P__LDB_LVDS0_TX2, - MX53_PAD_LVDS0_TX1_P__LDB_LVDS0_TX1, - MX53_PAD_LVDS0_TX0_P__LDB_LVDS0_TX0, - MX53_PAD_LVDS1_TX3_P__LDB_LVDS1_TX3, - MX53_PAD_LVDS1_TX2_P__LDB_LVDS1_TX2, - MX53_PAD_LVDS1_CLK_P__LDB_LVDS1_CLK, - MX53_PAD_LVDS1_TX1_P__LDB_LVDS1_TX1, - MX53_PAD_LVDS1_TX0_P__LDB_LVDS1_TX0, - /* I2C1 */ - MX53_PAD_CSI0_DAT8__I2C1_SDA, - MX53_PAD_CSI0_DAT9__I2C1_SCL, - /* UART1 */ - MX53_PAD_CSI0_DAT10__UART1_TXD_MUX, - MX53_PAD_CSI0_DAT11__UART1_RXD_MUX, - /* CSI0 */ - MX53_PAD_CSI0_DAT12__IPU_CSI0_D_12, - MX53_PAD_CSI0_DAT13__IPU_CSI0_D_13, - MX53_PAD_CSI0_DAT14__IPU_CSI0_D_14, - MX53_PAD_CSI0_DAT15__IPU_CSI0_D_15, - MX53_PAD_CSI0_DAT16__IPU_CSI0_D_16, - MX53_PAD_CSI0_DAT17__IPU_CSI0_D_17, - MX53_PAD_CSI0_DAT18__IPU_CSI0_D_18, - MX53_PAD_CSI0_DAT19__IPU_CSI0_D_19, - MX53_PAD_CSI0_VSYNC__IPU_CSI0_VSYNC, - MX53_PAD_CSI0_MCLK__IPU_CSI0_HSYNC, - MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK, - /* DISPLAY */ - MX53_PAD_DI0_DISP_CLK__IPU_DI0_DISP_CLK, - MX53_PAD_DI0_PIN15__IPU_DI0_PIN15, - MX53_PAD_DI0_PIN2__IPU_DI0_PIN2, - MX53_PAD_DI0_PIN3__IPU_DI0_PIN3, - MX53_PAD_DISP0_DAT0__IPU_DISP0_DAT_0, - MX53_PAD_DISP0_DAT1__IPU_DISP0_DAT_1, - MX53_PAD_DISP0_DAT2__IPU_DISP0_DAT_2, - MX53_PAD_DISP0_DAT3__IPU_DISP0_DAT_3, - MX53_PAD_DISP0_DAT4__IPU_DISP0_DAT_4, - MX53_PAD_DISP0_DAT5__IPU_DISP0_DAT_5, - MX53_PAD_DISP0_DAT6__IPU_DISP0_DAT_6, - MX53_PAD_DISP0_DAT7__IPU_DISP0_DAT_7, - MX53_PAD_DISP0_DAT8__IPU_DISP0_DAT_8, - MX53_PAD_DISP0_DAT9__IPU_DISP0_DAT_9, - MX53_PAD_DISP0_DAT10__IPU_DISP0_DAT_10, - MX53_PAD_DISP0_DAT11__IPU_DISP0_DAT_11, - MX53_PAD_DISP0_DAT12__IPU_DISP0_DAT_12, - MX53_PAD_DISP0_DAT13__IPU_DISP0_DAT_13, - MX53_PAD_DISP0_DAT14__IPU_DISP0_DAT_14, - MX53_PAD_DISP0_DAT15__IPU_DISP0_DAT_15, - MX53_PAD_DISP0_DAT16__IPU_DISP0_DAT_16, - MX53_PAD_DISP0_DAT17__IPU_DISP0_DAT_17, - MX53_PAD_DISP0_DAT18__IPU_DISP0_DAT_18, - MX53_PAD_DISP0_DAT19__IPU_DISP0_DAT_19, - MX53_PAD_DISP0_DAT20__IPU_DISP0_DAT_20, - MX53_PAD_DISP0_DAT21__IPU_DISP0_DAT_21, - MX53_PAD_DISP0_DAT22__IPU_DISP0_DAT_22, - MX53_PAD_DISP0_DAT23__IPU_DISP0_DAT_23, - /* Audio CLK*/ - MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK, - /* PWM */ - MX53_PAD_GPIO_1__PWM2_PWMO, - /* SPDIF */ - MX53_PAD_GPIO_7__SPDIF_PLOCK, - MX53_PAD_GPIO_17__SPDIF_OUT1, - /* GPIO */ - MX53_PAD_PATA_DA_1__GPIO7_7, /* LED */ - MX53_PAD_PATA_DA_2__GPIO7_8, - MX53_PAD_PATA_DATA5__GPIO2_5, - MX53_PAD_PATA_DATA6__GPIO2_6, - MX53_PAD_PATA_DATA14__GPIO2_14, - MX53_PAD_PATA_DATA15__GPIO2_15, - MX53_PAD_PATA_INTRQ__GPIO7_2, - MX53_PAD_EIM_WAIT__GPIO5_0, - MX53_PAD_NANDF_WP_B__GPIO6_9, - MX53_PAD_NANDF_RB0__GPIO6_10, - MX53_PAD_NANDF_CS1__GPIO6_14, - MX53_PAD_NANDF_CS2__GPIO6_15, - MX53_PAD_NANDF_CS3__GPIO6_16, - MX53_PAD_GPIO_5__GPIO1_5, - MX53_PAD_GPIO_16__GPIO7_11, - MX53_PAD_GPIO_8__GPIO1_8, -}; - -#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake) \ -{ \ - .gpio = gpio_num, \ - .type = EV_KEY, \ - .code = ev_code, \ - .active_low = act_low, \ - .desc = "btn " descr, \ - .wakeup = wake, \ -} - -static struct gpio_keys_button loco_buttons[] = { - GPIO_BUTTON(MX53_LOCO_POWER, KEY_POWER, 1, "power", 0), - GPIO_BUTTON(MX53_LOCO_UI1, KEY_VOLUMEUP, 1, "volume-up", 0), - GPIO_BUTTON(MX53_LOCO_UI2, KEY_VOLUMEDOWN, 1, "volume-down", 0), -}; - -static const struct gpio_keys_platform_data loco_button_data __initconst = { - .buttons = loco_buttons, - .nbuttons = ARRAY_SIZE(loco_buttons), -}; - -static const struct esdhc_platform_data mx53_loco_sd1_data __initconst = { - .cd_gpio = LOCO_SD1_CD, - .cd_type = ESDHC_CD_GPIO, - .wp_type = ESDHC_WP_NONE, -}; - -static const struct esdhc_platform_data mx53_loco_sd3_data __initconst = { - .cd_gpio = LOCO_SD3_CD, - .wp_gpio = LOCO_SD3_WP, - .cd_type = ESDHC_CD_GPIO, - .wp_type = ESDHC_WP_GPIO, -}; - -static inline void mx53_loco_fec_reset(void) -{ - int ret; - - /* reset FEC PHY */ - ret = gpio_request(LOCO_FEC_PHY_RST, "fec-phy-reset"); - if (ret) { - printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); - return; - } - gpio_direction_output(LOCO_FEC_PHY_RST, 0); - msleep(1); - gpio_set_value(LOCO_FEC_PHY_RST, 1); -} - -static struct fec_platform_data mx53_loco_fec_data = { - .phy = PHY_INTERFACE_MODE_RMII, -}; - -static const struct imxi2c_platform_data mx53_loco_i2c_data __initconst = { - .bitrate = 100000, -}; - -static const struct gpio_led mx53loco_leds[] __initconst = { - { - .name = "green", - .default_trigger = "heartbeat", - .gpio = LOCO_LED, - }, -}; - -static const struct gpio_led_platform_data mx53loco_leds_data __initconst = { - .leds = mx53loco_leds, - .num_leds = ARRAY_SIZE(mx53loco_leds), -}; - -void __init imx53_qsb_common_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads, - ARRAY_SIZE(mx53_loco_pads)); -} - -static struct i2c_board_info mx53loco_i2c_devices[] = { - { - I2C_BOARD_INFO("mma8450", 0x1C), - }, -}; - -static void __init mx53_loco_board_init(void) -{ - int ret; - imx53_soc_init(); - imx53_qsb_common_init(); - - imx53_add_imx_uart(0, NULL); - mx53_loco_fec_reset(); - imx53_add_fec(&mx53_loco_fec_data); - imx53_add_imx2_wdt(0, NULL); - - ret = gpio_request_one(LOCO_ACCEL_EN, GPIOF_OUT_INIT_HIGH, "accel_en"); - if (ret) - pr_err("Cannot request ACCEL_EN pin: %d\n", ret); - - i2c_register_board_info(0, mx53loco_i2c_devices, - ARRAY_SIZE(mx53loco_i2c_devices)); - imx53_add_imx_i2c(0, &mx53_loco_i2c_data); - imx53_add_imx_i2c(1, &mx53_loco_i2c_data); - imx53_add_sdhci_esdhc_imx(0, &mx53_loco_sd1_data); - imx53_add_sdhci_esdhc_imx(2, &mx53_loco_sd3_data); - imx_add_gpio_keys(&loco_button_data); - gpio_led_register_device(-1, &mx53loco_leds_data); - imx53_add_ahci_imx(); -} - -static void __init mx53_loco_timer_init(void) -{ - mx53_clocks_init(32768, 24000000, 0, 0); -} - -static struct sys_timer mx53_loco_timer = { - .init = mx53_loco_timer_init, -}; - -MACHINE_START(MX53_LOCO, "Freescale MX53 LOCO Board") - .map_io = mx53_map_io, - .init_early = imx53_init_early, - .init_irq = mx53_init_irq, - .handle_irq = imx53_handle_irq, - .timer = &mx53_loco_timer, - .init_machine = mx53_loco_board_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/board-mx53_smd.c b/arch/arm/mach-mx5/board-mx53_smd.c deleted file mode 100644 index d498573..0000000 --- a/arch/arm/mach-mx5/board-mx53_smd.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * 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. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "devices-imx53.h" - -#define SMD_FEC_PHY_RST IMX_GPIO_NR(7, 6) -#define MX53_SMD_SATA_PWR_EN IMX_GPIO_NR(3, 3) - -static iomux_v3_cfg_t mx53_smd_pads[] = { - MX53_PAD_CSI0_DAT10__UART1_TXD_MUX, - MX53_PAD_CSI0_DAT11__UART1_RXD_MUX, - - MX53_PAD_PATA_BUFFER_EN__UART2_RXD_MUX, - MX53_PAD_PATA_DMARQ__UART2_TXD_MUX, - - MX53_PAD_PATA_CS_0__UART3_TXD_MUX, - MX53_PAD_PATA_CS_1__UART3_RXD_MUX, - MX53_PAD_PATA_DA_1__UART3_CTS, - MX53_PAD_PATA_DA_2__UART3_RTS, - /* I2C1 */ - MX53_PAD_CSI0_DAT8__I2C1_SDA, - MX53_PAD_CSI0_DAT9__I2C1_SCL, - /* SD1 */ - MX53_PAD_SD1_CMD__ESDHC1_CMD, - MX53_PAD_SD1_CLK__ESDHC1_CLK, - MX53_PAD_SD1_DATA0__ESDHC1_DAT0, - MX53_PAD_SD1_DATA1__ESDHC1_DAT1, - MX53_PAD_SD1_DATA2__ESDHC1_DAT2, - MX53_PAD_SD1_DATA3__ESDHC1_DAT3, - /* SD2 */ - MX53_PAD_SD2_CMD__ESDHC2_CMD, - MX53_PAD_SD2_CLK__ESDHC2_CLK, - MX53_PAD_SD2_DATA0__ESDHC2_DAT0, - MX53_PAD_SD2_DATA1__ESDHC2_DAT1, - MX53_PAD_SD2_DATA2__ESDHC2_DAT2, - MX53_PAD_SD2_DATA3__ESDHC2_DAT3, - /* SD3 */ - MX53_PAD_PATA_DATA8__ESDHC3_DAT0, - MX53_PAD_PATA_DATA9__ESDHC3_DAT1, - MX53_PAD_PATA_DATA10__ESDHC3_DAT2, - MX53_PAD_PATA_DATA11__ESDHC3_DAT3, - MX53_PAD_PATA_DATA0__ESDHC3_DAT4, - MX53_PAD_PATA_DATA1__ESDHC3_DAT5, - MX53_PAD_PATA_DATA2__ESDHC3_DAT6, - MX53_PAD_PATA_DATA3__ESDHC3_DAT7, - MX53_PAD_PATA_IORDY__ESDHC3_CLK, - MX53_PAD_PATA_RESET_B__ESDHC3_CMD, -}; - -static const struct imxuart_platform_data mx53_smd_uart_data __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static inline void mx53_smd_init_uart(void) -{ - imx53_add_imx_uart(0, NULL); - imx53_add_imx_uart(1, NULL); - imx53_add_imx_uart(2, &mx53_smd_uart_data); -} - -static inline void mx53_smd_fec_reset(void) -{ - int ret; - - /* reset FEC PHY */ - ret = gpio_request(SMD_FEC_PHY_RST, "fec-phy-reset"); - if (ret) { - printk(KERN_ERR"failed to get GPIO_FEC_PHY_RESET: %d\n", ret); - return; - } - gpio_direction_output(SMD_FEC_PHY_RST, 0); - msleep(1); - gpio_set_value(SMD_FEC_PHY_RST, 1); -} - -static struct fec_platform_data mx53_smd_fec_data = { - .phy = PHY_INTERFACE_MODE_RMII, -}; - -static const struct imxi2c_platform_data mx53_smd_i2c_data __initconst = { - .bitrate = 100000, -}; - -static inline void mx53_smd_ahci_pwr_on(void) -{ - int ret; - - /* Enable SATA PWR */ - ret = gpio_request_one(MX53_SMD_SATA_PWR_EN, - GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "ahci-sata-pwr"); - if (ret) { - pr_err("failed to enable SATA_PWR_EN: %d\n", ret); - return; - } -} - -void __init imx53_smd_common_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx53_smd_pads, - ARRAY_SIZE(mx53_smd_pads)); -} - -static void __init mx53_smd_board_init(void) -{ - imx53_soc_init(); - imx53_smd_common_init(); - - mx53_smd_init_uart(); - mx53_smd_fec_reset(); - imx53_add_fec(&mx53_smd_fec_data); - imx53_add_imx2_wdt(0, NULL); - imx53_add_imx_i2c(0, &mx53_smd_i2c_data); - imx53_add_sdhci_esdhc_imx(0, NULL); - imx53_add_sdhci_esdhc_imx(1, NULL); - imx53_add_sdhci_esdhc_imx(2, NULL); - mx53_smd_ahci_pwr_on(); - imx53_add_ahci_imx(); -} - -static void __init mx53_smd_timer_init(void) -{ - mx53_clocks_init(32768, 24000000, 22579200, 0); -} - -static struct sys_timer mx53_smd_timer = { - .init = mx53_smd_timer_init, -}; - -MACHINE_START(MX53_SMD, "Freescale MX53 SMD Board") - .map_io = mx53_map_io, - .init_early = imx53_init_early, - .init_irq = mx53_init_irq, - .handle_irq = imx53_handle_irq, - .timer = &mx53_smd_timer, - .init_machine = mx53_smd_board_init, -MACHINE_END diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c deleted file mode 100644 index 2aacf41..0000000 --- a/arch/arm/mach-mx5/clock-mx51-mx53.c +++ /dev/null @@ -1,1673 +0,0 @@ -/* - * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2009-2010 Amit Kucheria - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "crm_regs.h" - -/* External clock values passed-in by the board code */ -static unsigned long external_high_reference, external_low_reference; -static unsigned long oscillator_reference, ckih2_reference; - -static struct clk osc_clk; -static struct clk pll1_main_clk; -static struct clk pll1_sw_clk; -static struct clk pll2_sw_clk; -static struct clk pll3_sw_clk; -static struct clk mx53_pll4_sw_clk; -static struct clk lp_apm_clk; -static struct clk periph_apm_clk; -static struct clk ahb_clk; -static struct clk ipg_clk; -static struct clk usboh3_clk; -static struct clk emi_fast_clk; -static struct clk ipu_clk; -static struct clk mipi_hsc1_clk; -static struct clk esdhc1_clk; -static struct clk esdhc2_clk; -static struct clk esdhc3_mx53_clk; - -#define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ - -/* calculate best pre and post dividers to get the required divider */ -static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post, - u32 max_pre, u32 max_post) -{ - if (div >= max_pre * max_post) { - *pre = max_pre; - *post = max_post; - } else if (div >= max_pre) { - u32 min_pre, temp_pre, old_err, err; - min_pre = DIV_ROUND_UP(div, max_post); - old_err = max_pre; - for (temp_pre = max_pre; temp_pre >= min_pre; temp_pre--) { - err = div % temp_pre; - if (err == 0) { - *pre = temp_pre; - break; - } - err = temp_pre - err; - if (err < old_err) { - old_err = err; - *pre = temp_pre; - } - } - *post = DIV_ROUND_UP(div, *pre); - } else { - *pre = div; - *post = 1; - } -} - -static void _clk_ccgr_setclk(struct clk *clk, unsigned mode) -{ - u32 reg = __raw_readl(clk->enable_reg); - - reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift); - reg |= mode << clk->enable_shift; - - __raw_writel(reg, clk->enable_reg); -} - -static int _clk_ccgr_enable(struct clk *clk) -{ - _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_ON); - return 0; -} - -static void _clk_ccgr_disable(struct clk *clk) -{ - _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_OFF); -} - -static int _clk_ccgr_enable_inrun(struct clk *clk) -{ - _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_IDLE); - return 0; -} - -static void _clk_ccgr_disable_inwait(struct clk *clk) -{ - _clk_ccgr_setclk(clk, MXC_CCM_CCGRx_MOD_IDLE); -} - -/* - * For the 4-to-1 muxed input clock - */ -static inline u32 _get_mux(struct clk *parent, struct clk *m0, - struct clk *m1, struct clk *m2, struct clk *m3) -{ - if (parent == m0) - return 0; - else if (parent == m1) - return 1; - else if (parent == m2) - return 2; - else if (parent == m3) - return 3; - else - BUG(); - - return -EINVAL; -} - -static inline void __iomem *_mx51_get_pll_base(struct clk *pll) -{ - if (pll == &pll1_main_clk) - return MX51_DPLL1_BASE; - else if (pll == &pll2_sw_clk) - return MX51_DPLL2_BASE; - else if (pll == &pll3_sw_clk) - return MX51_DPLL3_BASE; - else - BUG(); - - return NULL; -} - -static inline void __iomem *_mx53_get_pll_base(struct clk *pll) -{ - if (pll == &pll1_main_clk) - return MX53_DPLL1_BASE; - else if (pll == &pll2_sw_clk) - return MX53_DPLL2_BASE; - else if (pll == &pll3_sw_clk) - return MX53_DPLL3_BASE; - else if (pll == &mx53_pll4_sw_clk) - return MX53_DPLL4_BASE; - else - BUG(); - - return NULL; -} - -static inline void __iomem *_get_pll_base(struct clk *pll) -{ - if (cpu_is_mx51()) - return _mx51_get_pll_base(pll); - else - return _mx53_get_pll_base(pll); -} - -static unsigned long clk_pll_get_rate(struct clk *clk) -{ - long mfi, mfn, mfd, pdf, ref_clk, mfn_abs; - unsigned long dp_op, dp_mfd, dp_mfn, dp_ctl, pll_hfsm, dbl; - void __iomem *pllbase; - s64 temp; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - pllbase = _get_pll_base(clk); - - dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); - pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM; - dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN; - - if (pll_hfsm == 0) { - dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP); - dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD); - dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN); - } else { - dp_op = __raw_readl(pllbase + MXC_PLL_DP_HFS_OP); - dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFD); - dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFN); - } - pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK; - mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET; - mfi = (mfi <= 5) ? 5 : mfi; - mfd = dp_mfd & MXC_PLL_DP_MFD_MASK; - mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK; - /* Sign extend to 32-bits */ - if (mfn >= 0x04000000) { - mfn |= 0xFC000000; - mfn_abs = -mfn; - } - - ref_clk = 2 * parent_rate; - if (dbl != 0) - ref_clk *= 2; - - ref_clk /= (pdf + 1); - temp = (u64) ref_clk * mfn_abs; - do_div(temp, mfd + 1); - if (mfn < 0) - temp = -temp; - temp = (ref_clk * mfi) + temp; - - return temp; -} - -static int _clk_pll_set_rate(struct clk *clk, unsigned long rate) -{ - u32 reg; - void __iomem *pllbase; - - long mfi, pdf, mfn, mfd = 999999; - s64 temp64; - unsigned long quad_parent_rate; - unsigned long pll_hfsm, dp_ctl; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - pllbase = _get_pll_base(clk); - - quad_parent_rate = 4 * parent_rate; - pdf = mfi = -1; - while (++pdf < 16 && mfi < 5) - mfi = rate * (pdf+1) / quad_parent_rate; - if (mfi > 15) - return -EINVAL; - pdf--; - - temp64 = rate * (pdf+1) - quad_parent_rate * mfi; - do_div(temp64, quad_parent_rate/1000000); - mfn = (long)temp64; - - dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); - /* use dpdck0_2 */ - __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL); - pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM; - if (pll_hfsm == 0) { - reg = mfi << 4 | pdf; - __raw_writel(reg, pllbase + MXC_PLL_DP_OP); - __raw_writel(mfd, pllbase + MXC_PLL_DP_MFD); - __raw_writel(mfn, pllbase + MXC_PLL_DP_MFN); - } else { - reg = mfi << 4 | pdf; - __raw_writel(reg, pllbase + MXC_PLL_DP_HFS_OP); - __raw_writel(mfd, pllbase + MXC_PLL_DP_HFS_MFD); - __raw_writel(mfn, pllbase + MXC_PLL_DP_HFS_MFN); - } - - return 0; -} - -static int _clk_pll_enable(struct clk *clk) -{ - u32 reg; - void __iomem *pllbase; - int i = 0; - - pllbase = _get_pll_base(clk); - reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); - if (reg & MXC_PLL_DP_CTL_UPEN) - return 0; - - reg |= MXC_PLL_DP_CTL_UPEN; - __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); - - /* Wait for lock */ - do { - reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); - if (reg & MXC_PLL_DP_CTL_LRF) - break; - - udelay(1); - } while (++i < MAX_DPLL_WAIT_TRIES); - - if (i == MAX_DPLL_WAIT_TRIES) { - pr_err("MX5: pll locking failed\n"); - return -EINVAL; - } - - return 0; -} - -static void _clk_pll_disable(struct clk *clk) -{ - u32 reg; - void __iomem *pllbase; - - pllbase = _get_pll_base(clk); - reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN; - __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); -} - -static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg, step; - - reg = __raw_readl(MXC_CCM_CCSR); - - /* When switching from pll_main_clk to a bypass clock, first select a - * multiplexed clock in 'step_sel', then shift the glitchless mux - * 'pll1_sw_clk_sel'. - * - * When switching back, do it in reverse order - */ - if (parent == &pll1_main_clk) { - /* Switch to pll1_main_clk */ - reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL; - __raw_writel(reg, MXC_CCM_CCSR); - /* step_clk mux switched to lp_apm, to save power. */ - reg = __raw_readl(MXC_CCM_CCSR); - reg &= ~MXC_CCM_CCSR_STEP_SEL_MASK; - reg |= (MXC_CCM_CCSR_STEP_SEL_LP_APM << - MXC_CCM_CCSR_STEP_SEL_OFFSET); - } else { - if (parent == &lp_apm_clk) { - step = MXC_CCM_CCSR_STEP_SEL_LP_APM; - } else if (parent == &pll2_sw_clk) { - step = MXC_CCM_CCSR_STEP_SEL_PLL2_DIVIDED; - } else if (parent == &pll3_sw_clk) { - step = MXC_CCM_CCSR_STEP_SEL_PLL3_DIVIDED; - } else - return -EINVAL; - - reg &= ~MXC_CCM_CCSR_STEP_SEL_MASK; - reg |= (step << MXC_CCM_CCSR_STEP_SEL_OFFSET); - - __raw_writel(reg, MXC_CCM_CCSR); - /* Switch to step_clk */ - reg = __raw_readl(MXC_CCM_CCSR); - reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL; - } - __raw_writel(reg, MXC_CCM_CCSR); - return 0; -} - -static unsigned long clk_pll1_sw_get_rate(struct clk *clk) -{ - u32 reg, div; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - reg = __raw_readl(MXC_CCM_CCSR); - - if (clk->parent == &pll2_sw_clk) { - div = ((reg & MXC_CCM_CCSR_PLL2_PODF_MASK) >> - MXC_CCM_CCSR_PLL2_PODF_OFFSET) + 1; - } else if (clk->parent == &pll3_sw_clk) { - div = ((reg & MXC_CCM_CCSR_PLL3_PODF_MASK) >> - MXC_CCM_CCSR_PLL3_PODF_OFFSET) + 1; - } else - div = 1; - return parent_rate / div; -} - -static int _clk_pll2_sw_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CCSR); - - if (parent == &pll2_sw_clk) - reg &= ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL; - else - reg |= MXC_CCM_CCSR_PLL2_SW_CLK_SEL; - - __raw_writel(reg, MXC_CCM_CCSR); - return 0; -} - -static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - if (parent == &osc_clk) - reg = __raw_readl(MXC_CCM_CCSR) & ~MXC_CCM_CCSR_LP_APM_SEL; - else - return -EINVAL; - - __raw_writel(reg, MXC_CCM_CCSR); - - return 0; -} - -static unsigned long clk_cpu_get_rate(struct clk *clk) -{ - u32 cacrr, div; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - cacrr = __raw_readl(MXC_CCM_CACRR); - div = (cacrr & MXC_CCM_CACRR_ARM_PODF_MASK) + 1; - - return parent_rate / div; -} - -static int clk_cpu_set_rate(struct clk *clk, unsigned long rate) -{ - u32 reg, cpu_podf; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - cpu_podf = parent_rate / rate - 1; - /* use post divider to change freq */ - reg = __raw_readl(MXC_CCM_CACRR); - reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; - reg |= cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; - __raw_writel(reg, MXC_CCM_CACRR); - - return 0; -} - -static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg, mux; - int i = 0; - - mux = _get_mux(parent, &pll1_sw_clk, &pll3_sw_clk, &lp_apm_clk, NULL); - - reg = __raw_readl(MXC_CCM_CBCMR) & ~MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK; - reg |= mux << MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET; - __raw_writel(reg, MXC_CCM_CBCMR); - - /* Wait for lock */ - do { - reg = __raw_readl(MXC_CCM_CDHIPR); - if (!(reg & MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY)) - break; - - udelay(1); - } while (++i < MAX_DPLL_WAIT_TRIES); - - if (i == MAX_DPLL_WAIT_TRIES) { - pr_err("MX5: Set parent for periph_apm clock failed\n"); - return -EINVAL; - } - - return 0; -} - -static int _clk_main_bus_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CBCDR); - - if (parent == &pll2_sw_clk) - reg &= ~MXC_CCM_CBCDR_PERIPH_CLK_SEL; - else if (parent == &periph_apm_clk) - reg |= MXC_CCM_CBCDR_PERIPH_CLK_SEL; - else - return -EINVAL; - - __raw_writel(reg, MXC_CCM_CBCDR); - - return 0; -} - -static struct clk main_bus_clk = { - .parent = &pll2_sw_clk, - .set_parent = _clk_main_bus_set_parent, -}; - -static unsigned long clk_ahb_get_rate(struct clk *clk) -{ - u32 reg, div; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - reg = __raw_readl(MXC_CCM_CBCDR); - div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >> - MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1; - return parent_rate / div; -} - - -static int _clk_ahb_set_rate(struct clk *clk, unsigned long rate) -{ - u32 reg, div; - unsigned long parent_rate; - int i = 0; - - parent_rate = clk_get_rate(clk->parent); - - div = parent_rate / rate; - if (div > 8 || div < 1 || ((parent_rate / div) != rate)) - return -EINVAL; - - reg = __raw_readl(MXC_CCM_CBCDR); - reg &= ~MXC_CCM_CBCDR_AHB_PODF_MASK; - reg |= (div - 1) << MXC_CCM_CBCDR_AHB_PODF_OFFSET; - __raw_writel(reg, MXC_CCM_CBCDR); - - /* Wait for lock */ - do { - reg = __raw_readl(MXC_CCM_CDHIPR); - if (!(reg & MXC_CCM_CDHIPR_AHB_PODF_BUSY)) - break; - - udelay(1); - } while (++i < MAX_DPLL_WAIT_TRIES); - - if (i == MAX_DPLL_WAIT_TRIES) { - pr_err("MX5: clk_ahb_set_rate failed\n"); - return -EINVAL; - } - - return 0; -} - -static unsigned long _clk_ahb_round_rate(struct clk *clk, - unsigned long rate) -{ - u32 div; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - div = parent_rate / rate; - if (div > 8) - div = 8; - else if (div == 0) - div++; - return parent_rate / div; -} - - -static int _clk_max_enable(struct clk *clk) -{ - u32 reg; - - _clk_ccgr_enable(clk); - - /* Handshake with MAX when LPM is entered. */ - reg = __raw_readl(MXC_CCM_CLPCR); - if (cpu_is_mx51()) - reg &= ~MX51_CCM_CLPCR_BYPASS_MAX_LPM_HS; - else if (cpu_is_mx53()) - reg &= ~MX53_CCM_CLPCR_BYPASS_MAX_LPM_HS; - __raw_writel(reg, MXC_CCM_CLPCR); - - return 0; -} - -static void _clk_max_disable(struct clk *clk) -{ - u32 reg; - - _clk_ccgr_disable_inwait(clk); - - /* No Handshake with MAX when LPM is entered as its disabled. */ - reg = __raw_readl(MXC_CCM_CLPCR); - if (cpu_is_mx51()) - reg |= MX51_CCM_CLPCR_BYPASS_MAX_LPM_HS; - else if (cpu_is_mx53()) - reg &= ~MX53_CCM_CLPCR_BYPASS_MAX_LPM_HS; - __raw_writel(reg, MXC_CCM_CLPCR); -} - -static unsigned long clk_ipg_get_rate(struct clk *clk) -{ - u32 reg, div; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - reg = __raw_readl(MXC_CCM_CBCDR); - div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >> - MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1; - - return parent_rate / div; -} - -static unsigned long clk_ipg_per_get_rate(struct clk *clk) -{ - u32 reg, prediv1, prediv2, podf; - unsigned long parent_rate; - - parent_rate = clk_get_rate(clk->parent); - - if (clk->parent == &main_bus_clk || clk->parent == &lp_apm_clk) { - /* the main_bus_clk is the one before the DVFS engine */ - reg = __raw_readl(MXC_CCM_CBCDR); - prediv1 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED1_MASK) >> - MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET) + 1; - prediv2 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED2_MASK) >> - MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET) + 1; - podf = ((reg & MXC_CCM_CBCDR_PERCLK_PODF_MASK) >> - MXC_CCM_CBCDR_PERCLK_PODF_OFFSET) + 1; - return parent_rate / (prediv1 * prediv2 * podf); - } else if (clk->parent == &ipg_clk) - return parent_rate; - else - BUG(); -} - -static int _clk_ipg_per_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CBCMR); - - reg &= ~MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL; - reg &= ~MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL; - - if (parent == &ipg_clk) - reg |= MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL; - else if (parent == &lp_apm_clk) - reg |= MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL; - else if (parent != &main_bus_clk) - return -EINVAL; - - __raw_writel(reg, MXC_CCM_CBCMR); - - return 0; -} - -#define clk_nfc_set_parent NULL - -static unsigned long clk_nfc_get_rate(struct clk *clk) -{ - unsigned long rate; - u32 reg, div; - - reg = __raw_readl(MXC_CCM_CBCDR); - div = ((reg & MXC_CCM_CBCDR_NFC_PODF_MASK) >> - MXC_CCM_CBCDR_NFC_PODF_OFFSET) + 1; - rate = clk_get_rate(clk->parent) / div; - WARN_ON(rate == 0); - return rate; -} - -static unsigned long clk_nfc_round_rate(struct clk *clk, - unsigned long rate) -{ - u32 div; - unsigned long parent_rate = clk_get_rate(clk->parent); - - if (!rate) - return -EINVAL; - - div = parent_rate / rate; - - if (parent_rate % rate) - div++; - - if (div > 8) - return -EINVAL; - - return parent_rate / div; - -} - -static int clk_nfc_set_rate(struct clk *clk, unsigned long rate) -{ - u32 reg, div; - - div = clk_get_rate(clk->parent) / rate; - if (div == 0) - div++; - if (((clk_get_rate(clk->parent) / div) != rate) || (div > 8)) - return -EINVAL; - - reg = __raw_readl(MXC_CCM_CBCDR); - reg &= ~MXC_CCM_CBCDR_NFC_PODF_MASK; - reg |= (div - 1) << MXC_CCM_CBCDR_NFC_PODF_OFFSET; - __raw_writel(reg, MXC_CCM_CBCDR); - - while (__raw_readl(MXC_CCM_CDHIPR) & - MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY){ - } - - return 0; -} - -static unsigned long get_high_reference_clock_rate(struct clk *clk) -{ - return external_high_reference; -} - -static unsigned long get_low_reference_clock_rate(struct clk *clk) -{ - return external_low_reference; -} - -static unsigned long get_oscillator_reference_clock_rate(struct clk *clk) -{ - return oscillator_reference; -} - -static unsigned long get_ckih2_reference_clock_rate(struct clk *clk) -{ - return ckih2_reference; -} - -static unsigned long clk_emi_slow_get_rate(struct clk *clk) -{ - u32 reg, div; - - reg = __raw_readl(MXC_CCM_CBCDR); - div = ((reg & MXC_CCM_CBCDR_EMI_PODF_MASK) >> - MXC_CCM_CBCDR_EMI_PODF_OFFSET) + 1; - - return clk_get_rate(clk->parent) / div; -} - -static unsigned long _clk_ddr_hf_get_rate(struct clk *clk) -{ - unsigned long rate; - u32 reg, div; - - reg = __raw_readl(MXC_CCM_CBCDR); - div = ((reg & MXC_CCM_CBCDR_DDR_PODF_MASK) >> - MXC_CCM_CBCDR_DDR_PODF_OFFSET) + 1; - rate = clk_get_rate(clk->parent) / div; - - return rate; -} - -/* External high frequency clock */ -static struct clk ckih_clk = { - .get_rate = get_high_reference_clock_rate, -}; - -static struct clk ckih2_clk = { - .get_rate = get_ckih2_reference_clock_rate, -}; - -static struct clk osc_clk = { - .get_rate = get_oscillator_reference_clock_rate, -}; - -/* External low frequency (32kHz) clock */ -static struct clk ckil_clk = { - .get_rate = get_low_reference_clock_rate, -}; - -static struct clk pll1_main_clk = { - .parent = &osc_clk, - .get_rate = clk_pll_get_rate, - .enable = _clk_pll_enable, - .disable = _clk_pll_disable, -}; - -/* Clock tree block diagram (WIP): - * CCM: Clock Controller Module - * - * PLL output -> | - * | CCM Switcher -> CCM_CLK_ROOT_GEN -> - * PLL bypass -> | - * - */ - -/* PLL1 SW supplies to ARM core */ -static struct clk pll1_sw_clk = { - .parent = &pll1_main_clk, - .set_parent = _clk_pll1_sw_set_parent, - .get_rate = clk_pll1_sw_get_rate, -}; - -/* PLL2 SW supplies to AXI/AHB/IP buses */ -static struct clk pll2_sw_clk = { - .parent = &osc_clk, - .get_rate = clk_pll_get_rate, - .set_rate = _clk_pll_set_rate, - .set_parent = _clk_pll2_sw_set_parent, - .enable = _clk_pll_enable, - .disable = _clk_pll_disable, -}; - -/* PLL3 SW supplies to serial clocks like USB, SSI, etc. */ -static struct clk pll3_sw_clk = { - .parent = &osc_clk, - .set_rate = _clk_pll_set_rate, - .get_rate = clk_pll_get_rate, - .enable = _clk_pll_enable, - .disable = _clk_pll_disable, -}; - -/* PLL4 SW supplies to LVDS Display Bridge(LDB) */ -static struct clk mx53_pll4_sw_clk = { - .parent = &osc_clk, - .set_rate = _clk_pll_set_rate, - .enable = _clk_pll_enable, - .disable = _clk_pll_disable, -}; - -/* Low-power Audio Playback Mode clock */ -static struct clk lp_apm_clk = { - .parent = &osc_clk, - .set_parent = _clk_lp_apm_set_parent, -}; - -static struct clk periph_apm_clk = { - .parent = &pll1_sw_clk, - .set_parent = _clk_periph_apm_set_parent, -}; - -static struct clk cpu_clk = { - .parent = &pll1_sw_clk, - .get_rate = clk_cpu_get_rate, - .set_rate = clk_cpu_set_rate, -}; - -static struct clk ahb_clk = { - .parent = &main_bus_clk, - .get_rate = clk_ahb_get_rate, - .set_rate = _clk_ahb_set_rate, - .round_rate = _clk_ahb_round_rate, -}; - -static struct clk iim_clk = { - .parent = &ipg_clk, - .enable_reg = MXC_CCM_CCGR0, - .enable_shift = MXC_CCM_CCGRx_CG15_OFFSET, -}; - -/* Main IP interface clock for access to registers */ -static struct clk ipg_clk = { - .parent = &ahb_clk, - .get_rate = clk_ipg_get_rate, -}; - -static struct clk ipg_perclk = { - .parent = &lp_apm_clk, - .get_rate = clk_ipg_per_get_rate, - .set_parent = _clk_ipg_per_set_parent, -}; - -static struct clk ahb_max_clk = { - .parent = &ahb_clk, - .enable_reg = MXC_CCM_CCGR0, - .enable_shift = MXC_CCM_CCGRx_CG14_OFFSET, - .enable = _clk_max_enable, - .disable = _clk_max_disable, -}; - -static struct clk aips_tz1_clk = { - .parent = &ahb_clk, - .secondary = &ahb_max_clk, - .enable_reg = MXC_CCM_CCGR0, - .enable_shift = MXC_CCM_CCGRx_CG12_OFFSET, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable_inwait, -}; - -static struct clk aips_tz2_clk = { - .parent = &ahb_clk, - .secondary = &ahb_max_clk, - .enable_reg = MXC_CCM_CCGR0, - .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable_inwait, -}; - -static struct clk gpc_dvfs_clk = { - .enable_reg = MXC_CCM_CCGR5, - .enable_shift = MXC_CCM_CCGRx_CG12_OFFSET, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable, -}; - -static struct clk gpt_32k_clk = { - .id = 0, - .parent = &ckil_clk, -}; - -static struct clk dummy_clk = { - .id = 0, -}; - -static struct clk emi_slow_clk = { - .parent = &pll2_sw_clk, - .enable_reg = MXC_CCM_CCGR5, - .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable_inwait, - .get_rate = clk_emi_slow_get_rate, -}; - -static int clk_ipu_enable(struct clk *clk) -{ - u32 reg; - - _clk_ccgr_enable(clk); - - /* Enable handshake with IPU when certain clock rates are changed */ - reg = __raw_readl(MXC_CCM_CCDR); - reg &= ~MXC_CCM_CCDR_IPU_HS_MASK; - __raw_writel(reg, MXC_CCM_CCDR); - - /* Enable handshake with IPU when LPM is entered */ - reg = __raw_readl(MXC_CCM_CLPCR); - reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; - __raw_writel(reg, MXC_CCM_CLPCR); - - return 0; -} - -static void clk_ipu_disable(struct clk *clk) -{ - u32 reg; - - _clk_ccgr_disable(clk); - - /* Disable handshake with IPU whe dividers are changed */ - reg = __raw_readl(MXC_CCM_CCDR); - reg |= MXC_CCM_CCDR_IPU_HS_MASK; - __raw_writel(reg, MXC_CCM_CCDR); - - /* Disable handshake with IPU when LPM is entered */ - reg = __raw_readl(MXC_CCM_CLPCR); - reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; - __raw_writel(reg, MXC_CCM_CLPCR); -} - -static struct clk ahbmux1_clk = { - .parent = &ahb_clk, - .secondary = &ahb_max_clk, - .enable_reg = MXC_CCM_CCGR0, - .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable_inwait, -}; - -static struct clk ipu_sec_clk = { - .parent = &emi_fast_clk, - .secondary = &ahbmux1_clk, -}; - -static struct clk ddr_hf_clk = { - .parent = &pll1_sw_clk, - .get_rate = _clk_ddr_hf_get_rate, -}; - -static struct clk ddr_clk = { - .parent = &ddr_hf_clk, -}; - -/* clock definitions for MIPI HSC unit which has been removed - * from documentation, but not from hardware - */ -static int _clk_hsc_enable(struct clk *clk) -{ - u32 reg; - - _clk_ccgr_enable(clk); - /* Handshake with IPU when certain clock rates are changed. */ - reg = __raw_readl(MXC_CCM_CCDR); - reg &= ~MXC_CCM_CCDR_HSC_HS_MASK; - __raw_writel(reg, MXC_CCM_CCDR); - - reg = __raw_readl(MXC_CCM_CLPCR); - reg &= ~MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS; - __raw_writel(reg, MXC_CCM_CLPCR); - - return 0; -} - -static void _clk_hsc_disable(struct clk *clk) -{ - u32 reg; - - _clk_ccgr_disable(clk); - /* No handshake with HSC as its not enabled. */ - reg = __raw_readl(MXC_CCM_CCDR); - reg |= MXC_CCM_CCDR_HSC_HS_MASK; - __raw_writel(reg, MXC_CCM_CCDR); - - reg = __raw_readl(MXC_CCM_CLPCR); - reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS; - __raw_writel(reg, MXC_CCM_CLPCR); -} - -static struct clk mipi_hsp_clk = { - .parent = &ipu_clk, - .enable_reg = MXC_CCM_CCGR4, - .enable_shift = MXC_CCM_CCGRx_CG6_OFFSET, - .enable = _clk_hsc_enable, - .disable = _clk_hsc_disable, - .secondary = &mipi_hsc1_clk, -}; - -#define DEFINE_CLOCK_CCGR(name, i, er, es, pfx, p, s) \ - static struct clk name = { \ - .id = i, \ - .enable_reg = er, \ - .enable_shift = es, \ - .get_rate = pfx##_get_rate, \ - .set_rate = pfx##_set_rate, \ - .round_rate = pfx##_round_rate, \ - .set_parent = pfx##_set_parent, \ - .enable = _clk_ccgr_enable, \ - .disable = _clk_ccgr_disable, \ - .parent = p, \ - .secondary = s, \ - } - -#define DEFINE_CLOCK_MAX(name, i, er, es, pfx, p, s) \ - static struct clk name = { \ - .id = i, \ - .enable_reg = er, \ - .enable_shift = es, \ - .get_rate = pfx##_get_rate, \ - .set_rate = pfx##_set_rate, \ - .set_parent = pfx##_set_parent, \ - .enable = _clk_max_enable, \ - .disable = _clk_max_disable, \ - .parent = p, \ - .secondary = s, \ - } - -#define CLK_GET_RATE(name, nr, bitsname) \ -static unsigned long clk_##name##_get_rate(struct clk *clk) \ -{ \ - u32 reg, pred, podf; \ - \ - reg = __raw_readl(MXC_CCM_CSCDR##nr); \ - pred = (reg & MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_MASK) \ - >> MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_OFFSET; \ - podf = (reg & MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_MASK) \ - >> MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_OFFSET; \ - \ - return DIV_ROUND_CLOSEST(clk_get_rate(clk->parent), \ - (pred + 1) * (podf + 1)); \ -} - -#define CLK_SET_PARENT(name, nr, bitsname) \ -static int clk_##name##_set_parent(struct clk *clk, struct clk *parent) \ -{ \ - u32 reg, mux; \ - \ - mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, \ - &pll3_sw_clk, &lp_apm_clk); \ - reg = __raw_readl(MXC_CCM_CSCMR##nr) & \ - ~MXC_CCM_CSCMR##nr##_##bitsname##_CLK_SEL_MASK; \ - reg |= mux << MXC_CCM_CSCMR##nr##_##bitsname##_CLK_SEL_OFFSET; \ - __raw_writel(reg, MXC_CCM_CSCMR##nr); \ - \ - return 0; \ -} - -#define CLK_SET_RATE(name, nr, bitsname) \ -static int clk_##name##_set_rate(struct clk *clk, unsigned long rate) \ -{ \ - u32 reg, div, parent_rate; \ - u32 pre = 0, post = 0; \ - \ - parent_rate = clk_get_rate(clk->parent); \ - div = parent_rate / rate; \ - \ - if ((parent_rate / div) != rate) \ - return -EINVAL; \ - \ - __calc_pre_post_dividers(div, &pre, &post, \ - (MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_MASK >> \ - MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_OFFSET) + 1, \ - (MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_MASK >> \ - MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_OFFSET) + 1);\ - \ - /* Set sdhc1 clock divider */ \ - reg = __raw_readl(MXC_CCM_CSCDR##nr) & \ - ~(MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_MASK \ - | MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_MASK); \ - reg |= (post - 1) << \ - MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PODF_OFFSET; \ - reg |= (pre - 1) << \ - MXC_CCM_CSCDR##nr##_##bitsname##_CLK_PRED_OFFSET; \ - __raw_writel(reg, MXC_CCM_CSCDR##nr); \ - \ - return 0; \ -} - -/* UART */ -CLK_GET_RATE(uart, 1, UART) -CLK_SET_PARENT(uart, 1, UART) - -static struct clk uart_root_clk = { - .parent = &pll2_sw_clk, - .get_rate = clk_uart_get_rate, - .set_parent = clk_uart_set_parent, -}; - -/* USBOH3 */ -CLK_GET_RATE(usboh3, 1, USBOH3) -CLK_SET_PARENT(usboh3, 1, USBOH3) - -static struct clk usboh3_clk = { - .parent = &pll2_sw_clk, - .get_rate = clk_usboh3_get_rate, - .set_parent = clk_usboh3_set_parent, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable, - .enable_reg = MXC_CCM_CCGR2, - .enable_shift = MXC_CCM_CCGRx_CG14_OFFSET, -}; - -static struct clk usb_ahb_clk = { - .parent = &ipg_clk, - .enable = _clk_ccgr_enable, - .disable = _clk_ccgr_disable, - .enable_reg = MXC_CCM_CCGR2, - .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, -}; - -static int clk_usb_phy1_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; - - if (parent == &pll3_sw_clk) - reg |= 1 << MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET; - - __raw_writel(reg, MXC_CCM_CSCMR1); - - return 0; -} - -static struct clk usb_phy1_clk = { - .parent = &pll3_sw_clk, - .set_parent = clk_usb_phy1_set_parent, - .enable = _clk_ccgr_enable, - .enable_reg = MXC_CCM_CCGR2, - .enable_shift = MXC_CCM_CCGRx_CG0_OFFSET, - .disable = _clk_ccgr_disable, -}; - -/* eCSPI */ -CLK_GET_RATE(ecspi, 2, CSPI) -CLK_SET_PARENT(ecspi, 1, CSPI) - -static struct clk ecspi_main_clk = { - .parent = &pll3_sw_clk, - .get_rate = clk_ecspi_get_rate, - .set_parent = clk_ecspi_set_parent, -}; - -/* eSDHC */ -CLK_GET_RATE(esdhc1, 1, ESDHC1_MSHC1) -CLK_SET_PARENT(esdhc1, 1, ESDHC1_MSHC1) -CLK_SET_RATE(esdhc1, 1, ESDHC1_MSHC1) - -/* mx51 specific */ -CLK_GET_RATE(esdhc2, 1, ESDHC2_MSHC2) -CLK_SET_PARENT(esdhc2, 1, ESDHC2_MSHC2) -CLK_SET_RATE(esdhc2, 1, ESDHC2_MSHC2) - -static int clk_esdhc3_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CSCMR1); - if (parent == &esdhc1_clk) - reg &= ~MXC_CCM_CSCMR1_ESDHC3_CLK_SEL; - else if (parent == &esdhc2_clk) - reg |= MXC_CCM_CSCMR1_ESDHC3_CLK_SEL; - else - return -EINVAL; - __raw_writel(reg, MXC_CCM_CSCMR1); - - return 0; -} - -static int clk_esdhc4_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CSCMR1); - if (parent == &esdhc1_clk) - reg &= ~MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; - else if (parent == &esdhc2_clk) - reg |= MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; - else - return -EINVAL; - __raw_writel(reg, MXC_CCM_CSCMR1); - - return 0; -} - -/* mx53 specific */ -static int clk_esdhc2_mx53_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CSCMR1); - if (parent == &esdhc1_clk) - reg &= ~MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL; - else if (parent == &esdhc3_mx53_clk) - reg |= MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL; - else - return -EINVAL; - __raw_writel(reg, MXC_CCM_CSCMR1); - - return 0; -} - -CLK_GET_RATE(esdhc3_mx53, 1, ESDHC3_MX53) -CLK_SET_PARENT(esdhc3_mx53, 1, ESDHC3_MX53) -CLK_SET_RATE(esdhc3_mx53, 1, ESDHC3_MX53) - -static int clk_esdhc4_mx53_set_parent(struct clk *clk, struct clk *parent) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CSCMR1); - if (parent == &esdhc1_clk) - reg &= ~MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; - else if (parent == &esdhc3_mx53_clk) - reg |= MXC_CCM_CSCMR1_ESDHC4_CLK_SEL; - else - return -EINVAL; - __raw_writel(reg, MXC_CCM_CSCMR1); - - return 0; -} - -#define DEFINE_CLOCK_FULL(name, i, er, es, gr, sr, e, d, p, s) \ - static struct clk name = { \ - .id = i, \ - .enable_reg = er, \ - .enable_shift = es, \ - .get_rate = gr, \ - .set_rate = sr, \ - .enable = e, \ - .disable = d, \ - .parent = p, \ - .secondary = s, \ - } - -#define DEFINE_CLOCK(name, i, er, es, gr, sr, p, s) \ - DEFINE_CLOCK_FULL(name, i, er, es, gr, sr, _clk_ccgr_enable, _clk_ccgr_disable, p, s) - -/* Shared peripheral bus arbiter */ -DEFINE_CLOCK(spba_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG0_OFFSET, - NULL, NULL, &ipg_clk, NULL); - -/* UART */ -DEFINE_CLOCK(uart1_ipg_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG3_OFFSET, - NULL, NULL, &ipg_clk, &aips_tz1_clk); -DEFINE_CLOCK(uart2_ipg_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG5_OFFSET, - NULL, NULL, &ipg_clk, &aips_tz1_clk); -DEFINE_CLOCK(uart3_ipg_clk, 2, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG7_OFFSET, - NULL, NULL, &ipg_clk, &spba_clk); -DEFINE_CLOCK(uart4_ipg_clk, 3, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG4_OFFSET, - NULL, NULL, &ipg_clk, &spba_clk); -DEFINE_CLOCK(uart5_ipg_clk, 4, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG6_OFFSET, - NULL, NULL, &ipg_clk, &spba_clk); -DEFINE_CLOCK(uart1_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG4_OFFSET, - NULL, NULL, &uart_root_clk, &uart1_ipg_clk); -DEFINE_CLOCK(uart2_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG6_OFFSET, - NULL, NULL, &uart_root_clk, &uart2_ipg_clk); -DEFINE_CLOCK(uart3_clk, 2, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG8_OFFSET, - NULL, NULL, &uart_root_clk, &uart3_ipg_clk); -DEFINE_CLOCK(uart4_clk, 3, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG5_OFFSET, - NULL, NULL, &uart_root_clk, &uart4_ipg_clk); -DEFINE_CLOCK(uart5_clk, 4, MXC_CCM_CCGR7, MXC_CCM_CCGRx_CG7_OFFSET, - NULL, NULL, &uart_root_clk, &uart5_ipg_clk); - -/* GPT */ -DEFINE_CLOCK(gpt_ipg_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG10_OFFSET, - NULL, NULL, &ipg_clk, NULL); -DEFINE_CLOCK(gpt_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG9_OFFSET, - NULL, NULL, &ipg_clk, &gpt_ipg_clk); - -DEFINE_CLOCK(pwm1_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG6_OFFSET, - NULL, NULL, &ipg_clk, NULL); -DEFINE_CLOCK(pwm2_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG8_OFFSET, - NULL, NULL, &ipg_clk, NULL); - -/* I2C */ -DEFINE_CLOCK(i2c1_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG9_OFFSET, - NULL, NULL, &ipg_perclk, NULL); -DEFINE_CLOCK(i2c2_clk, 1, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG10_OFFSET, - NULL, NULL, &ipg_perclk, NULL); -DEFINE_CLOCK(hsi2c_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG11_OFFSET, - NULL, NULL, &ipg_clk, NULL); -DEFINE_CLOCK(i2c3_mx53_clk, 0, MXC_CCM_CCGR1, MXC_CCM_CCGRx_CG11_OFFSET, - NULL, NULL, &ipg_perclk, NULL); - -/* FEC */ -DEFINE_CLOCK(fec_clk, 0, MXC_CCM_CCGR2, MXC_CCM_CCGRx_CG12_OFFSET, - NULL, NULL, &ipg_clk, NULL); - -/* NFC */ -DEFINE_CLOCK_CCGR(nfc_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG10_OFFSET, - clk_nfc, &emi_slow_clk, NULL); - -/* SSI */ -DEFINE_CLOCK(ssi1_ipg_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG8_OFFSET, - NULL, NULL, &ipg_clk, NULL); -DEFINE_CLOCK(ssi1_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG9_OFFSET, - NULL, NULL, &pll3_sw_clk, &ssi1_ipg_clk); -DEFINE_CLOCK(ssi2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG10_OFFSET, - NULL, NULL, &ipg_clk, NULL); -DEFINE_CLOCK(ssi2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG11_OFFSET, - NULL, NULL, &pll3_sw_clk, &ssi2_ipg_clk); -DEFINE_CLOCK(ssi3_ipg_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG12_OFFSET, - NULL, NULL, &ipg_clk, NULL); -DEFINE_CLOCK(ssi3_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG13_OFFSET, - NULL, NULL, &pll3_sw_clk, &ssi3_ipg_clk); - -/* eCSPI */ -DEFINE_CLOCK_FULL(ecspi1_ipg_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG9_OFFSET, - NULL, NULL, _clk_ccgr_enable_inrun, _clk_ccgr_disable, - &ipg_clk, &spba_clk); -DEFINE_CLOCK(ecspi1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG10_OFFSET, - NULL, NULL, &ecspi_main_clk, &ecspi1_ipg_clk); -DEFINE_CLOCK_FULL(ecspi2_ipg_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG11_OFFSET, - NULL, NULL, _clk_ccgr_enable_inrun, _clk_ccgr_disable, - &ipg_clk, &aips_tz2_clk); -DEFINE_CLOCK(ecspi2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG12_OFFSET, - NULL, NULL, &ecspi_main_clk, &ecspi2_ipg_clk); - -/* CSPI */ -DEFINE_CLOCK(cspi_ipg_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG9_OFFSET, - NULL, NULL, &ipg_clk, &aips_tz2_clk); -DEFINE_CLOCK(cspi_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG13_OFFSET, - NULL, NULL, &ipg_clk, &cspi_ipg_clk); - -/* SDMA */ -DEFINE_CLOCK(sdma_clk, 1, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG15_OFFSET, - NULL, NULL, &ahb_clk, NULL); - -/* eSDHC */ -DEFINE_CLOCK_FULL(esdhc1_ipg_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG0_OFFSET, - NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); -DEFINE_CLOCK_MAX(esdhc1_clk, 0, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG1_OFFSET, - clk_esdhc1, &pll2_sw_clk, &esdhc1_ipg_clk); -DEFINE_CLOCK_FULL(esdhc2_ipg_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG2_OFFSET, - NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); -DEFINE_CLOCK_FULL(esdhc3_ipg_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG4_OFFSET, - NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); -DEFINE_CLOCK_FULL(esdhc4_ipg_clk, 3, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG6_OFFSET, - NULL, NULL, _clk_max_enable, _clk_max_disable, &ipg_clk, NULL); - -/* mx51 specific */ -DEFINE_CLOCK_MAX(esdhc2_clk, 1, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG3_OFFSET, - clk_esdhc2, &pll2_sw_clk, &esdhc2_ipg_clk); - -static struct clk esdhc3_clk = { - .id = 2, - .parent = &esdhc1_clk, - .set_parent = clk_esdhc3_set_parent, - .enable_reg = MXC_CCM_CCGR3, - .enable_shift = MXC_CCM_CCGRx_CG5_OFFSET, - .enable = _clk_max_enable, - .disable = _clk_max_disable, - .secondary = &esdhc3_ipg_clk, -}; -static struct clk esdhc4_clk = { - .id = 3, - .parent = &esdhc1_clk, - .set_parent = clk_esdhc4_set_parent, - .enable_reg = MXC_CCM_CCGR3, - .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET, - .enable = _clk_max_enable, - .disable = _clk_max_disable, - .secondary = &esdhc4_ipg_clk, -}; - -/* mx53 specific */ -static struct clk esdhc2_mx53_clk = { - .id = 2, - .parent = &esdhc1_clk, - .set_parent = clk_esdhc2_mx53_set_parent, - .enable_reg = MXC_CCM_CCGR3, - .enable_shift = MXC_CCM_CCGRx_CG3_OFFSET, - .enable = _clk_max_enable, - .disable = _clk_max_disable, - .secondary = &esdhc3_ipg_clk, -}; - -DEFINE_CLOCK_MAX(esdhc3_mx53_clk, 2, MXC_CCM_CCGR3, MXC_CCM_CCGRx_CG5_OFFSET, - clk_esdhc3_mx53, &pll2_sw_clk, &esdhc2_ipg_clk); - -static struct clk esdhc4_mx53_clk = { - .id = 3, - .parent = &esdhc1_clk, - .set_parent = clk_esdhc4_mx53_set_parent, - .enable_reg = MXC_CCM_CCGR3, - .enable_shift = MXC_CCM_CCGRx_CG7_OFFSET, - .enable = _clk_max_enable, - .disable = _clk_max_disable, - .secondary = &esdhc4_ipg_clk, -}; - -static struct clk sata_clk = { - .parent = &ipg_clk, - .enable = _clk_max_enable, - .enable_reg = MXC_CCM_CCGR4, - .enable_shift = MXC_CCM_CCGRx_CG1_OFFSET, - .disable = _clk_max_disable, -}; - -static struct clk ahci_phy_clk = { - .parent = &usb_phy1_clk, -}; - -static struct clk ahci_dma_clk = { - .parent = &ahb_clk, -}; - -DEFINE_CLOCK(mipi_esc_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG5_OFFSET, NULL, NULL, NULL, &pll2_sw_clk); -DEFINE_CLOCK(mipi_hsc2_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG4_OFFSET, NULL, NULL, &mipi_esc_clk, &pll2_sw_clk); -DEFINE_CLOCK(mipi_hsc1_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG3_OFFSET, NULL, NULL, &mipi_hsc2_clk, &pll2_sw_clk); - -/* IPU */ -DEFINE_CLOCK_FULL(ipu_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG5_OFFSET, - NULL, NULL, clk_ipu_enable, clk_ipu_disable, &ahb_clk, &ipu_sec_clk); - -DEFINE_CLOCK_FULL(emi_fast_clk, 0, MXC_CCM_CCGR5, MXC_CCM_CCGRx_CG7_OFFSET, - NULL, NULL, _clk_ccgr_enable, _clk_ccgr_disable_inwait, - &ddr_clk, NULL); - -DEFINE_CLOCK(ipu_di0_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG5_OFFSET, - NULL, NULL, &pll3_sw_clk, NULL); -DEFINE_CLOCK(ipu_di1_clk, 0, MXC_CCM_CCGR6, MXC_CCM_CCGRx_CG6_OFFSET, - NULL, NULL, &pll3_sw_clk, NULL); - -/* PATA */ -DEFINE_CLOCK(pata_clk, 0, MXC_CCM_CCGR4, MXC_CCM_CCGRx_CG0_OFFSET, - NULL, NULL, &ipg_clk, &spba_clk); - -#define _REGISTER_CLOCK(d, n, c) \ - { \ - .dev_id = d, \ - .con_id = n, \ - .clk = &c, \ - }, - -static struct clk_lookup mx51_lookups[] = { - /* i.mx51 has the i.mx21 type uart */ - _REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk) - _REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk) - _REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk) - _REGISTER_CLOCK(NULL, "gpt", gpt_clk) - /* i.mx51 has the i.mx27 type fec */ - _REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk) - _REGISTER_CLOCK("mxc_pwm.0", "pwm", pwm1_clk) - _REGISTER_CLOCK("mxc_pwm.1", "pwm", pwm2_clk) - _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) - _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) - _REGISTER_CLOCK("imx-i2c.2", NULL, hsi2c_clk) - _REGISTER_CLOCK("mxc-ehci.0", "usb", usboh3_clk) - _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_ahb_clk) - _REGISTER_CLOCK("mxc-ehci.0", "usb_phy1", usb_phy1_clk) - _REGISTER_CLOCK("mxc-ehci.1", "usb", usboh3_clk) - _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_ahb_clk) - _REGISTER_CLOCK("mxc-ehci.2", "usb", usboh3_clk) - _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_ahb_clk) - _REGISTER_CLOCK("fsl-usb2-udc", "usb", usboh3_clk) - _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", ahb_clk) - _REGISTER_CLOCK("imx-keypad", NULL, dummy_clk) - _REGISTER_CLOCK("mxc_nand", NULL, nfc_clk) - _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) - _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) - _REGISTER_CLOCK("imx-ssi.2", NULL, ssi3_clk) - /* i.mx51 has the i.mx35 type sdma */ - _REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk) - _REGISTER_CLOCK(NULL, "ckih", ckih_clk) - _REGISTER_CLOCK(NULL, "ckih2", ckih2_clk) - _REGISTER_CLOCK(NULL, "gpt_32k", gpt_32k_clk) - _REGISTER_CLOCK("imx51-ecspi.0", NULL, ecspi1_clk) - _REGISTER_CLOCK("imx51-ecspi.1", NULL, ecspi2_clk) - /* i.mx51 has the i.mx35 type cspi */ - _REGISTER_CLOCK("imx35-cspi.0", NULL, cspi_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx51.0", NULL, esdhc1_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx51.1", NULL, esdhc2_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx51.2", NULL, esdhc3_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx51.3", NULL, esdhc4_clk) - _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk) - _REGISTER_CLOCK(NULL, "iim_clk", iim_clk) - _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk) - _REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk) - _REGISTER_CLOCK(NULL, "mipi_hsp", mipi_hsp_clk) - _REGISTER_CLOCK("imx-ipuv3", NULL, ipu_clk) - _REGISTER_CLOCK("imx-ipuv3", "di0", ipu_di0_clk) - _REGISTER_CLOCK("imx-ipuv3", "di1", ipu_di1_clk) - _REGISTER_CLOCK(NULL, "gpc_dvfs", gpc_dvfs_clk) - _REGISTER_CLOCK("pata_imx", NULL, pata_clk) -}; - -static struct clk_lookup mx53_lookups[] = { - /* i.mx53 has the i.mx21 type uart */ - _REGISTER_CLOCK("imx21-uart.0", NULL, uart1_clk) - _REGISTER_CLOCK("imx21-uart.1", NULL, uart2_clk) - _REGISTER_CLOCK("imx21-uart.2", NULL, uart3_clk) - _REGISTER_CLOCK("imx21-uart.3", NULL, uart4_clk) - _REGISTER_CLOCK("imx21-uart.4", NULL, uart5_clk) - _REGISTER_CLOCK(NULL, "gpt", gpt_clk) - /* i.mx53 has the i.mx25 type fec */ - _REGISTER_CLOCK("imx25-fec.0", NULL, fec_clk) - _REGISTER_CLOCK(NULL, "iim_clk", iim_clk) - _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) - _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) - _REGISTER_CLOCK("imx-i2c.2", NULL, i2c3_mx53_clk) - /* i.mx53 has the i.mx51 type ecspi */ - _REGISTER_CLOCK("imx51-ecspi.0", NULL, ecspi1_clk) - _REGISTER_CLOCK("imx51-ecspi.1", NULL, ecspi2_clk) - /* i.mx53 has the i.mx25 type cspi */ - _REGISTER_CLOCK("imx35-cspi.0", NULL, cspi_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx53.0", NULL, esdhc1_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx53.1", NULL, esdhc2_mx53_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx53.2", NULL, esdhc3_mx53_clk) - _REGISTER_CLOCK("sdhci-esdhc-imx53.3", NULL, esdhc4_mx53_clk) - _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk) - _REGISTER_CLOCK("imx2-wdt.1", NULL, dummy_clk) - /* i.mx53 has the i.mx35 type sdma */ - _REGISTER_CLOCK("imx35-sdma", NULL, sdma_clk) - _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) - _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) - _REGISTER_CLOCK("imx-ssi.2", NULL, ssi3_clk) - _REGISTER_CLOCK("imx-keypad", NULL, dummy_clk) - _REGISTER_CLOCK("pata_imx", NULL, pata_clk) - _REGISTER_CLOCK("imx53-ahci.0", "ahci", sata_clk) - _REGISTER_CLOCK("imx53-ahci.0", "ahci_phy", ahci_phy_clk) - _REGISTER_CLOCK("imx53-ahci.0", "ahci_dma", ahci_dma_clk) -}; - -static void clk_tree_init(void) -{ - u32 reg; - - ipg_perclk.set_parent(&ipg_perclk, &lp_apm_clk); - - /* - * Initialise the IPG PER CLK dividers to 3. IPG_PER_CLK should be at - * 8MHz, its derived from lp_apm. - * - * FIXME: Verify if true for all boards - */ - reg = __raw_readl(MXC_CCM_CBCDR); - reg &= ~MXC_CCM_CBCDR_PERCLK_PRED1_MASK; - reg &= ~MXC_CCM_CBCDR_PERCLK_PRED2_MASK; - reg &= ~MXC_CCM_CBCDR_PERCLK_PODF_MASK; - reg |= (2 << MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET); - __raw_writel(reg, MXC_CCM_CBCDR); -} - -int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, - unsigned long ckih1, unsigned long ckih2) -{ - int i; - - external_low_reference = ckil; - external_high_reference = ckih1; - ckih2_reference = ckih2; - oscillator_reference = osc; - - for (i = 0; i < ARRAY_SIZE(mx51_lookups); i++) - clkdev_add(&mx51_lookups[i]); - - clk_tree_init(); - - clk_enable(&cpu_clk); - clk_enable(&main_bus_clk); - - clk_enable(&iim_clk); - imx_print_silicon_rev("i.MX51", mx51_revision()); - clk_disable(&iim_clk); - - /* move usb_phy_clk to 24MHz */ - clk_set_parent(&usb_phy1_clk, &osc_clk); - - /* set the usboh3_clk parent to pll2_sw_clk */ - clk_set_parent(&usboh3_clk, &pll2_sw_clk); - - /* Set SDHC parents to be PLL2 */ - clk_set_parent(&esdhc1_clk, &pll2_sw_clk); - clk_set_parent(&esdhc2_clk, &pll2_sw_clk); - - /* set SDHC root clock as 166.25MHZ*/ - clk_set_rate(&esdhc1_clk, 166250000); - clk_set_rate(&esdhc2_clk, 166250000); - - /* System timer */ - mxc_timer_init(&gpt_clk, MX51_IO_ADDRESS(MX51_GPT1_BASE_ADDR), - MX51_INT_GPT); - return 0; -} - -int __init mx53_clocks_init(unsigned long ckil, unsigned long osc, - unsigned long ckih1, unsigned long ckih2) -{ - int i; - - external_low_reference = ckil; - external_high_reference = ckih1; - ckih2_reference = ckih2; - oscillator_reference = osc; - - for (i = 0; i < ARRAY_SIZE(mx53_lookups); i++) - clkdev_add(&mx53_lookups[i]); - - clk_tree_init(); - - clk_set_parent(&uart_root_clk, &pll3_sw_clk); - clk_enable(&cpu_clk); - clk_enable(&main_bus_clk); - - clk_enable(&iim_clk); - imx_print_silicon_rev("i.MX53", mx53_revision()); - clk_disable(&iim_clk); - - /* Set SDHC parents to be PLL2 */ - clk_set_parent(&esdhc1_clk, &pll2_sw_clk); - clk_set_parent(&esdhc3_mx53_clk, &pll2_sw_clk); - - /* set SDHC root clock as 200MHZ*/ - clk_set_rate(&esdhc1_clk, 200000000); - clk_set_rate(&esdhc3_mx53_clk, 200000000); - - /* System timer */ - mxc_timer_init(&gpt_clk, MX53_IO_ADDRESS(MX53_GPT1_BASE_ADDR), - MX53_INT_GPT); - return 0; -} - -static void __init clk_get_freq_dt(unsigned long *ckil, unsigned long *osc, - unsigned long *ckih1, unsigned long *ckih2) -{ - struct device_node *np; - - /* retrieve the freqency of fixed clocks from device tree */ - for_each_compatible_node(np, NULL, "fixed-clock") { - u32 rate; - if (of_property_read_u32(np, "clock-frequency", &rate)) - continue; - - if (of_device_is_compatible(np, "fsl,imx-ckil")) - *ckil = rate; - else if (of_device_is_compatible(np, "fsl,imx-osc")) - *osc = rate; - else if (of_device_is_compatible(np, "fsl,imx-ckih1")) - *ckih1 = rate; - else if (of_device_is_compatible(np, "fsl,imx-ckih2")) - *ckih2 = rate; - } -} - -int __init mx51_clocks_init_dt(void) -{ - unsigned long ckil, osc, ckih1, ckih2; - - clk_get_freq_dt(&ckil, &osc, &ckih1, &ckih2); - return mx51_clocks_init(ckil, osc, ckih1, ckih2); -} - -int __init mx53_clocks_init_dt(void) -{ - unsigned long ckil, osc, ckih1, ckih2; - - clk_get_freq_dt(&ckil, &osc, &ckih1, &ckih2); - return mx53_clocks_init(ckil, osc, ckih1, ckih2); -} diff --git a/arch/arm/mach-mx5/cpu.c b/arch/arm/mach-mx5/cpu.c deleted file mode 100644 index 5c53282..0000000 --- a/arch/arm/mach-mx5/cpu.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - * - * This file contains the CPU initialization code. - */ - -#include -#include -#include -#include -#include -#include - -static int mx5_cpu_rev = -1; - -#define IIM_SREV 0x24 -#define MX50_HW_ADADIG_DIGPROG 0xB0 - -static int get_mx51_srev(void) -{ - void __iomem *iim_base = MX51_IO_ADDRESS(MX51_IIM_BASE_ADDR); - u32 rev = readl(iim_base + IIM_SREV) & 0xff; - - switch (rev) { - case 0x0: - return IMX_CHIP_REVISION_2_0; - case 0x10: - return IMX_CHIP_REVISION_3_0; - default: - return IMX_CHIP_REVISION_UNKNOWN; - } -} - -/* - * Returns: - * the silicon revision of the cpu - * -EINVAL - not a mx51 - */ -int mx51_revision(void) -{ - if (!cpu_is_mx51()) - return -EINVAL; - - if (mx5_cpu_rev == -1) - mx5_cpu_rev = get_mx51_srev(); - - return mx5_cpu_rev; -} -EXPORT_SYMBOL(mx51_revision); - -#ifdef CONFIG_NEON - -/* - * All versions of the silicon before Rev. 3 have broken NEON implementations. - * Dependent on link order - so the assumption is that vfp_init is called - * before us. - */ -static int __init mx51_neon_fixup(void) -{ - if (!cpu_is_mx51()) - return 0; - - if (mx51_revision() < IMX_CHIP_REVISION_3_0 && (elf_hwcap & HWCAP_NEON)) { - elf_hwcap &= ~HWCAP_NEON; - pr_info("Turning off NEON support, detected broken NEON implementation\n"); - } - return 0; -} - -late_initcall(mx51_neon_fixup); -#endif - -static int get_mx53_srev(void) -{ - void __iomem *iim_base = MX51_IO_ADDRESS(MX53_IIM_BASE_ADDR); - u32 rev = readl(iim_base + IIM_SREV) & 0xff; - - switch (rev) { - case 0x0: - return IMX_CHIP_REVISION_1_0; - case 0x2: - return IMX_CHIP_REVISION_2_0; - case 0x3: - return IMX_CHIP_REVISION_2_1; - default: - return IMX_CHIP_REVISION_UNKNOWN; - } -} - -/* - * Returns: - * the silicon revision of the cpu - * -EINVAL - not a mx53 - */ -int mx53_revision(void) -{ - if (!cpu_is_mx53()) - return -EINVAL; - - if (mx5_cpu_rev == -1) - mx5_cpu_rev = get_mx53_srev(); - - return mx5_cpu_rev; -} -EXPORT_SYMBOL(mx53_revision); - -static int get_mx50_srev(void) -{ - void __iomem *anatop = ioremap(MX50_ANATOP_BASE_ADDR, SZ_8K); - u32 rev; - - if (!anatop) { - mx5_cpu_rev = -EINVAL; - return 0; - } - - rev = readl(anatop + MX50_HW_ADADIG_DIGPROG); - rev &= 0xff; - - iounmap(anatop); - if (rev == 0x0) - return IMX_CHIP_REVISION_1_0; - else if (rev == 0x1) - return IMX_CHIP_REVISION_1_1; - return 0; -} - -/* - * Returns: - * the silicon revision of the cpu - * -EINVAL - not a mx50 - */ -int mx50_revision(void) -{ - if (!cpu_is_mx50()) - return -EINVAL; - - if (mx5_cpu_rev == -1) - mx5_cpu_rev = get_mx50_srev(); - - return mx5_cpu_rev; -} -EXPORT_SYMBOL(mx50_revision); - -static int __init post_cpu_init(void) -{ - unsigned int reg; - void __iomem *base; - - if (cpu_is_mx51() || cpu_is_mx53()) { - if (cpu_is_mx51()) - base = MX51_IO_ADDRESS(MX51_AIPS1_BASE_ADDR); - else - base = MX53_IO_ADDRESS(MX53_AIPS1_BASE_ADDR); - - __raw_writel(0x0, base + 0x40); - __raw_writel(0x0, base + 0x44); - __raw_writel(0x0, base + 0x48); - __raw_writel(0x0, base + 0x4C); - reg = __raw_readl(base + 0x50) & 0x00FFFFFF; - __raw_writel(reg, base + 0x50); - - if (cpu_is_mx51()) - base = MX51_IO_ADDRESS(MX51_AIPS2_BASE_ADDR); - else - base = MX53_IO_ADDRESS(MX53_AIPS2_BASE_ADDR); - - __raw_writel(0x0, base + 0x40); - __raw_writel(0x0, base + 0x44); - __raw_writel(0x0, base + 0x48); - __raw_writel(0x0, base + 0x4C); - reg = __raw_readl(base + 0x50) & 0x00FFFFFF; - __raw_writel(reg, base + 0x50); - } - - return 0; -} - -postcore_initcall(post_cpu_init); diff --git a/arch/arm/mach-mx5/cpu_op-mx51.c b/arch/arm/mach-mx5/cpu_op-mx51.c deleted file mode 100644 index 9d34c3d..0000000 --- a/arch/arm/mach-mx5/cpu_op-mx51.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include - -static struct cpu_op mx51_cpu_op[] = { - { - .cpu_rate = 160000000,}, - { - .cpu_rate = 800000000,}, -}; - -struct cpu_op *mx51_get_cpu_op(int *op) -{ - *op = ARRAY_SIZE(mx51_cpu_op); - return mx51_cpu_op; -} diff --git a/arch/arm/mach-mx5/cpu_op-mx51.h b/arch/arm/mach-mx5/cpu_op-mx51.h deleted file mode 100644 index 97477fe..0000000 --- a/arch/arm/mach-mx5/cpu_op-mx51.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -extern struct cpu_op *mx51_get_cpu_op(int *op); diff --git a/arch/arm/mach-mx5/crm_regs.h b/arch/arm/mach-mx5/crm_regs.h deleted file mode 100644 index 5e11ba7..0000000 --- a/arch/arm/mach-mx5/crm_regs.h +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ -#ifndef __ARCH_ARM_MACH_MX51_CRM_REGS_H__ -#define __ARCH_ARM_MACH_MX51_CRM_REGS_H__ - -#define MX51_CCM_BASE MX51_IO_ADDRESS(MX51_CCM_BASE_ADDR) -#define MX51_DPLL1_BASE MX51_IO_ADDRESS(MX51_PLL1_BASE_ADDR) -#define MX51_DPLL2_BASE MX51_IO_ADDRESS(MX51_PLL2_BASE_ADDR) -#define MX51_DPLL3_BASE MX51_IO_ADDRESS(MX51_PLL3_BASE_ADDR) -#define MX51_CORTEXA8_BASE MX51_IO_ADDRESS(MX51_ARM_BASE_ADDR) -#define MX51_GPC_BASE MX51_IO_ADDRESS(MX51_GPC_BASE_ADDR) - -/*MX53*/ -#define MX53_CCM_BASE MX53_IO_ADDRESS(MX53_CCM_BASE_ADDR) -#define MX53_DPLL1_BASE MX53_IO_ADDRESS(MX53_PLL1_BASE_ADDR) -#define MX53_DPLL2_BASE MX53_IO_ADDRESS(MX53_PLL2_BASE_ADDR) -#define MX53_DPLL3_BASE MX53_IO_ADDRESS(MX53_PLL3_BASE_ADDR) -#define MX53_DPLL4_BASE MX53_IO_ADDRESS(MX53_PLL3_BASE_ADDR) - -/* PLL Register Offsets */ -#define MXC_PLL_DP_CTL 0x00 -#define MXC_PLL_DP_CONFIG 0x04 -#define MXC_PLL_DP_OP 0x08 -#define MXC_PLL_DP_MFD 0x0C -#define MXC_PLL_DP_MFN 0x10 -#define MXC_PLL_DP_MFNMINUS 0x14 -#define MXC_PLL_DP_MFNPLUS 0x18 -#define MXC_PLL_DP_HFS_OP 0x1C -#define MXC_PLL_DP_HFS_MFD 0x20 -#define MXC_PLL_DP_HFS_MFN 0x24 -#define MXC_PLL_DP_MFN_TOGC 0x28 -#define MXC_PLL_DP_DESTAT 0x2c - -/* PLL Register Bit definitions */ -#define MXC_PLL_DP_CTL_MUL_CTRL 0x2000 -#define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000 -#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET 12 -#define MXC_PLL_DP_CTL_ADE 0x800 -#define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400 -#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK (3 << 8) -#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET 8 -#define MXC_PLL_DP_CTL_HFSM 0x80 -#define MXC_PLL_DP_CTL_PRE 0x40 -#define MXC_PLL_DP_CTL_UPEN 0x20 -#define MXC_PLL_DP_CTL_RST 0x10 -#define MXC_PLL_DP_CTL_RCP 0x8 -#define MXC_PLL_DP_CTL_PLM 0x4 -#define MXC_PLL_DP_CTL_BRM0 0x2 -#define MXC_PLL_DP_CTL_LRF 0x1 - -#define MXC_PLL_DP_CONFIG_BIST 0x8 -#define MXC_PLL_DP_CONFIG_SJC_CE 0x4 -#define MXC_PLL_DP_CONFIG_AREN 0x2 -#define MXC_PLL_DP_CONFIG_LDREQ 0x1 - -#define MXC_PLL_DP_OP_MFI_OFFSET 4 -#define MXC_PLL_DP_OP_MFI_MASK (0xF << 4) -#define MXC_PLL_DP_OP_PDF_OFFSET 0 -#define MXC_PLL_DP_OP_PDF_MASK 0xF - -#define MXC_PLL_DP_MFD_OFFSET 0 -#define MXC_PLL_DP_MFD_MASK 0x07FFFFFF - -#define MXC_PLL_DP_MFN_OFFSET 0x0 -#define MXC_PLL_DP_MFN_MASK 0x07FFFFFF - -#define MXC_PLL_DP_MFN_TOGC_TOG_DIS (1 << 17) -#define MXC_PLL_DP_MFN_TOGC_TOG_EN (1 << 16) -#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET 0x0 -#define MXC_PLL_DP_MFN_TOGC_CNT_MASK 0xFFFF - -#define MXC_PLL_DP_DESTAT_TOG_SEL (1 << 31) -#define MXC_PLL_DP_DESTAT_MFN 0x07FFFFFF - -/* Register addresses of CCM*/ -#define MXC_CCM_CCR (MX51_CCM_BASE + 0x00) -#define MXC_CCM_CCDR (MX51_CCM_BASE + 0x04) -#define MXC_CCM_CSR (MX51_CCM_BASE + 0x08) -#define MXC_CCM_CCSR (MX51_CCM_BASE + 0x0C) -#define MXC_CCM_CACRR (MX51_CCM_BASE + 0x10) -#define MXC_CCM_CBCDR (MX51_CCM_BASE + 0x14) -#define MXC_CCM_CBCMR (MX51_CCM_BASE + 0x18) -#define MXC_CCM_CSCMR1 (MX51_CCM_BASE + 0x1C) -#define MXC_CCM_CSCMR2 (MX51_CCM_BASE + 0x20) -#define MXC_CCM_CSCDR1 (MX51_CCM_BASE + 0x24) -#define MXC_CCM_CS1CDR (MX51_CCM_BASE + 0x28) -#define MXC_CCM_CS2CDR (MX51_CCM_BASE + 0x2C) -#define MXC_CCM_CDCDR (MX51_CCM_BASE + 0x30) -#define MXC_CCM_CHSCDR (MX51_CCM_BASE + 0x34) -#define MXC_CCM_CSCDR2 (MX51_CCM_BASE + 0x38) -#define MXC_CCM_CSCDR3 (MX51_CCM_BASE + 0x3C) -#define MXC_CCM_CSCDR4 (MX51_CCM_BASE + 0x40) -#define MXC_CCM_CWDR (MX51_CCM_BASE + 0x44) -#define MXC_CCM_CDHIPR (MX51_CCM_BASE + 0x48) -#define MXC_CCM_CDCR (MX51_CCM_BASE + 0x4C) -#define MXC_CCM_CTOR (MX51_CCM_BASE + 0x50) -#define MXC_CCM_CLPCR (MX51_CCM_BASE + 0x54) -#define MXC_CCM_CISR (MX51_CCM_BASE + 0x58) -#define MXC_CCM_CIMR (MX51_CCM_BASE + 0x5C) -#define MXC_CCM_CCOSR (MX51_CCM_BASE + 0x60) -#define MXC_CCM_CGPR (MX51_CCM_BASE + 0x64) -#define MXC_CCM_CCGR0 (MX51_CCM_BASE + 0x68) -#define MXC_CCM_CCGR1 (MX51_CCM_BASE + 0x6C) -#define MXC_CCM_CCGR2 (MX51_CCM_BASE + 0x70) -#define MXC_CCM_CCGR3 (MX51_CCM_BASE + 0x74) -#define MXC_CCM_CCGR4 (MX51_CCM_BASE + 0x78) -#define MXC_CCM_CCGR5 (MX51_CCM_BASE + 0x7C) -#define MXC_CCM_CCGR6 (MX51_CCM_BASE + 0x80) -#define MXC_CCM_CCGR7 (MX51_CCM_BASE + 0x84) - -#define MXC_CCM_CMEOR (MX51_CCM_BASE + 0x84) - -/* Define the bits in register CCR */ -#define MXC_CCM_CCR_COSC_EN (1 << 12) -#define MXC_CCM_CCR_FPM_MULT_MASK (1 << 11) -#define MXC_CCM_CCR_CAMP2_EN (1 << 10) -#define MXC_CCM_CCR_CAMP1_EN (1 << 9) -#define MXC_CCM_CCR_FPM_EN (1 << 8) -#define MXC_CCM_CCR_OSCNT_OFFSET (0) -#define MXC_CCM_CCR_OSCNT_MASK (0xFF) - -/* Define the bits in register CCDR */ -#define MXC_CCM_CCDR_HSC_HS_MASK (0x1 << 18) -#define MXC_CCM_CCDR_IPU_HS_MASK (0x1 << 17) -#define MXC_CCM_CCDR_EMI_HS_MASK (0x1 << 16) - -/* Define the bits in register CSR */ -#define MXC_CCM_CSR_COSR_READY (1 << 5) -#define MXC_CCM_CSR_LVS_VALUE (1 << 4) -#define MXC_CCM_CSR_CAMP2_READY (1 << 3) -#define MXC_CCM_CSR_CAMP1_READY (1 << 2) -#define MXC_CCM_CSR_FPM_READY (1 << 1) -#define MXC_CCM_CSR_REF_EN_B (1 << 0) - -/* Define the bits in register CCSR */ -#define MXC_CCM_CCSR_LP_APM_SEL (0x1 << 9) -#define MXC_CCM_CCSR_STEP_SEL_OFFSET (7) -#define MXC_CCM_CCSR_STEP_SEL_MASK (0x3 << 7) -#define MXC_CCM_CCSR_STEP_SEL_LP_APM 0 -#define MXC_CCM_CCSR_STEP_SEL_PLL1_BYPASS 1 /* Only when JTAG connected? */ -#define MXC_CCM_CCSR_STEP_SEL_PLL2_DIVIDED 2 -#define MXC_CCM_CCSR_STEP_SEL_PLL3_DIVIDED 3 -#define MXC_CCM_CCSR_PLL2_PODF_OFFSET (5) -#define MXC_CCM_CCSR_PLL2_PODF_MASK (0x3 << 5) -#define MXC_CCM_CCSR_PLL3_PODF_OFFSET (3) -#define MXC_CCM_CCSR_PLL3_PODF_MASK (0x3 << 3) -#define MXC_CCM_CCSR_PLL1_SW_CLK_SEL (1 << 2) /* 0: pll1_main_clk, - 1: step_clk */ -#define MXC_CCM_CCSR_PLL2_SW_CLK_SEL (1 << 1) -#define MXC_CCM_CCSR_PLL3_SW_CLK_SEL (1 << 0) - -/* Define the bits in register CACRR */ -#define MXC_CCM_CACRR_ARM_PODF_OFFSET (0) -#define MXC_CCM_CACRR_ARM_PODF_MASK (0x7) - -/* Define the bits in register CBCDR */ -#define MXC_CCM_CBCDR_EMI_CLK_SEL (0x1 << 26) -#define MXC_CCM_CBCDR_PERIPH_CLK_SEL (0x1 << 25) -#define MXC_CCM_CBCDR_DDR_HF_SEL_OFFSET (30) -#define MXC_CCM_CBCDR_DDR_HF_SEL (0x1 << 30) -#define MXC_CCM_CBCDR_DDR_PODF_OFFSET (27) -#define MXC_CCM_CBCDR_DDR_PODF_MASK (0x7 << 27) -#define MXC_CCM_CBCDR_EMI_PODF_OFFSET (22) -#define MXC_CCM_CBCDR_EMI_PODF_MASK (0x7 << 22) -#define MXC_CCM_CBCDR_AXI_B_PODF_OFFSET (19) -#define MXC_CCM_CBCDR_AXI_B_PODF_MASK (0x7 << 19) -#define MXC_CCM_CBCDR_AXI_A_PODF_OFFSET (16) -#define MXC_CCM_CBCDR_AXI_A_PODF_MASK (0x7 << 16) -#define MXC_CCM_CBCDR_NFC_PODF_OFFSET (13) -#define MXC_CCM_CBCDR_NFC_PODF_MASK (0x7 << 13) -#define MXC_CCM_CBCDR_AHB_PODF_OFFSET (10) -#define MXC_CCM_CBCDR_AHB_PODF_MASK (0x7 << 10) -#define MXC_CCM_CBCDR_IPG_PODF_OFFSET (8) -#define MXC_CCM_CBCDR_IPG_PODF_MASK (0x3 << 8) -#define MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET (6) -#define MXC_CCM_CBCDR_PERCLK_PRED1_MASK (0x3 << 6) -#define MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET (3) -#define MXC_CCM_CBCDR_PERCLK_PRED2_MASK (0x7 << 3) -#define MXC_CCM_CBCDR_PERCLK_PODF_OFFSET (0) -#define MXC_CCM_CBCDR_PERCLK_PODF_MASK (0x7) - -/* Define the bits in register CBCMR */ -#define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_OFFSET (14) -#define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_MASK (0x3 << 14) -#define MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET (12) -#define MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK (0x3 << 12) -#define MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET (10) -#define MXC_CCM_CBCMR_DDR_CLK_SEL_MASK (0x3 << 10) -#define MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_OFFSET (8) -#define MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_MASK (0x3 << 8) -#define MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_OFFSET (6) -#define MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_MASK (0x3 << 6) -#define MXC_CCM_CBCMR_GPU_CLK_SEL_OFFSET (4) -#define MXC_CCM_CBCMR_GPU_CLK_SEL_MASK (0x3 << 4) -#define MXC_CCM_CBCMR_GPU2D_CLK_SEL_OFFSET (14) -#define MXC_CCM_CBCMR_GPU2D_CLK_SEL_MASK (0x3 << 14) -#define MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL (0x1 << 1) -#define MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL (0x1 << 0) - -/* Define the bits in register CSCMR1 */ -#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET (30) -#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK (0x3 << 30) -#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET (28) -#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK (0x3 << 28) -#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET (26) -#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL (0x1 << 26) -#define MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET (24) -#define MXC_CCM_CSCMR1_UART_CLK_SEL_MASK (0x3 << 24) -#define MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET (22) -#define MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK (0x3 << 22) -#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET (20) -#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK (0x3 << 20) -#define MXC_CCM_CSCMR1_ESDHC3_CLK_SEL (0x1 << 19) -#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_MX53_CLK_SEL (0x1 << 19) -#define MXC_CCM_CSCMR1_ESDHC4_CLK_SEL (0x1 << 18) -#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET (16) -#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK (0x3 << 16) -#define MXC_CCM_CSCMR1_ESDHC3_MX53_CLK_SEL_OFFSET (16) -#define MXC_CCM_CSCMR1_ESDHC3_MX53_CLK_SEL_MASK (0x3 << 16) -#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET (14) -#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK (0x3 << 14) -#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET (12) -#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK (0x3 << 12) -#define MXC_CCM_CSCMR1_SSI3_CLK_SEL (0x1 << 11) -#define MXC_CCM_CSCMR1_VPU_RCLK_SEL (0x1 << 10) -#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_OFFSET (8) -#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_MASK (0x3 << 8) -#define MXC_CCM_CSCMR1_TVE_CLK_SEL (0x1 << 7) -#define MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL (0x1 << 6) -#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET (4) -#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK (0x3 << 4) -#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL_OFFSET (2) -#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL_MASK (0x3 << 2) -#define MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL (0x1 << 1) -#define MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL (0x1) - -/* Define the bits in register CSCMR2 */ -#define MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(n) (26+n*3) -#define MXC_CCM_CSCMR2_DI_CLK_SEL_MASK(n) (0x7 << (26+n*3)) -#define MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_OFFSET (24) -#define MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_MASK (0x3 << 24) -#define MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_OFFSET (22) -#define MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_MASK (0x3 << 22) -#define MXC_CCM_CSCMR2_ESC_CLK_SEL_OFFSET (20) -#define MXC_CCM_CSCMR2_ESC_CLK_SEL_MASK (0x3 << 20) -#define MXC_CCM_CSCMR2_HSC2_CLK_SEL_OFFSET (18) -#define MXC_CCM_CSCMR2_HSC2_CLK_SEL_MASK (0x3 << 18) -#define MXC_CCM_CSCMR2_HSC1_CLK_SEL_OFFSET (16) -#define MXC_CCM_CSCMR2_HSC1_CLK_SEL_MASK (0x3 << 16) -#define MXC_CCM_CSCMR2_HSI2C_CLK_SEL_OFFSET (14) -#define MXC_CCM_CSCMR2_HSI2C_CLK_SEL_MASK (0x3 << 14) -#define MXC_CCM_CSCMR2_FIRI_CLK_SEL_OFFSET (12) -#define MXC_CCM_CSCMR2_FIRI_CLK_SEL_MASK (0x3 << 12) -#define MXC_CCM_CSCMR2_SIM_CLK_SEL_OFFSET (10) -#define MXC_CCM_CSCMR2_SIM_CLK_SEL_MASK (0x3 << 10) -#define MXC_CCM_CSCMR2_SLIMBUS_COM (0x1 << 9) -#define MXC_CCM_CSCMR2_SLIMBUS_CLK_SEL_OFFSET (6) -#define MXC_CCM_CSCMR2_SLIMBUS_CLK_SEL_MASK (0x7 << 6) -#define MXC_CCM_CSCMR2_SPDIF1_COM (1 << 5) -#define MXC_CCM_CSCMR2_SPDIF0_COM (1 << 4) -#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_OFFSET (2) -#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_MASK (0x3 << 2) -#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_OFFSET (0) -#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_MASK (0x3) - -/* Define the bits in register CSCDR1 */ -#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_OFFSET (22) -#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK (0x7 << 22) -#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET (19) -#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK (0x7 << 19) -#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PRED_OFFSET (22) -#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PRED_MASK (0x7 << 22) -#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PODF_OFFSET (19) -#define MXC_CCM_CSCDR1_ESDHC3_MX53_CLK_PODF_MASK (0x7 << 19) -#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET (16) -#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK (0x7 << 16) -#define MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET (14) -#define MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK (0x3 << 14) -#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_OFFSET (11) -#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_MASK (0x7 << 11) -#define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET (8) -#define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK (0x7 << 8) -#define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET (6) -#define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK (0x3 << 6) -#define MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET (3) -#define MXC_CCM_CSCDR1_UART_CLK_PRED_MASK (0x7 << 3) -#define MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET (0) -#define MXC_CCM_CSCDR1_UART_CLK_PODF_MASK (0x7) - -/* Define the bits in register CS1CDR and CS2CDR */ -#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_OFFSET (22) -#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_MASK (0x7 << 22) -#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_OFFSET (16) -#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_MASK (0x3F << 16) -#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET (6) -#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK (0x7 << 6) -#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET (0) -#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK (0x3F) - -#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_OFFSET (22) -#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_MASK (0x7 << 22) -#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_OFFSET (16) -#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_MASK (0x3F << 16) -#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET (6) -#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK (0x7 << 6) -#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET (0) -#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK (0x3F) - -/* Define the bits in register CDCDR */ -#define MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET (28) -#define MXC_CCM_CDCDR_TVE_CLK_PRED_MASK (0x7 << 28) -#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET (25) -#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK (0x7 << 25) -#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET (19) -#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK (0x3F << 19) -#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_OFFSET (16) -#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK (0x7 << 16) -#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_OFFSET (9) -#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK (0x3F << 9) -#define MXC_CCM_CDCDR_DI_CLK_PRED_OFFSET (6) -#define MXC_CCM_CDCDR_DI_CLK_PRED_MASK (0x7 << 6) -#define MXC_CCM_CDCDR_USB_PHY_PRED_OFFSET (3) -#define MXC_CCM_CDCDR_USB_PHY_PRED_MASK (0x7 << 3) -#define MXC_CCM_CDCDR_USB_PHY_PODF_OFFSET (0) -#define MXC_CCM_CDCDR_USB_PHY_PODF_MASK (0x7) - -/* Define the bits in register CHSCCDR */ -#define MXC_CCM_CHSCCDR_ESC_CLK_PRED_OFFSET (12) -#define MXC_CCM_CHSCCDR_ESC_CLK_PRED_MASK (0x7 << 12) -#define MXC_CCM_CHSCCDR_ESC_CLK_PODF_OFFSET (6) -#define MXC_CCM_CHSCCDR_ESC_CLK_PODF_MASK (0x3F << 6) -#define MXC_CCM_CHSCCDR_HSC2_CLK_PODF_OFFSET (3) -#define MXC_CCM_CHSCCDR_HSC2_CLK_PODF_MASK (0x7 << 3) -#define MXC_CCM_CHSCCDR_HSC1_CLK_PODF_OFFSET (0) -#define MXC_CCM_CHSCCDR_HSC1_CLK_PODF_MASK (0x7) - -/* Define the bits in register CSCDR2 */ -#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET (25) -#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK (0x7 << 25) -#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET (19) -#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK (0x3F << 19) -#define MXC_CCM_CSCDR2_SIM_CLK_PRED_OFFSET (16) -#define MXC_CCM_CSCDR2_SIM_CLK_PRED_MASK (0x7 << 16) -#define MXC_CCM_CSCDR2_SIM_CLK_PODF_OFFSET (9) -#define MXC_CCM_CSCDR2_SIM_CLK_PODF_MASK (0x3F << 9) -#define MXC_CCM_CSCDR2_SLIMBUS_CLK_PRED_OFFSET (6) -#define MXC_CCM_CSCDR2_SLIMBUS_PRED_MASK (0x7 << 6) -#define MXC_CCM_CSCDR2_SLIMBUS_PODF_OFFSET (0) -#define MXC_CCM_CSCDR2_SLIMBUS_PODF_MASK (0x3F) - -/* Define the bits in register CSCDR3 */ -#define MXC_CCM_CSCDR3_HSI2C_CLK_PRED_OFFSET (16) -#define MXC_CCM_CSCDR3_HSI2C_CLK_PRED_MASK (0x7 << 16) -#define MXC_CCM_CSCDR3_HSI2C_CLK_PODF_OFFSET (9) -#define MXC_CCM_CSCDR3_HSI2C_CLK_PODF_MASK (0x3F << 9) -#define MXC_CCM_CSCDR3_FIRI_CLK_PRED_OFFSET (6) -#define MXC_CCM_CSCDR3_FIRI_CLK_PRED_MASK (0x7 << 6) -#define MXC_CCM_CSCDR3_FIRI_CLK_PODF_OFFSET (0) -#define MXC_CCM_CSCDR3_FIRI_CLK_PODF_MASK (0x3F) - -/* Define the bits in register CSCDR4 */ -#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_OFFSET (16) -#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_MASK (0x7 << 16) -#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_OFFSET (9) -#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_MASK (0x3F << 9) -#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_OFFSET (6) -#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_MASK (0x7 << 6) -#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_OFFSET (0) -#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_MASK (0x3F) - -/* Define the bits in register CDHIPR */ -#define MXC_CCM_CDHIPR_ARM_PODF_BUSY (1 << 16) -#define MXC_CCM_CDHIPR_DDR_HF_CLK_SEL_BUSY (1 << 8) -#define MXC_CCM_CDHIPR_DDR_PODF_BUSY (1 << 7) -#define MXC_CCM_CDHIPR_EMI_CLK_SEL_BUSY (1 << 6) -#define MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY (1 << 5) -#define MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY (1 << 4) -#define MXC_CCM_CDHIPR_AHB_PODF_BUSY (1 << 3) -#define MXC_CCM_CDHIPR_EMI_PODF_BUSY (1 << 2) -#define MXC_CCM_CDHIPR_AXI_B_PODF_BUSY (1 << 1) -#define MXC_CCM_CDHIPR_AXI_A_PODF_BUSY (1 << 0) - -/* Define the bits in register CDCR */ -#define MXC_CCM_CDCR_ARM_FREQ_SHIFT_DIVIDER (0x1 << 2) -#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_OFFSET (0) -#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK (0x3) - -/* Define the bits in register CLPCR */ -#define MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS (0x1 << 23) -#define MXC_CCM_CLPCR_BYPASS_SCC_LPM_HS (0x1 << 22) -#define MX51_CCM_CLPCR_BYPASS_MAX_LPM_HS (0x1 << 21) -#define MX53_CCM_CLPCR_BYPASS_MAX_LPM_HS (0x1 << 25) -#define MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS (0x1 << 20) -#define MXC_CCM_CLPCR_BYPASS_EMI_LPM_HS (0x1 << 19) -#define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS (0x1 << 18) -#define MXC_CCM_CLPCR_BYPASS_RTIC_LPM_HS (0x1 << 17) -#define MXC_CCM_CLPCR_BYPASS_RNGC_LPM_HS (0x1 << 16) -#define MXC_CCM_CLPCR_COSC_PWRDOWN (0x1 << 11) -#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET (9) -#define MXC_CCM_CLPCR_STBY_COUNT_MASK (0x3 << 9) -#define MXC_CCM_CLPCR_VSTBY (0x1 << 8) -#define MXC_CCM_CLPCR_DIS_REF_OSC (0x1 << 7) -#define MXC_CCM_CLPCR_SBYOS (0x1 << 6) -#define MXC_CCM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) -#define MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET (3) -#define MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK (0x3 << 3) -#define MXC_CCM_CLPCR_LPM_OFFSET (0) -#define MXC_CCM_CLPCR_LPM_MASK (0x3) - -/* Define the bits in register CISR */ -#define MXC_CCM_CISR_ARM_PODF_LOADED (0x1 << 25) -#define MXC_CCM_CISR_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21) -#define MXC_CCM_CISR_AHB_PODF_LOADED (0x1 << 20) -#define MXC_CCM_CISR_EMI_PODF_LOADED (0x1 << 19) -#define MXC_CCM_CISR_AXI_B_PODF_LOADED (0x1 << 18) -#define MXC_CCM_CISR_AXI_A_PODF_LOADED (0x1 << 17) -#define MXC_CCM_CISR_DIVIDER_LOADED (0x1 << 16) -#define MXC_CCM_CISR_COSC_READY (0x1 << 6) -#define MXC_CCM_CISR_CKIH2_READY (0x1 << 5) -#define MXC_CCM_CISR_CKIH_READY (0x1 << 4) -#define MXC_CCM_CISR_FPM_READY (0x1 << 3) -#define MXC_CCM_CISR_LRF_PLL3 (0x1 << 2) -#define MXC_CCM_CISR_LRF_PLL2 (0x1 << 1) -#define MXC_CCM_CISR_LRF_PLL1 (0x1) - -/* Define the bits in register CIMR */ -#define MXC_CCM_CIMR_MASK_ARM_PODF_LOADED (0x1 << 25) -#define MXC_CCM_CIMR_MASK_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21) -#define MXC_CCM_CIMR_MASK_EMI_PODF_LOADED (0x1 << 20) -#define MXC_CCM_CIMR_MASK_AXI_C_PODF_LOADED (0x1 << 19) -#define MXC_CCM_CIMR_MASK_AXI_B_PODF_LOADED (0x1 << 18) -#define MXC_CCM_CIMR_MASK_AXI_A_PODF_LOADED (0x1 << 17) -#define MXC_CCM_CIMR_MASK_DIVIDER_LOADED (0x1 << 16) -#define MXC_CCM_CIMR_MASK_COSC_READY (0x1 << 5) -#define MXC_CCM_CIMR_MASK_CKIH_READY (0x1 << 4) -#define MXC_CCM_CIMR_MASK_FPM_READY (0x1 << 3) -#define MXC_CCM_CIMR_MASK_LRF_PLL3 (0x1 << 2) -#define MXC_CCM_CIMR_MASK_LRF_PLL2 (0x1 << 1) -#define MXC_CCM_CIMR_MASK_LRF_PLL1 (0x1) - -/* Define the bits in register CCOSR */ -#define MXC_CCM_CCOSR_CKO2_EN_OFFSET (0x1 << 24) -#define MXC_CCM_CCOSR_CKO2_DIV_OFFSET (21) -#define MXC_CCM_CCOSR_CKO2_DIV_MASK (0x7 << 21) -#define MXC_CCM_CCOSR_CKO2_SEL_OFFSET (16) -#define MXC_CCM_CCOSR_CKO2_SEL_MASK (0x1F << 16) -#define MXC_CCM_CCOSR_CKOL_EN (0x1 << 7) -#define MXC_CCM_CCOSR_CKOL_DIV_OFFSET (4) -#define MXC_CCM_CCOSR_CKOL_DIV_MASK (0x7 << 4) -#define MXC_CCM_CCOSR_CKOL_SEL_OFFSET (0) -#define MXC_CCM_CCOSR_CKOL_SEL_MASK (0xF) - -/* Define the bits in registers CGPR */ -#define MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE (0x1 << 4) -#define MXC_CCM_CGPR_FPM_SEL (0x1 << 3) -#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_OFFSET (0) -#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_MASK (0x7) - -/* Define the bits in registers CCGRx */ -#define MXC_CCM_CCGRx_CG_MASK 0x3 -#define MXC_CCM_CCGRx_MOD_OFF 0x0 -#define MXC_CCM_CCGRx_MOD_ON 0x3 -#define MXC_CCM_CCGRx_MOD_IDLE 0x1 - -#define MXC_CCM_CCGRx_CG15_MASK (0x3 << 30) -#define MXC_CCM_CCGRx_CG14_MASK (0x3 << 28) -#define MXC_CCM_CCGRx_CG13_MASK (0x3 << 26) -#define MXC_CCM_CCGRx_CG12_MASK (0x3 << 24) -#define MXC_CCM_CCGRx_CG11_MASK (0x3 << 22) -#define MXC_CCM_CCGRx_CG10_MASK (0x3 << 20) -#define MXC_CCM_CCGRx_CG9_MASK (0x3 << 18) -#define MXC_CCM_CCGRx_CG8_MASK (0x3 << 16) -#define MXC_CCM_CCGRx_CG5_MASK (0x3 << 10) -#define MXC_CCM_CCGRx_CG4_MASK (0x3 << 8) -#define MXC_CCM_CCGRx_CG3_MASK (0x3 << 6) -#define MXC_CCM_CCGRx_CG2_MASK (0x3 << 4) -#define MXC_CCM_CCGRx_CG1_MASK (0x3 << 2) -#define MXC_CCM_CCGRx_CG0_MASK (0x3 << 0) - -#define MXC_CCM_CCGRx_CG15_OFFSET 30 -#define MXC_CCM_CCGRx_CG14_OFFSET 28 -#define MXC_CCM_CCGRx_CG13_OFFSET 26 -#define MXC_CCM_CCGRx_CG12_OFFSET 24 -#define MXC_CCM_CCGRx_CG11_OFFSET 22 -#define MXC_CCM_CCGRx_CG10_OFFSET 20 -#define MXC_CCM_CCGRx_CG9_OFFSET 18 -#define MXC_CCM_CCGRx_CG8_OFFSET 16 -#define MXC_CCM_CCGRx_CG7_OFFSET 14 -#define MXC_CCM_CCGRx_CG6_OFFSET 12 -#define MXC_CCM_CCGRx_CG5_OFFSET 10 -#define MXC_CCM_CCGRx_CG4_OFFSET 8 -#define MXC_CCM_CCGRx_CG3_OFFSET 6 -#define MXC_CCM_CCGRx_CG2_OFFSET 4 -#define MXC_CCM_CCGRx_CG1_OFFSET 2 -#define MXC_CCM_CCGRx_CG0_OFFSET 0 - -#define MXC_DPTC_LP_BASE (MX51_GPC_BASE + 0x80) -#define MXC_DPTC_GP_BASE (MX51_GPC_BASE + 0x100) -#define MXC_DVFS_CORE_BASE (MX51_GPC_BASE + 0x180) -#define MXC_DPTC_PER_BASE (MX51_GPC_BASE + 0x1C0) -#define MXC_PGC_IPU_BASE (MX51_GPC_BASE + 0x220) -#define MXC_PGC_VPU_BASE (MX51_GPC_BASE + 0x240) -#define MXC_PGC_GPU_BASE (MX51_GPC_BASE + 0x260) -#define MXC_SRPG_NEON_BASE (MX51_GPC_BASE + 0x280) -#define MXC_SRPG_ARM_BASE (MX51_GPC_BASE + 0x2A0) -#define MXC_SRPG_EMPGC0_BASE (MX51_GPC_BASE + 0x2C0) -#define MXC_SRPG_EMPGC1_BASE (MX51_GPC_BASE + 0x2D0) -#define MXC_SRPG_MEGAMIX_BASE (MX51_GPC_BASE + 0x2E0) -#define MXC_SRPG_EMI_BASE (MX51_GPC_BASE + 0x300) - -/* CORTEXA8 platform */ -#define MXC_CORTEXA8_PLAT_PVID (MX51_CORTEXA8_BASE + 0x0) -#define MXC_CORTEXA8_PLAT_GPC (MX51_CORTEXA8_BASE + 0x4) -#define MXC_CORTEXA8_PLAT_PIC (MX51_CORTEXA8_BASE + 0x8) -#define MXC_CORTEXA8_PLAT_LPC (MX51_CORTEXA8_BASE + 0xC) -#define MXC_CORTEXA8_PLAT_NEON_LPC (MX51_CORTEXA8_BASE + 0x10) -#define MXC_CORTEXA8_PLAT_ICGC (MX51_CORTEXA8_BASE + 0x14) -#define MXC_CORTEXA8_PLAT_AMC (MX51_CORTEXA8_BASE + 0x18) -#define MXC_CORTEXA8_PLAT_NMC (MX51_CORTEXA8_BASE + 0x20) -#define MXC_CORTEXA8_PLAT_NMS (MX51_CORTEXA8_BASE + 0x24) - -/* DVFS CORE */ -#define MXC_DVFSTHRS (MXC_DVFS_CORE_BASE + 0x00) -#define MXC_DVFSCOUN (MXC_DVFS_CORE_BASE + 0x04) -#define MXC_DVFSSIG1 (MXC_DVFS_CORE_BASE + 0x08) -#define MXC_DVFSSIG0 (MXC_DVFS_CORE_BASE + 0x0C) -#define MXC_DVFSGPC0 (MXC_DVFS_CORE_BASE + 0x10) -#define MXC_DVFSGPC1 (MXC_DVFS_CORE_BASE + 0x14) -#define MXC_DVFSGPBT (MXC_DVFS_CORE_BASE + 0x18) -#define MXC_DVFSEMAC (MXC_DVFS_CORE_BASE + 0x1C) -#define MXC_DVFSCNTR (MXC_DVFS_CORE_BASE + 0x20) -#define MXC_DVFSLTR0_0 (MXC_DVFS_CORE_BASE + 0x24) -#define MXC_DVFSLTR0_1 (MXC_DVFS_CORE_BASE + 0x28) -#define MXC_DVFSLTR1_0 (MXC_DVFS_CORE_BASE + 0x2C) -#define MXC_DVFSLTR1_1 (MXC_DVFS_CORE_BASE + 0x30) -#define MXC_DVFSPT0 (MXC_DVFS_CORE_BASE + 0x34) -#define MXC_DVFSPT1 (MXC_DVFS_CORE_BASE + 0x38) -#define MXC_DVFSPT2 (MXC_DVFS_CORE_BASE + 0x3C) -#define MXC_DVFSPT3 (MXC_DVFS_CORE_BASE + 0x40) - -/* GPC */ -#define MXC_GPC_CNTR (MX51_GPC_BASE + 0x0) -#define MXC_GPC_PGR (MX51_GPC_BASE + 0x4) -#define MXC_GPC_VCR (MX51_GPC_BASE + 0x8) -#define MXC_GPC_ALL_PU (MX51_GPC_BASE + 0xC) -#define MXC_GPC_NEON (MX51_GPC_BASE + 0x10) -#define MXC_GPC_PGR_ARMPG_OFFSET 8 -#define MXC_GPC_PGR_ARMPG_MASK (3 << 8) - -/* PGC */ -#define MXC_PGC_IPU_PGCR (MXC_PGC_IPU_BASE + 0x0) -#define MXC_PGC_IPU_PGSR (MXC_PGC_IPU_BASE + 0xC) -#define MXC_PGC_VPU_PGCR (MXC_PGC_VPU_BASE + 0x0) -#define MXC_PGC_VPU_PGSR (MXC_PGC_VPU_BASE + 0xC) -#define MXC_PGC_GPU_PGCR (MXC_PGC_GPU_BASE + 0x0) -#define MXC_PGC_GPU_PGSR (MXC_PGC_GPU_BASE + 0xC) - -#define MXC_PGCR_PCR 1 -#define MXC_SRPGCR_PCR 1 -#define MXC_EMPGCR_PCR 1 -#define MXC_PGSR_PSR 1 - - -#define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0) -#define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1) - -/* SRPG */ -#define MXC_SRPG_NEON_SRPGCR (MXC_SRPG_NEON_BASE + 0x0) -#define MXC_SRPG_NEON_PUPSCR (MXC_SRPG_NEON_BASE + 0x4) -#define MXC_SRPG_NEON_PDNSCR (MXC_SRPG_NEON_BASE + 0x8) - -#define MXC_SRPG_ARM_SRPGCR (MXC_SRPG_ARM_BASE + 0x0) -#define MXC_SRPG_ARM_PUPSCR (MXC_SRPG_ARM_BASE + 0x4) -#define MXC_SRPG_ARM_PDNSCR (MXC_SRPG_ARM_BASE + 0x8) - -#define MXC_SRPG_EMPGC0_SRPGCR (MXC_SRPG_EMPGC0_BASE + 0x0) -#define MXC_SRPG_EMPGC0_PUPSCR (MXC_SRPG_EMPGC0_BASE + 0x4) -#define MXC_SRPG_EMPGC0_PDNSCR (MXC_SRPG_EMPGC0_BASE + 0x8) - -#define MXC_SRPG_EMPGC1_SRPGCR (MXC_SRPG_EMPGC1_BASE + 0x0) -#define MXC_SRPG_EMPGC1_PUPSCR (MXC_SRPG_EMPGC1_BASE + 0x4) -#define MXC_SRPG_EMPGC1_PDNSCR (MXC_SRPG_EMPGC1_BASE + 0x8) - -#define MXC_SRPG_MEGAMIX_SRPGCR (MXC_SRPG_MEGAMIX_BASE + 0x0) -#define MXC_SRPG_MEGAMIX_PUPSCR (MXC_SRPG_MEGAMIX_BASE + 0x4) -#define MXC_SRPG_MEGAMIX_PDNSCR (MXC_SRPG_MEGAMIX_BASE + 0x8) - -#define MXC_SRPGC_EMI_SRPGCR (MXC_SRPGC_EMI_BASE + 0x0) -#define MXC_SRPGC_EMI_PUPSCR (MXC_SRPGC_EMI_BASE + 0x4) -#define MXC_SRPGC_EMI_PDNSCR (MXC_SRPGC_EMI_BASE + 0x8) - -#endif /* __ARCH_ARM_MACH_MX51_CRM_REGS_H__ */ diff --git a/arch/arm/mach-mx5/devices-imx50.h b/arch/arm/mach-mx5/devices-imx50.h deleted file mode 100644 index 7216667..0000000 --- a/arch/arm/mach-mx5/devices-imx50.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * 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. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include - -extern const struct imx_imx_uart_1irq_data imx50_imx_uart_data[]; -#define imx50_add_imx_uart(id, pdata) \ - imx_add_imx_uart_1irq(&imx50_imx_uart_data[id], pdata) - -extern const struct imx_fec_data imx50_fec_data; -#define imx50_add_fec(pdata) \ - imx_add_fec(&imx50_fec_data, pdata) - -extern const struct imx_imx_i2c_data imx50_imx_i2c_data[]; -#define imx50_add_imx_i2c(id, pdata) \ - imx_add_imx_i2c(&imx50_imx_i2c_data[id], pdata) diff --git a/arch/arm/mach-mx5/devices-imx51.h b/arch/arm/mach-mx5/devices-imx51.h deleted file mode 100644 index af488bc..0000000 --- a/arch/arm/mach-mx5/devices-imx51.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2010 Pengutronix - * Uwe Kleine-Koenig - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - */ -#include -#include - -extern const struct imx_fec_data imx51_fec_data; -#define imx51_add_fec(pdata) \ - imx_add_fec(&imx51_fec_data, pdata) - -extern const struct imx_fsl_usb2_udc_data imx51_fsl_usb2_udc_data; -#define imx51_add_fsl_usb2_udc(pdata) \ - imx_add_fsl_usb2_udc(&imx51_fsl_usb2_udc_data, pdata) - -extern const struct imx_imx_i2c_data imx51_imx_i2c_data[]; -#define imx51_add_imx_i2c(id, pdata) \ - imx_add_imx_i2c(&imx51_imx_i2c_data[id], pdata) -#define imx51_add_hsi2c(pdata) \ - imx51_add_imx_i2c(2, pdata) - -extern const struct imx_imx_ssi_data imx51_imx_ssi_data[]; -#define imx51_add_imx_ssi(id, pdata) \ - imx_add_imx_ssi(&imx51_imx_ssi_data[id], pdata) - -extern const struct imx_imx_uart_1irq_data imx51_imx_uart_data[]; -#define imx51_add_imx_uart(id, pdata) \ - imx_add_imx_uart_1irq(&imx51_imx_uart_data[id], pdata) - -extern const struct imx_mxc_ehci_data imx51_mxc_ehci_otg_data; -#define imx51_add_mxc_ehci_otg(pdata) \ - imx_add_mxc_ehci(&imx51_mxc_ehci_otg_data, pdata) -extern const struct imx_mxc_ehci_data imx51_mxc_ehci_hs_data[]; -#define imx51_add_mxc_ehci_hs(id, pdata) \ - imx_add_mxc_ehci(&imx51_mxc_ehci_hs_data[id - 1], pdata) - -extern const struct imx_mxc_nand_data imx51_mxc_nand_data; -#define imx51_add_mxc_nand(pdata) \ - imx_add_mxc_nand(&imx51_mxc_nand_data, pdata) - -extern const struct imx_sdhci_esdhc_imx_data imx51_sdhci_esdhc_imx_data[]; -#define imx51_add_sdhci_esdhc_imx(id, pdata) \ - imx_add_sdhci_esdhc_imx(&imx51_sdhci_esdhc_imx_data[id], pdata) - -extern const struct imx_spi_imx_data imx51_cspi_data; -#define imx51_add_cspi(pdata) \ - imx_add_spi_imx(&imx51_cspi_data, pdata) - -extern const struct imx_spi_imx_data imx51_ecspi_data[]; -#define imx51_add_ecspi(id, pdata) \ - imx_add_spi_imx(&imx51_ecspi_data[id], pdata) - -extern const struct imx_imx2_wdt_data imx51_imx2_wdt_data[]; -#define imx51_add_imx2_wdt(id, pdata) \ - imx_add_imx2_wdt(&imx51_imx2_wdt_data[id]) - -extern const struct imx_mxc_pwm_data imx51_mxc_pwm_data[]; -#define imx51_add_mxc_pwm(id) \ - imx_add_mxc_pwm(&imx51_mxc_pwm_data[id]) - -extern const struct imx_imx_keypad_data imx51_imx_keypad_data; -#define imx51_add_imx_keypad(pdata) \ - imx_add_imx_keypad(&imx51_imx_keypad_data, pdata) - -extern const struct imx_pata_imx_data imx51_pata_imx_data; -#define imx51_add_pata_imx() \ - imx_add_pata_imx(&imx51_pata_imx_data) diff --git a/arch/arm/mach-mx5/devices-imx53.h b/arch/arm/mach-mx5/devices-imx53.h deleted file mode 100644 index 6e1e5d1..0000000 --- a/arch/arm/mach-mx5/devices-imx53.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2010 Yong Shen. - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - */ -#include -#include - -extern const struct imx_fec_data imx53_fec_data; -#define imx53_add_fec(pdata) \ - imx_add_fec(&imx53_fec_data, pdata) - -extern const struct imx_imx_uart_1irq_data imx53_imx_uart_data[]; -#define imx53_add_imx_uart(id, pdata) \ - imx_add_imx_uart_1irq(&imx53_imx_uart_data[id], pdata) - - -extern const struct imx_imx_i2c_data imx53_imx_i2c_data[]; -#define imx53_add_imx_i2c(id, pdata) \ - imx_add_imx_i2c(&imx53_imx_i2c_data[id], pdata) - -extern const struct imx_sdhci_esdhc_imx_data imx53_sdhci_esdhc_imx_data[]; -#define imx53_add_sdhci_esdhc_imx(id, pdata) \ - imx_add_sdhci_esdhc_imx(&imx53_sdhci_esdhc_imx_data[id], pdata) - -extern const struct imx_spi_imx_data imx53_ecspi_data[]; -#define imx53_add_ecspi(id, pdata) \ - imx_add_spi_imx(&imx53_ecspi_data[id], pdata) - -extern const struct imx_imx2_wdt_data imx53_imx2_wdt_data[]; -#define imx53_add_imx2_wdt(id, pdata) \ - imx_add_imx2_wdt(&imx53_imx2_wdt_data[id]) - -extern const struct imx_imx_ssi_data imx53_imx_ssi_data[]; -#define imx53_add_imx_ssi(id, pdata) \ - imx_add_imx_ssi(&imx53_imx_ssi_data[id], pdata) - -extern const struct imx_imx_keypad_data imx53_imx_keypad_data; -#define imx53_add_imx_keypad(pdata) \ - imx_add_imx_keypad(&imx53_imx_keypad_data, pdata) - -extern const struct imx_pata_imx_data imx53_pata_imx_data; -#define imx53_add_pata_imx() \ - imx_add_pata_imx(&imx53_pata_imx_data) - -extern struct platform_device *__init imx53_add_ahci_imx(void); diff --git a/arch/arm/mach-mx5/efika.h b/arch/arm/mach-mx5/efika.h deleted file mode 100644 index 014aa98..0000000 --- a/arch/arm/mach-mx5/efika.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _EFIKA_H -#define _EFIKA_H - -#define EFIKA_WLAN_EN IMX_GPIO_NR(2, 16) -#define EFIKA_WLAN_RESET IMX_GPIO_NR(2, 10) -#define EFIKA_USB_PHY_RESET IMX_GPIO_NR(2, 9) - -void __init efika_board_common_init(void); - -#endif diff --git a/arch/arm/mach-mx5/ehci.c b/arch/arm/mach-mx5/ehci.c deleted file mode 100644 index c17fa13..0000000 --- a/arch/arm/mach-mx5/ehci.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2009 Daniel Mack - * Copyright (C) 2010 Freescale Semiconductor, Inc. - * - * 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include -#include - -#include -#include - -#define MXC_OTG_OFFSET 0 -#define MXC_H1_OFFSET 0x200 -#define MXC_H2_OFFSET 0x400 - -/* USB_CTRL */ -#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */ -#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */ -#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */ -#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */ -#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */ - -/* USB_PHY_CTRL_FUNC */ -#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */ -#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */ - -/* USBH2CTRL */ -#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) -#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) -#define MXC_H2_UCTRL_H2PM_BIT (1 << 4) - -#define MXC_USBCMD_OFFSET 0x140 - -/* USBCMD */ -#define MXC_UCMD_ITC_NO_THRESHOLD_MASK (~(0xff << 16)) /* Interrupt Threshold Control */ - -int mx51_initialize_usb_hw(int port, unsigned int flags) -{ - unsigned int v; - void __iomem *usb_base; - void __iomem *usbotg_base; - void __iomem *usbother_base; - int ret = 0; - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) { - printk(KERN_ERR "%s(): ioremap failed\n", __func__); - return -ENOMEM; - } - - switch (port) { - case 0: /* OTG port */ - usbotg_base = usb_base + MXC_OTG_OFFSET; - break; - case 1: /* Host 1 port */ - usbotg_base = usb_base + MXC_H1_OFFSET; - break; - case 2: /* Host 2 port */ - usbotg_base = usb_base + MXC_H2_OFFSET; - break; - default: - printk(KERN_ERR"%s no such port %d\n", __func__, port); - ret = -ENOENT; - goto error; - } - usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; - - switch (port) { - case 0: /*OTG port */ - if (flags & MXC_EHCI_INTERNAL_PHY) { - v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); - - if (flags & MXC_EHCI_POWER_PINS_ENABLED) { - /* OC/USBPWR is not used */ - v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; - } else { - /* OC/USBPWR is used */ - v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; - } - __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); - - v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); - if (flags & MXC_EHCI_WAKEUP_ENABLED) - v |= MXC_OTG_UCTRL_OWIE_BIT;/* OTG wakeup enable */ - else - v &= ~MXC_OTG_UCTRL_OWIE_BIT;/* OTG wakeup disable */ - if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v |= MXC_OTG_UCTRL_OPM_BIT; - else - v &= ~MXC_OTG_UCTRL_OPM_BIT; - __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); - } - break; - case 1: /* Host 1 */ - /*Host ULPI */ - v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); - if (flags & MXC_EHCI_WAKEUP_ENABLED) { - /* HOST1 wakeup/ULPI intr enable */ - v |= (MXC_H1_UCTRL_H1WIE_BIT | MXC_H1_UCTRL_H1UIE_BIT); - } else { - /* HOST1 wakeup/ULPI intr disable */ - v &= ~(MXC_H1_UCTRL_H1WIE_BIT | MXC_H1_UCTRL_H1UIE_BIT); - } - - if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used*/ - else - v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used*/ - __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); - - v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); - if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ - else - v |= MXC_H1_OC_DIS_BIT; /* OC is not used */ - __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); - - v = __raw_readl(usbotg_base + MXC_USBCMD_OFFSET); - if (flags & MXC_EHCI_ITC_NO_THRESHOLD) - /* Interrupt Threshold Control:Immediate (no threshold) */ - v &= MXC_UCMD_ITC_NO_THRESHOLD_MASK; - __raw_writel(v, usbotg_base + MXC_USBCMD_OFFSET); - break; - case 2: /* Host 2 ULPI */ - v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); - if (flags & MXC_EHCI_WAKEUP_ENABLED) { - /* HOST1 wakeup/ULPI intr enable */ - v |= (MXC_H2_UCTRL_H2WIE_BIT | MXC_H2_UCTRL_H2UIE_BIT); - } else { - /* HOST1 wakeup/ULPI intr disable */ - v &= ~(MXC_H2_UCTRL_H2WIE_BIT | MXC_H2_UCTRL_H2UIE_BIT); - } - - if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used*/ - else - v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used*/ - __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); - break; - } - -error: - iounmap(usb_base); - return ret; -} - diff --git a/arch/arm/mach-mx5/eukrea_mbimx51-baseboard.c b/arch/arm/mach-mx5/eukrea_mbimx51-baseboard.c deleted file mode 100644 index a6a3ab8..0000000 --- a/arch/arm/mach-mx5/eukrea_mbimx51-baseboard.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright (C) 2010 Eric Bénard - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "devices-imx51.h" - -#define MBIMX51_TSC2007_GPIO IMX_GPIO_NR(3, 30) -#define MBIMX51_LED0 IMX_GPIO_NR(3, 5) -#define MBIMX51_LED1 IMX_GPIO_NR(3, 6) -#define MBIMX51_LED2 IMX_GPIO_NR(3, 7) -#define MBIMX51_LED3 IMX_GPIO_NR(3, 8) - -static const struct gpio_led mbimx51_leds[] __initconst = { - { - .name = "led0", - .default_trigger = "heartbeat", - .active_low = 1, - .gpio = MBIMX51_LED0, - }, - { - .name = "led1", - .default_trigger = "nand-disk", - .active_low = 1, - .gpio = MBIMX51_LED1, - }, - { - .name = "led2", - .default_trigger = "mmc0", - .active_low = 1, - .gpio = MBIMX51_LED2, - }, - { - .name = "led3", - .default_trigger = "default-on", - .active_low = 1, - .gpio = MBIMX51_LED3, - }, -}; - -static const struct gpio_led_platform_data mbimx51_leds_info __initconst = { - .leds = mbimx51_leds, - .num_leds = ARRAY_SIZE(mbimx51_leds), -}; - -static iomux_v3_cfg_t mbimx51_pads[] = { - /* UART2 */ - MX51_PAD_UART2_RXD__UART2_RXD, - MX51_PAD_UART2_TXD__UART2_TXD, - - /* UART3 */ - MX51_PAD_UART3_RXD__UART3_RXD, - MX51_PAD_UART3_TXD__UART3_TXD, - MX51_PAD_KEY_COL4__UART3_RTS, - MX51_PAD_KEY_COL5__UART3_CTS, - - /* TSC2007 IRQ */ - MX51_PAD_NANDF_D10__GPIO3_30, - - /* LEDS */ - MX51_PAD_DISPB2_SER_DIN__GPIO3_5, - MX51_PAD_DISPB2_SER_DIO__GPIO3_6, - MX51_PAD_DISPB2_SER_CLK__GPIO3_7, - MX51_PAD_DISPB2_SER_RS__GPIO3_8, - - /* KPP */ - MX51_PAD_KEY_ROW0__KEY_ROW0, - MX51_PAD_KEY_ROW1__KEY_ROW1, - MX51_PAD_KEY_ROW2__KEY_ROW2, - MX51_PAD_KEY_ROW3__KEY_ROW3, - MX51_PAD_KEY_COL0__KEY_COL0, - MX51_PAD_KEY_COL1__KEY_COL1, - MX51_PAD_KEY_COL2__KEY_COL2, - MX51_PAD_KEY_COL3__KEY_COL3, - - /* SD 1 */ - MX51_PAD_SD1_CMD__SD1_CMD, - MX51_PAD_SD1_CLK__SD1_CLK, - MX51_PAD_SD1_DATA0__SD1_DATA0, - MX51_PAD_SD1_DATA1__SD1_DATA1, - MX51_PAD_SD1_DATA2__SD1_DATA2, - MX51_PAD_SD1_DATA3__SD1_DATA3, - - /* SD 2 */ - MX51_PAD_SD2_CMD__SD2_CMD, - MX51_PAD_SD2_CLK__SD2_CLK, - MX51_PAD_SD2_DATA0__SD2_DATA0, - MX51_PAD_SD2_DATA1__SD2_DATA1, - MX51_PAD_SD2_DATA2__SD2_DATA2, - MX51_PAD_SD2_DATA3__SD2_DATA3, -}; - -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static int mbimx51_keymap[] = { - KEY(0, 0, KEY_1), - KEY(0, 1, KEY_2), - KEY(0, 2, KEY_3), - KEY(0, 3, KEY_UP), - - KEY(1, 0, KEY_4), - KEY(1, 1, KEY_5), - KEY(1, 2, KEY_6), - KEY(1, 3, KEY_LEFT), - - KEY(2, 0, KEY_7), - KEY(2, 1, KEY_8), - KEY(2, 2, KEY_9), - KEY(2, 3, KEY_RIGHT), - - KEY(3, 0, KEY_0), - KEY(3, 1, KEY_DOWN), - KEY(3, 2, KEY_ESC), - KEY(3, 3, KEY_ENTER), -}; - -static const struct matrix_keymap_data mbimx51_map_data __initconst = { - .keymap = mbimx51_keymap, - .keymap_size = ARRAY_SIZE(mbimx51_keymap), -}; - -static int tsc2007_get_pendown_state(void) -{ - return !gpio_get_value(MBIMX51_TSC2007_GPIO); -} - -struct tsc2007_platform_data tsc2007_data = { - .model = 2007, - .x_plate_ohms = 180, - .get_pendown_state = tsc2007_get_pendown_state, -}; - -static struct i2c_board_info mbimx51_i2c_devices[] = { - { - I2C_BOARD_INFO("tsc2007", 0x49), - .irq = IMX_GPIO_TO_IRQ(MBIMX51_TSC2007_GPIO), - .platform_data = &tsc2007_data, - }, { - I2C_BOARD_INFO("tlv320aic23", 0x1a), - }, -}; - -/* - * baseboard initialization. - */ -void __init eukrea_mbimx51_baseboard_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mbimx51_pads, - ARRAY_SIZE(mbimx51_pads)); - - imx51_add_imx_uart(1, NULL); - imx51_add_imx_uart(2, &uart_pdata); - - gpio_request(MBIMX51_LED0, "LED0"); - gpio_direction_output(MBIMX51_LED0, 1); - gpio_free(MBIMX51_LED0); - gpio_request(MBIMX51_LED1, "LED1"); - gpio_direction_output(MBIMX51_LED1, 1); - gpio_free(MBIMX51_LED1); - gpio_request(MBIMX51_LED2, "LED2"); - gpio_direction_output(MBIMX51_LED2, 1); - gpio_free(MBIMX51_LED2); - gpio_request(MBIMX51_LED3, "LED3"); - gpio_direction_output(MBIMX51_LED3, 1); - gpio_free(MBIMX51_LED3); - - gpio_led_register_device(-1, &mbimx51_leds_info); - - imx51_add_imx_keypad(&mbimx51_map_data); - - gpio_request(MBIMX51_TSC2007_GPIO, "tsc2007_irq"); - gpio_direction_input(MBIMX51_TSC2007_GPIO); - irq_set_irq_type(gpio_to_irq(MBIMX51_TSC2007_GPIO), - IRQF_TRIGGER_FALLING); - i2c_register_board_info(1, mbimx51_i2c_devices, - ARRAY_SIZE(mbimx51_i2c_devices)); - - imx51_add_sdhci_esdhc_imx(0, NULL); - imx51_add_sdhci_esdhc_imx(1, NULL); -} diff --git a/arch/arm/mach-mx5/eukrea_mbimxsd-baseboard.c b/arch/arm/mach-mx5/eukrea_mbimxsd-baseboard.c deleted file mode 100644 index d817fc8..0000000 --- a/arch/arm/mach-mx5/eukrea_mbimxsd-baseboard.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2010 Eric Benard - eric@eukrea.com - * - * Based on pcm970-baseboard.c which is : - * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) - * - * 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. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" - -static iomux_v3_cfg_t eukrea_mbimxsd_pads[] = { - /* LED */ - MX51_PAD_NANDF_D10__GPIO3_30, - /* SWITCH */ - NEW_PAD_CTRL(MX51_PAD_NANDF_D9__GPIO3_31, PAD_CTL_PUS_22K_UP | - PAD_CTL_PKE | PAD_CTL_SRE_FAST | - PAD_CTL_DSE_HIGH | PAD_CTL_PUE | PAD_CTL_HYS), - /* UART2 */ - MX51_PAD_UART2_RXD__UART2_RXD, - MX51_PAD_UART2_TXD__UART2_TXD, - /* UART 3 */ - MX51_PAD_UART3_RXD__UART3_RXD, - MX51_PAD_UART3_TXD__UART3_TXD, - MX51_PAD_KEY_COL4__UART3_RTS, - MX51_PAD_KEY_COL5__UART3_CTS, - /* SD */ - MX51_PAD_SD1_CMD__SD1_CMD, - MX51_PAD_SD1_CLK__SD1_CLK, - MX51_PAD_SD1_DATA0__SD1_DATA0, - MX51_PAD_SD1_DATA1__SD1_DATA1, - MX51_PAD_SD1_DATA2__SD1_DATA2, - MX51_PAD_SD1_DATA3__SD1_DATA3, - /* SD1 CD */ - NEW_PAD_CTRL(MX51_PAD_GPIO1_0__SD1_CD, PAD_CTL_PUS_22K_UP | - PAD_CTL_PKE | PAD_CTL_SRE_FAST | - PAD_CTL_DSE_HIGH | PAD_CTL_PUE | PAD_CTL_HYS), -}; - -#define GPIO_LED1 IMX_GPIO_NR(3, 30) -#define GPIO_SWITCH1 IMX_GPIO_NR(3, 31) - -static const struct gpio_led eukrea_mbimxsd_leds[] __initconst = { - { - .name = "led1", - .default_trigger = "heartbeat", - .active_low = 1, - .gpio = GPIO_LED1, - }, -}; - -static const struct gpio_led_platform_data - eukrea_mbimxsd_led_info __initconst = { - .leds = eukrea_mbimxsd_leds, - .num_leds = ARRAY_SIZE(eukrea_mbimxsd_leds), -}; - -static struct gpio_keys_button eukrea_mbimxsd_gpio_buttons[] = { - { - .gpio = GPIO_SWITCH1, - .code = BTN_0, - .desc = "BP1", - .active_low = 1, - .wakeup = 1, - }, -}; - -static const struct gpio_keys_platform_data - eukrea_mbimxsd_button_data __initconst = { - .buttons = eukrea_mbimxsd_gpio_buttons, - .nbuttons = ARRAY_SIZE(eukrea_mbimxsd_gpio_buttons), -}; - -static const struct imxuart_platform_data uart_pdata __initconst = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct i2c_board_info eukrea_mbimxsd_i2c_devices[] = { - { - I2C_BOARD_INFO("tlv320aic23", 0x1a), - }, -}; - -/* - * system init for baseboard usage. Will be called by cpuimx51sd init. - * - * Add platform devices present on this baseboard and init - * them from CPU side as far as required to use them later on - */ -void __init eukrea_mbimxsd51_baseboard_init(void) -{ - if (mxc_iomux_v3_setup_multiple_pads(eukrea_mbimxsd_pads, - ARRAY_SIZE(eukrea_mbimxsd_pads))) - printk(KERN_ERR "error setting mbimxsd pads !\n"); - - imx51_add_imx_uart(1, NULL); - imx51_add_imx_uart(2, &uart_pdata); - - imx51_add_sdhci_esdhc_imx(0, NULL); - - gpio_request(GPIO_LED1, "LED1"); - gpio_direction_output(GPIO_LED1, 1); - gpio_free(GPIO_LED1); - - gpio_request(GPIO_SWITCH1, "SWITCH1"); - gpio_direction_input(GPIO_SWITCH1); - gpio_free(GPIO_SWITCH1); - - i2c_register_board_info(0, eukrea_mbimxsd_i2c_devices, - ARRAY_SIZE(eukrea_mbimxsd_i2c_devices)); - - gpio_led_register_device(-1, &eukrea_mbimxsd_led_info); - imx_add_gpio_keys(&eukrea_mbimxsd_button_data); -} diff --git a/arch/arm/mach-mx5/imx51-dt.c b/arch/arm/mach-mx5/imx51-dt.c deleted file mode 100644 index ccc6158..0000000 --- a/arch/arm/mach-mx5/imx51-dt.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2011 Linaro Ltd. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Lookup table for attaching a specific name and platform_data pointer to - * devices as they get created by of_platform_populate(). Ideally this table - * would not exist, but the current clock implementation depends on some devices - * having a specific name. - */ -static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART1_BASE_ADDR, "imx21-uart.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART2_BASE_ADDR, "imx21-uart.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART3_BASE_ADDR, "imx21-uart.2", NULL), - OF_DEV_AUXDATA("fsl,imx51-fec", MX51_FEC_BASE_ADDR, "imx27-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx51.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx51.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx51.2", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx51.3", NULL), - OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-cspi", MX51_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-sdma", MX51_SDMA_BASE_ADDR, "imx35-sdma", NULL), - OF_DEV_AUXDATA("fsl,imx51-wdt", MX51_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), - { /* sentinel */ } -}; - -static void __init imx51_tzic_add_irq_domain(struct device_node *np, - struct device_node *interrupt_parent) -{ - irq_domain_add_simple(np, 0); -} - -static void __init imx51_gpio_add_irq_domain(struct device_node *np, - struct device_node *interrupt_parent) -{ - static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS - - 32 * 4; /* imx51 gets 4 gpio ports */ - - irq_domain_add_simple(np, gpio_irq_base); - gpio_irq_base += 32; -} - -static const struct of_device_id imx51_irq_match[] __initconst = { - { .compatible = "fsl,imx51-tzic", .data = imx51_tzic_add_irq_domain, }, - { .compatible = "fsl,imx51-gpio", .data = imx51_gpio_add_irq_domain, }, - { /* sentinel */ } -}; - -static const struct of_device_id imx51_iomuxc_of_match[] __initconst = { - { .compatible = "fsl,imx51-iomuxc-babbage", .data = imx51_babbage_common_init, }, - { /* sentinel */ } -}; - -static void __init imx51_dt_init(void) -{ - struct device_node *node; - const struct of_device_id *of_id; - void (*func)(void); - - of_irq_init(imx51_irq_match); - - node = of_find_matching_node(NULL, imx51_iomuxc_of_match); - if (node) { - of_id = of_match_node(imx51_iomuxc_of_match, node); - func = of_id->data; - func(); - of_node_put(node); - } - - of_platform_populate(NULL, of_default_bus_match_table, - imx51_auxdata_lookup, NULL); -} - -static void __init imx51_timer_init(void) -{ - mx51_clocks_init_dt(); -} - -static struct sys_timer imx51_timer = { - .init = imx51_timer_init, -}; - -static const char *imx51_dt_board_compat[] __initdata = { - "fsl,imx51-babbage", - NULL -}; - -DT_MACHINE_START(IMX51_DT, "Freescale i.MX51 (Device Tree Support)") - .map_io = mx51_map_io, - .init_early = imx51_init_early, - .init_irq = mx51_init_irq, - .handle_irq = imx51_handle_irq, - .timer = &imx51_timer, - .init_machine = imx51_dt_init, - .dt_compat = imx51_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-mx5/imx53-dt.c b/arch/arm/mach-mx5/imx53-dt.c deleted file mode 100644 index ccaa0b8..0000000 --- a/arch/arm/mach-mx5/imx53-dt.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2011 Linaro Ltd. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Lookup table for attaching a specific name and platform_data pointer to - * devices as they get created by of_platform_populate(). Ideally this table - * would not exist, but the current clock implementation depends on some devices - * having a specific name. - */ -static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART1_BASE_ADDR, "imx21-uart.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART2_BASE_ADDR, "imx21-uart.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART3_BASE_ADDR, "imx21-uart.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART4_BASE_ADDR, "imx21-uart.3", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART5_BASE_ADDR, "imx21-uart.4", NULL), - OF_DEV_AUXDATA("fsl,imx53-fec", MX53_FEC_BASE_ADDR, "imx25-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx53.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx53.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx53.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx53.3", NULL), - OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-cspi", MX53_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx-i2c.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-sdma", MX53_SDMA_BASE_ADDR, "imx35-sdma", NULL), - OF_DEV_AUXDATA("fsl,imx53-wdt", MX53_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), - { /* sentinel */ } -}; - -static void __init imx53_tzic_add_irq_domain(struct device_node *np, - struct device_node *interrupt_parent) -{ - irq_domain_add_simple(np, 0); -} - -static void __init imx53_gpio_add_irq_domain(struct device_node *np, - struct device_node *interrupt_parent) -{ - static int gpio_irq_base = MXC_GPIO_IRQ_START + ARCH_NR_GPIOS - - 32 * 7; /* imx53 gets 7 gpio ports */ - - irq_domain_add_simple(np, gpio_irq_base); - gpio_irq_base += 32; -} - -static const struct of_device_id imx53_irq_match[] __initconst = { - { .compatible = "fsl,imx53-tzic", .data = imx53_tzic_add_irq_domain, }, - { .compatible = "fsl,imx53-gpio", .data = imx53_gpio_add_irq_domain, }, - { /* sentinel */ } -}; - -static const struct of_device_id imx53_iomuxc_of_match[] __initconst = { - { .compatible = "fsl,imx53-iomuxc-ard", .data = imx53_ard_common_init, }, - { .compatible = "fsl,imx53-iomuxc-evk", .data = imx53_evk_common_init, }, - { .compatible = "fsl,imx53-iomuxc-qsb", .data = imx53_qsb_common_init, }, - { .compatible = "fsl,imx53-iomuxc-smd", .data = imx53_smd_common_init, }, - { /* sentinel */ } -}; - -static void __init imx53_dt_init(void) -{ - struct device_node *node; - const struct of_device_id *of_id; - void (*func)(void); - - of_irq_init(imx53_irq_match); - - node = of_find_matching_node(NULL, imx53_iomuxc_of_match); - if (node) { - of_id = of_match_node(imx53_iomuxc_of_match, node); - func = of_id->data; - func(); - of_node_put(node); - } - - of_platform_populate(NULL, of_default_bus_match_table, - imx53_auxdata_lookup, NULL); -} - -static void __init imx53_timer_init(void) -{ - mx53_clocks_init_dt(); -} - -static struct sys_timer imx53_timer = { - .init = imx53_timer_init, -}; - -static const char *imx53_dt_board_compat[] __initdata = { - "fsl,imx53-ard", - "fsl,imx53-evk", - "fsl,imx53-qsb", - "fsl,imx53-smd", - NULL -}; - -DT_MACHINE_START(IMX53_DT, "Freescale i.MX53 (Device Tree Support)") - .map_io = mx53_map_io, - .init_early = imx53_init_early, - .init_irq = mx53_init_irq, - .handle_irq = imx53_handle_irq, - .timer = &imx53_timer, - .init_machine = imx53_dt_init, - .dt_compat = imx53_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-mx5/mm.c b/arch/arm/mach-mx5/mm.c deleted file mode 100644 index 26eacc9..0000000 --- a/arch/arm/mach-mx5/mm.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - * - * Create static mapping between physical to virtual memory. - */ - -#include -#include - -#include - -#include -#include -#include -#include - -static void imx5_idle(void) -{ - mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); -} - -/* - * Define the MX50 memory map. - */ -static struct map_desc mx50_io_desc[] __initdata = { - imx_map_entry(MX50, TZIC, MT_DEVICE), - imx_map_entry(MX50, SPBA0, MT_DEVICE), - imx_map_entry(MX50, AIPS1, MT_DEVICE), - imx_map_entry(MX50, AIPS2, MT_DEVICE), -}; - -/* - * Define the MX51 memory map. - */ -static struct map_desc mx51_io_desc[] __initdata = { - imx_map_entry(MX51, TZIC, MT_DEVICE), - imx_map_entry(MX51, IRAM, MT_DEVICE), - imx_map_entry(MX51, AIPS1, MT_DEVICE), - imx_map_entry(MX51, SPBA0, MT_DEVICE), - imx_map_entry(MX51, AIPS2, MT_DEVICE), -}; - -/* - * Define the MX53 memory map. - */ -static struct map_desc mx53_io_desc[] __initdata = { - imx_map_entry(MX53, TZIC, MT_DEVICE), - imx_map_entry(MX53, AIPS1, MT_DEVICE), - imx_map_entry(MX53, SPBA0, MT_DEVICE), - imx_map_entry(MX53, AIPS2, MT_DEVICE), -}; - -/* - * This function initializes the memory map. It is called during the - * system startup to create static physical to virtual memory mappings - * for the IO modules. - */ -void __init mx50_map_io(void) -{ - iotable_init(mx50_io_desc, ARRAY_SIZE(mx50_io_desc)); -} - -void __init mx51_map_io(void) -{ - iotable_init(mx51_io_desc, ARRAY_SIZE(mx51_io_desc)); -} - -void __init mx53_map_io(void) -{ - iotable_init(mx53_io_desc, ARRAY_SIZE(mx53_io_desc)); -} - -void __init imx50_init_early(void) -{ - mxc_set_cpu_type(MXC_CPU_MX50); - mxc_iomux_v3_init(MX50_IO_ADDRESS(MX50_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX50_IO_ADDRESS(MX50_WDOG_BASE_ADDR)); -} - -void __init imx51_init_early(void) -{ - mxc_set_cpu_type(MXC_CPU_MX51); - mxc_iomux_v3_init(MX51_IO_ADDRESS(MX51_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX51_IO_ADDRESS(MX51_WDOG1_BASE_ADDR)); - imx_idle = imx5_idle; -} - -void __init imx53_init_early(void) -{ - mxc_set_cpu_type(MXC_CPU_MX53); - mxc_iomux_v3_init(MX53_IO_ADDRESS(MX53_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX53_IO_ADDRESS(MX53_WDOG1_BASE_ADDR)); -} - -void __init mx50_init_irq(void) -{ - tzic_init_irq(MX50_IO_ADDRESS(MX50_TZIC_BASE_ADDR)); -} - -void __init mx51_init_irq(void) -{ - tzic_init_irq(MX51_IO_ADDRESS(MX51_TZIC_BASE_ADDR)); -} - -void __init mx53_init_irq(void) -{ - tzic_init_irq(MX53_IO_ADDRESS(MX53_TZIC_BASE_ADDR)); -} - -static struct sdma_script_start_addrs imx51_sdma_script __initdata = { - .ap_2_ap_addr = 642, - .uart_2_mcu_addr = 817, - .mcu_2_app_addr = 747, - .mcu_2_shp_addr = 961, - .ata_2_mcu_addr = 1473, - .mcu_2_ata_addr = 1392, - .app_2_per_addr = 1033, - .app_2_mcu_addr = 683, - .shp_2_per_addr = 1251, - .shp_2_mcu_addr = 892, -}; - -static struct sdma_platform_data imx51_sdma_pdata __initdata = { - .fw_name = "sdma-imx51.bin", - .script_addrs = &imx51_sdma_script, -}; - -static struct sdma_script_start_addrs imx53_sdma_script __initdata = { - .ap_2_ap_addr = 642, - .app_2_mcu_addr = 683, - .mcu_2_app_addr = 747, - .uart_2_mcu_addr = 817, - .shp_2_mcu_addr = 891, - .mcu_2_shp_addr = 960, - .uartsh_2_mcu_addr = 1032, - .spdif_2_mcu_addr = 1100, - .mcu_2_spdif_addr = 1134, - .firi_2_mcu_addr = 1193, - .mcu_2_firi_addr = 1290, -}; - -static struct sdma_platform_data imx53_sdma_pdata __initdata = { - .fw_name = "sdma-imx53.bin", - .script_addrs = &imx53_sdma_script, -}; - -void __init imx50_soc_init(void) -{ - /* i.mx50 has the i.mx31 type gpio */ - mxc_register_gpio("imx31-gpio", 0, MX50_GPIO1_BASE_ADDR, SZ_16K, MX50_INT_GPIO1_LOW, MX50_INT_GPIO1_HIGH); - mxc_register_gpio("imx31-gpio", 1, MX50_GPIO2_BASE_ADDR, SZ_16K, MX50_INT_GPIO2_LOW, MX50_INT_GPIO2_HIGH); - mxc_register_gpio("imx31-gpio", 2, MX50_GPIO3_BASE_ADDR, SZ_16K, MX50_INT_GPIO3_LOW, MX50_INT_GPIO3_HIGH); - mxc_register_gpio("imx31-gpio", 3, MX50_GPIO4_BASE_ADDR, SZ_16K, MX50_INT_GPIO4_LOW, MX50_INT_GPIO4_HIGH); - mxc_register_gpio("imx31-gpio", 4, MX50_GPIO5_BASE_ADDR, SZ_16K, MX50_INT_GPIO5_LOW, MX50_INT_GPIO5_HIGH); - mxc_register_gpio("imx31-gpio", 5, MX50_GPIO6_BASE_ADDR, SZ_16K, MX50_INT_GPIO6_LOW, MX50_INT_GPIO6_HIGH); -} - -void __init imx51_soc_init(void) -{ - /* i.mx51 has the i.mx31 type gpio */ - mxc_register_gpio("imx31-gpio", 0, MX51_GPIO1_BASE_ADDR, SZ_16K, MX51_INT_GPIO1_LOW, MX51_INT_GPIO1_HIGH); - mxc_register_gpio("imx31-gpio", 1, MX51_GPIO2_BASE_ADDR, SZ_16K, MX51_INT_GPIO2_LOW, MX51_INT_GPIO2_HIGH); - mxc_register_gpio("imx31-gpio", 2, MX51_GPIO3_BASE_ADDR, SZ_16K, MX51_INT_GPIO3_LOW, MX51_INT_GPIO3_HIGH); - mxc_register_gpio("imx31-gpio", 3, MX51_GPIO4_BASE_ADDR, SZ_16K, MX51_INT_GPIO4_LOW, MX51_INT_GPIO4_HIGH); - - /* i.mx51 has the i.mx35 type sdma */ - imx_add_imx_sdma("imx35-sdma", MX51_SDMA_BASE_ADDR, MX51_INT_SDMA, &imx51_sdma_pdata); -} - -void __init imx53_soc_init(void) -{ - /* i.mx53 has the i.mx31 type gpio */ - mxc_register_gpio("imx31-gpio", 0, MX53_GPIO1_BASE_ADDR, SZ_16K, MX53_INT_GPIO1_LOW, MX53_INT_GPIO1_HIGH); - mxc_register_gpio("imx31-gpio", 1, MX53_GPIO2_BASE_ADDR, SZ_16K, MX53_INT_GPIO2_LOW, MX53_INT_GPIO2_HIGH); - mxc_register_gpio("imx31-gpio", 2, MX53_GPIO3_BASE_ADDR, SZ_16K, MX53_INT_GPIO3_LOW, MX53_INT_GPIO3_HIGH); - mxc_register_gpio("imx31-gpio", 3, MX53_GPIO4_BASE_ADDR, SZ_16K, MX53_INT_GPIO4_LOW, MX53_INT_GPIO4_HIGH); - mxc_register_gpio("imx31-gpio", 4, MX53_GPIO5_BASE_ADDR, SZ_16K, MX53_INT_GPIO5_LOW, MX53_INT_GPIO5_HIGH); - mxc_register_gpio("imx31-gpio", 5, MX53_GPIO6_BASE_ADDR, SZ_16K, MX53_INT_GPIO6_LOW, MX53_INT_GPIO6_HIGH); - mxc_register_gpio("imx31-gpio", 6, MX53_GPIO7_BASE_ADDR, SZ_16K, MX53_INT_GPIO7_LOW, MX53_INT_GPIO7_HIGH); - - /* i.mx53 has the i.mx35 type sdma */ - imx_add_imx_sdma("imx35-sdma", MX53_SDMA_BASE_ADDR, MX53_INT_SDMA, &imx53_sdma_pdata); -} diff --git a/arch/arm/mach-mx5/mx51_efika.c b/arch/arm/mach-mx5/mx51_efika.c deleted file mode 100644 index ec6ca91..0000000 --- a/arch/arm/mach-mx5/mx51_efika.c +++ /dev/null @@ -1,632 +0,0 @@ -/* - * based on code from the following - * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2009-2010 Pegatron Corporation. All Rights Reserved. - * Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "devices-imx51.h" -#include "efika.h" -#include "cpu_op-mx51.h" - -#define MX51_USB_CTRL_1_OFFSET 0x10 -#define MX51_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) -#define MX51_USB_PLL_DIV_19_2_MHZ 0x01 - -#define EFIKAMX_USB_HUB_RESET IMX_GPIO_NR(1, 5) -#define EFIKAMX_USBH1_STP IMX_GPIO_NR(1, 27) - -#define EFIKAMX_SPI_CS0 IMX_GPIO_NR(4, 24) -#define EFIKAMX_SPI_CS1 IMX_GPIO_NR(4, 25) - -#define EFIKAMX_PMIC IMX_GPIO_NR(1, 6) - -static iomux_v3_cfg_t mx51efika_pads[] = { - /* UART1 */ - MX51_PAD_UART1_RXD__UART1_RXD, - MX51_PAD_UART1_TXD__UART1_TXD, - MX51_PAD_UART1_RTS__UART1_RTS, - MX51_PAD_UART1_CTS__UART1_CTS, - - /* SD 1 */ - MX51_PAD_SD1_CMD__SD1_CMD, - MX51_PAD_SD1_CLK__SD1_CLK, - MX51_PAD_SD1_DATA0__SD1_DATA0, - MX51_PAD_SD1_DATA1__SD1_DATA1, - MX51_PAD_SD1_DATA2__SD1_DATA2, - MX51_PAD_SD1_DATA3__SD1_DATA3, - - /* SD 2 */ - MX51_PAD_SD2_CMD__SD2_CMD, - MX51_PAD_SD2_CLK__SD2_CLK, - MX51_PAD_SD2_DATA0__SD2_DATA0, - MX51_PAD_SD2_DATA1__SD2_DATA1, - MX51_PAD_SD2_DATA2__SD2_DATA2, - MX51_PAD_SD2_DATA3__SD2_DATA3, - - /* SD/MMC WP/CD */ - MX51_PAD_GPIO1_0__SD1_CD, - MX51_PAD_GPIO1_1__SD1_WP, - MX51_PAD_GPIO1_7__SD2_WP, - MX51_PAD_GPIO1_8__SD2_CD, - - /* spi */ - MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, - MX51_PAD_CSPI1_MISO__ECSPI1_MISO, - MX51_PAD_CSPI1_SS0__GPIO4_24, - MX51_PAD_CSPI1_SS1__GPIO4_25, - MX51_PAD_CSPI1_RDY__ECSPI1_RDY, - MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, - MX51_PAD_GPIO1_6__GPIO1_6, - - /* USB HOST1 */ - MX51_PAD_USBH1_CLK__USBH1_CLK, - MX51_PAD_USBH1_DIR__USBH1_DIR, - MX51_PAD_USBH1_NXT__USBH1_NXT, - MX51_PAD_USBH1_DATA0__USBH1_DATA0, - MX51_PAD_USBH1_DATA1__USBH1_DATA1, - MX51_PAD_USBH1_DATA2__USBH1_DATA2, - MX51_PAD_USBH1_DATA3__USBH1_DATA3, - MX51_PAD_USBH1_DATA4__USBH1_DATA4, - MX51_PAD_USBH1_DATA5__USBH1_DATA5, - MX51_PAD_USBH1_DATA6__USBH1_DATA6, - MX51_PAD_USBH1_DATA7__USBH1_DATA7, - - /* USB HUB RESET */ - MX51_PAD_GPIO1_5__GPIO1_5, - - /* WLAN */ - MX51_PAD_EIM_A22__GPIO2_16, - MX51_PAD_EIM_A16__GPIO2_10, - - /* USB PHY RESET */ - MX51_PAD_EIM_D27__GPIO2_9, -}; - -/* Serial ports */ -static const struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -/* This function is board specific as the bit mask for the plldiv will also - * be different for other Freescale SoCs, thus a common bitmask is not - * possible and cannot get place in /plat-mxc/ehci.c. - */ -static int initialize_otg_port(struct platform_device *pdev) -{ - u32 v; - void __iomem *usb_base; - void __iomem *usbother_base; - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - if (!usb_base) - return -ENOMEM; - usbother_base = (void __iomem *)(usb_base + MX5_USBOTHER_REGS_OFFSET); - - /* Set the PHY clock to 19.2MHz */ - v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - v &= ~MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK; - v |= MX51_USB_PLL_DIV_19_2_MHZ; - __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC2_OFFSET); - iounmap(usb_base); - - mdelay(10); - - return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_INTERNAL_PHY); -} - -static const struct mxc_usbh_platform_data dr_utmi_config __initconst = { - .init = initialize_otg_port, - .portsc = MXC_EHCI_UTMI_16BIT, -}; - -static int initialize_usbh1_port(struct platform_device *pdev) -{ - iomux_v3_cfg_t usbh1stp = MX51_PAD_USBH1_STP__USBH1_STP; - iomux_v3_cfg_t usbh1gpio = MX51_PAD_USBH1_STP__GPIO1_27; - u32 v; - void __iomem *usb_base; - void __iomem *socregs_base; - - mxc_iomux_v3_setup_pad(usbh1gpio); - gpio_request(EFIKAMX_USBH1_STP, "usbh1_stp"); - gpio_direction_output(EFIKAMX_USBH1_STP, 0); - msleep(1); - gpio_set_value(EFIKAMX_USBH1_STP, 1); - msleep(1); - - usb_base = ioremap(MX51_USB_OTG_BASE_ADDR, SZ_4K); - socregs_base = (void __iomem *)(usb_base + MX5_USBOTHER_REGS_OFFSET); - - /* The clock for the USBH1 ULPI port will come externally */ - /* from the PHY. */ - v = __raw_readl(socregs_base + MX51_USB_CTRL_1_OFFSET); - __raw_writel(v | MX51_USB_CTRL_UH1_EXT_CLK_EN, - socregs_base + MX51_USB_CTRL_1_OFFSET); - - iounmap(usb_base); - - gpio_free(EFIKAMX_USBH1_STP); - mxc_iomux_v3_setup_pad(usbh1stp); - - mdelay(10); - - return mx51_initialize_usb_hw(pdev->id, MXC_EHCI_ITC_NO_THRESHOLD); -} - -static struct mxc_usbh_platform_data usbh1_config __initdata = { - .init = initialize_usbh1_port, - .portsc = MXC_EHCI_MODE_ULPI, -}; - -static void mx51_efika_hubreset(void) -{ - gpio_request(EFIKAMX_USB_HUB_RESET, "usb_hub_rst"); - gpio_direction_output(EFIKAMX_USB_HUB_RESET, 1); - msleep(1); - gpio_set_value(EFIKAMX_USB_HUB_RESET, 0); - msleep(1); - gpio_set_value(EFIKAMX_USB_HUB_RESET, 1); -} - -static void __init mx51_efika_usb(void) -{ - mx51_efika_hubreset(); - - /* pulling it low, means no USB at all... */ - gpio_request(EFIKA_USB_PHY_RESET, "usb_phy_reset"); - gpio_direction_output(EFIKA_USB_PHY_RESET, 0); - msleep(1); - gpio_set_value(EFIKA_USB_PHY_RESET, 1); - - usbh1_config.otg = imx_otg_ulpi_create(ULPI_OTG_DRVVBUS | - ULPI_OTG_DRVVBUS_EXT | ULPI_OTG_EXTVBUSIND); - - imx51_add_mxc_ehci_otg(&dr_utmi_config); - if (usbh1_config.otg) - imx51_add_mxc_ehci_hs(1, &usbh1_config); -} - -static struct mtd_partition mx51_efika_spi_nor_partitions[] = { - { - .name = "u-boot", - .offset = 0, - .size = SZ_256K, - }, - { - .name = "config", - .offset = MTDPART_OFS_APPEND, - .size = SZ_64K, - }, -}; - -static struct flash_platform_data mx51_efika_spi_flash_data = { - .name = "spi_flash", - .parts = mx51_efika_spi_nor_partitions, - .nr_parts = ARRAY_SIZE(mx51_efika_spi_nor_partitions), - .type = "sst25vf032b", -}; - -static struct regulator_consumer_supply sw1_consumers[] = { - { - .supply = "cpu_vcc", - } -}; - -static struct regulator_consumer_supply vdig_consumers[] = { - /* sgtl5000 */ - REGULATOR_SUPPLY("VDDA", "1-000a"), - REGULATOR_SUPPLY("VDDD", "1-000a"), -}; - -static struct regulator_consumer_supply vvideo_consumers[] = { - /* sgtl5000 */ - REGULATOR_SUPPLY("VDDIO", "1-000a"), -}; - -static struct regulator_consumer_supply vsd_consumers[] = { - REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx51.0"), - REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx51.1"), -}; - -static struct regulator_consumer_supply pwgt1_consumer[] = { - { - .supply = "pwgt1", - } -}; - -static struct regulator_consumer_supply pwgt2_consumer[] = { - { - .supply = "pwgt2", - } -}; - -static struct regulator_consumer_supply coincell_consumer[] = { - { - .supply = "coincell", - } -}; - -static struct regulator_init_data sw1_init = { - .constraints = { - .name = "SW1", - .min_uV = 600000, - .max_uV = 1375000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .valid_modes_mask = 0, - .always_on = 1, - .boot_on = 1, - .state_mem = { - .uV = 850000, - .mode = REGULATOR_MODE_NORMAL, - .enabled = 1, - }, - }, - .num_consumer_supplies = ARRAY_SIZE(sw1_consumers), - .consumer_supplies = sw1_consumers, -}; - -static struct regulator_init_data sw2_init = { - .constraints = { - .name = "SW2", - .min_uV = 900000, - .max_uV = 1850000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .always_on = 1, - .boot_on = 1, - .state_mem = { - .uV = 950000, - .mode = REGULATOR_MODE_NORMAL, - .enabled = 1, - }, - } -}; - -static struct regulator_init_data sw3_init = { - .constraints = { - .name = "SW3", - .min_uV = 1100000, - .max_uV = 1850000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .always_on = 1, - .boot_on = 1, - } -}; - -static struct regulator_init_data sw4_init = { - .constraints = { - .name = "SW4", - .min_uV = 1100000, - .max_uV = 1850000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .always_on = 1, - .boot_on = 1, - } -}; - -static struct regulator_init_data viohi_init = { - .constraints = { - .name = "VIOHI", - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data vusb_init = { - .constraints = { - .name = "VUSB", - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data swbst_init = { - .constraints = { - .name = "SWBST", - } -}; - -static struct regulator_init_data vdig_init = { - .constraints = { - .name = "VDIG", - .min_uV = 1050000, - .max_uV = 1800000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - .always_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(vdig_consumers), - .consumer_supplies = vdig_consumers, -}; - -static struct regulator_init_data vpll_init = { - .constraints = { - .name = "VPLL", - .min_uV = 1050000, - .max_uV = 1800000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data vusb2_init = { - .constraints = { - .name = "VUSB2", - .min_uV = 2400000, - .max_uV = 2775000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data vvideo_init = { - .constraints = { - .name = "VVIDEO", - .min_uV = 2775000, - .max_uV = 2775000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - .apply_uV = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(vvideo_consumers), - .consumer_supplies = vvideo_consumers, -}; - -static struct regulator_init_data vaudio_init = { - .constraints = { - .name = "VAUDIO", - .min_uV = 2300000, - .max_uV = 3000000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - } -}; - -static struct regulator_init_data vsd_init = { - .constraints = { - .name = "VSD", - .min_uV = 1800000, - .max_uV = 3150000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE, - .boot_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(vsd_consumers), - .consumer_supplies = vsd_consumers, -}; - -static struct regulator_init_data vcam_init = { - .constraints = { - .name = "VCAM", - .min_uV = 2500000, - .max_uV = 3000000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, - .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL, - .boot_on = 1, - } -}; - -static struct regulator_init_data vgen1_init = { - .constraints = { - .name = "VGEN1", - .min_uV = 1200000, - .max_uV = 3150000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data vgen2_init = { - .constraints = { - .name = "VGEN2", - .min_uV = 1200000, - .max_uV = 3150000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data vgen3_init = { - .constraints = { - .name = "VGEN3", - .min_uV = 1800000, - .max_uV = 2900000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - .boot_on = 1, - .always_on = 1, - } -}; - -static struct regulator_init_data gpo1_init = { - .constraints = { - .name = "GPO1", - } -}; - -static struct regulator_init_data gpo2_init = { - .constraints = { - .name = "GPO2", - } -}; - -static struct regulator_init_data gpo3_init = { - .constraints = { - .name = "GPO3", - } -}; - -static struct regulator_init_data gpo4_init = { - .constraints = { - .name = "GPO4", - } -}; - -static struct regulator_init_data pwgt1_init = { - .constraints = { - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - .boot_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(pwgt1_consumer), - .consumer_supplies = pwgt1_consumer, -}; - -static struct regulator_init_data pwgt2_init = { - .constraints = { - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - .boot_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(pwgt2_consumer), - .consumer_supplies = pwgt2_consumer, -}; - -static struct regulator_init_data vcoincell_init = { - .constraints = { - .name = "COINCELL", - .min_uV = 3000000, - .max_uV = 3000000, - .valid_ops_mask = - REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(coincell_consumer), - .consumer_supplies = coincell_consumer, -}; - -static struct mc13xxx_regulator_init_data mx51_efika_regulators[] = { - { .id = MC13892_SW1, .init_data = &sw1_init }, - { .id = MC13892_SW2, .init_data = &sw2_init }, - { .id = MC13892_SW3, .init_data = &sw3_init }, - { .id = MC13892_SW4, .init_data = &sw4_init }, - { .id = MC13892_SWBST, .init_data = &swbst_init }, - { .id = MC13892_VIOHI, .init_data = &viohi_init }, - { .id = MC13892_VPLL, .init_data = &vpll_init }, - { .id = MC13892_VDIG, .init_data = &vdig_init }, - { .id = MC13892_VSD, .init_data = &vsd_init }, - { .id = MC13892_VUSB2, .init_data = &vusb2_init }, - { .id = MC13892_VVIDEO, .init_data = &vvideo_init }, - { .id = MC13892_VAUDIO, .init_data = &vaudio_init }, - { .id = MC13892_VCAM, .init_data = &vcam_init }, - { .id = MC13892_VGEN1, .init_data = &vgen1_init }, - { .id = MC13892_VGEN2, .init_data = &vgen2_init }, - { .id = MC13892_VGEN3, .init_data = &vgen3_init }, - { .id = MC13892_VUSB, .init_data = &vusb_init }, - { .id = MC13892_GPO1, .init_data = &gpo1_init }, - { .id = MC13892_GPO2, .init_data = &gpo2_init }, - { .id = MC13892_GPO3, .init_data = &gpo3_init }, - { .id = MC13892_GPO4, .init_data = &gpo4_init }, - { .id = MC13892_PWGT1SPI, .init_data = &pwgt1_init }, - { .id = MC13892_PWGT2SPI, .init_data = &pwgt2_init }, - { .id = MC13892_VCOINCELL, .init_data = &vcoincell_init }, -}; - -static struct mc13xxx_platform_data mx51_efika_mc13892_data = { - .flags = MC13XXX_USE_RTC, - .regulators = { - .num_regulators = ARRAY_SIZE(mx51_efika_regulators), - .regulators = mx51_efika_regulators, - }, -}; - -static struct spi_board_info mx51_efika_spi_board_info[] __initdata = { - { - .modalias = "m25p80", - .max_speed_hz = 25000000, - .bus_num = 0, - .chip_select = 1, - .platform_data = &mx51_efika_spi_flash_data, - .irq = -1, - }, - { - .modalias = "mc13892", - .max_speed_hz = 1000000, - .bus_num = 0, - .chip_select = 0, - .platform_data = &mx51_efika_mc13892_data, - .irq = IMX_GPIO_TO_IRQ(EFIKAMX_PMIC), - }, -}; - -static int mx51_efika_spi_cs[] = { - EFIKAMX_SPI_CS0, - EFIKAMX_SPI_CS1, -}; - -static const struct spi_imx_master mx51_efika_spi_pdata __initconst = { - .chipselect = mx51_efika_spi_cs, - .num_chipselect = ARRAY_SIZE(mx51_efika_spi_cs), -}; - -void __init efika_board_common_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx51efika_pads, - ARRAY_SIZE(mx51efika_pads)); - imx51_add_imx_uart(0, &uart_pdata); - mx51_efika_usb(); - - /* FIXME: comes from original code. check this. */ - if (mx51_revision() < IMX_CHIP_REVISION_2_0) - sw2_init.constraints.state_mem.uV = 1100000; - else if (mx51_revision() == IMX_CHIP_REVISION_2_0) { - sw2_init.constraints.state_mem.uV = 1250000; - sw1_init.constraints.state_mem.uV = 1000000; - } - if (machine_is_mx51_efikasb()) - vgen1_init.constraints.max_uV = 1200000; - - gpio_request(EFIKAMX_PMIC, "pmic irq"); - gpio_direction_input(EFIKAMX_PMIC); - spi_register_board_info(mx51_efika_spi_board_info, - ARRAY_SIZE(mx51_efika_spi_board_info)); - imx51_add_ecspi(0, &mx51_efika_spi_pdata); - - imx51_add_pata_imx(); - -#if defined(CONFIG_CPU_FREQ_IMX) - get_cpu_op = mx51_get_cpu_op; -#endif -} diff --git a/arch/arm/mach-mx5/pm-imx5.c b/arch/arm/mach-mx5/pm-imx5.c deleted file mode 100644 index 98052fc..0000000 --- a/arch/arm/mach-mx5/pm-imx5.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "crm_regs.h" - -static struct clk *gpc_dvfs_clk; - -static int mx5_suspend_prepare(void) -{ - return clk_enable(gpc_dvfs_clk); -} - -static int mx5_suspend_enter(suspend_state_t state) -{ - switch (state) { - case PM_SUSPEND_MEM: - mx5_cpu_lp_set(STOP_POWER_OFF); - break; - case PM_SUSPEND_STANDBY: - mx5_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); - break; - default: - return -EINVAL; - } - - if (state == PM_SUSPEND_MEM) { - local_flush_tlb_all(); - flush_cache_all(); - - /*clear the EMPGC0/1 bits */ - __raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR); - __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); - } - cpu_do_idle(); - return 0; -} - -static void mx5_suspend_finish(void) -{ - clk_disable(gpc_dvfs_clk); -} - -static int mx5_pm_valid(suspend_state_t state) -{ - return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX); -} - -static const struct platform_suspend_ops mx5_suspend_ops = { - .valid = mx5_pm_valid, - .prepare = mx5_suspend_prepare, - .enter = mx5_suspend_enter, - .finish = mx5_suspend_finish, -}; - -static int __init mx5_pm_init(void) -{ - if (gpc_dvfs_clk == NULL) - gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); - - if (!IS_ERR(gpc_dvfs_clk)) { - if (cpu_is_mx51()) - suspend_set_ops(&mx5_suspend_ops); - } else - return -EPERM; - - return 0; -} -device_initcall(mx5_pm_init); diff --git a/arch/arm/mach-mx5/system.c b/arch/arm/mach-mx5/system.c deleted file mode 100644 index 144ebeb..0000000 --- a/arch/arm/mach-mx5/system.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ -#include -#include -#include -#include -#include "crm_regs.h" - -/* set cpu low power mode before WFI instruction. This function is called - * mx5 because it can be used for mx50, mx51, and mx53.*/ -void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) -{ - u32 plat_lpc, arm_srpgcr, ccm_clpcr; - u32 empgc0, empgc1; - int stop_mode = 0; - - /* always allow platform to issue a deep sleep mode request */ - plat_lpc = __raw_readl(MXC_CORTEXA8_PLAT_LPC) & - ~(MXC_CORTEXA8_PLAT_LPC_DSM); - ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK); - arm_srpgcr = __raw_readl(MXC_SRPG_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR); - empgc0 = __raw_readl(MXC_SRPG_EMPGC0_SRPGCR) & ~(MXC_SRPGCR_PCR); - empgc1 = __raw_readl(MXC_SRPG_EMPGC1_SRPGCR) & ~(MXC_SRPGCR_PCR); - - switch (mode) { - case WAIT_CLOCKED: - break; - case WAIT_UNCLOCKED: - ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; - break; - case WAIT_UNCLOCKED_POWER_OFF: - case STOP_POWER_OFF: - plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM - | MXC_CORTEXA8_PLAT_LPC_DBG_DSM; - if (mode == WAIT_UNCLOCKED_POWER_OFF) { - ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; - ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; - ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; - stop_mode = 0; - } else { - ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; - ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; - ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; - ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; - stop_mode = 1; - } - arm_srpgcr |= MXC_SRPGCR_PCR; - - if (tzic_enable_wake(1) != 0) - return; - break; - case STOP_POWER_ON: - ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; - break; - default: - printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); - return; - } - - __raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC); - __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); - __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR); - - /* Enable NEON SRPG for all but MX50TO1.0. */ - if (mx50_revision() != IMX_CHIP_REVISION_1_0) - __raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR); - - if (stop_mode) { - empgc0 |= MXC_SRPGCR_PCR; - empgc1 |= MXC_SRPGCR_PCR; - - __raw_writel(empgc0, MXC_SRPG_EMPGC0_SRPGCR); - __raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR); - } -} diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index a08a951..fdde4d1 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -17,25 +17,16 @@ config ARCH_IMX_V4_V5 and ARMv5 SoCs config ARCH_IMX_V6_V7 - bool "i.MX3, i.MX6" + bool "i.MX3, i.MX5, i.MX6" select AUTO_ZRELADDR if !ZBOOT_ROM select ARM_PATCH_PHYS_VIRT help - This enables support for systems based on the Freescale i.MX3 and i.MX6 - family. - -config ARCH_MX5 - bool "i.MX50, i.MX51, i.MX53" - select AUTO_ZRELADDR - select ARM_PATCH_PHYS_VIRT - help - This enables support for machines using Freescale's i.MX50 and i.MX53 - processors. + This enables support for systems based on the Freescale i.MX3, i.MX5 + and i.MX6 family. endchoice source "arch/arm/mach-imx/Kconfig" -source "arch/arm/mach-mx5/Kconfig" endmenu -- cgit v0.10.2 From 3f07f355704bc2ae26399f17c6d5d46c9a3d78d1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 8 Nov 2011 16:40:37 +0100 Subject: ARM i.MX: Update defconfig This updates the defconfig for i.MX3, i.MX5 and i.MX6. - all these can be compiled together now, so remove mx3_defconfig and mx5_defconfig and introduce a imx_v6_v7_defconfig instead. Signed-off-by: Sascha Hauer diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig new file mode 100644 index 0000000..3a4fb2e --- /dev/null +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -0,0 +1,193 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_EXPERT=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +CONFIG_ARCH_MXC=y +CONFIG_MACH_MX31LILLY=y +CONFIG_MACH_MX31LITE=y +CONFIG_MACH_PCM037=y +CONFIG_MACH_PCM037_EET=y +CONFIG_MACH_MX31_3DS=y +CONFIG_MACH_MX31MOBOARD=y +CONFIG_MACH_QONG=y +CONFIG_MACH_ARMADILLO5X0=y +CONFIG_MACH_KZM_ARM11_01=y +CONFIG_MACH_PCM043=y +CONFIG_MACH_MX35_3DS=y +CONFIG_MACH_EUKREA_CPUIMX35=y +CONFIG_MACH_VPR200=y +CONFIG_MACH_IMX51_DT=y +CONFIG_MACH_MX51_3DS=y +CONFIG_MACH_EUKREA_CPUIMX51=y +CONFIG_MACH_EUKREA_CPUIMX51SD=y +CONFIG_MACH_MX51_EFIKAMX=y +CONFIG_MACH_MX51_EFIKASB=y +CONFIG_MACH_IMX53_DT=y +CONFIG_SOC_IMX6Q=y +CONFIG_MXC_PWM=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +# CONFIG_WIRELESS is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_CONNECTOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_PATA_IMX=y +CONFIG_NETDEVICES=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_FEC=y +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_SEEQ is not set +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_WLAN is not set +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +# CONFIG_HWMON is not set +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_MC13XXX=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_MC13892=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_MXC=y +CONFIG_USB_STORAGE=y +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_MXC=y +CONFIG_DMADEVICES=y +CONFIG_IMX_SDMA=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_CONFIGFS_FS=m +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_LZO=m +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m diff --git a/arch/arm/configs/mx3_defconfig b/arch/arm/configs/mx3_defconfig deleted file mode 100644 index cb0717f..0000000 --- a/arch/arm/configs/mx3_defconfig +++ /dev/null @@ -1,144 +0,0 @@ -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EXPERT=y -CONFIG_SLAB=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODULE_FORCE_UNLOAD=y -CONFIG_MODVERSIONS=y -# CONFIG_BLK_DEV_BSG is not set -CONFIG_ARCH_MXC=y -CONFIG_MACH_MX31ADS_WM1133_EV1=y -CONFIG_MACH_MX31LILLY=y -CONFIG_MACH_MX31LITE=y -CONFIG_MACH_PCM037=y -CONFIG_MACH_PCM037_EET=y -CONFIG_MACH_MX31_3DS=y -CONFIG_MACH_MX31MOBOARD=y -CONFIG_MACH_QONG=y -CONFIG_MACH_ARMADILLO5X0=y -CONFIG_MACH_KZM_ARM11_01=y -CONFIG_MACH_PCM043=y -CONFIG_MACH_MX35_3DS=y -CONFIG_MACH_EUKREA_CPUIMX35=y -CONFIG_MXC_IRQ_PRIOR=y -CONFIG_MXC_PWM=y -CONFIG_ARM_ERRATA_411920=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_PREEMPT=y -CONFIG_AEABI=y -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw ip=off" -CONFIG_VFP=y -CONFIG_PM_DEBUG=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set -# CONFIG_INET_LRO is not set -# CONFIG_INET_DIAG is not set -# CONFIG_IPV6 is not set -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_FW_LOADER=m -CONFIG_MTD=y -CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_CFI=y -CONFIG_MTD_PHYSMAP=y -CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_MXC=y -CONFIG_MTD_UBI=y -# CONFIG_BLK_DEV is not set -CONFIG_MISC_DEVICES=y -CONFIG_EEPROM_AT24=y -CONFIG_NETDEVICES=y -CONFIG_SMSC_PHY=y -CONFIG_NET_ETHERNET=y -CONFIG_SMSC911X=y -CONFIG_DNET=y -# CONFIG_NETDEV_1000 is not set -# CONFIG_NETDEV_10000 is not set -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_KEYBOARD_ATKBD is not set -CONFIG_KEYBOARD_IMX=y -# CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO is not set -# CONFIG_VT is not set -# CONFIG_LEGACY_PTYS is not set -CONFIG_SERIAL_8250=m -CONFIG_SERIAL_8250_EXTENDED=y -CONFIG_SERIAL_8250_SHARE_IRQ=y -CONFIG_SERIAL_IMX=y -CONFIG_SERIAL_IMX_CONSOLE=y -# CONFIG_HW_RANDOM is not set -CONFIG_I2C=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_IMX=y -CONFIG_SPI=y -CONFIG_W1=y -CONFIG_W1_MASTER_MXC=y -CONFIG_W1_SLAVE_THERM=y -# CONFIG_HWMON is not set -CONFIG_WATCHDOG=y -CONFIG_IMX2_WDT=y -CONFIG_MFD_WM8350_I2C=y -CONFIG_REGULATOR=y -CONFIG_REGULATOR_WM8350=y -CONFIG_MEDIA_SUPPORT=y -CONFIG_VIDEO_DEV=y -# CONFIG_RC_CORE is not set -# CONFIG_MEDIA_TUNER_CUSTOMISE is not set -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9M001=y -CONFIG_SOC_CAMERA_MT9M111=y -CONFIG_SOC_CAMERA_MT9T031=y -CONFIG_SOC_CAMERA_MT9V022=y -CONFIG_SOC_CAMERA_TW9910=y -CONFIG_SOC_CAMERA_OV772X=y -CONFIG_VIDEO_MX3=y -# CONFIG_RADIO_ADAPTERS is not set -CONFIG_FB=y -CONFIG_SOUND=y -CONFIG_SND=y -# CONFIG_SND_ARM is not set -# CONFIG_SND_SPI is not set -CONFIG_SND_SOC=y -CONFIG_SND_IMX_SOC=y -CONFIG_SND_MXC_SOC_WM1133_EV1=y -CONFIG_SND_SOC_PHYCORE_AC97=y -CONFIG_SND_SOC_EUKREA_TLV320=y -CONFIG_USB=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_MXC=y -CONFIG_USB_GADGET=m -CONFIG_USB_FSL_USB2=m -CONFIG_USB_G_SERIAL=m -CONFIG_USB_ULPI=y -CONFIG_MMC=y -CONFIG_MMC_MXC=y -CONFIG_RTC_CLASS=y -CONFIG_RTC_MXC=y -CONFIG_DMADEVICES=y -# CONFIG_DNOTIFY is not set -CONFIG_TMPFS=y -CONFIG_JFFS2_FS=y -CONFIG_UBIFS_FS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_NFS_V4=y -CONFIG_ROOT_NFS=y -# CONFIG_ENABLE_WARN_DEPRECATED is not set -# CONFIG_ENABLE_MUST_CHECK is not set -CONFIG_SYSCTL_SYSCALL_CHECK=y -# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/arm/configs/mx5_defconfig b/arch/arm/configs/mx5_defconfig deleted file mode 100644 index d0d8dfe..0000000 --- a/arch/arm/configs/mx5_defconfig +++ /dev/null @@ -1,184 +0,0 @@ -CONFIG_EXPERIMENTAL=y -# CONFIG_LOCALVERSION_AUTO is not set -CONFIG_KERNEL_LZO=y -CONFIG_SYSVIPC=y -CONFIG_LOG_BUF_SHIFT=18 -CONFIG_RELAY=y -CONFIG_EXPERT=y -# CONFIG_SLUB_DEBUG is not set -# CONFIG_COMPAT_BRK is not set -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_MODULE_SRCVERSION_ALL=y -# CONFIG_LBDAF is not set -# CONFIG_BLK_DEV_BSG is not set -CONFIG_ARCH_MXC=y -CONFIG_ARCH_MX5=y -CONFIG_MACH_MX51_BABBAGE=y -CONFIG_MACH_MX51_3DS=y -CONFIG_MACH_EUKREA_CPUIMX51=y -CONFIG_MACH_EUKREA_CPUIMX51SD=y -CONFIG_MACH_MX51_EFIKAMX=y -CONFIG_MACH_MX51_EFIKASB=y -CONFIG_MACH_MX53_EVK=y -CONFIG_MACH_MX53_SMD=y -CONFIG_MACH_MX53_LOCO=y -CONFIG_MACH_MX53_ARD=y -CONFIG_MXC_PWM=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_VMSPLIT_2G=y -CONFIG_PREEMPT_VOLUNTARY=y -CONFIG_AEABI=y -# CONFIG_OABI_COMPAT is not set -CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 -CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" -CONFIG_VFP=y -CONFIG_NEON=y -CONFIG_BINFMT_MISC=m -CONFIG_PM_DEBUG=y -CONFIG_PM_TEST_SUSPEND=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set -# CONFIG_INET_LRO is not set -# CONFIG_IPV6 is not set -# CONFIG_WIRELESS is not set -CONFIG_DEVTMPFS=y -CONFIG_DEVTMPFS_MOUNT=y -# CONFIG_STANDALONE is not set -CONFIG_CONNECTOR=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=65536 -# CONFIG_SCSI_PROC_FS is not set -CONFIG_BLK_DEV_SD=y -CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_CONSTANTS=y -CONFIG_SCSI_LOGGING=y -CONFIG_SCSI_SCAN_ASYNC=y -# CONFIG_SCSI_LOWLEVEL is not set -CONFIG_ATA=y -CONFIG_PATA_IMX=y -CONFIG_NETDEVICES=y -CONFIG_MII=m -CONFIG_MARVELL_PHY=y -CONFIG_DAVICOM_PHY=y -CONFIG_QSEMI_PHY=y -CONFIG_LXT_PHY=y -CONFIG_CICADA_PHY=y -CONFIG_VITESSE_PHY=y -CONFIG_SMSC_PHY=y -CONFIG_BROADCOM_PHY=y -CONFIG_ICPLUS_PHY=y -CONFIG_REALTEK_PHY=y -CONFIG_NATIONAL_PHY=y -CONFIG_STE10XP=y -CONFIG_LSI_ET1011C_PHY=y -CONFIG_MICREL_PHY=y -CONFIG_NET_ETHERNET=y -# CONFIG_NETDEV_1000 is not set -# CONFIG_NETDEV_10000 is not set -# CONFIG_WLAN is not set -# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_EVBUG=m -CONFIG_KEYBOARD_GPIO=y -CONFIG_MOUSE_PS2=m -CONFIG_MOUSE_PS2_ELANTECH=y -CONFIG_INPUT_MISC=y -CONFIG_INPUT_MMA8450=y -CONFIG_SERIO_SERPORT=m -CONFIG_VT_HW_CONSOLE_BINDING=y -# CONFIG_LEGACY_PTYS is not set -# CONFIG_DEVKMEM is not set -CONFIG_SERIAL_IMX=y -CONFIG_SERIAL_IMX_CONSOLE=y -CONFIG_HW_RANDOM=y -CONFIG_I2C=y -# CONFIG_I2C_COMPAT is not set -CONFIG_I2C_CHARDEV=y -# CONFIG_I2C_HELPER_AUTO is not set -CONFIG_I2C_ALGOBIT=m -CONFIG_I2C_ALGOPCF=m -CONFIG_I2C_ALGOPCA=m -CONFIG_I2C_IMX=y -CONFIG_SPI=y -CONFIG_SPI_IMX=y -CONFIG_GPIO_SYSFS=y -# CONFIG_HWMON is not set -CONFIG_WATCHDOG=y -CONFIG_IMX2_WDT=y -CONFIG_MFD_MC13XXX=y -CONFIG_REGULATOR=y -CONFIG_REGULATOR_MC13892=y -CONFIG_USB=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_MXC=y -CONFIG_USB_STORAGE=y -CONFIG_MMC=y -CONFIG_MMC_BLOCK=m -CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_PLTFM=y -CONFIG_MMC_SDHCI_ESDHC_IMX=y -CONFIG_NEW_LEDS=y -CONFIG_LEDS_CLASS=y -CONFIG_RTC_CLASS=y -CONFIG_RTC_INTF_DEV_UIE_EMUL=y -CONFIG_RTC_MXC=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT2_FS_SECURITY=y -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_EXT3_FS_SECURITY=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_POSIX_ACL=y -CONFIG_EXT4_FS_SECURITY=y -CONFIG_QUOTA=y -CONFIG_QUOTA_NETLINK_INTERFACE=y -# CONFIG_PRINT_QUOTA_WARNING is not set -CONFIG_AUTOFS4_FS=y -CONFIG_FUSE_FS=y -CONFIG_ISO9660_FS=m -CONFIG_JOLIET=y -CONFIG_ZISOFS=y -CONFIG_UDF_FS=m -CONFIG_MSDOS_FS=m -CONFIG_VFAT_FS=y -CONFIG_TMPFS=y -CONFIG_CONFIGFS_FS=m -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_NFS_V3_ACL=y -CONFIG_NFS_V4=y -CONFIG_ROOT_NFS=y -CONFIG_NLS_DEFAULT="cp437" -CONFIG_NLS_CODEPAGE_437=y -CONFIG_NLS_ASCII=y -CONFIG_NLS_ISO8859_1=y -CONFIG_NLS_ISO8859_15=m -CONFIG_NLS_UTF8=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_FS=y -# CONFIG_SCHED_DEBUG is not set -# CONFIG_DEBUG_BUGVERBOSE is not set -# CONFIG_FTRACE is not set -# CONFIG_ARM_UNWIND is not set -CONFIG_SECURITYFS=y -CONFIG_CRYPTO_DEFLATE=m -CONFIG_CRYPTO_LZO=m -# CONFIG_CRYPTO_ANSI_CPRNG is not set -# CONFIG_CRYPTO_HW is not set -CONFIG_CRC_CCITT=m -CONFIG_CRC_T10DIF=y -CONFIG_CRC7=m -CONFIG_LIBCRC32C=m -- cgit v0.10.2 From dbb004294a934d030a9d716692a1dd6f677fc90e Mon Sep 17 00:00:00 2001 From: Ciaran McCormick Date: Mon, 31 Oct 2011 19:29:26 +0000 Subject: dma: fix spacing for method declaration, coding style issue in iop-adma.c Signed-off-by: Ciaran McCormick Signed-off-by: Vinod Koul diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index e03f811..686dc76 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -1746,12 +1746,12 @@ static struct platform_driver iop_adma_driver = { }, }; -static int __init iop_adma_init (void) +static int __init iop_adma_init(void) { return platform_driver_register(&iop_adma_driver); } -static void __exit iop_adma_exit (void) +static void __exit iop_adma_exit(void) { platform_driver_unregister(&iop_adma_driver); return; -- cgit v0.10.2 From 8e2d41f8c85613a3739e8b2600ac2c66c08422f3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 24 Aug 2011 08:41:09 +0200 Subject: dma i.MX: remove individual SOC dependency The individual SoC dependency in Kconfig hardly scales anymore. Instead of having such a fine grained dependency just depend on ARCH_MXC and risk that the uninformed user has to look in the help text to figure out which driver is the correct one. Signed-off-by: Sascha Hauer Cc: Vinod Koul Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index ab8f469..e514c45 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -124,7 +124,7 @@ config MV_XOR config MX3_IPU bool "MX3x Image Processing Unit support" - depends on ARCH_MX3 + depends on ARCH_MXC select DMA_ENGINE default y help @@ -216,11 +216,11 @@ config PCH_DMA config IMX_SDMA tristate "i.MX SDMA support" - depends on ARCH_MX25 || ARCH_MX3 || ARCH_MX5 + depends on ARCH_MXC select DMA_ENGINE help Support the i.MX SDMA engine. This engine is integrated into - Freescale i.MX25/31/35/51 chips. + Freescale i.MX25/31/35/51/53 chips. config IMX_DMA tristate "i.MX DMA support" -- cgit v0.10.2 From e79e72be298dc4726a641686032b8296c2fb473e Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 17 Nov 2011 16:14:22 +0900 Subject: pch_dma: Change company name OKI SEMICONDUCTOR to LAPIS Semiconductor On October 1 in 2011, OKI SEMICONDUCTOR Co., Ltd. changed the company name in to LAPIS Semiconductor Co., Ltd. Signed-off-by: Tomoya MORINAGA Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e514c45..606592e 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -201,13 +201,13 @@ config PL330_DMA platform_data for a dma-pl330 device. config PCH_DMA - tristate "Intel EG20T PCH / OKI Semi IOH(ML7213/ML7223) DMA support" + tristate "Intel EG20T PCH / LAPIS Semi IOH(ML7213/ML7223) DMA support" depends on PCI && X86 select DMA_ENGINE help Enable support for Intel EG20T PCH DMA engine. - This driver also can be used for OKI SEMICONDUCTOR IOH(Input/ + This driver also can be used for LAPIS Semiconductor IOH(Input/ Output Hub), ML7213 and ML7223. ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is for MP(Media Phone) use. diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index a6d0e3d..a9b8a79 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -1,7 +1,7 @@ /* * Topcliff PCH DMA controller driver * Copyright (c) 2010 Intel Corporation - * Copyright (C) 2011 OKI SEMICONDUCTOR CO., LTD. + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1057,7 +1057,7 @@ static void __exit pch_dma_exit(void) module_init(pch_dma_init); module_exit(pch_dma_exit); -MODULE_DESCRIPTION("Intel EG20T PCH / OKI SEMICONDUCTOR ML7213 IOH " +MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semiconductor ML7213/ML7223 IOH " "DMA controller driver"); MODULE_AUTHOR("Yong Wang "); MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From ca7fe2db892dcf91b2c72ee352eda4ff867903a7 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 17 Nov 2011 16:14:23 +0900 Subject: pch_dma: Support new device LAPIS Semiconductor ML7831 IOH ML7831 is companion chip for Intel Atom E6xx series. Signed-off-by: Tomoya MORINAGA Signed-off-by: Vinod Koul diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 606592e..7ec0d6c 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -201,18 +201,18 @@ config PL330_DMA platform_data for a dma-pl330 device. config PCH_DMA - tristate "Intel EG20T PCH / LAPIS Semi IOH(ML7213/ML7223) DMA support" + tristate "Intel EG20T PCH / LAPIS Semicon IOH(ML7213/ML7223/ML7831) DMA" depends on PCI && X86 select DMA_ENGINE help Enable support for Intel EG20T PCH DMA engine. This driver also can be used for LAPIS Semiconductor IOH(Input/ - Output Hub), ML7213 and ML7223. - ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is - for MP(Media Phone) use. - ML7213/ML7223 is companion chip for Intel Atom E6xx series. - ML7213/ML7223 is completely compatible for Intel EG20T PCH. + Output Hub), ML7213, ML7223 and ML7831. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is + for MP(Media Phone) use and ML7831 IOH is for general purpose use. + ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. config IMX_SDMA tristate "i.MX SDMA support" diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index a9b8a79..60aca37 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -1018,6 +1018,8 @@ static void __devexit pch_dma_remove(struct pci_dev *pdev) #define PCI_DEVICE_ID_ML7223_DMA2_4CH 0x800E #define PCI_DEVICE_ID_ML7223_DMA3_4CH 0x8017 #define PCI_DEVICE_ID_ML7223_DMA4_4CH 0x803B +#define PCI_DEVICE_ID_ML7831_DMA1_8CH 0x8810 +#define PCI_DEVICE_ID_ML7831_DMA2_4CH 0x8815 DEFINE_PCI_DEVICE_TABLE(pch_dma_id_table) = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_EG20T_PCH_DMA_8CH), 8 }, @@ -1030,6 +1032,8 @@ DEFINE_PCI_DEVICE_TABLE(pch_dma_id_table) = { { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA2_4CH), 4}, /* Video SPI */ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA3_4CH), 4}, /* Security */ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_DMA4_4CH), 4}, /* FPGA */ + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_DMA1_8CH), 8}, /* UART */ + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_DMA2_4CH), 4}, /* SPI */ { 0, }, }; @@ -1057,7 +1061,7 @@ static void __exit pch_dma_exit(void) module_init(pch_dma_init); module_exit(pch_dma_exit); -MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semiconductor ML7213/ML7223 IOH " +MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH " "DMA controller driver"); MODULE_AUTHOR("Yong Wang "); MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 8341544c9d95610f86b560333c7b22dd1096ca4f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 21:50:31 +0530 Subject: USB-musb: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves usb/musb driver to use new enum Signed-off-by: Vinod Koul Cc: Felipe Balbi Cc: Greg Kroah-Hartman Acked-by: Felipe Balbi Acked-by: Mian Yousaf Kaukab diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index ef4333f..f8fd0dd 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -112,7 +112,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; struct dma_chan *dma_chan = ux500_channel->dma_chan; struct dma_async_tx_descriptor *dma_desc; - enum dma_data_direction direction; + enum dma_transfer_direction direction; struct scatterlist sg; struct dma_slave_config slave_conf; enum dma_slave_buswidth addr_width; @@ -132,7 +132,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, sg_dma_address(&sg) = dma_addr; sg_dma_len(&sg) = len; - direction = ux500_channel->is_tx ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + direction = ux500_channel->is_tx ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; addr_width = (len & 0x3) ? DMA_SLAVE_BUSWIDTH_1_BYTE : DMA_SLAVE_BUSWIDTH_4_BYTES; -- cgit v0.10.2 From 55ba4e5ed4ac57b60fe56acfd324f6a87123cc34 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Oct 2011 21:57:58 +0530 Subject: USB-renesas: move to dma_transfer_direction fixup usage of dma direction by introducing dma_transfer_direction, this patch moves usb/renesas driver to use new enum Signed-off-by: Vinod Koul Cc: Felipe Balbi Cc: Greg Kroah-Hartman Acked-by: Felipe Balbi Acked-by: Kuninori Morimoto diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index a34430f..051c9a9 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -591,10 +591,10 @@ static void usbhsf_dma_prepare_tasklet(unsigned long data) struct dma_async_tx_descriptor *desc; struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); struct device *dev = usbhs_priv_to_dev(priv); - enum dma_data_direction dir; + enum dma_transfer_direction dir; dma_cookie_t cookie; - dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; sg_init_table(&sg, 1); sg_set_page(&sg, virt_to_page(pkt->dma), -- cgit v0.10.2 From b14dab792dee3245b628e046d80a7fad5573fea6 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 13 Oct 2011 12:33:30 +0530 Subject: DMAEngine: Define interleaved transfer request api Define a new api that could be used for doing fancy data transfers like interleaved to contiguous copy and vice-versa. Traditional SG_list based transfers tend to be very inefficient in such cases as where the interleave and chunk are only a few bytes, which call for a very condensed api to convey pattern of the transfer. This api supports all 4 variants of scatter-gather and contiguous transfer. Of course, neither can this api help transfers that don't lend to DMA by nature, i.e, scattered tiny read/writes with no periodic pattern. Also since now we support SLAVE channels that might not provide device_prep_slave_sg callback but device_prep_interleaved_dma, remove the BUG_ON check. Signed-off-by: Jassi Brar Acked-by: Barry Song [renamed dmaxfer_template to dma_interleaved_template did fixup after the enum dma_transfer_merge] Signed-off-by: Vinod Koul diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt index 94b7e0f..bbe6cb3 100644 --- a/Documentation/dmaengine.txt +++ b/Documentation/dmaengine.txt @@ -75,6 +75,10 @@ The slave DMA usage consists of following steps: slave_sg - DMA a list of scatter gather buffers from/to a peripheral dma_cyclic - Perform a cyclic DMA operation from/to a peripheral till the operation is explicitly stopped. + interleaved_dma - This is common to Slave as well as M2M clients. For slave + address of devices' fifo could be already known to the driver. + Various types of operations could be expressed by setting + appropriate values to the 'dma_interleaved_template' members. A non-NULL return of this transfer API represents a "descriptor" for the given transaction. @@ -89,6 +93,10 @@ The slave DMA usage consists of following steps: struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_data_direction direction); + struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags); + The peripheral driver is expected to have mapped the scatterlist for the DMA operation prior to calling device_prep_slave_sg, and must keep the scatterlist mapped until the DMA operation has completed. diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index b48967b..a6c6051 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -693,12 +693,12 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_interrupt); BUG_ON(dma_has_cap(DMA_SG, device->cap_mask) && !device->device_prep_dma_sg); - BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && - !device->device_prep_slave_sg); BUG_ON(dma_has_cap(DMA_CYCLIC, device->cap_mask) && !device->device_prep_dma_cyclic); BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && !device->device_control); + BUG_ON(dma_has_cap(DMA_INTERLEAVE, device->cap_mask) && + !device->device_prep_interleaved_dma); BUG_ON(!device->device_alloc_chan_resources); BUG_ON(!device->device_free_chan_resources); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index a865b3a..5532bb8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -71,10 +71,10 @@ enum dma_transaction_type { DMA_ASYNC_TX, DMA_SLAVE, DMA_CYCLIC, -}; - + DMA_INTERLEAVE, /* last transaction type for creation of the capabilities mask */ -#define DMA_TX_TYPE_END (DMA_CYCLIC + 1) + DMA_TX_TYPE_END, +}; /** * enum dma_transfer_direction - dma transfer mode and direction indicator @@ -91,6 +91,74 @@ enum dma_transfer_direction { }; /** + * Interleaved Transfer Request + * ---------------------------- + * A chunk is collection of contiguous bytes to be transfered. + * The gap(in bytes) between two chunks is called inter-chunk-gap(ICG). + * ICGs may or maynot change between chunks. + * A FRAME is the smallest series of contiguous {chunk,icg} pairs, + * that when repeated an integral number of times, specifies the transfer. + * A transfer template is specification of a Frame, the number of times + * it is to be repeated and other per-transfer attributes. + * + * Practically, a client driver would have ready a template for each + * type of transfer it is going to need during its lifetime and + * set only 'src_start' and 'dst_start' before submitting the requests. + * + * + * | Frame-1 | Frame-2 | ~ | Frame-'numf' | + * |====....==.===...=...|====....==.===...=...| ~ |====....==.===...=...| + * + * == Chunk size + * ... ICG + */ + +/** + * struct data_chunk - Element of scatter-gather list that makes a frame. + * @size: Number of bytes to read from source. + * size_dst := fn(op, size_src), so doesn't mean much for destination. + * @icg: Number of bytes to jump after last src/dst address of this + * chunk and before first src/dst address for next chunk. + * Ignored for dst(assumed 0), if dst_inc is true and dst_sgl is false. + * Ignored for src(assumed 0), if src_inc is true and src_sgl is false. + */ +struct data_chunk { + size_t size; + size_t icg; +}; + +/** + * struct dma_interleaved_template - Template to convey DMAC the transfer pattern + * and attributes. + * @src_start: Bus address of source for the first chunk. + * @dst_start: Bus address of destination for the first chunk. + * @dir: Specifies the type of Source and Destination. + * @src_inc: If the source address increments after reading from it. + * @dst_inc: If the destination address increments after writing to it. + * @src_sgl: If the 'icg' of sgl[] applies to Source (scattered read). + * Otherwise, source is read contiguously (icg ignored). + * Ignored if src_inc is false. + * @dst_sgl: If the 'icg' of sgl[] applies to Destination (scattered write). + * Otherwise, destination is filled contiguously (icg ignored). + * Ignored if dst_inc is false. + * @numf: Number of frames in this template. + * @frame_size: Number of chunks in a frame i.e, size of sgl[]. + * @sgl: Array of {chunk,icg} pairs that make up a frame. + */ +struct dma_interleaved_template { + dma_addr_t src_start; + dma_addr_t dst_start; + enum dma_transfer_direction dir; + bool src_inc; + bool dst_inc; + bool src_sgl; + bool dst_sgl; + size_t numf; + size_t frame_size; + struct data_chunk sgl[0]; +}; + +/** * enum dma_ctrl_flags - DMA flags to augment operation preparation, * control completion, and communicate status. * @DMA_PREP_INTERRUPT - trigger an interrupt (callback) upon completion of @@ -445,6 +513,7 @@ struct dma_tx_state { * @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio. * The function takes a buffer of size buf_len. The callback function will * be called after period_len bytes have been transferred. + * @device_prep_interleaved_dma: Transfer expression in a generic way. * @device_control: manipulate all pending operations on a channel, returns * zero or error code * @device_tx_status: poll for transaction completion, the optional @@ -509,6 +578,9 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction); + struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags); int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg); -- cgit v0.10.2 From ca21a146a45a179a2a7bc86d938a2fbf571a7510 Mon Sep 17 00:00:00 2001 From: Rongjun Ying Date: Thu, 27 Oct 2011 19:22:39 -0700 Subject: dmaengine: add CSR SiRFprimaII DMAC driver Cc: Jassi Brar Cc: Arnd Bergmann Cc: Linus Walleij Signed-off-by: Rongjun Ying Signed-off-by: Barry Song [fixed direction enums and cyclic api based on changes already merged] Signed-off-by: Vinod Koul diff --git a/MAINTAINERS b/MAINTAINERS index 4808256..1b141d7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -749,6 +749,7 @@ M: Barry Song L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-prima2/ +F: drivers/dma/sirf-dma* ARM/EBSA110 MACHINE SUPPORT M: Russell King diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 7ec0d6c..f1a2749 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -187,6 +187,13 @@ config TIMB_DMA help Enable support for the Timberdale FPGA DMA engine. +config SIRF_DMA + tristate "CSR SiRFprimaII DMA support" + depends on ARCH_PRIMA2 + select DMA_ENGINE + help + Enable support for the CSR SiRFprimaII DMA engine. + config ARCH_HAS_ASYNC_TX_FIND_CHANNEL bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 30cf3b1..009a222 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_IMX_SDMA) += imx-sdma.o obj-$(CONFIG_IMX_DMA) += imx-dma.o obj-$(CONFIG_MXS_DMA) += mxs-dma.o obj-$(CONFIG_TIMB_DMA) += timb_dma.o +obj-$(CONFIG_SIRF_DMA) += sirf-dma.o obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c new file mode 100644 index 0000000..55ec679 --- /dev/null +++ b/drivers/dma/sirf-dma.c @@ -0,0 +1,717 @@ +/* + * DMA controller driver for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIRFSOC_DMA_DESCRIPTORS 16 +#define SIRFSOC_DMA_CHANNELS 16 + +#define SIRFSOC_DMA_CH_ADDR 0x00 +#define SIRFSOC_DMA_CH_XLEN 0x04 +#define SIRFSOC_DMA_CH_YLEN 0x08 +#define SIRFSOC_DMA_CH_CTRL 0x0C + +#define SIRFSOC_DMA_WIDTH_0 0x100 +#define SIRFSOC_DMA_CH_VALID 0x140 +#define SIRFSOC_DMA_CH_INT 0x144 +#define SIRFSOC_DMA_INT_EN 0x148 +#define SIRFSOC_DMA_CH_LOOP_CTRL 0x150 + +#define SIRFSOC_DMA_MODE_CTRL_BIT 4 +#define SIRFSOC_DMA_DIR_CTRL_BIT 5 + +/* xlen and dma_width register is in 4 bytes boundary */ +#define SIRFSOC_DMA_WORD_LEN 4 + +struct sirfsoc_dma_desc { + struct dma_async_tx_descriptor desc; + struct list_head node; + + /* SiRFprimaII 2D-DMA parameters */ + + int xlen; /* DMA xlen */ + int ylen; /* DMA ylen */ + int width; /* DMA width */ + int dir; + bool cyclic; /* is loop DMA? */ + u32 addr; /* DMA buffer address */ +}; + +struct sirfsoc_dma_chan { + struct dma_chan chan; + struct list_head free; + struct list_head prepared; + struct list_head queued; + struct list_head active; + struct list_head completed; + dma_cookie_t completed_cookie; + unsigned long happened_cyclic; + unsigned long completed_cyclic; + + /* Lock for this structure */ + spinlock_t lock; + + int mode; +}; + +struct sirfsoc_dma { + struct dma_device dma; + struct tasklet_struct tasklet; + struct sirfsoc_dma_chan channels[SIRFSOC_DMA_CHANNELS]; + void __iomem *base; + int irq; +}; + +#define DRV_NAME "sirfsoc_dma" + +/* Convert struct dma_chan to struct sirfsoc_dma_chan */ +static inline +struct sirfsoc_dma_chan *dma_chan_to_sirfsoc_dma_chan(struct dma_chan *c) +{ + return container_of(c, struct sirfsoc_dma_chan, chan); +} + +/* Convert struct dma_chan to struct sirfsoc_dma */ +static inline struct sirfsoc_dma *dma_chan_to_sirfsoc_dma(struct dma_chan *c) +{ + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(c); + return container_of(schan, struct sirfsoc_dma, channels[c->chan_id]); +} + +/* Execute all queued DMA descriptors */ +static void sirfsoc_dma_execute(struct sirfsoc_dma_chan *schan) +{ + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); + int cid = schan->chan.chan_id; + struct sirfsoc_dma_desc *sdesc = NULL; + + /* + * lock has been held by functions calling this, so we don't hold + * lock again + */ + + sdesc = list_first_entry(&schan->queued, struct sirfsoc_dma_desc, + node); + /* Move the first queued descriptor to active list */ + list_move_tail(&schan->queued, &schan->active); + + /* Start the DMA transfer */ + writel_relaxed(sdesc->width, sdma->base + SIRFSOC_DMA_WIDTH_0 + + cid * 4); + writel_relaxed(cid | (schan->mode << SIRFSOC_DMA_MODE_CTRL_BIT) | + (sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT), + sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_CTRL); + writel_relaxed(sdesc->xlen, sdma->base + cid * 0x10 + + SIRFSOC_DMA_CH_XLEN); + writel_relaxed(sdesc->ylen, sdma->base + cid * 0x10 + + SIRFSOC_DMA_CH_YLEN); + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) | + (1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); + + /* + * writel has an implict memory write barrier to make sure data is + * flushed into memory before starting DMA + */ + writel(sdesc->addr >> 2, sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR); + + if (sdesc->cyclic) { + writel((1 << cid) | 1 << (cid + 16) | + readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + schan->happened_cyclic = schan->completed_cyclic = 0; + } +} + +/* Interrupt handler */ +static irqreturn_t sirfsoc_dma_irq(int irq, void *data) +{ + struct sirfsoc_dma *sdma = data; + struct sirfsoc_dma_chan *schan; + struct sirfsoc_dma_desc *sdesc = NULL; + u32 is; + int ch; + + is = readl(sdma->base + SIRFSOC_DMA_CH_INT); + while ((ch = fls(is) - 1) >= 0) { + is &= ~(1 << ch); + writel_relaxed(1 << ch, sdma->base + SIRFSOC_DMA_CH_INT); + schan = &sdma->channels[ch]; + + spin_lock(&schan->lock); + + sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc, + node); + if (!sdesc->cyclic) { + /* Execute queued descriptors */ + list_splice_tail_init(&schan->active, &schan->completed); + if (!list_empty(&schan->queued)) + sirfsoc_dma_execute(schan); + } else + schan->happened_cyclic++; + + spin_unlock(&schan->lock); + } + + /* Schedule tasklet */ + tasklet_schedule(&sdma->tasklet); + + return IRQ_HANDLED; +} + +/* process completed descriptors */ +static void sirfsoc_dma_process_completed(struct sirfsoc_dma *sdma) +{ + dma_cookie_t last_cookie = 0; + struct sirfsoc_dma_chan *schan; + struct sirfsoc_dma_desc *sdesc; + struct dma_async_tx_descriptor *desc; + unsigned long flags; + unsigned long happened_cyclic; + LIST_HEAD(list); + int i; + + for (i = 0; i < sdma->dma.chancnt; i++) { + schan = &sdma->channels[i]; + + /* Get all completed descriptors */ + spin_lock_irqsave(&schan->lock, flags); + if (!list_empty(&schan->completed)) { + list_splice_tail_init(&schan->completed, &list); + spin_unlock_irqrestore(&schan->lock, flags); + + /* Execute callbacks and run dependencies */ + list_for_each_entry(sdesc, &list, node) { + desc = &sdesc->desc; + + if (desc->callback) + desc->callback(desc->callback_param); + + last_cookie = desc->cookie; + dma_run_dependencies(desc); + } + + /* Free descriptors */ + spin_lock_irqsave(&schan->lock, flags); + list_splice_tail_init(&list, &schan->free); + schan->completed_cookie = last_cookie; + spin_unlock_irqrestore(&schan->lock, flags); + } else { + /* for cyclic channel, desc is always in active list */ + sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc, + node); + + if (!sdesc || (sdesc && !sdesc->cyclic)) { + /* without active cyclic DMA */ + spin_unlock_irqrestore(&schan->lock, flags); + continue; + } + + /* cyclic DMA */ + happened_cyclic = schan->happened_cyclic; + spin_unlock_irqrestore(&schan->lock, flags); + + desc = &sdesc->desc; + while (happened_cyclic != schan->completed_cyclic) { + if (desc->callback) + desc->callback(desc->callback_param); + schan->completed_cyclic++; + } + } + } +} + +/* DMA Tasklet */ +static void sirfsoc_dma_tasklet(unsigned long data) +{ + struct sirfsoc_dma *sdma = (void *)data; + + sirfsoc_dma_process_completed(sdma); +} + +/* Submit descriptor to hardware */ +static dma_cookie_t sirfsoc_dma_tx_submit(struct dma_async_tx_descriptor *txd) +{ + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(txd->chan); + struct sirfsoc_dma_desc *sdesc; + unsigned long flags; + dma_cookie_t cookie; + + sdesc = container_of(txd, struct sirfsoc_dma_desc, desc); + + spin_lock_irqsave(&schan->lock, flags); + + /* Move descriptor to queue */ + list_move_tail(&sdesc->node, &schan->queued); + + /* Update cookie */ + cookie = schan->chan.cookie + 1; + if (cookie <= 0) + cookie = 1; + + schan->chan.cookie = cookie; + sdesc->desc.cookie = cookie; + + spin_unlock_irqrestore(&schan->lock, flags); + + return cookie; +} + +static int sirfsoc_dma_slave_config(struct sirfsoc_dma_chan *schan, + struct dma_slave_config *config) +{ + unsigned long flags; + + if ((config->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || + (config->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)) + return -EINVAL; + + spin_lock_irqsave(&schan->lock, flags); + schan->mode = (config->src_maxburst == 4 ? 1 : 0); + spin_unlock_irqrestore(&schan->lock, flags); + + return 0; +} + +static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) +{ + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan); + int cid = schan->chan.chan_id; + unsigned long flags; + + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & + ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); + + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) + & ~((1 << cid) | 1 << (cid + 16)), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + + spin_lock_irqsave(&schan->lock, flags); + list_splice_tail_init(&schan->active, &schan->free); + list_splice_tail_init(&schan->queued, &schan->free); + spin_unlock_irqrestore(&schan->lock, flags); + + return 0; +} + +static int sirfsoc_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct dma_slave_config *config; + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + + switch (cmd) { + case DMA_TERMINATE_ALL: + return sirfsoc_dma_terminate_all(schan); + case DMA_SLAVE_CONFIG: + config = (struct dma_slave_config *)arg; + return sirfsoc_dma_slave_config(schan, config); + + default: + break; + } + + return -ENOSYS; +} + +/* Alloc channel resources */ +static int sirfsoc_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan); + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + struct sirfsoc_dma_desc *sdesc; + unsigned long flags; + LIST_HEAD(descs); + int i; + + /* Alloc descriptors for this channel */ + for (i = 0; i < SIRFSOC_DMA_DESCRIPTORS; i++) { + sdesc = kzalloc(sizeof(*sdesc), GFP_KERNEL); + if (!sdesc) { + dev_notice(sdma->dma.dev, "Memory allocation error. " + "Allocated only %u descriptors\n", i); + break; + } + + dma_async_tx_descriptor_init(&sdesc->desc, chan); + sdesc->desc.flags = DMA_CTRL_ACK; + sdesc->desc.tx_submit = sirfsoc_dma_tx_submit; + + list_add_tail(&sdesc->node, &descs); + } + + /* Return error only if no descriptors were allocated */ + if (i == 0) + return -ENOMEM; + + spin_lock_irqsave(&schan->lock, flags); + + list_splice_tail_init(&descs, &schan->free); + spin_unlock_irqrestore(&schan->lock, flags); + + return i; +} + +/* Free channel resources */ +static void sirfsoc_dma_free_chan_resources(struct dma_chan *chan) +{ + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + struct sirfsoc_dma_desc *sdesc, *tmp; + unsigned long flags; + LIST_HEAD(descs); + + spin_lock_irqsave(&schan->lock, flags); + + /* Channel must be idle */ + BUG_ON(!list_empty(&schan->prepared)); + BUG_ON(!list_empty(&schan->queued)); + BUG_ON(!list_empty(&schan->active)); + BUG_ON(!list_empty(&schan->completed)); + + /* Move data */ + list_splice_tail_init(&schan->free, &descs); + + spin_unlock_irqrestore(&schan->lock, flags); + + /* Free descriptors */ + list_for_each_entry_safe(sdesc, tmp, &descs, node) + kfree(sdesc); +} + +/* Send pending descriptor to hardware */ +static void sirfsoc_dma_issue_pending(struct dma_chan *chan) +{ + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&schan->lock, flags); + + if (list_empty(&schan->active) && !list_empty(&schan->queued)) + sirfsoc_dma_execute(schan); + + spin_unlock_irqrestore(&schan->lock, flags); +} + +/* Check request completion status */ +static enum dma_status +sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + unsigned long flags; + dma_cookie_t last_used; + dma_cookie_t last_complete; + + spin_lock_irqsave(&schan->lock, flags); + last_used = schan->chan.cookie; + last_complete = schan->completed_cookie; + spin_unlock_irqrestore(&schan->lock, flags); + + dma_set_tx_state(txstate, last_complete, last_used, 0); + return dma_async_is_complete(cookie, last_complete, last_used); +} + +static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan); + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + struct sirfsoc_dma_desc *sdesc = NULL; + unsigned long iflags; + int ret; + + if ((xt->dir != DMA_MEM_TO_DEV) || (xt->dir != DMA_DEV_TO_MEM)) { + ret = -EINVAL; + goto err_dir; + } + + /* Get free descriptor */ + spin_lock_irqsave(&schan->lock, iflags); + if (!list_empty(&schan->free)) { + sdesc = list_first_entry(&schan->free, struct sirfsoc_dma_desc, + node); + list_del(&sdesc->node); + } + spin_unlock_irqrestore(&schan->lock, iflags); + + if (!sdesc) { + /* try to free completed descriptors */ + sirfsoc_dma_process_completed(sdma); + ret = 0; + goto no_desc; + } + + /* Place descriptor in prepared list */ + spin_lock_irqsave(&schan->lock, iflags); + + /* + * Number of chunks in a frame can only be 1 for prima2 + * and ylen (number of frame - 1) must be at least 0 + */ + if ((xt->frame_size == 1) && (xt->numf > 0)) { + sdesc->cyclic = 0; + sdesc->xlen = xt->sgl[0].size / SIRFSOC_DMA_WORD_LEN; + sdesc->width = (xt->sgl[0].size + xt->sgl[0].icg) / + SIRFSOC_DMA_WORD_LEN; + sdesc->ylen = xt->numf - 1; + if (xt->dir == DMA_MEM_TO_DEV) { + sdesc->addr = xt->src_start; + sdesc->dir = 1; + } else { + sdesc->addr = xt->dst_start; + sdesc->dir = 0; + } + + list_add_tail(&sdesc->node, &schan->prepared); + } else { + pr_err("sirfsoc DMA Invalid xfer\n"); + ret = -EINVAL; + goto err_xfer; + } + spin_unlock_irqrestore(&schan->lock, iflags); + + return &sdesc->desc; +err_xfer: + spin_unlock_irqrestore(&schan->lock, iflags); +no_desc: +err_dir: + return ERR_PTR(ret); +} + +static struct dma_async_tx_descriptor * +sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr, + size_t buf_len, size_t period_len, + enum dma_transfer_direction direction) +{ + struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); + struct sirfsoc_dma_desc *sdesc = NULL; + unsigned long iflags; + + /* + * we only support cycle transfer with 2 period + * If the X-length is set to 0, it would be the loop mode. + * The DMA address keeps increasing until reaching the end of a loop + * area whose size is defined by (DMA_WIDTH x (Y_LENGTH + 1)). Then + * the DMA address goes back to the beginning of this area. + * In loop mode, the DMA data region is divided into two parts, BUFA + * and BUFB. DMA controller generates interrupts twice in each loop: + * when the DMA address reaches the end of BUFA or the end of the + * BUFB + */ + if (buf_len != 2 * period_len) + return ERR_PTR(-EINVAL); + + /* Get free descriptor */ + spin_lock_irqsave(&schan->lock, iflags); + if (!list_empty(&schan->free)) { + sdesc = list_first_entry(&schan->free, struct sirfsoc_dma_desc, + node); + list_del(&sdesc->node); + } + spin_unlock_irqrestore(&schan->lock, iflags); + + if (!sdesc) + return 0; + + /* Place descriptor in prepared list */ + spin_lock_irqsave(&schan->lock, iflags); + sdesc->addr = addr; + sdesc->cyclic = 1; + sdesc->xlen = 0; + sdesc->ylen = buf_len / SIRFSOC_DMA_WORD_LEN - 1; + sdesc->width = 1; + list_add_tail(&sdesc->node, &schan->prepared); + spin_unlock_irqrestore(&schan->lock, iflags); + + return &sdesc->desc; +} + +/* + * The DMA controller consists of 16 independent DMA channels. + * Each channel is allocated to a different function + */ +bool sirfsoc_dma_filter_id(struct dma_chan *chan, void *chan_id) +{ + unsigned int ch_nr = (unsigned int) chan_id; + + if (ch_nr == chan->chan_id + + chan->device->dev_id * SIRFSOC_DMA_CHANNELS) + return true; + + return false; +} +EXPORT_SYMBOL(sirfsoc_dma_filter_id); + +static int __devinit sirfsoc_dma_probe(struct platform_device *op) +{ + struct device_node *dn = op->dev.of_node; + struct device *dev = &op->dev; + struct dma_device *dma; + struct sirfsoc_dma *sdma; + struct sirfsoc_dma_chan *schan; + struct resource res; + ulong regs_start, regs_size; + u32 id; + int ret, i; + + sdma = devm_kzalloc(dev, sizeof(*sdma), GFP_KERNEL); + if (!sdma) { + dev_err(dev, "Memory exhausted!\n"); + return -ENOMEM; + } + + if (of_property_read_u32(dn, "cell-index", &id)) { + dev_err(dev, "Fail to get DMAC index\n"); + ret = -ENODEV; + goto free_mem; + } + + sdma->irq = irq_of_parse_and_map(dn, 0); + if (sdma->irq == NO_IRQ) { + dev_err(dev, "Error mapping IRQ!\n"); + ret = -EINVAL; + goto free_mem; + } + + ret = of_address_to_resource(dn, 0, &res); + if (ret) { + dev_err(dev, "Error parsing memory region!\n"); + goto free_mem; + } + + regs_start = res.start; + regs_size = resource_size(&res); + + sdma->base = devm_ioremap(dev, regs_start, regs_size); + if (!sdma->base) { + dev_err(dev, "Error mapping memory region!\n"); + ret = -ENOMEM; + goto irq_dispose; + } + + ret = devm_request_irq(dev, sdma->irq, &sirfsoc_dma_irq, 0, DRV_NAME, + sdma); + if (ret) { + dev_err(dev, "Error requesting IRQ!\n"); + ret = -EINVAL; + goto unmap_mem; + } + + dma = &sdma->dma; + dma->dev = dev; + dma->chancnt = SIRFSOC_DMA_CHANNELS; + + dma->device_alloc_chan_resources = sirfsoc_dma_alloc_chan_resources; + dma->device_free_chan_resources = sirfsoc_dma_free_chan_resources; + dma->device_issue_pending = sirfsoc_dma_issue_pending; + dma->device_control = sirfsoc_dma_control; + dma->device_tx_status = sirfsoc_dma_tx_status; + dma->device_prep_interleaved_dma = sirfsoc_dma_prep_interleaved; + dma->device_prep_dma_cyclic = sirfsoc_dma_prep_cyclic; + + INIT_LIST_HEAD(&dma->channels); + dma_cap_set(DMA_SLAVE, dma->cap_mask); + dma_cap_set(DMA_CYCLIC, dma->cap_mask); + dma_cap_set(DMA_INTERLEAVE, dma->cap_mask); + dma_cap_set(DMA_PRIVATE, dma->cap_mask); + + for (i = 0; i < dma->chancnt; i++) { + schan = &sdma->channels[i]; + + schan->chan.device = dma; + schan->chan.cookie = 1; + schan->completed_cookie = schan->chan.cookie; + + INIT_LIST_HEAD(&schan->free); + INIT_LIST_HEAD(&schan->prepared); + INIT_LIST_HEAD(&schan->queued); + INIT_LIST_HEAD(&schan->active); + INIT_LIST_HEAD(&schan->completed); + + spin_lock_init(&schan->lock); + list_add_tail(&schan->chan.device_node, &dma->channels); + } + + tasklet_init(&sdma->tasklet, sirfsoc_dma_tasklet, (unsigned long)sdma); + + /* Register DMA engine */ + dev_set_drvdata(dev, sdma); + ret = dma_async_device_register(dma); + if (ret) + goto free_irq; + + dev_info(dev, "initialized SIRFSOC DMAC driver\n"); + + return 0; + +free_irq: + devm_free_irq(dev, sdma->irq, sdma); +irq_dispose: + irq_dispose_mapping(sdma->irq); +unmap_mem: + iounmap(sdma->base); +free_mem: + devm_kfree(dev, sdma); + return ret; +} + +static int __devexit sirfsoc_dma_remove(struct platform_device *op) +{ + struct device *dev = &op->dev; + struct sirfsoc_dma *sdma = dev_get_drvdata(dev); + + dma_async_device_unregister(&sdma->dma); + devm_free_irq(dev, sdma->irq, sdma); + irq_dispose_mapping(sdma->irq); + iounmap(sdma->base); + devm_kfree(dev, sdma); + return 0; +} + +static struct of_device_id sirfsoc_dma_match[] = { + { .compatible = "sirf,prima2-dmac", }, + {}, +}; + +static struct platform_driver sirfsoc_dma_driver = { + .probe = sirfsoc_dma_probe, + .remove = __devexit_p(sirfsoc_dma_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = sirfsoc_dma_match, + }, +}; + +static int __init sirfsoc_dma_init(void) +{ + return platform_driver_register(&sirfsoc_dma_driver); +} +module_init(sirfsoc_dma_init); + +static void __exit sirfsoc_dma_exit(void) +{ + platform_driver_unregister(&sirfsoc_dma_driver); +} +module_exit(sirfsoc_dma_exit); + +MODULE_AUTHOR("Rongjun Ying , " + "Barry Song "); +MODULE_DESCRIPTION("SIRFSOC DMA control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/sirfsoc_dma.h b/include/linux/sirfsoc_dma.h new file mode 100644 index 0000000..29d9593 --- /dev/null +++ b/include/linux/sirfsoc_dma.h @@ -0,0 +1,6 @@ +#ifndef _SIRFSOC_DMA_H_ +#define _SIRFSOC_DMA_H_ + +bool sirfsoc_dma_filter_id(struct dma_chan *chan, void *chan_id); + +#endif -- cgit v0.10.2 From 553575f1ae048aa44682b46b3c51929a0b3ad337 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 18 Nov 2011 03:32:01 -0500 Subject: tools turbostat: recognize and run properly on IVB Signed-off-by: Len Brown diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 8b2d37b..4b05b44 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -809,6 +809,8 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) case 0x2C: /* Westmere EP - Gulftown */ case 0x2A: /* SNB */ case 0x2D: /* SNB Xeon */ + case 0x3A: /* IVB */ + case 0x3D: /* IVB Xeon */ return 1; case 0x2E: /* Nehalem-EX Xeon - Beckton */ case 0x2F: /* Westmere-EX Xeon - Eagleton */ -- cgit v0.10.2 From 7fb3e75e1833743d5faf3adbae46b63f503c6fdf Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Thu, 17 Nov 2011 17:26:41 +0530 Subject: dmaengine/ste_dma40: support pm in dma40 This patch adds power management support to the dma40 driver. The DMA registers are backed up and restored, during suspend/resume. Also flags to track the dma usage have been introduced to facilitate this. Patch also includes few other minor changes, related to formatting, comments. Signed-off-by: Narayanan G Acked-by: Linus Walleij Signed-off-by: Vinod Koul diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 15b311d5..c2cf8cf 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -32,6 +34,9 @@ /* Maximum iterations taken before giving up suspending a channel */ #define D40_SUSPEND_MAX_IT 500 +/* Milliseconds */ +#define DMA40_AUTOSUSPEND_DELAY 100 + /* Hardware requirement on LCLA alignment */ #define LCLA_ALIGNMENT 0x40000 @@ -62,6 +67,55 @@ enum d40_command { D40_DMA_SUSPENDED = 3 }; +/* + * These are the registers that has to be saved and later restored + * when the DMA hw is powered off. + * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works. + */ +static u32 d40_backup_regs[] = { + D40_DREG_LCPA, + D40_DREG_LCLA, + D40_DREG_PRMSE, + D40_DREG_PRMSO, + D40_DREG_PRMOE, + D40_DREG_PRMOO, +}; + +#define BACKUP_REGS_SZ ARRAY_SIZE(d40_backup_regs) + +/* TODO: Check if all these registers have to be saved/restored on dma40 v3 */ +static u32 d40_backup_regs_v3[] = { + D40_DREG_PSEG1, + D40_DREG_PSEG2, + D40_DREG_PSEG3, + D40_DREG_PSEG4, + D40_DREG_PCEG1, + D40_DREG_PCEG2, + D40_DREG_PCEG3, + D40_DREG_PCEG4, + D40_DREG_RSEG1, + D40_DREG_RSEG2, + D40_DREG_RSEG3, + D40_DREG_RSEG4, + D40_DREG_RCEG1, + D40_DREG_RCEG2, + D40_DREG_RCEG3, + D40_DREG_RCEG4, +}; + +#define BACKUP_REGS_SZ_V3 ARRAY_SIZE(d40_backup_regs_v3) + +static u32 d40_backup_regs_chan[] = { + D40_CHAN_REG_SSCFG, + D40_CHAN_REG_SSELT, + D40_CHAN_REG_SSPTR, + D40_CHAN_REG_SSLNK, + D40_CHAN_REG_SDCFG, + D40_CHAN_REG_SDELT, + D40_CHAN_REG_SDPTR, + D40_CHAN_REG_SDLNK, +}; + /** * struct d40_lli_pool - Structure for keeping LLIs in memory * @@ -96,7 +150,7 @@ struct d40_lli_pool { * during a transfer. * @node: List entry. * @is_in_client_list: true if the client owns this descriptor. - * the previous one. + * @cyclic: true if this is a cyclic job * * This descriptor is used for both logical and physical transfers. */ @@ -143,6 +197,7 @@ struct d40_lcla_pool { * channels. * * @lock: A lock protection this entity. + * @reserved: True if used by secure world or otherwise. * @num: The physical channel number of this entity. * @allocated_src: Bit mapped to show which src event line's are mapped to * this physical channel. Can also be free or physically allocated. @@ -152,6 +207,7 @@ struct d40_lcla_pool { */ struct d40_phy_res { spinlock_t lock; + bool reserved; int num; u32 allocated_src; u32 allocated_dst; @@ -185,7 +241,6 @@ struct d40_base; * @src_def_cfg: Default cfg register setting for src. * @dst_def_cfg: Default cfg register setting for dst. * @log_def: Default logical channel settings. - * @lcla: Space for one dst src pair for logical channel transfers. * @lcpa: Pointer to dst and src lcpa settings. * @runtime_addr: runtime configured address. * @runtime_direction: runtime configured direction. @@ -241,6 +296,7 @@ struct d40_chan { * @dma_both: dma_device channels that can do both memcpy and slave transfers. * @dma_slave: dma_device channels that can do only do slave transfers. * @dma_memcpy: dma_device channels that can do only do memcpy transfers. + * @phy_chans: Room for all possible physical channels in system. * @log_chans: Room for all possible logical channels in system. * @lookup_log_chans: Used to map interrupt number to logical channel. Points * to log_chans entries. @@ -254,6 +310,13 @@ struct d40_chan { * @phy_lcpa: The physical address of the LCPA. * @lcpa_size: The size of the LCPA area. * @desc_slab: cache for descriptors. + * @reg_val_backup: Here the values of some hardware registers are stored + * before the DMA is powered off. They are restored when the power is back on. + * @reg_val_backup_v3: Backup of registers that only exits on dma40 v3 and + * later. + * @reg_val_backup_chan: Backup data for standard channel parameter registers. + * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off. + * @initialized: true if the dma has been initialized */ struct d40_base { spinlock_t interrupt_lock; @@ -282,6 +345,11 @@ struct d40_base { dma_addr_t phy_lcpa; resource_size_t lcpa_size; struct kmem_cache *desc_slab; + u32 reg_val_backup[BACKUP_REGS_SZ]; + u32 reg_val_backup_v3[BACKUP_REGS_SZ_V3]; + u32 *reg_val_backup_chan; + u16 gcc_pwr_off_mask; + bool initialized; }; /** @@ -479,13 +547,14 @@ static struct d40_desc *d40_desc_get(struct d40_chan *d40c) struct d40_desc *d; struct d40_desc *_d; - list_for_each_entry_safe(d, _d, &d40c->client, node) + list_for_each_entry_safe(d, _d, &d40c->client, node) { if (async_tx_test_ack(&d->txd)) { d40_desc_remove(d); desc = d; memset(desc, 0, sizeof(*desc)); break; } + } } if (!desc) @@ -740,7 +809,61 @@ static int d40_sg_2_dmalen(struct scatterlist *sgl, int sg_len, return len; } -/* Support functions for logical channels */ + +#ifdef CONFIG_PM +static void dma40_backup(void __iomem *baseaddr, u32 *backup, + u32 *regaddr, int num, bool save) +{ + int i; + + for (i = 0; i < num; i++) { + void __iomem *addr = baseaddr + regaddr[i]; + + if (save) + backup[i] = readl_relaxed(addr); + else + writel_relaxed(backup[i], addr); + } +} + +static void d40_save_restore_registers(struct d40_base *base, bool save) +{ + int i; + + /* Save/Restore channel specific registers */ + for (i = 0; i < base->num_phy_chans; i++) { + void __iomem *addr; + int idx; + + if (base->phy_res[i].reserved) + continue; + + addr = base->virtbase + D40_DREG_PCBASE + i * D40_DREG_PCDELTA; + idx = i * ARRAY_SIZE(d40_backup_regs_chan); + + dma40_backup(addr, &base->reg_val_backup_chan[idx], + d40_backup_regs_chan, + ARRAY_SIZE(d40_backup_regs_chan), + save); + } + + /* Save/Restore global registers */ + dma40_backup(base->virtbase, base->reg_val_backup, + d40_backup_regs, ARRAY_SIZE(d40_backup_regs), + save); + + /* Save/Restore registers only existing on dma40 v3 and later */ + if (base->rev >= 3) + dma40_backup(base->virtbase, base->reg_val_backup_v3, + d40_backup_regs_v3, + ARRAY_SIZE(d40_backup_regs_v3), + save); +} +#else +static void d40_save_restore_registers(struct d40_base *base, bool save) +{ +} +#endif static int d40_channel_execute_command(struct d40_chan *d40c, enum d40_command command) @@ -1013,6 +1136,7 @@ static int d40_pause(struct d40_chan *d40c) if (!d40c->busy) return 0; + pm_runtime_get_sync(d40c->base->dev); spin_lock_irqsave(&d40c->lock, flags); res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); @@ -1025,7 +1149,8 @@ static int d40_pause(struct d40_chan *d40c) D40_DMA_RUN); } } - + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); spin_unlock_irqrestore(&d40c->lock, flags); return res; } @@ -1039,7 +1164,7 @@ static int d40_resume(struct d40_chan *d40c) return 0; spin_lock_irqsave(&d40c->lock, flags); - + pm_runtime_get_sync(d40c->base->dev); if (d40c->base->rev == 0) if (chan_is_logical(d40c)) { res = d40_channel_execute_command(d40c, @@ -1057,6 +1182,8 @@ static int d40_resume(struct d40_chan *d40c) } no_suspend: + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); spin_unlock_irqrestore(&d40c->lock, flags); return res; } @@ -1129,7 +1256,10 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c) d40d = d40_first_queued(d40c); if (d40d != NULL) { - d40c->busy = true; + if (!d40c->busy) + d40c->busy = true; + + pm_runtime_get_sync(d40c->base->dev); /* Remove from queue */ d40_desc_remove(d40d); @@ -1190,6 +1320,8 @@ static void dma_tc_handle(struct d40_chan *d40c) if (d40_queue_start(d40c) == NULL) d40c->busy = false; + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); } d40c->pending_tx++; @@ -1643,10 +1775,11 @@ static int d40_free_dma(struct d40_chan *d40c) return -EINVAL; } + pm_runtime_get_sync(d40c->base->dev); res = d40_channel_execute_command(d40c, D40_DMA_SUSPEND_REQ); if (res) { chan_err(d40c, "suspend failed\n"); - return res; + goto out; } if (chan_is_logical(d40c)) { @@ -1664,13 +1797,11 @@ static int d40_free_dma(struct d40_chan *d40c) if (d40_chan_has_events(d40c)) { res = d40_channel_execute_command(d40c, D40_DMA_RUN); - if (res) { + if (res) chan_err(d40c, "Executing RUN command\n"); - return res; - } } - return 0; + goto out; } } else { (void) d40_alloc_mask_free(phy, is_src, 0); @@ -1680,13 +1811,23 @@ static int d40_free_dma(struct d40_chan *d40c) res = d40_channel_execute_command(d40c, D40_DMA_STOP); if (res) { chan_err(d40c, "Failed to stop channel\n"); - return res; + goto out; } + + if (d40c->busy) { + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); + } + + d40c->busy = false; d40c->phy_chan = NULL; d40c->configured = false; d40c->base->lookup_phy_chans[phy->num] = NULL; +out: - return 0; + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); + return res; } static bool d40_is_paused(struct d40_chan *d40c) @@ -2016,9 +2157,11 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) err = d40_allocate_channel(d40c); if (err) { chan_err(d40c, "Failed to allocate channel\n"); + d40c->configured = false; goto fail; } + pm_runtime_get_sync(d40c->base->dev); /* Fill in basic CFG register values */ d40_phy_cfg(&d40c->dma_cfg, &d40c->src_def_cfg, &d40c->dst_def_cfg, chan_is_logical(d40c)); @@ -2046,6 +2189,8 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) if (is_free_phy) d40_config_write(d40c); fail: + pm_runtime_mark_last_busy(d40c->base->dev); + pm_runtime_put_autosuspend(d40c->base->dev); spin_unlock_irqrestore(&d40c->lock, flags); return err; } @@ -2519,6 +2664,55 @@ failure1: return err; } +/* Suspend resume functionality */ +#ifdef CONFIG_PM +static int dma40_pm_suspend(struct device *dev) +{ + if (!pm_runtime_suspended(dev)) + return -EBUSY; + + return 0; +} + +static int dma40_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct d40_base *base = platform_get_drvdata(pdev); + + d40_save_restore_registers(base, true); + + /* Don't disable/enable clocks for v1 due to HW bugs */ + if (base->rev != 1) + writel_relaxed(base->gcc_pwr_off_mask, + base->virtbase + D40_DREG_GCC); + + return 0; +} + +static int dma40_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct d40_base *base = platform_get_drvdata(pdev); + + if (base->initialized) + d40_save_restore_registers(base, false); + + writel_relaxed(D40_DREG_GCC_ENABLE_ALL, + base->virtbase + D40_DREG_GCC); + return 0; +} + + +static const struct dev_pm_ops dma40_pm_ops = { + .suspend = dma40_pm_suspend, + .runtime_suspend = dma40_runtime_suspend, + .runtime_resume = dma40_runtime_resume, +}; +#define DMA40_PM_OPS (&dma40_pm_ops) +#else +#define DMA40_PM_OPS NULL +#endif + /* Initialization functions. */ static int __init d40_phy_res_init(struct d40_base *base) @@ -2527,6 +2721,7 @@ static int __init d40_phy_res_init(struct d40_base *base) int num_phy_chans_avail = 0; u32 val[2]; int odd_even_bit = -2; + int gcc = D40_DREG_GCC_ENA; val[0] = readl(base->virtbase + D40_DREG_PRSME); val[1] = readl(base->virtbase + D40_DREG_PRSMO); @@ -2538,9 +2733,17 @@ static int __init d40_phy_res_init(struct d40_base *base) /* Mark security only channels as occupied */ base->phy_res[i].allocated_src = D40_ALLOC_PHY; base->phy_res[i].allocated_dst = D40_ALLOC_PHY; + base->phy_res[i].reserved = true; + gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(i), + D40_DREG_GCC_SRC); + gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(i), + D40_DREG_GCC_DST); + + } else { base->phy_res[i].allocated_src = D40_ALLOC_FREE; base->phy_res[i].allocated_dst = D40_ALLOC_FREE; + base->phy_res[i].reserved = false; num_phy_chans_avail++; } spin_lock_init(&base->phy_res[i].lock); @@ -2552,6 +2755,11 @@ static int __init d40_phy_res_init(struct d40_base *base) base->phy_res[chan].allocated_src = D40_ALLOC_PHY; base->phy_res[chan].allocated_dst = D40_ALLOC_PHY; + base->phy_res[chan].reserved = true; + gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(chan), + D40_DREG_GCC_SRC); + gcc |= D40_DREG_GCC_EVTGRP_ENA(D40_PHYS_TO_GROUP(chan), + D40_DREG_GCC_DST); num_phy_chans_avail--; } @@ -2572,6 +2780,15 @@ static int __init d40_phy_res_init(struct d40_base *base) val[0] = val[0] >> 2; } + /* + * To keep things simple, Enable all clocks initially. + * The clocks will get managed later post channel allocation. + * The clocks for the event lines on which reserved channels exists + * are not managed here. + */ + writel(D40_DREG_GCC_ENABLE_ALL, base->virtbase + D40_DREG_GCC); + base->gcc_pwr_off_mask = gcc; + return num_phy_chans_avail; } @@ -2699,10 +2916,15 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) goto failure; } - base->lcla_pool.alloc_map = kzalloc(num_phy_chans * - sizeof(struct d40_desc *) * - D40_LCLA_LINK_PER_EVENT_GRP, + base->reg_val_backup_chan = kmalloc(base->num_phy_chans * + sizeof(d40_backup_regs_chan), GFP_KERNEL); + if (!base->reg_val_backup_chan) + goto failure; + + base->lcla_pool.alloc_map = + kzalloc(num_phy_chans * sizeof(struct d40_desc *) + * D40_LCLA_LINK_PER_EVENT_GRP, GFP_KERNEL); if (!base->lcla_pool.alloc_map) goto failure; @@ -2741,9 +2963,9 @@ failure: static void __init d40_hw_init(struct d40_base *base) { - static const struct d40_reg_val dma_init_reg[] = { + static struct d40_reg_val dma_init_reg[] = { /* Clock every part of the DMA block from start */ - { .reg = D40_DREG_GCC, .val = 0x0000ff01}, + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, /* Interrupts on all logical channels */ { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, @@ -2960,6 +3182,12 @@ static int __init d40_probe(struct platform_device *pdev) goto failure; } + pm_runtime_irq_safe(base->dev); + pm_runtime_set_autosuspend_delay(base->dev, DMA40_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(base->dev); + pm_runtime_enable(base->dev); + pm_runtime_resume(base->dev); + base->initialized = true; err = d40_dmaengine_init(base, num_reserved_chans); if (err) goto failure; @@ -3013,6 +3241,7 @@ static struct platform_driver d40_driver = { .driver = { .owner = THIS_MODULE, .name = D40_NAME, + .pm = DMA40_PM_OPS, }, }; diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index b44c4551..8d3d490 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h @@ -16,6 +16,8 @@ #define D40_TYPE_TO_GROUP(type) (type / 16) #define D40_TYPE_TO_EVENT(type) (type % 16) +#define D40_GROUP_SIZE 8 +#define D40_PHYS_TO_GROUP(phys) ((phys & (D40_GROUP_SIZE - 1)) / 2) /* Most bits of the CFG register are the same in log as in phy mode */ #define D40_SREG_CFG_MST_POS 15 @@ -123,6 +125,15 @@ /* DMA Register Offsets */ #define D40_DREG_GCC 0x000 +#define D40_DREG_GCC_ENA 0x1 +/* This assumes that there are only 4 event groups */ +#define D40_DREG_GCC_ENABLE_ALL 0xff01 +#define D40_DREG_GCC_EVTGRP_POS 8 +#define D40_DREG_GCC_SRC 0 +#define D40_DREG_GCC_DST 1 +#define D40_DREG_GCC_EVTGRP_ENA(x, y) \ + (1 << (D40_DREG_GCC_EVTGRP_POS + 2 * x + y)) + #define D40_DREG_PRTYP 0x004 #define D40_DREG_PRSME 0x008 #define D40_DREG_PRSMO 0x00C -- cgit v0.10.2 From e69664336d6400cc1685716ee5f04ef74e85703e Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 18 Nov 2011 16:38:02 +0800 Subject: IMX/DMA : set the DMA direction in the sdma_control() Set the right DMA direction in the sdma_control(), else we will get the wrong log when enable the DYNAMIC_DEBUG. Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 2e03571..d9e5933 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1102,6 +1102,7 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, sdmac->watermark_level = dmaengine_cfg->dst_maxburst; sdmac->word_size = dmaengine_cfg->dst_addr_width; } + sdmac->direction = dmaengine_cfg->direction; return sdma_config_channel(sdmac); default: return -ENOSYS; -- cgit v0.10.2 From 61e183f8306934a9f66557f69f1f0f56f18dca06 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 17 Nov 2011 16:01:29 +0530 Subject: dmaengine/dw_dmac: Reconfigure interrupt and chan_cfg register on resume In S2R all DMA registers are reset by hardware and thus they are required to be reprogrammed. The channels which aren't reprogrammed are channel configuration and interrupt enable registers, which are currently programmed at chan_alloc time. This patch creates another routine to initialize a channel. It will try to initialize channel on every dwc_dostart() call. If channel is already initialised then it simply returns, otherwise it configures registers. This routine will also initialize registers on wakeup from S2R, as we mark channels as uninitialized on suspend. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index decca1c..9b592b0 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -166,6 +166,38 @@ dwc_assign_cookie(struct dw_dma_chan *dwc, struct dw_desc *desc) return cookie; } +static void dwc_initialize(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_dma_slave *dws = dwc->chan.private; + u32 cfghi = DWC_CFGH_FIFO_MODE; + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); + + if (dwc->initialized == true) + return; + + if (dws) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); + + cfghi = dws->cfg_hi; + cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; + } + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); + + /* Enable interrupts */ + channel_set_bit(dw, MASK.XFER, dwc->mask); + channel_set_bit(dw, MASK.BLOCK, dwc->mask); + channel_set_bit(dw, MASK.ERROR, dwc->mask); + + dwc->initialized = true; +} + /*----------------------------------------------------------------------*/ /* Called with dwc->lock held and bh disabled */ @@ -189,6 +221,8 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) return; } + dwc_initialize(dwc); + channel_writel(dwc, LLP, first->txd.phys); channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); @@ -959,10 +993,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma *dw = to_dw_dma(chan->device); struct dw_desc *desc; - struct dw_dma_slave *dws; int i; - u32 cfghi; - u32 cfglo; unsigned long flags; dev_vdbg(chan2dev(chan), "alloc_chan_resources\n"); @@ -975,26 +1006,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) dwc->completed = chan->cookie = 1; - cfghi = DWC_CFGH_FIFO_MODE; - cfglo = 0; - - dws = chan->private; - if (dws) { - /* - * We need controller-specific data to set up slave - * transfers. - */ - BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); - - cfghi = dws->cfg_hi; - cfglo = dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; - } - - cfglo |= DWC_CFGL_CH_PRIOR(dwc->priority); - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); - /* * NOTE: some controllers may have additional features that we * need to initialize here, like "scatter-gather" (which @@ -1026,11 +1037,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) i = ++dwc->descs_allocated; } - /* Enable interrupts */ - channel_set_bit(dw, MASK.XFER, dwc->mask); - channel_set_bit(dw, MASK.BLOCK, dwc->mask); - channel_set_bit(dw, MASK.ERROR, dwc->mask); - spin_unlock_irqrestore(&dwc->lock, flags); dev_dbg(chan2dev(chan), @@ -1058,6 +1064,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); list_splice_init(&dwc->free_list, &list); dwc->descs_allocated = 0; + dwc->initialized = false; /* Disable interrupts */ channel_clear_bit(dw, MASK.XFER, dwc->mask); @@ -1335,6 +1342,8 @@ EXPORT_SYMBOL(dw_dma_cyclic_free); static void dw_dma_off(struct dw_dma *dw) { + int i; + dma_writel(dw, CFG, 0); channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); @@ -1345,6 +1354,9 @@ static void dw_dma_off(struct dw_dma *dw) while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) cpu_relax(); + + for (i = 0; i < dw->dma.chancnt; i++) + dw->chan[i].initialized = false; } static int __init dw_probe(struct platform_device *pdev) @@ -1533,6 +1545,7 @@ static int dw_suspend_noirq(struct device *dev) dw_dma_off(platform_get_drvdata(pdev)); clk_disable(dw->clk); + return 0; } diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index c341951..5eef694 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -140,6 +140,7 @@ struct dw_dma_chan { u8 mask; u8 priority; bool paused; + bool initialized; spinlock_t lock; -- cgit v0.10.2 From d5613947addb1e1096a3be29dfe817905e385469 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 28 Nov 2011 08:51:16 +0530 Subject: dmaengine: timb_dma: fix the spare warinings Sparse warns: drivers/dma/timb_dma.c:168:17: warning: mixing different enum types drivers/dma/timb_dma.c:168:17: int enum dma_transfer_direction versus drivers/dma/timb_dma.c:168:17: int enum dma_data_direction drivers/dma/timb_dma.c:172:32: warning: mixing different enum types drivers/dma/timb_dma.c:172:32: int enum dma_transfer_direction versus drivers/dma/timb_dma.c:172:32: int enum dma_data_direction Reported-by: Dan Carpenter Signed-off-by: Vinod Koul diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 8c88072..d142ae1 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -166,10 +166,10 @@ static void __td_unmap_desc(struct timb_dma_chan *td_chan, const u8 *dma_desc, if (single) dma_unmap_single(chan2dev(&td_chan->chan), addr, len, - td_chan->direction); + DMA_TO_DEVICE); else dma_unmap_page(chan2dev(&td_chan->chan), addr, len, - td_chan->direction); + DMA_TO_DEVICE); } static void __td_unmap_descs(struct timb_dma_desc *td_desc, bool single) @@ -398,7 +398,7 @@ static struct timb_dma_desc *td_alloc_init_desc(struct timb_dma_chan *td_chan) td_desc->txd.flags = DMA_CTRL_ACK; td_desc->txd.phys = dma_map_single(chan2dmadev(chan), - td_desc->desc_list, td_desc->desc_list_len, DMA_MEM_TO_DEV); + td_desc->desc_list, td_desc->desc_list_len, DMA_TO_DEVICE); err = dma_mapping_error(chan2dmadev(chan), td_desc->txd.phys); if (err) { @@ -419,7 +419,7 @@ static void td_free_desc(struct timb_dma_desc *td_desc) { dev_dbg(chan2dev(td_desc->txd.chan), "Freeing desc: %p\n", td_desc); dma_unmap_single(chan2dmadev(td_desc->txd.chan), td_desc->txd.phys, - td_desc->desc_list_len, DMA_MEM_TO_DEV); + td_desc->desc_list_len, DMA_TO_DEVICE); kfree(td_desc->desc_list); kfree(td_desc); -- cgit v0.10.2 From 28c7a19d230228ab9ae61c300c5003a2400fadd3 Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Tue, 22 Nov 2011 13:56:55 +0530 Subject: dmaengine/ste_dma40: Add support to use lcla area from esram This patch provides an option of having the lcla (link address) in ESRAM instead of allocating it. The bool value (use_esram_lcla) in the stedma40_platform_data if set to true, then the lcla address would be taken from platform resources. Also, the corresponding esram regulator is managed in the suspend/resume functions. Signed-off-by: Narayanan G Signed-off-by: Vinod Koul diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h index 38b041a..3177bed 100644 --- a/arch/arm/plat-nomadik/include/plat/ste_dma40.h +++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h @@ -153,6 +153,7 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_phy; struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; + bool use_esram_lcla; }; #ifdef CONFIG_STE_DMA40 diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index c2cf8cf..aff128a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -304,6 +304,7 @@ struct d40_chan { * to phy_chans entries. * @plat_data: Pointer to provided platform_data which is the driver * configuration. + * @lcpa_regulator: Pointer to hold the regulator for the esram bank for lcla. * @phy_res: Vector containing all physical channels. * @lcla_pool: lcla pool settings and data. * @lcpa_base: The virtual mapped address of LCPA. @@ -338,6 +339,7 @@ struct d40_base { struct d40_chan **lookup_log_chans; struct d40_chan **lookup_phy_chans; struct stedma40_platform_data *plat_data; + struct regulator *lcpa_regulator; /* Physical half channels */ struct d40_phy_res *phy_res; struct d40_lcla_pool lcla_pool; @@ -605,6 +607,7 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) bool cyclic = desc->cyclic; int curr_lcla = -EINVAL; int first_lcla = 0; + bool use_esram_lcla = chan->base->plat_data->use_esram_lcla; bool linkback; /* @@ -677,11 +680,16 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) &lli->src[lli_current], next_lcla, flags); - dma_sync_single_range_for_device(chan->base->dev, - pool->dma_addr, lcla_offset, - 2 * sizeof(struct d40_log_lli), - DMA_TO_DEVICE); - + /* + * Cache maintenance is not needed if lcla is + * mapped in esram + */ + if (!use_esram_lcla) { + dma_sync_single_range_for_device(chan->base->dev, + pool->dma_addr, lcla_offset, + 2 * sizeof(struct d40_log_lli), + DMA_TO_DEVICE); + } curr_lcla = next_lcla; if (curr_lcla == -EINVAL || curr_lcla == first_lcla) { @@ -2668,10 +2676,15 @@ failure1: #ifdef CONFIG_PM static int dma40_pm_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct d40_base *base = platform_get_drvdata(pdev); + int ret = 0; if (!pm_runtime_suspended(dev)) return -EBUSY; - return 0; + if (base->lcpa_regulator) + ret = regulator_disable(base->lcpa_regulator); + return ret; } static int dma40_runtime_suspend(struct device *dev) @@ -2702,11 +2715,23 @@ static int dma40_runtime_resume(struct device *dev) return 0; } +static int dma40_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct d40_base *base = platform_get_drvdata(pdev); + int ret = 0; + + if (base->lcpa_regulator) + ret = regulator_enable(base->lcpa_regulator); + + return ret; +} static const struct dev_pm_ops dma40_pm_ops = { .suspend = dma40_pm_suspend, .runtime_suspend = dma40_runtime_suspend, .runtime_resume = dma40_runtime_resume, + .resume = dma40_resume, }; #define DMA40_PM_OPS (&dma40_pm_ops) #else @@ -3165,11 +3190,31 @@ static int __init d40_probe(struct platform_device *pdev) d40_err(&pdev->dev, "Failed to ioremap LCPA region\n"); goto failure; } + /* If lcla has to be located in ESRAM we don't need to allocate */ + if (base->plat_data->use_esram_lcla) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lcla_esram"); + if (!res) { + ret = -ENOENT; + d40_err(&pdev->dev, + "No \"lcla_esram\" memory resource\n"); + goto failure; + } + base->lcla_pool.base = ioremap(res->start, + resource_size(res)); + if (!base->lcla_pool.base) { + ret = -ENOMEM; + d40_err(&pdev->dev, "Failed to ioremap LCLA region\n"); + goto failure; + } + writel(res->start, base->virtbase + D40_DREG_LCLA); - ret = d40_lcla_allocate(base); - if (ret) { - d40_err(&pdev->dev, "Failed to allocate LCLA area\n"); - goto failure; + } else { + ret = d40_lcla_allocate(base); + if (ret) { + d40_err(&pdev->dev, "Failed to allocate LCLA area\n"); + goto failure; + } } spin_lock_init(&base->lcla_pool.lock); @@ -3187,6 +3232,26 @@ static int __init d40_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(base->dev); pm_runtime_enable(base->dev); pm_runtime_resume(base->dev); + + if (base->plat_data->use_esram_lcla) { + + base->lcpa_regulator = regulator_get(base->dev, "lcla_esram"); + if (IS_ERR(base->lcpa_regulator)) { + d40_err(&pdev->dev, "Failed to get lcpa_regulator\n"); + base->lcpa_regulator = NULL; + goto failure; + } + + ret = regulator_enable(base->lcpa_regulator); + if (ret) { + d40_err(&pdev->dev, + "Failed to enable lcpa_regulator\n"); + regulator_put(base->lcpa_regulator); + base->lcpa_regulator = NULL; + goto failure; + } + } + base->initialized = true; err = d40_dmaengine_init(base, num_reserved_chans); if (err) @@ -3204,6 +3269,11 @@ failure: if (base->virtbase) iounmap(base->virtbase); + if (base->lcla_pool.base && base->plat_data->use_esram_lcla) { + iounmap(base->lcla_pool.base); + base->lcla_pool.base = NULL; + } + if (base->lcla_pool.dma_addr) dma_unmap_single(base->dev, base->lcla_pool.dma_addr, SZ_1K * base->num_phy_chans, @@ -3226,6 +3296,11 @@ failure: clk_put(base->clk); } + if (base->lcpa_regulator) { + regulator_disable(base->lcpa_regulator); + regulator_put(base->lcpa_regulator); + } + kfree(base->lcla_pool.alloc_map); kfree(base->lookup_log_chans); kfree(base->lookup_phy_chans); -- cgit v0.10.2 From 0ab88a01832d529c48e2a51f3d24662c143df39a Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 22 Nov 2011 11:55:52 +0100 Subject: dmaengine: at_hdmac: add const to struct platform_device_id definition Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 3b0c28f..3b44f24 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1191,7 +1191,7 @@ static const struct of_device_id atmel_dma_dt_ids[] = { MODULE_DEVICE_TABLE(of, atmel_dma_dt_ids); #endif -static struct platform_device_id atdma_devtypes[] = { +static const struct platform_device_id atdma_devtypes[] = { { .name = "at91sam9rl_dma", .driver_data = ATDMA_DEVTYPE_SAM9RL, -- cgit v0.10.2 From dcc817346d311bbd9dac6fc9d6bddb552134851c Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 22 Nov 2011 11:55:53 +0100 Subject: dmaengine: at_hdmac: empty entry shape modification Trivial form modification to unify structure look. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 3b44f24..30ec030 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1185,7 +1185,9 @@ static const struct of_device_id atmel_dma_dt_ids[] = { }, { .compatible = "atmel,at91sam9g45-dma", .data = (void *)ATDMA_DEVTYPE_SAM9G45 - }, { /* sentinel */ } + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, atmel_dma_dt_ids); -- cgit v0.10.2 From 02f88be9488a3d831f073c1161b1e5feacb9d3ec Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 22 Nov 2011 11:55:54 +0100 Subject: dmaengine: at_hdmac: simplify device selection from platform data or DT Using a configuration structure simplify the finding of SoC dependent parameters. Both platform data and device tree ids are using these structures. This will separate data from code and remove the need for an enum. Idea from Grant Likely. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 30ec030..97f87b2 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1177,14 +1177,22 @@ static void atc_free_chan_resources(struct dma_chan *chan) /*-- Module Management -----------------------------------------------*/ +/* cap_mask is a multi-u32 bitfield, fill it with proper C code. */ +static struct at_dma_platform_data at91sam9rl_config = { + .nr_channels = 2, +}; +static struct at_dma_platform_data at91sam9g45_config = { + .nr_channels = 8, +}; + #if defined(CONFIG_OF) static const struct of_device_id atmel_dma_dt_ids[] = { { .compatible = "atmel,at91sam9rl-dma", - .data = (void *)ATDMA_DEVTYPE_SAM9RL + .data = &at91sam9rl_config, }, { .compatible = "atmel,at91sam9g45-dma", - .data = (void *)ATDMA_DEVTYPE_SAM9G45 + .data = &at91sam9g45_config, }, { /* sentinel */ } @@ -1196,26 +1204,27 @@ MODULE_DEVICE_TABLE(of, atmel_dma_dt_ids); static const struct platform_device_id atdma_devtypes[] = { { .name = "at91sam9rl_dma", - .driver_data = ATDMA_DEVTYPE_SAM9RL, + .driver_data = (unsigned long) &at91sam9rl_config, }, { .name = "at91sam9g45_dma", - .driver_data = ATDMA_DEVTYPE_SAM9G45, + .driver_data = (unsigned long) &at91sam9g45_config, }, { /* sentinel */ } }; -static inline enum atdma_devtype __init at_dma_get_driver_data( - struct platform_device *pdev) +static inline struct at_dma_platform_data * __init at_dma_get_driver_data( + struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(atmel_dma_dt_ids, pdev->dev.of_node); if (match == NULL) - return ATDMA_DEVTYPE_UNDEFINED; - return (enum atdma_devtype)match->data; + return NULL; + return match->data; } - return platform_get_device_id(pdev)->driver_data; + return (struct at_dma_platform_data *) + platform_get_device_id(pdev)->driver_data; } /** @@ -1242,26 +1251,17 @@ static int __init at_dma_probe(struct platform_device *pdev) int irq; int err; int i; - u32 nr_channels; - dma_cap_mask_t cap_mask = {}; - enum atdma_devtype atdmatype; + struct at_dma_platform_data *plat_dat; - dma_cap_set(DMA_MEMCPY, cap_mask); + /* setup platform data for each SoC */ + dma_cap_set(DMA_MEMCPY, at91sam9rl_config.cap_mask); + dma_cap_set(DMA_MEMCPY, at91sam9g45_config.cap_mask); + dma_cap_set(DMA_SLAVE, at91sam9g45_config.cap_mask); /* get DMA parameters from controller type */ - atdmatype = at_dma_get_driver_data(pdev); - - switch (atdmatype) { - case ATDMA_DEVTYPE_SAM9RL: - nr_channels = 2; - break; - case ATDMA_DEVTYPE_SAM9G45: - nr_channels = 8; - dma_cap_set(DMA_SLAVE, cap_mask); - break; - default: - return -EINVAL; - } + plat_dat = at_dma_get_driver_data(pdev); + if (!plat_dat) + return -ENODEV; io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) @@ -1272,15 +1272,14 @@ static int __init at_dma_probe(struct platform_device *pdev) return irq; size = sizeof(struct at_dma); - size += nr_channels * sizeof(struct at_dma_chan); + size += plat_dat->nr_channels * sizeof(struct at_dma_chan); atdma = kzalloc(size, GFP_KERNEL); if (!atdma) return -ENOMEM; /* discover transaction capabilities */ - atdma->dma_common.cap_mask = cap_mask; - atdma->all_chan_mask = (1 << nr_channels) - 1; - atdma->devtype = atdmatype; + atdma->dma_common.cap_mask = plat_dat->cap_mask; + atdma->all_chan_mask = (1 << plat_dat->nr_channels) - 1; size = resource_size(io); if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { @@ -1326,7 +1325,7 @@ static int __init at_dma_probe(struct platform_device *pdev) /* initialize channels related values */ INIT_LIST_HEAD(&atdma->dma_common.channels); - for (i = 0; i < nr_channels; i++) { + for (i = 0; i < plat_dat->nr_channels; i++) { struct at_dma_chan *atchan = &atdma->chan[i]; atchan->chan_common.device = &atdma->dma_common; @@ -1371,7 +1370,7 @@ static int __init at_dma_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel AHB DMA Controller ( %s%s), %d channels\n", dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask) ? "cpy " : "", dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) ? "slave " : "", - nr_channels); + plat_dat->nr_channels); dma_async_device_register(&atdma->dma_common); diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index d7d6737..dcaedfc 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -248,12 +248,6 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) /*-- Controller ------------------------------------------------------*/ -enum atdma_devtype { - ATDMA_DEVTYPE_UNDEFINED = 0, - ATDMA_DEVTYPE_SAM9RL, /* compatible with SAM9RL DMA controller */ - ATDMA_DEVTYPE_SAM9G45, /* compatible with SAM9G45 DMA controller */ -}; - /** * struct at_dma - internal representation of an Atmel HDMA Controller * @chan_common: common dmaengine dma_device object members @@ -267,7 +261,6 @@ enum atdma_devtype { */ struct at_dma { struct dma_device dma_common; - enum atdma_devtype devtype; void __iomem *regs; struct clk *clk; u32 save_imr; -- cgit v0.10.2 From b62cfc5e0e59d1e0192105144b724b0c332720a0 Mon Sep 17 00:00:00 2001 From: Rafal Prylowski Date: Sat, 26 Nov 2011 12:54:07 +0200 Subject: dma/ep93xx_dma: fix initialization of M2M control register Setting the flags in case of IDE didn't have any effect since the control register is overwritten few lines below. So move these to be after the control register is initialized. Signed-off-by: Rafal Prylowski Signed-off-by: Mika Westerberg Acked-by: H Hartley Sweeten Signed-off-by: Vinod Koul diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 009851b..526a742 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -459,10 +459,6 @@ static int m2m_hw_setup(struct ep93xx_dma_chan *edmac) * This IDE part is totally untested. Values below are taken * from the EP93xx Users's Guide and might not be correct. */ - control |= M2M_CONTROL_NO_HDSK; - control |= M2M_CONTROL_RSS_IDE; - control |= M2M_CONTROL_PW_16; - if (data->direction == DMA_MEM_TO_DEV) { /* Worst case from the UG */ control = (3 << M2M_CONTROL_PWSC_SHIFT); @@ -473,6 +469,10 @@ static int m2m_hw_setup(struct ep93xx_dma_chan *edmac) control |= M2M_CONTROL_SAH; control |= M2M_CONTROL_TM_RX; } + + control |= M2M_CONTROL_NO_HDSK; + control |= M2M_CONTROL_RSS_IDE; + control |= M2M_CONTROL_PW_16; break; default: -- cgit v0.10.2 From 6d0709d2000ae7dbead759715f57ba381b7057bb Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sat, 26 Nov 2011 12:54:08 +0200 Subject: dma/ep93xx_dma: prevent ep93xx_dma_tasklet() to reference an empty list If dma_terminate_all() is called before the ep93xx_dma_tasklet() gets to run, it tries to access an empty ->active list which results following OOPS: Internal error: Oops - undefined instruction: 0 [#1] CPU: 0 Not tainted (3.2.0-rc1EP-1+ #1008) PC is at 0xc184c868 LR is at ep93xx_dma_tasklet+0xec/0x164 pc : [] lr : [] psr: 00000013 sp : c02b7e70 ip : ffffffff fp : c02b7ea4 r10: 00000100 r9 : 80000013 r8 : c02b7e50 r7 : c02b7e70 r6 : c02b7ea4 r5 : 000000a4 r4 : c02b7e70 r3 : c02b751d r2 : 8ae34598 r1 : c184c6e0 r0 : c02b7ea4 Flags: nzcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: c000717f Table: c0004000 DAC: 00000017 Process swapper (pid: 0, stack limit = 0xc02b6270) Stack: (0xc02b7e70 to 0xc02b8000) 7e60: c02b7ea4 c02b7e70 c0008b64 c02bd5c4 7e80: c02d60e0 00000000 00000000 c02bd44c c02d60e0 00000100 c02b7ec4 c02b7ea8 7ea0: c001c49c c012b44c 00000018 00000001 c02d60e0 c02b6000 c02b7f04 c02b7ec8 7ec0: c001cbc0 c001c3e4 c02b7eec c02b7ed8 00000006 0000000a c02bf674 c02c458c 7ee0: 00000011 00000000 c02b7f7c c0004000 41129200 c02b0c80 c02b7f14 c02b7f08 7f00: c001cdd0 c001cb38 c02b7f34 c02b7f18 c000983c c001cd98 c0009a60 60000013 7f20: fefb0001 c02b7f7c c02b7f44 c02b7f38 c0008190 c0009810 c02b7f9c c02b7f48 7f40: c0008b64 c0008190 c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504 7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90 7f80: c0009a54 c0009a60 60000013 ffffffff c02b7fbc c02b7fa0 c000a03c c0009a40 7fa0: c02b80b0 c02b19dc c02b19d8 c02baa80 c02b7fcc c02b7fc0 c02384e4 c0009fd4 7fc0: c02b7ff4 c02b7fd0 c029d924 c0238494 c029d49c 00000000 00000000 c02b19dc 7fe0: c0007175 c02b803c 00000000 c02b7ff8 c000803c c029d700 00000000 00000000 Backtrace: [] (ep93xx_dma_tasklet+0x0/0x164) from [] (tasklet_action+0xc8/0xdc) [] (tasklet_action+0x0/0xdc) from [] (__do_softirq+0x98/0x154) r7:c02b6000 r6:c02d60e0 r5:00000001 r4:00000018 [] (__do_softirq+0x0/0x154) from [] (irq_exit+0x48/0x50) [] (irq_exit+0x0/0x50) from [] (handle_IRQ+0x3c/0x8c) [] (handle_IRQ+0x0/0x8c) from [] (asm_do_IRQ+0x10/0x14) r7:c02b7f7c r6:fefb0001 r5:60000013 r4:c0009a60 [] (asm_do_IRQ+0x0/0x14) from [] (__irq_svc+0x24/0xc0) Exception stack(0xc02b7f48 to 0xc02b7f90) 7f40: c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504 7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90 7f80: c0009a54 c0009a60 60000013 ffffffff [] (default_idle+0x0/0x34) from [] (cpu_idle+0x78/0xb0) [] (cpu_idle+0x0/0xb0) from [] (rest_init+0x60/0x78) r7:c02baa80 r6:c02b19d8 r5:c02b19dc r4:c02b80b0 [] (rest_init+0x0/0x78) from [] (start_kernel+0x234/0x278) [] (start_kernel+0x0/0x278) from [] (0xc000803c) r5:c02b803c r4:c0007175 Code: 42555300 54535953 643d4d45 65766972 (53007372) To make the code a bit more robust against things like these, we modify ep93xx_dma_get_active() to return NULL in case of empty ->active list and make sure that callers handle this correctly. Reported-by: Rafal Prylowski Signed-off-by: Mika Westerberg Acked-by: H Hartley Sweeten Signed-off-by: Vinod Koul diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 526a742..59e7a96 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -246,6 +246,9 @@ static void ep93xx_dma_set_active(struct ep93xx_dma_chan *edmac, static struct ep93xx_dma_desc * ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) { + if (list_empty(&edmac->active)) + return NULL; + return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node); } @@ -263,16 +266,22 @@ ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) */ static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac) { + struct ep93xx_dma_desc *desc; + list_rotate_left(&edmac->active); if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) return true; + desc = ep93xx_dma_get_active(edmac); + if (!desc) + return false; + /* * If txd.cookie is set it means that we are back in the first * descriptor in the chain and hence done with it. */ - return !ep93xx_dma_get_active(edmac)->txd.cookie; + return !desc->txd.cookie; } /* @@ -327,9 +336,15 @@ static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac) static void m2p_fill_desc(struct ep93xx_dma_chan *edmac) { - struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); + struct ep93xx_dma_desc *desc; u32 bus_addr; + desc = ep93xx_dma_get_active(edmac); + if (!desc) { + dev_warn(chan2dev(edmac), "M2P: empty descriptor list\n"); + return; + } + if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_MEM_TO_DEV) bus_addr = desc->src_addr; else @@ -491,7 +506,13 @@ static void m2m_hw_shutdown(struct ep93xx_dma_chan *edmac) static void m2m_fill_desc(struct ep93xx_dma_chan *edmac) { - struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); + struct ep93xx_dma_desc *desc; + + desc = ep93xx_dma_get_active(edmac); + if (!desc) { + dev_warn(chan2dev(edmac), "M2M: empty descriptor list\n"); + return; + } if (edmac->buffer == 0) { writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0); @@ -669,24 +690,30 @@ static void ep93xx_dma_tasklet(unsigned long data) { struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data; struct ep93xx_dma_desc *desc, *d; - dma_async_tx_callback callback; - void *callback_param; + dma_async_tx_callback callback = NULL; + void *callback_param = NULL; LIST_HEAD(list); spin_lock_irq(&edmac->lock); + /* + * If dma_terminate_all() was called before we get to run, the active + * list has become empty. If that happens we aren't supposed to do + * anything more than call ep93xx_dma_advance_work(). + */ desc = ep93xx_dma_get_active(edmac); - if (desc->complete) { - edmac->last_completed = desc->txd.cookie; - list_splice_init(&edmac->active, &list); + if (desc) { + if (desc->complete) { + edmac->last_completed = desc->txd.cookie; + list_splice_init(&edmac->active, &list); + } + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; } spin_unlock_irq(&edmac->lock); /* Pick up the next descriptor from the queue */ ep93xx_dma_advance_work(edmac); - callback = desc->txd.callback; - callback_param = desc->txd.callback_param; - /* Now we can release all the chained descriptors */ list_for_each_entry_safe(desc, d, &list, node) { /* @@ -706,13 +733,22 @@ static void ep93xx_dma_tasklet(unsigned long data) static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id) { struct ep93xx_dma_chan *edmac = dev_id; + struct ep93xx_dma_desc *desc; irqreturn_t ret = IRQ_HANDLED; spin_lock(&edmac->lock); + desc = ep93xx_dma_get_active(edmac); + if (!desc) { + dev_warn(chan2dev(edmac), + "got interrupt while active list is empty\n"); + spin_unlock(&edmac->lock); + return IRQ_NONE; + } + switch (edmac->edma->hw_interrupt(edmac)) { case INTERRUPT_DONE: - ep93xx_dma_get_active(edmac)->complete = true; + desc->complete = true; tasklet_schedule(&edmac->tasklet); break; -- cgit v0.10.2 From ab59a510c6ad6b3add5125df64843be754782de6 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 2 Dec 2011 10:16:25 +0800 Subject: IMX/SDMA : save the real count for one DMA transaction. When we use the SDMA in the UART driver(such as imx6q), we will meet one situation: Assume we set 64 bytes for the RX DMA buffer. The RX DMA buffer has received some data, but not full. An Aging DMA request will be received by the SDMA controller if we enable the IDDMAEN(UCR4[6]) in this case. So the UART driver needs to know the count of the real received bytes, and push them to upper layer. Add two new fields to sdmac, and update the `residue` in sdma_tx_status(). Signed-off-by: Huang Shijie Signed-off-by: Vinod Koul diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index d9e5933..f59fd8f 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -268,6 +268,8 @@ struct sdma_channel { struct dma_async_tx_descriptor desc; dma_cookie_t last_completed; enum dma_status status; + unsigned int chn_count; + unsigned int chn_real_count; }; #define IMX_DMA_SG_LOOP (1 << 0) @@ -503,6 +505,7 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) struct sdma_buffer_descriptor *bd; int i, error = 0; + sdmac->chn_real_count = 0; /* * non loop mode. Iterate over all descriptors, collect * errors and call callback function @@ -512,6 +515,7 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) if (bd->mode.status & (BD_DONE | BD_RROR)) error = -EIO; + sdmac->chn_real_count += bd->mode.count; } if (error) @@ -519,9 +523,9 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac) else sdmac->status = DMA_SUCCESS; + sdmac->last_completed = sdmac->desc.cookie; if (sdmac->desc.callback) sdmac->desc.callback(sdmac->desc.callback_param); - sdmac->last_completed = sdmac->desc.cookie; } static void mxc_sdma_handle_channel(struct sdma_channel *sdmac) @@ -941,6 +945,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( goto err_out; } + sdmac->chn_count = 0; for_each_sg(sgl, sg, sg_len, i) { struct sdma_buffer_descriptor *bd = &sdmac->bd[i]; int param; @@ -957,6 +962,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( } bd->mode.count = count; + sdmac->chn_count += count; if (sdmac->word_size > DMA_SLAVE_BUSWIDTH_4_BYTES) { ret = -EINVAL; @@ -1120,7 +1126,8 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan, last_used = chan->cookie; - dma_set_tx_state(txstate, sdmac->last_completed, last_used, 0); + dma_set_tx_state(txstate, sdmac->last_completed, last_used, + sdmac->chn_count - sdmac->chn_real_count); return sdmac->status; } -- cgit v0.10.2 From c94e910535be72f0c6ac0c69e6acd8d44414e80d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 26 Nov 2011 15:11:12 +0800 Subject: dmaengine: convert drivers/dma/* to use module_platform_driver() This patch converts the drivers in drivers/dma/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Dan Williams Cc: Piotr Ziecik Cc: Rongjun Ying Cc: Barry Song <21cnbao@gmail.com> Cc: Pelagicore AB Signed-off-by: Axel Lin Signed-off-by: Vinod Koul diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 686dc76..04be90b 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -1735,8 +1735,6 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) spin_unlock_bh(&iop_chan->lock); } -MODULE_ALIAS("platform:iop-adma"); - static struct platform_driver iop_adma_driver = { .probe = iop_adma_probe, .remove = __devexit_p(iop_adma_remove), @@ -1746,19 +1744,9 @@ static struct platform_driver iop_adma_driver = { }, }; -static int __init iop_adma_init(void) -{ - return platform_driver_register(&iop_adma_driver); -} - -static void __exit iop_adma_exit(void) -{ - platform_driver_unregister(&iop_adma_driver); - return; -} -module_exit(iop_adma_exit); -module_init(iop_adma_init); +module_platform_driver(iop_adma_driver); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("IOP ADMA Engine Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:iop-adma"); diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 8ba4edc..4d6d4cf 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -835,17 +835,7 @@ static struct platform_driver mpc_dma_driver = { }, }; -static int __init mpc_dma_init(void) -{ - return platform_driver_register(&mpc_dma_driver); -} -module_init(mpc_dma_init); - -static void __exit mpc_dma_exit(void) -{ - platform_driver_unregister(&mpc_dma_driver); -} -module_exit(mpc_dma_exit); +module_platform_driver(mpc_dma_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Piotr Ziecik "); diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 55ec679..2333810 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -699,17 +699,7 @@ static struct platform_driver sirfsoc_dma_driver = { }, }; -static int __init sirfsoc_dma_init(void) -{ - return platform_driver_register(&sirfsoc_dma_driver); -} -module_init(sirfsoc_dma_init); - -static void __exit sirfsoc_dma_exit(void) -{ - platform_driver_unregister(&sirfsoc_dma_driver); -} -module_exit(sirfsoc_dma_exit); +module_platform_driver(sirfsoc_dma_driver); MODULE_AUTHOR("Rongjun Ying , " "Barry Song "); diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index d142ae1..a6f9c16 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -841,17 +841,7 @@ static struct platform_driver td_driver = { .remove = __exit_p(td_remove), }; -static int __init td_init(void) -{ - return platform_driver_register(&td_driver); -} -module_init(td_init); - -static void __exit td_exit(void) -{ - platform_driver_unregister(&td_driver); -} -module_exit(td_exit); +module_platform_driver(td_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Timberdale DMA controller driver"); -- cgit v0.10.2 From 8f0d30f9ee333f4fb3458a9a33f7ead5269ea8f3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 29 Nov 2011 12:56:50 +0530 Subject: dmaengine/amba-pl08x: Config ccfg and increment phychan_hold if phychan is true Currently, if plchan->phychan is true, we return immediately from prep_phy_chan(). We must configure txd->ccfg and increment phychan_hold before returning. Otherwise, request line number wouldn't be configured in this txd. Reported-by: Rajeev Kumar Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 41c62fd..9ebceca 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -854,8 +854,10 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, int ret; /* Check if we already have a channel */ - if (plchan->phychan) - return 0; + if (plchan->phychan) { + ch = plchan->phychan; + goto got_channel; + } ch = pl08x_get_phy_channel(pl08x, plchan); if (!ch) { @@ -880,21 +882,22 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, return -EBUSY; } ch->signal = ret; - - /* Assign the flow control signal to this channel */ - if (txd->direction == DMA_MEM_TO_DEV) - txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT; - else if (txd->direction == DMA_DEV_TO_MEM) - txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT; } + plchan->phychan = ch; dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n", ch->id, ch->signal, plchan->name); +got_channel: + /* Assign the flow control signal to this channel */ + if (txd->direction == DMA_MEM_TO_DEV) + txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT; + else if (txd->direction == DMA_DEV_TO_MEM) + txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT; + plchan->phychan_hold++; - plchan->phychan = ch; return 0; } -- cgit v0.10.2 From 5cd326fd27da347925019fcc041b79bad8dd55ed Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Wed, 30 Nov 2011 19:20:42 +0530 Subject: dmaengine/ste_dma40: allow fixed physical channel Allow logical channels to specify the physical channel they want to use. This is needed to avoid two peripherals operating on the same physical channel during some special use-cases. (like mmc and usb during a usb mass storage case). Signed-off-by: Rabin Vincent Signed-off-by: Narayanan G Reviewed-by: Linus Walleij Signed-off-by: Vinod Koul diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h index 3177bed..fd0ee84 100644 --- a/arch/arm/plat-nomadik/include/plat/ste_dma40.h +++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h @@ -113,7 +113,8 @@ struct stedma40_half_channel_info { * @dst_dev_type: Dst device type * @src_info: Parameters for dst half channel * @dst_info: Parameters for dst half channel - * + * @use_fixed_channel: if true, use physical channel specified by phy_channel + * @phy_channel: physical channel to use, only if use_fixed_channel is true * * This structure has to be filled by the client drivers. * It is recommended to do all dma configurations for clients in the machine. @@ -129,6 +130,9 @@ struct stedma40_chan_cfg { int dst_dev_type; struct stedma40_half_channel_info src_info; struct stedma40_half_channel_info dst_info; + + bool use_fixed_channel; + int phy_channel; }; /** diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index aff128a..972dc35 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1545,11 +1545,16 @@ static int d40_validate_conf(struct d40_chan *d40c, return res; } -static bool d40_alloc_mask_set(struct d40_phy_res *phy, bool is_src, - int log_event_line, bool is_log) +static bool d40_alloc_mask_set(struct d40_phy_res *phy, + bool is_src, int log_event_line, bool is_log, + bool *first_user) { unsigned long flags; spin_lock_irqsave(&phy->lock, flags); + + *first_user = ((phy->allocated_src | phy->allocated_dst) + == D40_ALLOC_FREE); + if (!is_log) { /* Physical interrupts are masked per physical full channel */ if (phy->allocated_src == D40_ALLOC_FREE && @@ -1630,7 +1635,7 @@ out: return is_free; } -static int d40_allocate_channel(struct d40_chan *d40c) +static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) { int dev_type; int event_group; @@ -1666,7 +1671,8 @@ static int d40_allocate_channel(struct d40_chan *d40c) for (i = 0; i < d40c->base->num_phy_chans; i++) { if (d40_alloc_mask_set(&phys[i], is_src, - 0, is_log)) + 0, is_log, + first_phy_user)) goto found_phy; } } else @@ -1676,7 +1682,8 @@ static int d40_allocate_channel(struct d40_chan *d40c) if (d40_alloc_mask_set(&phys[i], is_src, 0, - is_log)) + is_log, + first_phy_user)) goto found_phy; } } @@ -1692,6 +1699,25 @@ found_phy: /* Find logical channel */ for (j = 0; j < d40c->base->num_phy_chans; j += 8) { int phy_num = j + event_group * 2; + + if (d40c->dma_cfg.use_fixed_channel) { + i = d40c->dma_cfg.phy_channel; + + if ((i != phy_num) && (i != phy_num + 1)) { + dev_err(chan2dev(d40c), + "invalid fixed phy channel %d\n", i); + return -EINVAL; + } + + if (d40_alloc_mask_set(&phys[i], is_src, event_line, + is_log, first_phy_user)) + goto found_log; + + dev_err(chan2dev(d40c), + "could not allocate fixed phy channel %d\n", i); + return -EINVAL; + } + /* * Spread logical channels across all available physical rather * than pack every logical channel at the first available phy @@ -1700,13 +1726,15 @@ found_phy: if (is_src) { for (i = phy_num; i < phy_num + 2; i++) { if (d40_alloc_mask_set(&phys[i], is_src, - event_line, is_log)) + event_line, is_log, + first_phy_user)) goto found_log; } } else { for (i = phy_num + 1; i >= phy_num; i--) { if (d40_alloc_mask_set(&phys[i], is_src, - event_line, is_log)) + event_line, is_log, + first_phy_user)) goto found_log; } } @@ -2160,9 +2188,8 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) goto fail; } } - is_free_phy = (d40c->phy_chan == NULL); - err = d40_allocate_channel(d40c); + err = d40_allocate_channel(d40c, &is_free_phy); if (err) { chan_err(d40c, "Failed to allocate channel\n"); d40c->configured = false; @@ -2189,6 +2216,12 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; } + dev_dbg(chan2dev(d40c), "allocated %s channel (phy %d%s)\n", + chan_is_logical(d40c) ? "logical" : "physical", + d40c->phy_chan->num, + d40c->dma_cfg.use_fixed_channel ? ", fixed" : ""); + + /* * Only write channel configuration to the DMA if the physical * resource is free. In case of multiple logical channels -- cgit v0.10.2 From b755706cd726e5d465c28c2cd64c618419034981 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 7 Dec 2011 11:47:40 -0800 Subject: ARM: OMAP2+: board-generic: Add missing handle_irq callbacks The following commit: 6b2f55d7851aa358d3a99cff344c560c4967f042, is adding the support for the CONFIG_MULTI_IRQ_HANDLER but did not update all the machine descriptors supported in the DT board-generic.c file. It thus break the DT boot on OMAP3 and OMAP4 boards. Add the proper handle_irq callbacks for OMAP3 and OMAP4 generic machine descriptors. Signed-off-by: Benoit Cousson Acked-by: Marc Zyngier Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 63b5416..e493877 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -103,6 +104,7 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)") .map_io = omap242x_map_io, .init_early = omap2420_init_early, .init_irq = omap2_init_irq, + .handle_irq = omap2_intc_handle_irq, .init_machine = omap_generic_init, .timer = &omap2_timer, .dt_compat = omap242x_boards_compat, @@ -140,6 +142,7 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)") .map_io = omap3_map_io, .init_early = omap3430_init_early, .init_irq = omap3_init_irq, + .handle_irq = omap3_intc_handle_irq, .init_machine = omap3_init, .timer = &omap3_timer, .dt_compat = omap3_boards_compat, @@ -158,6 +161,7 @@ DT_MACHINE_START(OMAP4_DT, "Generic OMAP4 (Flattened Device Tree)") .map_io = omap4_map_io, .init_early = omap4430_init_early, .init_irq = gic_init_irq, + .handle_irq = gic_handle_irq, .init_machine = omap4_init, .timer = &omap4_timer, .dt_compat = omap4_boards_compat, -- cgit v0.10.2 From c99e78435342a65a6a0bf9b86f87fd05831858d2 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 1 Dec 2011 14:58:51 +0100 Subject: i.MX IPU DMA: Fix wrong burstsize settings The burstsize (npb in struct chan_param_mem) is set in ipu_ch_param_set_size() once. The number of allowed pixels in a burst depend on the pixel format and the rotation mode. For 16bit formats 16 pixels are allowed whereas for 32bit formats only 8 pixels are allowed. Set these values correctly in ipu_ch_param_set_size() and do not overwrite them afterwards. We do not support rotation right now, so ignore this case. This patch fixes the wrong burstsize setting of 16 pixels for 32bpp. Signed-off-by: Sascha Hauer Signed-off-by: Vinod Koul diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index e16e280..6212b16 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -312,7 +312,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, case IPU_PIX_FMT_RGB565: params->ip.bpp = 2; params->ip.pfs = 4; - params->ip.npb = 7; + params->ip.npb = 15; params->ip.sat = 2; /* SAT = 32-bit access */ params->ip.ofs0 = 0; /* Red bit offset */ params->ip.ofs1 = 5; /* Green bit offset */ @@ -422,12 +422,6 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, params->pp.nsb = 1; } -static void ipu_ch_param_set_burst_size(union chan_param_mem *params, - uint16_t burst_pixels) -{ - params->pp.npb = burst_pixels - 1; -} - static void ipu_ch_param_set_buffer(union chan_param_mem *params, dma_addr_t buf0, dma_addr_t buf1) { @@ -690,23 +684,6 @@ static int ipu_init_channel_buffer(struct idmac_channel *ichan, ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes); ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1); ipu_ch_param_set_rotation(¶ms, rot_mode); - /* Some channels (rotation) have restriction on burst length */ - switch (channel) { - case IDMAC_IC_7: /* Hangs with burst 8, 16, other values - invalid - Table 44-30 */ -/* - ipu_ch_param_set_burst_size(¶ms, 8); - */ - break; - case IDMAC_SDC_0: - case IDMAC_SDC_1: - /* In original code only IPU_PIX_FMT_RGB565 was setting burst */ - ipu_ch_param_set_burst_size(¶ms, 16); - break; - case IDMAC_IC_0: - default: - break; - } spin_lock_irqsave(&ipu->lock, flags); -- cgit v0.10.2 From f910fb8fcd1c97788f2291c8646597bcd87ee061 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 1 Dec 2011 14:58:52 +0100 Subject: video i.MX IPU: Fix display connections The IPU internally works on 32bit colors. It can arbitrarily map between pixel formats and internal representation and also between internal representation and the physical connection to the display. The driver used to change the mapping between internal representation and display connection depending on the user selected bpp which is wrong. Instead, the mapping is specified by the hardware, so an additional field in platform data is added to describe the connection between i.MX and the display. The default for this field is RGB666 which seems to be the only configuration which works without this patch, so I assumed that all in Kernel boards are connected this way. This patch has been tested on a RGB666 connected display and a RGB888 connected display in both 16bpp and 32bpp modes. Signed-off-by: Sascha Hauer Signed-off-by: Vinod Koul diff --git a/arch/arm/plat-mxc/include/mach/mx3fb.h b/arch/arm/plat-mxc/include/mach/mx3fb.h index ac24c5c..fdbe600 100644 --- a/arch/arm/plat-mxc/include/mach/mx3fb.h +++ b/arch/arm/plat-mxc/include/mach/mx3fb.h @@ -22,6 +22,20 @@ #define FB_SYNC_SWAP_RGB 0x04000000 #define FB_SYNC_CLK_SEL_EN 0x02000000 +/* + * Specify the way your display is connected. The IPU can arbitrarily + * map the internal colors to the external data lines. We only support + * the following mappings at the moment. + */ +enum disp_data_mapping { + /* blue -> d[0..5], green -> d[6..11], red -> d[12..17] */ + IPU_DISP_DATA_MAPPING_RGB666, + /* blue -> d[0..4], green -> d[5..10], red -> d[11..15] */ + IPU_DISP_DATA_MAPPING_RGB565, + /* blue -> d[0..7], green -> d[8..15], red -> d[16..23] */ + IPU_DISP_DATA_MAPPING_RGB888, +}; + /** * struct mx3fb_platform_data - mx3fb platform data * @@ -33,6 +47,7 @@ struct mx3fb_platform_data { const char *name; const struct fb_videomode *mode; int num_modes; + enum disp_data_mapping disp_data_fmt; }; #endif diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 1981e36..727a514 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -245,6 +245,7 @@ struct mx3fb_data { uint32_t h_start_width; uint32_t v_start_width; + enum disp_data_mapping disp_data_fmt; }; struct dma_chan_request { @@ -287,11 +288,14 @@ static void mx3fb_write_reg(struct mx3fb_data *mx3fb, u32 value, unsigned long r __raw_writel(value, mx3fb->reg_base + reg); } -static const uint32_t di_mappings[] = { - 0x1600AAAA, 0x00E05555, 0x00070000, 3, /* RGB888 */ - 0x0005000F, 0x000B000F, 0x0011000F, 1, /* RGB666 */ - 0x0011000F, 0x000B000F, 0x0005000F, 1, /* BGR666 */ - 0x0004003F, 0x000A000F, 0x000F003F, 1 /* RGB565 */ +struct di_mapping { + uint32_t b0, b1, b2; +}; + +static const struct di_mapping di_mappings[] = { + [IPU_DISP_DATA_MAPPING_RGB666] = { 0x0005000f, 0x000b000f, 0x0011000f }, + [IPU_DISP_DATA_MAPPING_RGB565] = { 0x0004003f, 0x000a000f, 0x000f003f }, + [IPU_DISP_DATA_MAPPING_RGB888] = { 0x00070000, 0x000f0000, 0x00170000 }, }; static void sdc_fb_init(struct mx3fb_info *fbi) @@ -425,7 +429,6 @@ static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel * @pixel_clk: desired pixel clock frequency in Hz. * @width: width of panel in pixels. * @height: height of panel in pixels. - * @pixel_fmt: pixel format of buffer as FOURCC ASCII code. * @h_start_width: number of pixel clocks between the HSYNC signal pulse * and the start of valid data. * @h_sync_width: width of the HSYNC signal in units of pixel clocks. @@ -442,7 +445,6 @@ static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, uint32_t pixel_clk, uint16_t width, uint16_t height, - enum pixel_fmt pixel_fmt, uint16_t h_start_width, uint16_t h_sync_width, uint16_t h_end_width, uint16_t v_start_width, uint16_t v_sync_width, uint16_t v_end_width, @@ -453,6 +455,7 @@ static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, uint32_t old_conf; uint32_t div; struct clk *ipu_clk; + const struct di_mapping *map; dev_dbg(mx3fb->dev, "panel size = %d x %d", width, height); @@ -540,36 +543,10 @@ static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, sig.Vsync_pol << DI_D3_VSYNC_POL_SHIFT; mx3fb_write_reg(mx3fb, old_conf, DI_DISP_SIG_POL); - switch (pixel_fmt) { - case IPU_PIX_FMT_RGB24: - mx3fb_write_reg(mx3fb, di_mappings[0], DI_DISP3_B0_MAP); - mx3fb_write_reg(mx3fb, di_mappings[1], DI_DISP3_B1_MAP); - mx3fb_write_reg(mx3fb, di_mappings[2], DI_DISP3_B2_MAP); - mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | - ((di_mappings[3] - 1) << 12), DI_DISP_ACC_CC); - break; - case IPU_PIX_FMT_RGB666: - mx3fb_write_reg(mx3fb, di_mappings[4], DI_DISP3_B0_MAP); - mx3fb_write_reg(mx3fb, di_mappings[5], DI_DISP3_B1_MAP); - mx3fb_write_reg(mx3fb, di_mappings[6], DI_DISP3_B2_MAP); - mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | - ((di_mappings[7] - 1) << 12), DI_DISP_ACC_CC); - break; - case IPU_PIX_FMT_BGR666: - mx3fb_write_reg(mx3fb, di_mappings[8], DI_DISP3_B0_MAP); - mx3fb_write_reg(mx3fb, di_mappings[9], DI_DISP3_B1_MAP); - mx3fb_write_reg(mx3fb, di_mappings[10], DI_DISP3_B2_MAP); - mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | - ((di_mappings[11] - 1) << 12), DI_DISP_ACC_CC); - break; - default: - mx3fb_write_reg(mx3fb, di_mappings[12], DI_DISP3_B0_MAP); - mx3fb_write_reg(mx3fb, di_mappings[13], DI_DISP3_B1_MAP); - mx3fb_write_reg(mx3fb, di_mappings[14], DI_DISP3_B2_MAP); - mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | - ((di_mappings[15] - 1) << 12), DI_DISP_ACC_CC); - break; - } + map = &di_mappings[mx3fb->disp_data_fmt]; + mx3fb_write_reg(mx3fb, map->b0, DI_DISP3_B0_MAP); + mx3fb_write_reg(mx3fb, map->b1, DI_DISP3_B1_MAP); + mx3fb_write_reg(mx3fb, map->b2, DI_DISP3_B2_MAP); spin_unlock_irqrestore(&mx3fb->lock, lock_flags); @@ -780,8 +757,6 @@ static int __set_par(struct fb_info *fbi, bool lock) if (sdc_init_panel(mx3fb, mode, (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, fbi->var.xres, fbi->var.yres, - (fbi->var.sync & FB_SYNC_SWAP_RGB) ? - IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666, fbi->var.left_margin, fbi->var.hsync_len, fbi->var.right_margin + @@ -1349,6 +1324,12 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) const struct fb_videomode *mode; int ret, num_modes; + if (mx3fb_pdata->disp_data_fmt >= ARRAY_SIZE(di_mappings)) { + dev_err(dev, "Illegal display data format %d\n", + mx3fb_pdata->disp_data_fmt); + return -EINVAL; + } + ichan->client = mx3fb; irq = ichan->eof_irq; @@ -1402,6 +1383,8 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) mx3fbi->mx3fb = mx3fb; mx3fbi->blank = FB_BLANK_NORMAL; + mx3fb->disp_data_fmt = mx3fb_pdata->disp_data_fmt; + init_completion(&mx3fbi->flip_cmpl); disable_irq(ichan->eof_irq); dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq); -- cgit v0.10.2 From 3506c0d507144d9b0f19efd5a56d289f70611179 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Tue, 6 Dec 2011 16:15:54 +0530 Subject: DMA: PL330: Remove pm_runtime_xxx calls from pl330 probe/remove amba_probe() now calls pm_runtime_get_noresume() and pm_runtime_enable() for the devices before the device probe is called. Hence we don't need to call pm_runtime_get_xxx and pm_runtime_enable() in device probe again. In the same way, since amba_remove() calls the respective pm_runtime functions, those functions need not be called from device remove. This patch fixes following run time error with pl330 driver. dma-pl330 dma-pl330.0: Unbalanced pm_runtime_enable! dma-pl330 dma-pl330.0: failed to get runtime pm Signed-off-by: Giridhar Maruthy Signed-off-by: Tushar Behera Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index e0da795..a573757 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -820,17 +820,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) amba_set_drvdata(adev, pdmac); -#ifdef CONFIG_PM_RUNTIME - /* to use the runtime PM helper functions */ - pm_runtime_enable(&adev->dev); - - /* enable the power domain */ - if (pm_runtime_get_sync(&adev->dev)) { - dev_err(&adev->dev, "failed to get runtime pm\n"); - ret = -ENODEV; - goto probe_err1; - } -#else +#ifndef CONFIG_PM_RUNTIME /* enable dma clk */ clk_enable(pdmac->clk); #endif @@ -971,10 +961,7 @@ static int __devexit pl330_remove(struct amba_device *adev) res = &adev->res; release_mem_region(res->start, resource_size(res)); -#ifdef CONFIG_PM_RUNTIME - pm_runtime_put(&adev->dev); - pm_runtime_disable(&adev->dev); -#else +#ifndef CONFIG_PM_RUNTIME clk_disable(pdmac->clk); #endif -- cgit v0.10.2 From e26d99aed42ec6cdc9540d19c77ac5d4dd2c5b00 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 14 Nov 2011 12:30:30 -0500 Subject: target: reshuffle headers Create a new headers, drivers/target/target_core_internal.h that is supposed to hold all target_core-internal prototypes. Move all non-exported includes from include/target to it, and merge the smaller prototype-only includes inside drivers/target into it as well. Mark functions that were found to not be called outside their implementation file static. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 1dcbef4..097eb5a 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -37,8 +37,8 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" -#include "target_core_hba.h" #include "target_core_ua.h" static int core_alua_check_transition(int state, int *primary); diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 831468b..8f2934f 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -31,8 +31,9 @@ #include #include #include + +#include "target_core_internal.h" #include "target_core_ua.h" -#include "target_core_cdb.h" static void target_fill_alua_data(struct se_port *port, unsigned char *buf) diff --git a/drivers/target/target_core_cdb.h b/drivers/target/target_core_cdb.h deleted file mode 100644 index ad6b1e3..0000000 --- a/drivers/target/target_core_cdb.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef TARGET_CORE_CDB_H -#define TARGET_CORE_CDB_H - -int target_emulate_inquiry(struct se_task *task); -int target_emulate_readcapacity(struct se_task *task); -int target_emulate_readcapacity_16(struct se_task *task); -int target_emulate_modesense(struct se_task *task); -int target_emulate_request_sense(struct se_task *task); -int target_emulate_unmap(struct se_task *task); -int target_emulate_write_same(struct se_task *task); -int target_emulate_synchronize_cache(struct se_task *task); -int target_emulate_noop(struct se_task *task); - -#endif /* TARGET_CORE_CDB_H */ diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 93d4f6a..ac55af5 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -46,11 +46,10 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" -#include "target_core_hba.h" #include "target_core_pr.h" #include "target_core_rd.h" -#include "target_core_stat.h" extern struct t10_alua_lu_gp *default_lu_gp; diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 9b86394..28ffe0b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -47,8 +47,8 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" -#include "target_core_hba.h" #include "target_core_pr.h" #include "target_core_ua.h" diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 09b6f87..fa1e4eb 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -44,10 +44,9 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" -#include "target_core_hba.h" #include "target_core_pr.h" -#include "target_core_stat.h" #define TF_CIT_SETUP(_name, _item_ops, _group_ops, _attrs) \ static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) \ diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index ec4249b..dd9ae92 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -40,7 +40,7 @@ #include #include -#include "target_core_hba.h" +#include "target_core_internal.h" #include "target_core_pr.h" /* diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c index c68019d..26c1238 100644 --- a/drivers/target/target_core_hba.c +++ b/drivers/target/target_core_hba.c @@ -41,7 +41,7 @@ #include #include -#include "target_core_hba.h" +#include "target_core_internal.h" static LIST_HEAD(subsystem_list); static DEFINE_MUTEX(subsystem_mutex); diff --git a/drivers/target/target_core_hba.h b/drivers/target/target_core_hba.h deleted file mode 100644 index bb0fea5..0000000 --- a/drivers/target/target_core_hba.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef TARGET_CORE_HBA_H -#define TARGET_CORE_HBA_H - -extern struct se_hba *core_alloc_hba(const char *, u32, u32); -extern int core_delete_hba(struct se_hba *); - -#endif /* TARGET_CORE_HBA_H */ diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h new file mode 100644 index 0000000..26f135e --- /dev/null +++ b/drivers/target/target_core_internal.h @@ -0,0 +1,123 @@ +#ifndef TARGET_CORE_INTERNAL_H +#define TARGET_CORE_INTERNAL_H + +/* target_core_alua.c */ +extern struct t10_alua_lu_gp *default_lu_gp; + +/* target_core_cdb.c */ +int target_emulate_inquiry(struct se_task *task); +int target_emulate_readcapacity(struct se_task *task); +int target_emulate_readcapacity_16(struct se_task *task); +int target_emulate_modesense(struct se_task *task); +int target_emulate_request_sense(struct se_task *task); +int target_emulate_unmap(struct se_task *task); +int target_emulate_write_same(struct se_task *task); +int target_emulate_synchronize_cache(struct se_task *task); +int target_emulate_noop(struct se_task *task); + +/* target_core_device.c */ +struct se_dev_entry *core_get_se_deve_from_rtpi(struct se_node_acl *, u16); +int core_free_device_list_for_node(struct se_node_acl *, + struct se_portal_group *); +void core_dec_lacl_count(struct se_node_acl *, struct se_cmd *); +void core_update_device_list_access(u32, u32, struct se_node_acl *); +int core_update_device_list_for_node(struct se_lun *, struct se_lun_acl *, + u32, u32, struct se_node_acl *, struct se_portal_group *, int); +void core_clear_lun_from_tpg(struct se_lun *, struct se_portal_group *); +int core_dev_export(struct se_device *, struct se_portal_group *, + struct se_lun *); +void core_dev_unexport(struct se_device *, struct se_portal_group *, + struct se_lun *); +int target_report_luns(struct se_task *); +void se_release_device_for_hba(struct se_device *); +void se_release_vpd_for_dev(struct se_device *); +int se_free_virtual_device(struct se_device *, struct se_hba *); +int se_dev_check_online(struct se_device *); +int se_dev_check_shutdown(struct se_device *); +void se_dev_set_default_attribs(struct se_device *, struct se_dev_limits *); +int se_dev_set_task_timeout(struct se_device *, u32); +int se_dev_set_max_unmap_lba_count(struct se_device *, u32); +int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32); +int se_dev_set_unmap_granularity(struct se_device *, u32); +int se_dev_set_unmap_granularity_alignment(struct se_device *, u32); +int se_dev_set_emulate_dpo(struct se_device *, int); +int se_dev_set_emulate_fua_write(struct se_device *, int); +int se_dev_set_emulate_fua_read(struct se_device *, int); +int se_dev_set_emulate_write_cache(struct se_device *, int); +int se_dev_set_emulate_ua_intlck_ctrl(struct se_device *, int); +int se_dev_set_emulate_tas(struct se_device *, int); +int se_dev_set_emulate_tpu(struct se_device *, int); +int se_dev_set_emulate_tpws(struct se_device *, int); +int se_dev_set_enforce_pr_isids(struct se_device *, int); +int se_dev_set_is_nonrot(struct se_device *, int); +int se_dev_set_emulate_rest_reord(struct se_device *dev, int); +int se_dev_set_queue_depth(struct se_device *, u32); +int se_dev_set_max_sectors(struct se_device *, u32); +int se_dev_set_optimal_sectors(struct se_device *, u32); +int se_dev_set_block_size(struct se_device *, u32); +struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_hba *, + struct se_device *, u32); +int core_dev_del_lun(struct se_portal_group *, u32); +struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32); +struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *, + u32, char *, int *); +int core_dev_add_initiator_node_lun_acl(struct se_portal_group *, + struct se_lun_acl *, u32, u32); +int core_dev_del_initiator_node_lun_acl(struct se_portal_group *, + struct se_lun *, struct se_lun_acl *); +void core_dev_free_initiator_node_lun_acl(struct se_portal_group *, + struct se_lun_acl *lacl); +int core_dev_setup_virtual_lun0(void); +void core_dev_release_virtual_lun0(void); + +/* target_core_hba.c */ +struct se_hba *core_alloc_hba(const char *, u32, u32); +int core_delete_hba(struct se_hba *); + +/* target_core_tmr.c */ +int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *, + struct list_head *, struct se_cmd *); + +/* target_core_tpg.c */ +extern struct se_device *g_lun0_dev; + +struct se_node_acl *__core_tpg_get_initiator_node_acl(struct se_portal_group *tpg, + const char *); +struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg, + unsigned char *); +void core_tpg_add_node_to_devs(struct se_node_acl *, struct se_portal_group *); +void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *); +struct se_lun *core_tpg_pre_addlun(struct se_portal_group *, u32); +int core_tpg_post_addlun(struct se_portal_group *, struct se_lun *, + u32, void *); +struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32, int *); +int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); + +/* target_core_transport.c */ +extern struct kmem_cache *se_tmr_req_cache; + +int init_se_kmem_caches(void); +void release_se_kmem_caches(void); +u32 scsi_get_new_index(scsi_index_t); +void transport_subsystem_check_init(void); +void transport_cmd_finish_abort(struct se_cmd *, int); +void __transport_remove_task_from_execute_queue(struct se_task *, + struct se_device *); +unsigned char *transport_dump_cmd_direction(struct se_cmd *); +void transport_dump_dev_state(struct se_device *, char *, int *); +void transport_dump_dev_info(struct se_device *, struct se_lun *, + unsigned long long, char *, int *); +void transport_dump_vpd_proto_id(struct t10_vpd *, unsigned char *, int); +int transport_dump_vpd_assoc(struct t10_vpd *, unsigned char *, int); +int transport_dump_vpd_ident_type(struct t10_vpd *, unsigned char *, int); +int transport_dump_vpd_ident(struct t10_vpd *, unsigned char *, int); +bool target_stop_task(struct se_task *task, unsigned long *flags); +int transport_clear_lun_from_sessions(struct se_lun *); +void transport_send_task_abort(struct se_cmd *); + +/* target_core_stat.c */ +void target_stat_setup_dev_default_groups(struct se_subsystem_dev *); +void target_stat_setup_port_default_groups(struct se_lun *); +void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *); + +#endif /* TARGET_CORE_INTERNAL_H */ diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 95dee70..36fa9a5 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -40,7 +40,7 @@ #include #include -#include "target_core_hba.h" +#include "target_core_internal.h" #include "target_core_pr.h" #include "target_core_ua.h" diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index 874152a..d2ba926 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -48,7 +48,7 @@ #include #include -#include "target_core_hba.h" +#include "target_core_internal.h" #ifndef INITIAL_JIFFIES #define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ)) diff --git a/drivers/target/target_core_stat.h b/drivers/target/target_core_stat.h deleted file mode 100644 index 86c252f..0000000 --- a/drivers/target/target_core_stat.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TARGET_CORE_STAT_H -#define TARGET_CORE_STAT_H - -extern void target_stat_setup_dev_default_groups(struct se_subsystem_dev *); -extern void target_stat_setup_port_default_groups(struct se_lun *); -extern void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *); - -#endif /*** TARGET_CORE_STAT_H ***/ diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 6845228..47bc26b 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -38,6 +38,7 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" #include "target_core_pr.h" diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 8ddd133..0a936fa 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -44,8 +44,7 @@ #include #include -#include "target_core_hba.h" -#include "target_core_stat.h" +#include "target_core_internal.h" extern struct se_device *g_lun0_dev; diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0257658..e0ea63d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -52,9 +52,8 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" -#include "target_core_cdb.h" -#include "target_core_hba.h" #include "target_core_pr.h" #include "target_core_ua.h" @@ -212,14 +211,13 @@ u32 scsi_get_new_index(scsi_index_t type) return new_index; } -void transport_init_queue_obj(struct se_queue_obj *qobj) +static void transport_init_queue_obj(struct se_queue_obj *qobj) { atomic_set(&qobj->queue_cnt, 0); INIT_LIST_HEAD(&qobj->qobj_list); init_waitqueue_head(&qobj->thread_wq); spin_lock_init(&qobj->cmd_queue_lock); } -EXPORT_SYMBOL(transport_init_queue_obj); void transport_subsystem_check_init(void) { @@ -896,7 +894,7 @@ void __transport_remove_task_from_execute_queue(struct se_task *task, atomic_dec(&dev->execute_tasks); } -void transport_remove_task_from_execute_queue( +static void transport_remove_task_from_execute_queue( struct se_task *task, struct se_device *dev) { @@ -3346,6 +3344,32 @@ static inline void transport_free_pages(struct se_cmd *cmd) } /** + * transport_release_cmd - free a command + * @cmd: command to free + * + * This routine unconditionally frees a command, and reference counting + * or list removal must be done in the caller. + */ +static void transport_release_cmd(struct se_cmd *cmd) +{ + BUG_ON(!cmd->se_tfo); + + if (cmd->se_tmr_req) + core_tmr_release_req(cmd->se_tmr_req); + if (cmd->t_task_cdb != cmd->__t_task_cdb) + kfree(cmd->t_task_cdb); + /* + * Check if target_wait_for_sess_cmds() is expecting to + * release se_cmd directly here.. + */ + if (cmd->check_release != 0 && cmd->se_tfo->check_release_cmd) + if (cmd->se_tfo->check_release_cmd(cmd) != 0) + return; + + cmd->se_tfo->release_cmd(cmd); +} + +/** * transport_put_cmd - release a reference to a command * @cmd: command to release * @@ -3870,33 +3894,6 @@ queue_full: return 0; } -/** - * transport_release_cmd - free a command - * @cmd: command to free - * - * This routine unconditionally frees a command, and reference counting - * or list removal must be done in the caller. - */ -void transport_release_cmd(struct se_cmd *cmd) -{ - BUG_ON(!cmd->se_tfo); - - if (cmd->se_tmr_req) - core_tmr_release_req(cmd->se_tmr_req); - if (cmd->t_task_cdb != cmd->__t_task_cdb) - kfree(cmd->t_task_cdb); - /* - * Check if target_wait_for_sess_cmds() is expecting to - * release se_cmd directly here.. - */ - if (cmd->check_release != 0 && cmd->se_tfo->check_release_cmd) - if (cmd->se_tfo->check_release_cmd(cmd) != 0) - return; - - cmd->se_tfo->release_cmd(cmd); -} -EXPORT_SYMBOL(transport_release_cmd); - void transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) { if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) { @@ -4545,11 +4542,7 @@ void transport_send_task_abort(struct se_cmd *cmd) cmd->se_tfo->queue_status(cmd); } -/* transport_generic_do_tmr(): - * - * - */ -int transport_generic_do_tmr(struct se_cmd *cmd) +static int transport_generic_do_tmr(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; struct se_tmr_req *tmr = cmd->se_tmr_req; diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 50a480d..e2ae53c 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -35,8 +35,8 @@ #include #include +#include "target_core_internal.h" #include "target_core_alua.h" -#include "target_core_hba.h" #include "target_core_pr.h" #include "target_core_ua.h" diff --git a/include/target/target_core_device.h b/include/target/target_core_device.h index 2be31ff..c6123a4 100644 --- a/include/target/target_core_device.h +++ b/include/target/target_core_device.h @@ -1,63 +1,9 @@ #ifndef TARGET_CORE_DEVICE_H #define TARGET_CORE_DEVICE_H + +// external extern int transport_lookup_cmd_lun(struct se_cmd *, u32); extern int transport_lookup_tmr_lun(struct se_cmd *, u32); -extern struct se_dev_entry *core_get_se_deve_from_rtpi( - struct se_node_acl *, u16); -extern int core_free_device_list_for_node(struct se_node_acl *, - struct se_portal_group *); -extern void core_dec_lacl_count(struct se_node_acl *, struct se_cmd *); -extern void core_update_device_list_access(u32, u32, struct se_node_acl *); -extern int core_update_device_list_for_node(struct se_lun *, struct se_lun_acl *, u32, - u32, struct se_node_acl *, - struct se_portal_group *, int); -extern void core_clear_lun_from_tpg(struct se_lun *, struct se_portal_group *); -extern int core_dev_export(struct se_device *, struct se_portal_group *, - struct se_lun *); -extern void core_dev_unexport(struct se_device *, struct se_portal_group *, - struct se_lun *); -extern int target_report_luns(struct se_task *); -extern void se_release_device_for_hba(struct se_device *); -extern void se_release_vpd_for_dev(struct se_device *); -extern void se_clear_dev_ports(struct se_device *); -extern int se_free_virtual_device(struct se_device *, struct se_hba *); -extern int se_dev_check_online(struct se_device *); -extern int se_dev_check_shutdown(struct se_device *); -extern void se_dev_set_default_attribs(struct se_device *, struct se_dev_limits *); -extern int se_dev_set_task_timeout(struct se_device *, u32); -extern int se_dev_set_max_unmap_lba_count(struct se_device *, u32); -extern int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32); -extern int se_dev_set_unmap_granularity(struct se_device *, u32); -extern int se_dev_set_unmap_granularity_alignment(struct se_device *, u32); -extern int se_dev_set_emulate_dpo(struct se_device *, int); -extern int se_dev_set_emulate_fua_write(struct se_device *, int); -extern int se_dev_set_emulate_fua_read(struct se_device *, int); -extern int se_dev_set_emulate_write_cache(struct se_device *, int); -extern int se_dev_set_emulate_ua_intlck_ctrl(struct se_device *, int); -extern int se_dev_set_emulate_tas(struct se_device *, int); -extern int se_dev_set_emulate_tpu(struct se_device *, int); -extern int se_dev_set_emulate_tpws(struct se_device *, int); -extern int se_dev_set_enforce_pr_isids(struct se_device *, int); -extern int se_dev_set_is_nonrot(struct se_device *, int); -extern int se_dev_set_emulate_rest_reord(struct se_device *dev, int); -extern int se_dev_set_queue_depth(struct se_device *, u32); -extern int se_dev_set_max_sectors(struct se_device *, u32); -extern int se_dev_set_optimal_sectors(struct se_device *, u32); -extern int se_dev_set_block_size(struct se_device *, u32); -extern struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_hba *, - struct se_device *, u32); -extern int core_dev_del_lun(struct se_portal_group *, u32); -extern struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32); -extern struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *, - u32, char *, int *); -extern int core_dev_add_initiator_node_lun_acl(struct se_portal_group *, - struct se_lun_acl *, u32, u32); -extern int core_dev_del_initiator_node_lun_acl(struct se_portal_group *, - struct se_lun *, struct se_lun_acl *); -extern void core_dev_free_initiator_node_lun_acl(struct se_portal_group *, - struct se_lun_acl *lacl); -extern int core_dev_setup_virtual_lun0(void); -extern void core_dev_release_virtual_lun0(void); #endif /* TARGET_CORE_DEVICE_H */ diff --git a/include/target/target_core_tmr.h b/include/target/target_core_tmr.h index d5876e1..36b3e0d 100644 --- a/include/target/target_core_tmr.h +++ b/include/target/target_core_tmr.h @@ -25,11 +25,7 @@ enum tcm_tmrsp_table { TMR_FUNCTION_REJECTED = 255, }; -extern struct kmem_cache *se_tmr_req_cache; - extern struct se_tmr_req *core_tmr_alloc_req(struct se_cmd *, void *, u8, gfp_t); extern void core_tmr_release_req(struct se_tmr_req *); -extern int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *, - struct list_head *, struct se_cmd *); #endif /* TARGET_CORE_TMR_H */ diff --git a/include/target/target_core_tpg.h b/include/target/target_core_tpg.h index 77e1872..80ae59f 100644 --- a/include/target/target_core_tpg.h +++ b/include/target/target_core_tpg.h @@ -1,17 +1,9 @@ #ifndef TARGET_CORE_TPG_H #define TARGET_CORE_TPG_H -extern struct se_node_acl *__core_tpg_get_initiator_node_acl(struct se_portal_group *tpg, - const char *); -extern struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg, - unsigned char *); -extern void core_tpg_add_node_to_devs(struct se_node_acl *, - struct se_portal_group *); extern struct se_node_acl *core_tpg_check_initiator_node_acl( struct se_portal_group *, unsigned char *); -extern void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *); -extern void core_tpg_wait_for_mib_ref(struct se_node_acl *); extern void core_tpg_clear_object_luns(struct se_portal_group *); extern struct se_node_acl *core_tpg_add_initiator_node_acl( struct se_portal_group *, @@ -26,10 +18,5 @@ extern int core_tpg_register(struct target_core_fabric_ops *, struct se_portal_group *, void *, int); extern int core_tpg_deregister(struct se_portal_group *); -extern struct se_lun *core_tpg_pre_addlun(struct se_portal_group *, u32); -extern int core_tpg_post_addlun(struct se_portal_group *, struct se_lun *, u32, - void *); -extern struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32, int *); -extern int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); #endif /* TARGET_CORE_TPG_H */ diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h index dac4f2d..68fe1f2 100644 --- a/include/target/target_core_transport.h +++ b/include/target/target_core_transport.h @@ -76,14 +76,8 @@ struct se_subsystem_api; -extern int init_se_kmem_caches(void); -extern void release_se_kmem_caches(void); -extern u32 scsi_get_new_index(scsi_index_t); -extern void transport_init_queue_obj(struct se_queue_obj *); -extern void transport_subsystem_check_init(void); extern int transport_subsystem_register(struct se_subsystem_api *); extern void transport_subsystem_release(struct se_subsystem_api *); -extern void transport_load_plugins(void); extern struct se_session *transport_init_session(void); extern void __transport_register_session(struct se_portal_group *, struct se_node_acl *, @@ -94,32 +88,14 @@ extern void transport_register_session(struct se_portal_group *, extern void transport_free_session(struct se_session *); extern void transport_deregister_session_configfs(struct se_session *); extern void transport_deregister_session(struct se_session *); -extern void transport_cmd_finish_abort(struct se_cmd *, int); extern void transport_complete_sync_cache(struct se_cmd *, int); extern void transport_complete_task(struct se_task *, int); -extern void transport_add_task_to_execute_queue(struct se_task *, - struct se_task *, - struct se_device *); -extern void transport_remove_task_from_execute_queue(struct se_task *, - struct se_device *); -extern void __transport_remove_task_from_execute_queue(struct se_task *, - struct se_device *); -unsigned char *transport_dump_cmd_direction(struct se_cmd *); -extern void transport_dump_dev_state(struct se_device *, char *, int *); -extern void transport_dump_dev_info(struct se_device *, struct se_lun *, - unsigned long long, char *, int *); -extern void transport_dump_vpd_proto_id(struct t10_vpd *, - unsigned char *, int); + extern void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *); -extern int transport_dump_vpd_assoc(struct t10_vpd *, - unsigned char *, int); extern int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); -extern int transport_dump_vpd_ident_type(struct t10_vpd *, - unsigned char *, int); extern int transport_set_vpd_ident_type(struct t10_vpd *, unsigned char *); -extern int transport_dump_vpd_ident(struct t10_vpd *, - unsigned char *, int); extern int transport_set_vpd_ident(struct t10_vpd *, unsigned char *); + extern struct se_device *transport_add_device_to_core_hba(struct se_hba *, struct se_subsystem_api *, struct se_subsystem_dev *, u32, @@ -136,29 +112,22 @@ extern int transport_handle_cdb_direct(struct se_cmd *); extern int transport_generic_handle_cdb_map(struct se_cmd *); extern int transport_generic_handle_data(struct se_cmd *); extern int transport_generic_handle_tmr(struct se_cmd *); -extern bool target_stop_task(struct se_task *task, unsigned long *flags); extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32, struct scatterlist *, u32); -extern int transport_clear_lun_from_sessions(struct se_lun *); extern bool transport_wait_for_tasks(struct se_cmd *); extern int transport_check_aborted_status(struct se_cmd *, int); extern int transport_send_check_condition_and_sense(struct se_cmd *, u8, int); -extern void transport_send_task_abort(struct se_cmd *); -extern void transport_release_cmd(struct se_cmd *); extern void transport_generic_free_cmd(struct se_cmd *, int); extern void target_get_sess_cmd(struct se_session *, struct se_cmd *); extern int target_put_sess_cmd(struct se_session *, struct se_cmd *); extern void target_splice_sess_cmd_list(struct se_session *); extern void target_wait_for_sess_cmds(struct se_session *, int); -extern void transport_generic_wait_for_cmds(struct se_cmd *, int); extern void transport_do_task_sg_chain(struct se_cmd *); extern void transport_generic_process_write(struct se_cmd *); extern int transport_generic_new_cmd(struct se_cmd *); -extern int transport_generic_do_tmr(struct se_cmd *); /* From target_core_alua.c */ extern int core_alua_check_nonop_delay(struct se_cmd *); /* From target_core_cdb.c */ -extern int transport_emulate_control_cdb(struct se_task *); extern void target_get_task_cdb(struct se_task *task, unsigned char *cdb); /* -- cgit v0.10.2 From c4795fb20edf2fe2c862c8fe9f8b681edeb79ac1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Nov 2011 09:46:48 -0500 Subject: target: header reshuffle, part2 This reorganized the headers under include/target into: - target_core_base.h stays as is with all target-wide data stuctures and defines - target_core_backend.h contains the whole interface to I/O backends - target_core_fabric.h contains the whole interface to fabric modules Except for those only the various configfs macro headers stay around. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/Documentation/target/tcm_mod_builder.py b/Documentation/target/tcm_mod_builder.py index 7ef9b84..e2abb29 100755 --- a/Documentation/target/tcm_mod_builder.py +++ b/Documentation/target/tcm_mod_builder.py @@ -230,14 +230,9 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "#include \n" buf += "#include \n\n" buf += "#include \n" - buf += "#include \n" - buf += "#include \n" + buf += "#include \n" buf += "#include \n" - buf += "#include \n" - buf += "#include \n" - buf += "#include \n" buf += "#include \n" - buf += "#include \n" buf += "#include \n\n" buf += "#include \"" + fabric_mod_name + "_base.h\"\n" buf += "#include \"" + fabric_mod_name + "_fabric.h\"\n\n" @@ -514,7 +509,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): def tcm_mod_scan_fabric_ops(tcm_dir): - fabric_ops_api = tcm_dir + "include/target/target_core_fabric_ops.h" + fabric_ops_api = tcm_dir + "include/target/target_core_fabric.h" print "Using tcm_mod_scan_fabric_ops: " + fabric_ops_api process_fo = 0; @@ -579,11 +574,7 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "#include \n" buf += "#include \n\n" buf += "#include \n" - buf += "#include \n" - buf += "#include \n" - buf += "#include \n" - buf += "#include \n" - buf += "#include \n" + buf += "#include \n" buf += "#include \n\n" buf += "#include \"" + fabric_mod_name + "_base.h\"\n" buf += "#include \"" + fabric_mod_name + "_fabric.h\"\n\n" diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 8599545..f74a62d 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -27,8 +27,7 @@ #include #include #include -#include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_parameters.h" diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index db32784..c04d389 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -22,12 +22,8 @@ #include #include #include -#include -#include +#include #include -#include -#include -#include #include #include diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c index a19fa5e..f63ea35 100644 --- a/drivers/target/iscsi/iscsi_target_device.c +++ b/drivers/target/iscsi/iscsi_target_device.c @@ -21,8 +21,7 @@ #include #include -#include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_device.h" diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index b7ffc3c..4784511 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_seq_pdu_list.h" diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 101b1be..255c0d6 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_seq_pdu_list.h" diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c index 0b8404c..1af1f21 100644 --- a/drivers/target/iscsi/iscsi_target_erl2.c +++ b/drivers/target/iscsi/iscsi_target_erl2.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_datain_values.h" diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index d734bde..d74e278e 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_tq.h" diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 98936cb..53eea5c 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_parameters.h" diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c index aeafbe0..ac047d8 100644 --- a/drivers/target/iscsi/iscsi_target_nodeattrib.c +++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c @@ -19,7 +19,6 @@ ******************************************************************************/ #include -#include #include "iscsi_target_core.h" #include "iscsi_target_device.h" diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index f1db830..5a1aac0 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "iscsi_target_core.h" diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c index 490207e..255ed35 100644 --- a/drivers/target/iscsi/iscsi_target_tmr.c +++ b/drivers/target/iscsi/iscsi_target_tmr.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "iscsi_target_core.h" #include "iscsi_target_seq_pdu_list.h" diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index d4cf2cd..cfbcf6e 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -19,10 +19,8 @@ ******************************************************************************/ #include -#include -#include +#include #include -#include #include "iscsi_target_core.h" #include "iscsi_target_erl0.h" diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 02348f7..c1daea1 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -22,9 +22,7 @@ #include #include #include -#include -#include -#include +#include #include #include "iscsi_target_core.h" diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 81d5832..d427a29 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -33,14 +33,9 @@ #include #include -#include -#include +#include #include -#include #include -#include -#include -#include #include "tcm_loop.h" diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 097eb5a..1b1edd1 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -32,9 +32,8 @@ #include #include -#include -#include -#include +#include +#include #include #include "target_core_internal.h" diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 8f2934f..5921950 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -29,8 +29,8 @@ #include #include -#include -#include +#include +#include #include "target_core_internal.h" #include "target_core_ua.h" diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index ac55af5..3c35890 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -39,9 +39,8 @@ #include #include -#include -#include -#include +#include +#include #include #include #include diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 28ffe0b..1f74de2 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -42,10 +42,8 @@ #include #include -#include -#include -#include -#include +#include +#include #include "target_core_internal.h" #include "target_core_alua.h" diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index fa1e4eb..4f77cce 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -36,10 +36,7 @@ #include #include -#include -#include -#include -#include +#include #include #include #include diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index dd9ae92..32d7feb 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -34,10 +34,7 @@ #include #include -#include -#include -#include -#include +#include #include #include "target_core_internal.h" diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index b4864fb..ed7e053 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -37,8 +37,7 @@ #include #include -#include -#include +#include #include "target_core_file.h" diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c index 26c1238..3dd1bd4 100644 --- a/drivers/target/target_core_hba.c +++ b/drivers/target/target_core_hba.c @@ -37,9 +37,8 @@ #include #include -#include -#include -#include +#include +#include #include "target_core_internal.h" diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 4aa9922..feebbfe 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -42,8 +42,7 @@ #include #include -#include -#include +#include #include "target_core_iblock.h" diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 36fa9a5..d2b73b3 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -33,11 +33,8 @@ #include #include -#include -#include -#include -#include -#include +#include +#include #include #include "target_core_internal.h" diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 8b15e56..5d8851d 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -44,8 +44,7 @@ #include #include -#include -#include +#include #include "target_core_pscsi.h" diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 02e51fa..94b4087 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -37,9 +37,7 @@ #include #include -#include -#include -#include +#include #include "target_core_rd.h" diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index d2ba926..6d8a904 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -43,8 +43,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 47bc26b..fc9d748 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -32,10 +32,8 @@ #include #include -#include -#include -#include -#include +#include +#include #include #include "target_core_internal.h" diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 0a936fa..b766802 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -39,10 +39,8 @@ #include #include -#include -#include -#include -#include +#include +#include #include "target_core_internal.h" diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index e0ea63d..72c13b7 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -45,11 +45,8 @@ #include #include -#include -#include -#include -#include -#include +#include +#include #include #include "target_core_internal.h" diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index e2ae53c..3e12f6b 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -30,9 +30,7 @@ #include #include -#include -#include -#include +#include #include #include "target_core_internal.h" diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 71fc9ce..dbbbc83 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -39,12 +39,8 @@ #include #include -#include -#include -#include -#include +#include #include -#include #include #include "tcm_fc.h" diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 9402b73..73852fb 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -41,12 +41,8 @@ #include #include -#include -#include +#include #include -#include -#include -#include #include #include diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index 1369b1c..d8cabc2 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -48,10 +48,7 @@ #include #include -#include -#include -#include -#include +#include #include #include diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 3269213..4c0507c 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -40,10 +40,7 @@ #include #include -#include -#include -#include -#include +#include #include #include diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h new file mode 100644 index 0000000..4866499 --- /dev/null +++ b/include/target/target_core_backend.h @@ -0,0 +1,65 @@ +#ifndef TARGET_CORE_BACKEND_H +#define TARGET_CORE_BACKEND_H + +#define TRANSPORT_PLUGIN_PHBA_PDEV 1 +#define TRANSPORT_PLUGIN_VHBA_PDEV 2 +#define TRANSPORT_PLUGIN_VHBA_VDEV 3 + +struct se_subsystem_api { + struct list_head sub_api_list; + + char name[16]; + struct module *owner; + + u8 transport_type; + + unsigned int fua_write_emulated : 1; + unsigned int write_cache_emulated : 1; + + int (*attach_hba)(struct se_hba *, u32); + void (*detach_hba)(struct se_hba *); + int (*pmode_enable_hba)(struct se_hba *, unsigned long); + void *(*allocate_virtdevice)(struct se_hba *, const char *); + struct se_device *(*create_virtdevice)(struct se_hba *, + struct se_subsystem_dev *, void *); + void (*free_device)(void *); + int (*transport_complete)(struct se_task *task); + struct se_task *(*alloc_task)(unsigned char *cdb); + int (*do_task)(struct se_task *); + int (*do_discard)(struct se_device *, sector_t, u32); + void (*do_sync_cache)(struct se_task *); + void (*free_task)(struct se_task *); + ssize_t (*check_configfs_dev_params)(struct se_hba *, + struct se_subsystem_dev *); + ssize_t (*set_configfs_dev_params)(struct se_hba *, + struct se_subsystem_dev *, const char *, ssize_t); + ssize_t (*show_configfs_dev_params)(struct se_hba *, + struct se_subsystem_dev *, char *); + u32 (*get_device_rev)(struct se_device *); + u32 (*get_device_type)(struct se_device *); + sector_t (*get_blocks)(struct se_device *); + unsigned char *(*get_sense_buffer)(struct se_task *); +}; + +int transport_subsystem_register(struct se_subsystem_api *); +void transport_subsystem_release(struct se_subsystem_api *); + +struct se_device *transport_add_device_to_core_hba(struct se_hba *, + struct se_subsystem_api *, struct se_subsystem_dev *, u32, + void *, struct se_dev_limits *, const char *, const char *); + +void transport_complete_sync_cache(struct se_cmd *, int); +void transport_complete_task(struct se_task *, int); + +void target_get_task_cdb(struct se_task *, unsigned char *); + +void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *); +int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); +int transport_set_vpd_ident_type(struct t10_vpd *, unsigned char *); +int transport_set_vpd_ident(struct t10_vpd *, unsigned char *); + +/* core helpers also used by command snooping in pscsi */ +void *transport_kmap_first_data_page(struct se_cmd *); +void transport_kunmap_first_data_page(struct se_cmd *); + +#endif /* TARGET_CORE_BACKEND_H */ diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 6873c7d..1d90fb3 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -10,6 +10,7 @@ #include #define TARGET_CORE_MOD_VERSION "v4.1.0-rc1-ml" +#define TARGET_CORE_VERSION TARGET_CORE_MOD_VERSION /* Maximum Number of LUNs per Target Portal Group */ /* Don't raise above 511 or REPORT_LUNS needs to handle >1 page */ @@ -53,6 +54,72 @@ /* Used by transport_get_inquiry_vpd_device_ident() */ #define INQUIRY_VPD_DEVICE_IDENTIFIER_LEN 254 +/* Attempts before moving from SHORT to LONG */ +#define PYX_TRANSPORT_WINDOW_CLOSED_THRESHOLD 3 +#define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_SHORT 3 /* In milliseconds */ +#define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_LONG 10 /* In milliseconds */ + +#define PYX_TRANSPORT_STATUS_INTERVAL 5 /* In seconds */ + +/* + * struct se_subsystem_dev->su_dev_flags +*/ +#define SDF_FIRMWARE_VPD_UNIT_SERIAL 0x00000001 +#define SDF_EMULATED_VPD_UNIT_SERIAL 0x00000002 +#define SDF_USING_UDEV_PATH 0x00000004 +#define SDF_USING_ALIAS 0x00000008 + +/* + * struct se_device->dev_flags + */ +#define DF_READ_ONLY 0x00000001 +#define DF_SPC2_RESERVATIONS 0x00000002 +#define DF_SPC2_RESERVATIONS_WITH_ISID 0x00000004 + +/* struct se_dev_attrib sanity values */ +/* Default max_unmap_lba_count */ +#define DA_MAX_UNMAP_LBA_COUNT 0 +/* Default max_unmap_block_desc_count */ +#define DA_MAX_UNMAP_BLOCK_DESC_COUNT 0 +/* Default unmap_granularity */ +#define DA_UNMAP_GRANULARITY_DEFAULT 0 +/* Default unmap_granularity_alignment */ +#define DA_UNMAP_GRANULARITY_ALIGNMENT_DEFAULT 0 +/* Emulation for Direct Page Out */ +#define DA_EMULATE_DPO 0 +/* Emulation for Forced Unit Access WRITEs */ +#define DA_EMULATE_FUA_WRITE 1 +/* Emulation for Forced Unit Access READs */ +#define DA_EMULATE_FUA_READ 0 +/* Emulation for WriteCache and SYNCHRONIZE_CACHE */ +#define DA_EMULATE_WRITE_CACHE 0 +/* Emulation for UNIT ATTENTION Interlock Control */ +#define DA_EMULATE_UA_INTLLCK_CTRL 0 +/* Emulation for TASK_ABORTED status (TAS) by default */ +#define DA_EMULATE_TAS 1 +/* Emulation for Thin Provisioning UNMAP using block/blk-lib.c:blkdev_issue_discard() */ +#define DA_EMULATE_TPU 0 +/* + * Emulation for Thin Provisioning WRITE_SAME w/ UNMAP=1 bit using + * block/blk-lib.c:blkdev_issue_discard() + */ +#define DA_EMULATE_TPWS 0 +/* No Emulation for PSCSI by default */ +#define DA_EMULATE_RESERVATIONS 0 +/* No Emulation for PSCSI by default */ +#define DA_EMULATE_ALUA 0 +/* Enforce SCSI Initiator Port TransportID with 'ISID' for PR */ +#define DA_ENFORCE_PR_ISIDS 1 +#define DA_STATUS_MAX_SECTORS_MIN 16 +#define DA_STATUS_MAX_SECTORS_MAX 8192 +/* By default don't report non-rotating (solid state) medium */ +#define DA_IS_NONROT 0 +/* Queue Algorithm Modifier default for restricted reordering in control mode page */ +#define DA_EMULATE_REST_REORD 0 + +#define SE_MODE_PAGE_BUF 512 + + /* struct se_hba->hba_flags */ enum hba_flags_table { HBA_FLAGS_INTERNAL_USE = 0x01, @@ -158,6 +225,30 @@ enum tcm_sense_reason_table { TCM_RESERVATION_CONFLICT = 0x10, }; +/* fabric independent task management function values */ +enum tcm_tmreq_table { + TMR_ABORT_TASK = 1, + TMR_ABORT_TASK_SET = 2, + TMR_CLEAR_ACA = 3, + TMR_CLEAR_TASK_SET = 4, + TMR_LUN_RESET = 5, + TMR_TARGET_WARM_RESET = 6, + TMR_TARGET_COLD_RESET = 7, + TMR_FABRIC_TMR = 255, +}; + +/* fabric independent task management response values */ +enum tcm_tmrsp_table { + TMR_FUNCTION_COMPLETE = 0, + TMR_TASK_DOES_NOT_EXIST = 1, + TMR_LUN_DOES_NOT_EXIST = 2, + TMR_TASK_STILL_ALLEGIANT = 3, + TMR_TASK_FAILOVER_NOT_SUPPORTED = 4, + TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED = 5, + TMR_FUNCTION_AUTHORIZATION_FAILED = 6, + TMR_FUNCTION_REJECTED = 255, +}; + struct se_obj { atomic_t obj_access_count; } ____cacheline_aligned; diff --git a/include/target/target_core_device.h b/include/target/target_core_device.h deleted file mode 100644 index c6123a4..0000000 --- a/include/target/target_core_device.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef TARGET_CORE_DEVICE_H -#define TARGET_CORE_DEVICE_H - - -// external -extern int transport_lookup_cmd_lun(struct se_cmd *, u32); -extern int transport_lookup_tmr_lun(struct se_cmd *, u32); - -#endif /* TARGET_CORE_DEVICE_H */ diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h new file mode 100644 index 0000000..d035d865 --- /dev/null +++ b/include/target/target_core_fabric.h @@ -0,0 +1,189 @@ +#ifndef TARGET_CORE_FABRIC_H +#define TARGET_CORE_FABRIC_H + +struct target_core_fabric_ops { + struct configfs_subsystem *tf_subsys; + /* + * Optional to signal struct se_task->task_sg[] padding entries + * for scatterlist chaining using transport_do_task_sg_link(), + * disabled by default + */ + bool task_sg_chaining; + char *(*get_fabric_name)(void); + u8 (*get_fabric_proto_ident)(struct se_portal_group *); + char *(*tpg_get_wwn)(struct se_portal_group *); + u16 (*tpg_get_tag)(struct se_portal_group *); + u32 (*tpg_get_default_depth)(struct se_portal_group *); + u32 (*tpg_get_pr_transport_id)(struct se_portal_group *, + struct se_node_acl *, + struct t10_pr_registration *, int *, + unsigned char *); + u32 (*tpg_get_pr_transport_id_len)(struct se_portal_group *, + struct se_node_acl *, + struct t10_pr_registration *, int *); + char *(*tpg_parse_pr_out_transport_id)(struct se_portal_group *, + const char *, u32 *, char **); + int (*tpg_check_demo_mode)(struct se_portal_group *); + int (*tpg_check_demo_mode_cache)(struct se_portal_group *); + int (*tpg_check_demo_mode_write_protect)(struct se_portal_group *); + int (*tpg_check_prod_mode_write_protect)(struct se_portal_group *); + /* + * Optionally used by fabrics to allow demo-mode login, but not + * expose any TPG LUNs, and return 'not connected' in standard + * inquiry response + */ + int (*tpg_check_demo_mode_login_only)(struct se_portal_group *); + struct se_node_acl *(*tpg_alloc_fabric_acl)( + struct se_portal_group *); + void (*tpg_release_fabric_acl)(struct se_portal_group *, + struct se_node_acl *); + u32 (*tpg_get_inst_index)(struct se_portal_group *); + /* + * Optional function pointer for TCM to perform command map + * from TCM processing thread context, for those struct se_cmd + * initially allocated in interrupt context. + */ + int (*new_cmd_map)(struct se_cmd *); + /* + * Optional to release struct se_cmd and fabric dependent allocated + * I/O descriptor in transport_cmd_check_stop(). + * + * Returning 1 will signal a descriptor has been released. + * Returning 0 will signal a descriptor has not been released. + */ + int (*check_stop_free)(struct se_cmd *); + /* + * Optional check for active I/O shutdown + */ + int (*check_release_cmd)(struct se_cmd *); + void (*release_cmd)(struct se_cmd *); + /* + * Called with spin_lock_bh(struct se_portal_group->session_lock held. + */ + int (*shutdown_session)(struct se_session *); + void (*close_session)(struct se_session *); + void (*stop_session)(struct se_session *, int, int); + void (*fall_back_to_erl0)(struct se_session *); + int (*sess_logged_in)(struct se_session *); + u32 (*sess_get_index)(struct se_session *); + /* + * Used only for SCSI fabrics that contain multi-value TransportIDs + * (like iSCSI). All other SCSI fabrics should set this to NULL. + */ + u32 (*sess_get_initiator_sid)(struct se_session *, + unsigned char *, u32); + int (*write_pending)(struct se_cmd *); + int (*write_pending_status)(struct se_cmd *); + void (*set_default_node_attributes)(struct se_node_acl *); + u32 (*get_task_tag)(struct se_cmd *); + int (*get_cmd_state)(struct se_cmd *); + int (*queue_data_in)(struct se_cmd *); + int (*queue_status)(struct se_cmd *); + int (*queue_tm_rsp)(struct se_cmd *); + u16 (*set_fabric_sense_len)(struct se_cmd *, u32); + u16 (*get_fabric_sense_len)(void); + int (*is_state_remove)(struct se_cmd *); + /* + * fabric module calls for target_core_fabric_configfs.c + */ + struct se_wwn *(*fabric_make_wwn)(struct target_fabric_configfs *, + struct config_group *, const char *); + void (*fabric_drop_wwn)(struct se_wwn *); + struct se_portal_group *(*fabric_make_tpg)(struct se_wwn *, + struct config_group *, const char *); + void (*fabric_drop_tpg)(struct se_portal_group *); + int (*fabric_post_link)(struct se_portal_group *, + struct se_lun *); + void (*fabric_pre_unlink)(struct se_portal_group *, + struct se_lun *); + struct se_tpg_np *(*fabric_make_np)(struct se_portal_group *, + struct config_group *, const char *); + void (*fabric_drop_np)(struct se_tpg_np *); + struct se_node_acl *(*fabric_make_nodeacl)(struct se_portal_group *, + struct config_group *, const char *); + void (*fabric_drop_nodeacl)(struct se_node_acl *); +}; + +struct se_session *transport_init_session(void); +void __transport_register_session(struct se_portal_group *, + struct se_node_acl *, struct se_session *, void *); +void transport_register_session(struct se_portal_group *, + struct se_node_acl *, struct se_session *, void *); +void transport_free_session(struct se_session *); +void transport_deregister_session_configfs(struct se_session *); +void transport_deregister_session(struct se_session *); + + +void transport_init_se_cmd(struct se_cmd *, struct target_core_fabric_ops *, + struct se_session *, u32, int, int, unsigned char *); +int transport_lookup_cmd_lun(struct se_cmd *, u32); +int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *); +int transport_handle_cdb_direct(struct se_cmd *); +int transport_generic_handle_cdb_map(struct se_cmd *); +int transport_generic_handle_data(struct se_cmd *); +int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, + struct scatterlist *, u32, struct scatterlist *, u32); +void transport_do_task_sg_chain(struct se_cmd *); +int transport_generic_new_cmd(struct se_cmd *); + +void transport_generic_process_write(struct se_cmd *); + +void transport_generic_free_cmd(struct se_cmd *, int); + +bool transport_wait_for_tasks(struct se_cmd *); +int transport_check_aborted_status(struct se_cmd *, int); +int transport_send_check_condition_and_sense(struct se_cmd *, u8, int); + +void target_get_sess_cmd(struct se_session *, struct se_cmd *); +int target_put_sess_cmd(struct se_session *, struct se_cmd *); +void target_splice_sess_cmd_list(struct se_session *); +void target_wait_for_sess_cmds(struct se_session *, int); + +int core_alua_check_nonop_delay(struct se_cmd *); + +struct se_tmr_req *core_tmr_alloc_req(struct se_cmd *, void *, u8, gfp_t); +void core_tmr_release_req(struct se_tmr_req *); +int transport_generic_handle_tmr(struct se_cmd *); +int transport_lookup_tmr_lun(struct se_cmd *, u32); + +struct se_node_acl *core_tpg_check_initiator_node_acl(struct se_portal_group *, + unsigned char *); +void core_tpg_clear_object_luns(struct se_portal_group *); +struct se_node_acl *core_tpg_add_initiator_node_acl(struct se_portal_group *, + struct se_node_acl *, const char *, u32); +int core_tpg_del_initiator_node_acl(struct se_portal_group *, + struct se_node_acl *, int); +int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *, + unsigned char *, u32, int); +int core_tpg_register(struct target_core_fabric_ops *, struct se_wwn *, + struct se_portal_group *, void *, int); +int core_tpg_deregister(struct se_portal_group *); + +/* SAS helpers */ +u8 sas_get_fabric_proto_ident(struct se_portal_group *); +u32 sas_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *, + struct t10_pr_registration *, int *, unsigned char *); +u32 sas_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *, + struct t10_pr_registration *, int *); +char *sas_parse_pr_out_transport_id(struct se_portal_group *, const char *, + u32 *, char **); + +/* FC helpers */ +u8 fc_get_fabric_proto_ident(struct se_portal_group *); +u32 fc_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *, + struct t10_pr_registration *, int *, unsigned char *); +u32 fc_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *, + struct t10_pr_registration *, int *); +char *fc_parse_pr_out_transport_id(struct se_portal_group *, const char *, + u32 *, char **); + +/* iSCSI helpers */ +u8 iscsi_get_fabric_proto_ident(struct se_portal_group *); +u32 iscsi_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *, + struct t10_pr_registration *, int *, unsigned char *); +u32 iscsi_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *, + struct t10_pr_registration *, int *); +char *iscsi_parse_pr_out_transport_id(struct se_portal_group *, const char *, + u32 *, char **); + +#endif /* TARGET_CORE_FABRICH */ diff --git a/include/target/target_core_fabric_lib.h b/include/target/target_core_fabric_lib.h deleted file mode 100644 index c2f8d0e..0000000 --- a/include/target/target_core_fabric_lib.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TARGET_CORE_FABRIC_LIB_H -#define TARGET_CORE_FABRIC_LIB_H - -extern u8 sas_get_fabric_proto_ident(struct se_portal_group *); -extern u32 sas_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *, - struct t10_pr_registration *, int *, unsigned char *); -extern u32 sas_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *, - struct t10_pr_registration *, int *); -extern char *sas_parse_pr_out_transport_id(struct se_portal_group *, - const char *, u32 *, char **); - -extern u8 fc_get_fabric_proto_ident(struct se_portal_group *); -extern u32 fc_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *, - struct t10_pr_registration *, int *, unsigned char *); -extern u32 fc_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *, - struct t10_pr_registration *, int *); -extern char *fc_parse_pr_out_transport_id(struct se_portal_group *, - const char *, u32 *, char **); - -extern u8 iscsi_get_fabric_proto_ident(struct se_portal_group *); -extern u32 iscsi_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *, - struct t10_pr_registration *, int *, unsigned char *); -extern u32 iscsi_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *, - struct t10_pr_registration *, int *); -extern char *iscsi_parse_pr_out_transport_id(struct se_portal_group *, - const char *, u32 *, char **); - -#endif /* TARGET_CORE_FABRIC_LIB_H */ diff --git a/include/target/target_core_fabric_ops.h b/include/target/target_core_fabric_ops.h deleted file mode 100644 index 0256825..0000000 --- a/include/target/target_core_fabric_ops.h +++ /dev/null @@ -1,105 +0,0 @@ -/* Defined in target_core_configfs.h */ -struct target_fabric_configfs; - -struct target_core_fabric_ops { - struct configfs_subsystem *tf_subsys; - /* - * Optional to signal struct se_task->task_sg[] padding entries - * for scatterlist chaining using transport_do_task_sg_link(), - * disabled by default - */ - bool task_sg_chaining; - char *(*get_fabric_name)(void); - u8 (*get_fabric_proto_ident)(struct se_portal_group *); - char *(*tpg_get_wwn)(struct se_portal_group *); - u16 (*tpg_get_tag)(struct se_portal_group *); - u32 (*tpg_get_default_depth)(struct se_portal_group *); - u32 (*tpg_get_pr_transport_id)(struct se_portal_group *, - struct se_node_acl *, - struct t10_pr_registration *, int *, - unsigned char *); - u32 (*tpg_get_pr_transport_id_len)(struct se_portal_group *, - struct se_node_acl *, - struct t10_pr_registration *, int *); - char *(*tpg_parse_pr_out_transport_id)(struct se_portal_group *, - const char *, u32 *, char **); - int (*tpg_check_demo_mode)(struct se_portal_group *); - int (*tpg_check_demo_mode_cache)(struct se_portal_group *); - int (*tpg_check_demo_mode_write_protect)(struct se_portal_group *); - int (*tpg_check_prod_mode_write_protect)(struct se_portal_group *); - /* - * Optionally used by fabrics to allow demo-mode login, but not - * expose any TPG LUNs, and return 'not connected' in standard - * inquiry response - */ - int (*tpg_check_demo_mode_login_only)(struct se_portal_group *); - struct se_node_acl *(*tpg_alloc_fabric_acl)( - struct se_portal_group *); - void (*tpg_release_fabric_acl)(struct se_portal_group *, - struct se_node_acl *); - u32 (*tpg_get_inst_index)(struct se_portal_group *); - /* - * Optional function pointer for TCM to perform command map - * from TCM processing thread context, for those struct se_cmd - * initially allocated in interrupt context. - */ - int (*new_cmd_map)(struct se_cmd *); - /* - * Optional to release struct se_cmd and fabric dependent allocated - * I/O descriptor in transport_cmd_check_stop(). - * - * Returning 1 will signal a descriptor has been released. - * Returning 0 will signal a descriptor has not been released. - */ - int (*check_stop_free)(struct se_cmd *); - /* - * Optional check for active I/O shutdown - */ - int (*check_release_cmd)(struct se_cmd *); - void (*release_cmd)(struct se_cmd *); - /* - * Called with spin_lock_bh(struct se_portal_group->session_lock held. - */ - int (*shutdown_session)(struct se_session *); - void (*close_session)(struct se_session *); - void (*stop_session)(struct se_session *, int, int); - void (*fall_back_to_erl0)(struct se_session *); - int (*sess_logged_in)(struct se_session *); - u32 (*sess_get_index)(struct se_session *); - /* - * Used only for SCSI fabrics that contain multi-value TransportIDs - * (like iSCSI). All other SCSI fabrics should set this to NULL. - */ - u32 (*sess_get_initiator_sid)(struct se_session *, - unsigned char *, u32); - int (*write_pending)(struct se_cmd *); - int (*write_pending_status)(struct se_cmd *); - void (*set_default_node_attributes)(struct se_node_acl *); - u32 (*get_task_tag)(struct se_cmd *); - int (*get_cmd_state)(struct se_cmd *); - int (*queue_data_in)(struct se_cmd *); - int (*queue_status)(struct se_cmd *); - int (*queue_tm_rsp)(struct se_cmd *); - u16 (*set_fabric_sense_len)(struct se_cmd *, u32); - u16 (*get_fabric_sense_len)(void); - int (*is_state_remove)(struct se_cmd *); - /* - * fabric module calls for target_core_fabric_configfs.c - */ - struct se_wwn *(*fabric_make_wwn)(struct target_fabric_configfs *, - struct config_group *, const char *); - void (*fabric_drop_wwn)(struct se_wwn *); - struct se_portal_group *(*fabric_make_tpg)(struct se_wwn *, - struct config_group *, const char *); - void (*fabric_drop_tpg)(struct se_portal_group *); - int (*fabric_post_link)(struct se_portal_group *, - struct se_lun *); - void (*fabric_pre_unlink)(struct se_portal_group *, - struct se_lun *); - struct se_tpg_np *(*fabric_make_np)(struct se_portal_group *, - struct config_group *, const char *); - void (*fabric_drop_np)(struct se_tpg_np *); - struct se_node_acl *(*fabric_make_nodeacl)(struct se_portal_group *, - struct config_group *, const char *); - void (*fabric_drop_nodeacl)(struct se_node_acl *); -}; diff --git a/include/target/target_core_tmr.h b/include/target/target_core_tmr.h deleted file mode 100644 index 36b3e0d..0000000 --- a/include/target/target_core_tmr.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TARGET_CORE_TMR_H -#define TARGET_CORE_TMR_H - -/* fabric independent task management function values */ -enum tcm_tmreq_table { - TMR_ABORT_TASK = 1, - TMR_ABORT_TASK_SET = 2, - TMR_CLEAR_ACA = 3, - TMR_CLEAR_TASK_SET = 4, - TMR_LUN_RESET = 5, - TMR_TARGET_WARM_RESET = 6, - TMR_TARGET_COLD_RESET = 7, - TMR_FABRIC_TMR = 255, -}; - -/* fabric independent task management response values */ -enum tcm_tmrsp_table { - TMR_FUNCTION_COMPLETE = 0, - TMR_TASK_DOES_NOT_EXIST = 1, - TMR_LUN_DOES_NOT_EXIST = 2, - TMR_TASK_STILL_ALLEGIANT = 3, - TMR_TASK_FAILOVER_NOT_SUPPORTED = 4, - TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED = 5, - TMR_FUNCTION_AUTHORIZATION_FAILED = 6, - TMR_FUNCTION_REJECTED = 255, -}; - -extern struct se_tmr_req *core_tmr_alloc_req(struct se_cmd *, void *, u8, gfp_t); -extern void core_tmr_release_req(struct se_tmr_req *); - -#endif /* TARGET_CORE_TMR_H */ diff --git a/include/target/target_core_tpg.h b/include/target/target_core_tpg.h deleted file mode 100644 index 80ae59f..0000000 --- a/include/target/target_core_tpg.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TARGET_CORE_TPG_H -#define TARGET_CORE_TPG_H - -extern struct se_node_acl *core_tpg_check_initiator_node_acl( - struct se_portal_group *, - unsigned char *); -extern void core_tpg_clear_object_luns(struct se_portal_group *); -extern struct se_node_acl *core_tpg_add_initiator_node_acl( - struct se_portal_group *, - struct se_node_acl *, - const char *, u32); -extern int core_tpg_del_initiator_node_acl(struct se_portal_group *, - struct se_node_acl *, int); -extern int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *, - unsigned char *, u32, int); -extern int core_tpg_register(struct target_core_fabric_ops *, - struct se_wwn *, - struct se_portal_group *, void *, - int); -extern int core_tpg_deregister(struct se_portal_group *); - -#endif /* TARGET_CORE_TPG_H */ diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h deleted file mode 100644 index 68fe1f2..0000000 --- a/include/target/target_core_transport.h +++ /dev/null @@ -1,256 +0,0 @@ -#ifndef TARGET_CORE_TRANSPORT_H -#define TARGET_CORE_TRANSPORT_H - -#define TARGET_CORE_VERSION TARGET_CORE_MOD_VERSION - -/* Attempts before moving from SHORT to LONG */ -#define PYX_TRANSPORT_WINDOW_CLOSED_THRESHOLD 3 -#define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_SHORT 3 /* In milliseconds */ -#define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_LONG 10 /* In milliseconds */ - -#define PYX_TRANSPORT_STATUS_INTERVAL 5 /* In seconds */ - -#define TRANSPORT_PLUGIN_PHBA_PDEV 1 -#define TRANSPORT_PLUGIN_VHBA_PDEV 2 -#define TRANSPORT_PLUGIN_VHBA_VDEV 3 - -/* - * struct se_subsystem_dev->su_dev_flags -*/ -#define SDF_FIRMWARE_VPD_UNIT_SERIAL 0x00000001 -#define SDF_EMULATED_VPD_UNIT_SERIAL 0x00000002 -#define SDF_USING_UDEV_PATH 0x00000004 -#define SDF_USING_ALIAS 0x00000008 - -/* - * struct se_device->dev_flags - */ -#define DF_READ_ONLY 0x00000001 -#define DF_SPC2_RESERVATIONS 0x00000002 -#define DF_SPC2_RESERVATIONS_WITH_ISID 0x00000004 - -/* struct se_dev_attrib sanity values */ -/* Default max_unmap_lba_count */ -#define DA_MAX_UNMAP_LBA_COUNT 0 -/* Default max_unmap_block_desc_count */ -#define DA_MAX_UNMAP_BLOCK_DESC_COUNT 0 -/* Default unmap_granularity */ -#define DA_UNMAP_GRANULARITY_DEFAULT 0 -/* Default unmap_granularity_alignment */ -#define DA_UNMAP_GRANULARITY_ALIGNMENT_DEFAULT 0 -/* Emulation for Direct Page Out */ -#define DA_EMULATE_DPO 0 -/* Emulation for Forced Unit Access WRITEs */ -#define DA_EMULATE_FUA_WRITE 1 -/* Emulation for Forced Unit Access READs */ -#define DA_EMULATE_FUA_READ 0 -/* Emulation for WriteCache and SYNCHRONIZE_CACHE */ -#define DA_EMULATE_WRITE_CACHE 0 -/* Emulation for UNIT ATTENTION Interlock Control */ -#define DA_EMULATE_UA_INTLLCK_CTRL 0 -/* Emulation for TASK_ABORTED status (TAS) by default */ -#define DA_EMULATE_TAS 1 -/* Emulation for Thin Provisioning UNMAP using block/blk-lib.c:blkdev_issue_discard() */ -#define DA_EMULATE_TPU 0 -/* - * Emulation for Thin Provisioning WRITE_SAME w/ UNMAP=1 bit using - * block/blk-lib.c:blkdev_issue_discard() - */ -#define DA_EMULATE_TPWS 0 -/* No Emulation for PSCSI by default */ -#define DA_EMULATE_RESERVATIONS 0 -/* No Emulation for PSCSI by default */ -#define DA_EMULATE_ALUA 0 -/* Enforce SCSI Initiator Port TransportID with 'ISID' for PR */ -#define DA_ENFORCE_PR_ISIDS 1 -#define DA_STATUS_MAX_SECTORS_MIN 16 -#define DA_STATUS_MAX_SECTORS_MAX 8192 -/* By default don't report non-rotating (solid state) medium */ -#define DA_IS_NONROT 0 -/* Queue Algorithm Modifier default for restricted reordering in control mode page */ -#define DA_EMULATE_REST_REORD 0 - -#define SE_MODE_PAGE_BUF 512 - -#define MOD_MAX_SECTORS(ms, bs) (ms % (PAGE_SIZE / bs)) - -struct se_subsystem_api; - -extern int transport_subsystem_register(struct se_subsystem_api *); -extern void transport_subsystem_release(struct se_subsystem_api *); -extern struct se_session *transport_init_session(void); -extern void __transport_register_session(struct se_portal_group *, - struct se_node_acl *, - struct se_session *, void *); -extern void transport_register_session(struct se_portal_group *, - struct se_node_acl *, - struct se_session *, void *); -extern void transport_free_session(struct se_session *); -extern void transport_deregister_session_configfs(struct se_session *); -extern void transport_deregister_session(struct se_session *); -extern void transport_complete_sync_cache(struct se_cmd *, int); -extern void transport_complete_task(struct se_task *, int); - -extern void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *); -extern int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); -extern int transport_set_vpd_ident_type(struct t10_vpd *, unsigned char *); -extern int transport_set_vpd_ident(struct t10_vpd *, unsigned char *); - -extern struct se_device *transport_add_device_to_core_hba(struct se_hba *, - struct se_subsystem_api *, - struct se_subsystem_dev *, u32, - void *, struct se_dev_limits *, - const char *, const char *); -extern void transport_init_se_cmd(struct se_cmd *, - struct target_core_fabric_ops *, - struct se_session *, u32, int, int, - unsigned char *); -void *transport_kmap_first_data_page(struct se_cmd *cmd); -void transport_kunmap_first_data_page(struct se_cmd *cmd); -extern int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *); -extern int transport_handle_cdb_direct(struct se_cmd *); -extern int transport_generic_handle_cdb_map(struct se_cmd *); -extern int transport_generic_handle_data(struct se_cmd *); -extern int transport_generic_handle_tmr(struct se_cmd *); -extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32, - struct scatterlist *, u32); -extern bool transport_wait_for_tasks(struct se_cmd *); -extern int transport_check_aborted_status(struct se_cmd *, int); -extern int transport_send_check_condition_and_sense(struct se_cmd *, u8, int); -extern void transport_generic_free_cmd(struct se_cmd *, int); -extern void target_get_sess_cmd(struct se_session *, struct se_cmd *); -extern int target_put_sess_cmd(struct se_session *, struct se_cmd *); -extern void target_splice_sess_cmd_list(struct se_session *); -extern void target_wait_for_sess_cmds(struct se_session *, int); -extern void transport_do_task_sg_chain(struct se_cmd *); -extern void transport_generic_process_write(struct se_cmd *); -extern int transport_generic_new_cmd(struct se_cmd *); -/* From target_core_alua.c */ -extern int core_alua_check_nonop_delay(struct se_cmd *); -/* From target_core_cdb.c */ -extern void target_get_task_cdb(struct se_task *task, unsigned char *cdb); - -/* - * Each se_transport_task_t can have N number of possible struct se_task's - * for the storage transport(s) to possibly execute. - * Used primarily for splitting up CDBs that exceed the physical storage - * HBA's maximum sector count per task. - */ -struct se_mem { - struct page *se_page; - u32 se_len; - u32 se_off; - struct list_head se_list; -} ____cacheline_aligned; - -/* - * Each type of disk transport supported MUST have a template defined - * within its .h file. - */ -struct se_subsystem_api { - /* - * The Name. :-) - */ - char name[16]; - /* - * Transport Type. - */ - u8 transport_type; - - unsigned int fua_write_emulated : 1; - unsigned int write_cache_emulated : 1; - - /* - * struct module for struct se_hba references - */ - struct module *owner; - /* - * Used for global se_subsystem_api list_head - */ - struct list_head sub_api_list; - /* - * attach_hba(): - */ - int (*attach_hba)(struct se_hba *, u32); - /* - * detach_hba(): - */ - void (*detach_hba)(struct se_hba *); - /* - * pmode_hba(): Used for TCM/pSCSI subsystem plugin HBA -> - * Linux/SCSI struct Scsi_Host passthrough - */ - int (*pmode_enable_hba)(struct se_hba *, unsigned long); - /* - * allocate_virtdevice(): - */ - void *(*allocate_virtdevice)(struct se_hba *, const char *); - /* - * create_virtdevice(): Only for Virtual HBAs - */ - struct se_device *(*create_virtdevice)(struct se_hba *, - struct se_subsystem_dev *, void *); - /* - * free_device(): - */ - void (*free_device)(void *); - - /* - * transport_complete(): - * - * Use transport_generic_complete() for majority of DAS transport - * drivers. Provided out of convenience. - */ - int (*transport_complete)(struct se_task *task); - struct se_task *(*alloc_task)(unsigned char *cdb); - /* - * do_task(): - */ - int (*do_task)(struct se_task *); - /* - * Used by virtual subsystem plugins IBLOCK and FILEIO to emulate - * UNMAP and WRITE_SAME_* w/ UNMAP=1 <-> Linux/Block Discard - */ - int (*do_discard)(struct se_device *, sector_t, u32); - /* - * Used by virtual subsystem plugins IBLOCK and FILEIO to emulate - * SYNCHRONIZE_CACHE_* <-> Linux/Block blkdev_issue_flush() - */ - void (*do_sync_cache)(struct se_task *); - /* - * free_task(): - */ - void (*free_task)(struct se_task *); - /* - * check_configfs_dev_params(): - */ - ssize_t (*check_configfs_dev_params)(struct se_hba *, struct se_subsystem_dev *); - /* - * set_configfs_dev_params(): - */ - ssize_t (*set_configfs_dev_params)(struct se_hba *, struct se_subsystem_dev *, - const char *, ssize_t); - /* - * show_configfs_dev_params(): - */ - ssize_t (*show_configfs_dev_params)(struct se_hba *, struct se_subsystem_dev *, - char *); - /* - * get_device_rev(): - */ - u32 (*get_device_rev)(struct se_device *); - /* - * get_device_type(): - */ - u32 (*get_device_type)(struct se_device *); - /* - * Get the sector_t from a subsystem backstore.. - */ - sector_t (*get_blocks)(struct se_device *); - /* - * get_sense_buffer(): - */ - unsigned char *(*get_sense_buffer)(struct se_task *); -} ____cacheline_aligned; - -#endif /* TARGET_CORE_TRANSPORT_H */ -- cgit v0.10.2 From ef804a849ffae058a783e9dddd24cc1a555bbdb4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Nov 2011 06:53:58 -0500 Subject: target: fold se_task.task_sense into task_flags Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 72c13b7..e13fafa 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -709,7 +709,7 @@ void transport_complete_task(struct se_task *task, int success) if (dev && dev->transport->transport_complete) { if (dev->transport->transport_complete(task) != 0) { cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; - task->task_sense = 1; + task->task_flags |= TF_HAS_SENSE; success = 1; } } @@ -2346,7 +2346,7 @@ static int transport_get_sense_data(struct se_cmd *cmd) list_for_each_entry_safe(task, task_tmp, &cmd->t_task_list, t_list) { - if (!task->task_sense) + if (!(task->task_flags & TF_HAS_SENSE)) continue; if (!dev->transport->get_sense_buffer) { diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 1d90fb3..7f3acdd 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -138,11 +138,12 @@ enum transport_tpg_type_table { TRANSPORT_TPG_TYPE_DISCOVERY = 1, }; -/* Used for generate timer flags */ +/* struct se_task->task_flags */ enum se_task_flags { TF_ACTIVE = (1 << 0), TF_SENT = (1 << 1), TF_REQUEST_STOP = (1 << 2), + TF_HAS_SENSE = (1 << 3), }; /* Special transport agnostic struct se_cmd->t_states */ @@ -488,7 +489,6 @@ struct se_task { struct scatterlist *task_sg; u32 task_sg_nents; u16 task_flags; - u8 task_sense; u8 task_scsi_status; int task_error_status; enum dma_data_direction task_data_direction; -- cgit v0.10.2 From 41e16e981679124c78c30f046d4f0b71d86ff1b2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Nov 2011 06:54:15 -0500 Subject: target: remove the se_task task_error_status field We only reach transport_complete_task once per task, so the test and set on task_error_status is never going to have an effect. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index e13fafa..ae112ac 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -738,13 +738,7 @@ void transport_complete_task(struct se_task *task, int success) } if (cmd->t_tasks_failed) { - if (!task->task_error_status) { - task->task_error_status = - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - cmd->scsi_sense_reason = - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - + cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; INIT_WORK(&cmd->work, target_complete_failure_work); } else { atomic_set(&cmd->t_transport_complete, 1); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 7f3acdd..e2473e6 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -490,7 +490,6 @@ struct se_task { u32 task_sg_nents; u16 task_flags; u8 task_scsi_status; - int task_error_status; enum dma_data_direction task_data_direction; atomic_t task_state_active; struct list_head t_list; -- cgit v0.10.2 From 1880807adb21d741f08b747956c90bf4a6f95fbf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Nov 2011 06:54:36 -0500 Subject: target: make the se_task task_state_active a normal bool There is no need to make task_state_active an atomic_t given that it is always set under the execute_task_lock so we can make it a simple bool. Also rename it to t_state_active to be closer to the list it guards, and make sure all checks before the list addion/removal actually happen under execute_task_lock. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index fc9d748..b4c9bb7 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -221,7 +221,7 @@ static void core_tmr_drain_task_list( continue; list_move_tail(&task->t_state_list, &drain_task_list); - atomic_set(&task->task_state_active, 0); + task->t_state_active = false; /* * Remove from task execute list before processing drain_task_list */ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ae112ac..511836e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -421,18 +421,18 @@ static void transport_all_task_dev_remove_state(struct se_cmd *cmd) if (task->task_flags & TF_ACTIVE) continue; - if (!atomic_read(&task->task_state_active)) - continue; - spin_lock_irqsave(&dev->execute_task_lock, flags); - list_del(&task->t_state_list); - pr_debug("Removed ITT: 0x%08x dev: %p task[%p]\n", - cmd->se_tfo->get_task_tag(cmd), dev, task); - spin_unlock_irqrestore(&dev->execute_task_lock, flags); + if (task->t_state_active) { + pr_debug("Removed ITT: 0x%08x dev: %p task[%p]\n", + cmd->se_tfo->get_task_tag(cmd), dev, task); - atomic_set(&task->task_state_active, 0); - atomic_dec(&cmd->t_task_cdbs_ex_left); + list_del(&task->t_state_list); + atomic_dec(&cmd->t_task_cdbs_ex_left); + task->t_state_active = false; + } + spin_unlock_irqrestore(&dev->execute_task_lock, flags); } + } /* transport_cmd_check_stop(): @@ -813,7 +813,7 @@ static void __transport_add_task_to_execute_queue( head_of_queue = transport_add_task_check_sam_attr(task, task_prev, dev); atomic_inc(&dev->execute_tasks); - if (atomic_read(&task->task_state_active)) + if (task->t_state_active) return; /* * Determine if this task needs to go to HEAD_OF_QUEUE for the @@ -827,7 +827,7 @@ static void __transport_add_task_to_execute_queue( else list_add_tail(&task->t_state_list, &dev->state_task_list); - atomic_set(&task->task_state_active, 1); + task->t_state_active = true; pr_debug("Added ITT: 0x%08x task[%p] to dev: %p\n", task->task_se_cmd->se_tfo->get_task_tag(task->task_se_cmd), @@ -842,17 +842,16 @@ static void transport_add_tasks_to_state_queue(struct se_cmd *cmd) spin_lock_irqsave(&cmd->t_state_lock, flags); list_for_each_entry(task, &cmd->t_task_list, t_list) { - if (atomic_read(&task->task_state_active)) - continue; - spin_lock(&dev->execute_task_lock); - list_add_tail(&task->t_state_list, &dev->state_task_list); - atomic_set(&task->task_state_active, 1); - - pr_debug("Added ITT: 0x%08x task[%p] to dev: %p\n", - task->task_se_cmd->se_tfo->get_task_tag( - task->task_se_cmd), task, dev); - + if (!task->t_state_active) { + list_add_tail(&task->t_state_list, + &dev->state_task_list); + task->t_state_active = true; + + pr_debug("Added ITT: 0x%08x task[%p] to dev: %p\n", + task->task_se_cmd->se_tfo->get_task_tag( + task->task_se_cmd), task, dev); + } spin_unlock(&dev->execute_task_lock); } spin_unlock_irqrestore(&cmd->t_state_lock, flags); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index e2473e6..311f5fc 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -491,10 +491,10 @@ struct se_task { u16 task_flags; u8 task_scsi_status; enum dma_data_direction task_data_direction; - atomic_t task_state_active; struct list_head t_list; struct list_head t_execute_list; struct list_head t_state_list; + bool t_state_active; struct completion task_stop_comp; } ____cacheline_aligned; -- cgit v0.10.2 From 90c161b643d9531d271110876a14e68b49172d8a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 23 Nov 2011 20:53:17 +0100 Subject: target: use \n as a separator for configuration The command | echo rd_pages=32768 > ramdisk/control Does not work because it writes "rd_pages=32768\n" and the parser which matches for "rd_pages=%d" does not recognize it due to the \n. One way of fixing this would be using "echo -n" instead. This patch adds \n to the list of separators so we don't have to use the -n argument which I find is more convinient. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 3c35890..37b2b3d 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1450,7 +1450,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( return -ENOMEM; orig = opts; - while ((ptr = strsep(&opts, ",")) != NULL) { + while ((ptr = strsep(&opts, ",\n")) != NULL) { if (!*ptr) continue; diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index ed7e053..91bf11a 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -497,7 +497,7 @@ static ssize_t fd_set_configfs_dev_params( orig = opts; - while ((ptr = strsep(&opts, ",")) != NULL) { + while ((ptr = strsep(&opts, ",\n")) != NULL) { if (!*ptr) continue; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index feebbfe..3c1a7aa 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -390,7 +390,7 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba, orig = opts; - while ((ptr = strsep(&opts, ",")) != NULL) { + while ((ptr = strsep(&opts, ",\n")) != NULL) { if (!*ptr) continue; diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 5d8851d..bb44ed4 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -817,7 +817,7 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba, orig = opts; - while ((ptr = strsep(&opts, ",")) != NULL) { + while ((ptr = strsep(&opts, ",\n")) != NULL) { if (!*ptr) continue; diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 94b4087..8b68f7b 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -472,7 +472,7 @@ static ssize_t rd_set_configfs_dev_params( orig = opts; - while ((ptr = strsep(&opts, ",")) != NULL) { + while ((ptr = strsep(&opts, ",\n")) != NULL) { if (!*ptr) continue; -- cgit v0.10.2 From c165f69c2ccbbb4ecf16c5d29d65922286bf62ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Engel?= Date: Thu, 24 Nov 2011 02:04:42 +0100 Subject: target: Move core_scsi3_check_cdb_abort_and_preempt And make it static afterwards. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index d2b73b3..429ad72 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -2981,21 +2981,6 @@ static void core_scsi3_release_preempt_and_abort( } } -int core_scsi3_check_cdb_abort_and_preempt( - struct list_head *preempt_and_abort_list, - struct se_cmd *cmd) -{ - struct t10_pr_registration *pr_reg, *pr_reg_tmp; - - list_for_each_entry_safe(pr_reg, pr_reg_tmp, preempt_and_abort_list, - pr_reg_abort_list) { - if (pr_reg->pr_res_key == cmd->pr_res_key) - return 0; - } - - return 1; -} - static int core_scsi3_pro_preempt( struct se_cmd *cmd, int type, diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h index b97f694..7a233fe 100644 --- a/drivers/target/target_core_pr.h +++ b/drivers/target/target_core_pr.h @@ -60,8 +60,6 @@ extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *, struct se_node_acl *); extern void core_scsi3_free_all_registrations(struct se_device *); extern unsigned char *core_scsi3_pr_dump_type(int); -extern int core_scsi3_check_cdb_abort_and_preempt(struct list_head *, - struct se_cmd *); extern int target_scsi3_emulate_pr_in(struct se_task *task); extern int target_scsi3_emulate_pr_out(struct se_task *task); diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index b4c9bb7..22cefeb 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -100,6 +100,21 @@ static void core_tmr_handle_tas_abort( transport_cmd_finish_abort(cmd, 0); } +static int core_scsi3_check_cdb_abort_and_preempt( + struct list_head *preempt_and_abort_list, + struct se_cmd *cmd) +{ + struct t10_pr_registration *pr_reg, *pr_reg_tmp; + + list_for_each_entry_safe(pr_reg, pr_reg_tmp, preempt_and_abort_list, + pr_reg_abort_list) { + if (pr_reg->pr_res_key == cmd->pr_res_key) + return 0; + } + + return 1; +} + static void core_tmr_drain_tmr_list( struct se_device *dev, struct se_tmr_req *tmr, -- cgit v0.10.2 From feae85644f1460b3373ef5141183ee43e6191b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Engel?= Date: Thu, 24 Nov 2011 02:05:12 +0100 Subject: target: simplify target_check_cdb_and_preempt - rename to target_check_cdb_and_preempt - use non-safe list_for_each_entry - move common check into callee (simplifying callers) Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 22cefeb..dcb0618 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -100,15 +100,15 @@ static void core_tmr_handle_tas_abort( transport_cmd_finish_abort(cmd, 0); } -static int core_scsi3_check_cdb_abort_and_preempt( - struct list_head *preempt_and_abort_list, - struct se_cmd *cmd) +static int target_check_cdb_and_preempt(struct list_head *list, + struct se_cmd *cmd) { - struct t10_pr_registration *pr_reg, *pr_reg_tmp; + struct t10_pr_registration *reg; - list_for_each_entry_safe(pr_reg, pr_reg_tmp, preempt_and_abort_list, - pr_reg_abort_list) { - if (pr_reg->pr_res_key == cmd->pr_res_key) + if (!list) + return 0; + list_for_each_entry(reg, list, pr_reg_abort_list) { + if (reg->pr_res_key == cmd->pr_res_key) return 0; } @@ -146,9 +146,7 @@ static void core_tmr_drain_tmr_list( * parameter (eg: for PROUT PREEMPT_AND_ABORT service action * skip non regisration key matching TMRs. */ - if (preempt_and_abort_list && - (core_scsi3_check_cdb_abort_and_preempt( - preempt_and_abort_list, cmd) != 0)) + if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd)) continue; spin_lock(&cmd->t_state_lock); @@ -225,9 +223,7 @@ static void core_tmr_drain_task_list( * For PREEMPT_AND_ABORT usage, only process commands * with a matching reservation key. */ - if (preempt_and_abort_list && - (core_scsi3_check_cdb_abort_and_preempt( - preempt_and_abort_list, cmd) != 0)) + if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd)) continue; /* * Not aborting PROUT PREEMPT_AND_ABORT CDB.. @@ -335,9 +331,7 @@ static void core_tmr_drain_cmd_list( * For PREEMPT_AND_ABORT usage, only process commands * with a matching reservation key. */ - if (preempt_and_abort_list && - (core_scsi3_check_cdb_abort_and_preempt( - preempt_and_abort_list, cmd) != 0)) + if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd)) continue; /* * Not aborting PROUT PREEMPT_AND_ABORT CDB.. -- cgit v0.10.2 From 8359cf43b9dccddeebb0d247146719a14ce6371a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Engel?= Date: Thu, 24 Nov 2011 02:05:51 +0100 Subject: target: remove useless casts A reader should spend an extra moment whenever noticing a cast, because either something special is going on that deserves extra attention or, as is all too often the case, the code is wrong. These casts, afaics, have all been useless. They cast a foo* to a foo*, cast a void* to the assigned type, cast a foo* to void*, before assigning it to a void* variable, etc. In a few cases I also removed an additional &...[0], which is equally useless. Lastly I added three FIXMEs where, to the best of my judgement, the code appears to have a bug. It would be good if someone could check these. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index f74a62d..ac44af1 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -283,8 +283,8 @@ static struct iscsi_np *iscsit_get_np( sock_in6 = (struct sockaddr_in6 *)sockaddr; sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr; - if (!memcmp((void *)&sock_in6->sin6_addr.in6_u, - (void *)&sock_in6_e->sin6_addr.in6_u, + if (!memcmp(&sock_in6->sin6_addr.in6_u, + &sock_in6_e->sin6_addr.in6_u, sizeof(struct in6_addr))) ip_match = 1; @@ -1224,7 +1224,7 @@ static void iscsit_do_crypto_hash_buf( crypto_hash_init(hash); - sg_init_one(&sg, (u8 *)buf, payload_length); + sg_init_one(&sg, buf, payload_length); crypto_hash_update(hash, &sg, payload_length); if (padding) { @@ -1602,7 +1602,7 @@ static int iscsit_handle_nop_out( /* * Attach ping data to struct iscsi_cmd->buf_ptr. */ - cmd->buf_ptr = (void *)ping_data; + cmd->buf_ptr = ping_data; cmd->buf_ptr_size = payload_length; pr_debug("Got %u bytes of NOPOUT ping" @@ -3196,7 +3196,7 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) end_of_buf = 1; goto eob; } - memcpy((void *)payload + payload_len, buf, len); + memcpy(payload + payload_len, buf, len); payload_len += len; spin_lock(&tiqn->tiqn_tpg_lock); @@ -3228,7 +3228,7 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) end_of_buf = 1; goto eob; } - memcpy((void *)payload + payload_len, buf, len); + memcpy(payload + payload_len, buf, len); payload_len += len; } spin_unlock(&tpg->tpg_np_lock); @@ -3485,7 +3485,7 @@ int iscsi_target_tx_thread(void *arg) struct iscsi_conn *conn; struct iscsi_queue_req *qr = NULL; struct se_cmd *se_cmd; - struct iscsi_thread_set *ts = (struct iscsi_thread_set *)arg; + struct iscsi_thread_set *ts = arg; /* * Allow ourselves to be interrupted by SIGINT so that a * connection recovery / failure event can be triggered externally. @@ -3774,7 +3774,7 @@ int iscsi_target_rx_thread(void *arg) u8 buffer[ISCSI_HDR_LEN], opcode; u32 checksum = 0, digest = 0; struct iscsi_conn *conn = NULL; - struct iscsi_thread_set *ts = (struct iscsi_thread_set *)arg; + struct iscsi_thread_set *ts = arg; struct kvec iov; /* * Allow ourselves to be interrupted by SIGINT so that a diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 1cd6ce3..92a2526 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -82,7 +82,7 @@ static void chap_gen_challenge( unsigned int *c_len) { unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1]; - struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; + struct iscsi_chap *chap = conn->auth_protocol; memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1); @@ -120,7 +120,7 @@ static struct iscsi_chap *chap_server_open( if (!conn->auth_protocol) return NULL; - chap = (struct iscsi_chap *) conn->auth_protocol; + chap = conn->auth_protocol; /* * We only support MD5 MDA presently. */ @@ -172,7 +172,7 @@ static int chap_server_compute_md5( unsigned char client_digest[MD5_SIGNATURE_SIZE]; unsigned char server_digest[MD5_SIGNATURE_SIZE]; unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH]; - struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; + struct iscsi_chap *chap = conn->auth_protocol; struct crypto_hash *tfm; struct hash_desc desc; struct scatterlist sg; @@ -246,7 +246,7 @@ static int chap_server_compute_md5( goto out; } - sg_init_one(&sg, (void *)&chap->id, 1); + sg_init_one(&sg, &chap->id, 1); ret = crypto_hash_update(&desc, &sg, 1); if (ret < 0) { pr_err("crypto_hash_update() failed for id\n"); @@ -254,7 +254,7 @@ static int chap_server_compute_md5( goto out; } - sg_init_one(&sg, (void *)&auth->password, strlen(auth->password)); + sg_init_one(&sg, &auth->password, strlen(auth->password)); ret = crypto_hash_update(&desc, &sg, strlen(auth->password)); if (ret < 0) { pr_err("crypto_hash_update() failed for password\n"); @@ -262,7 +262,7 @@ static int chap_server_compute_md5( goto out; } - sg_init_one(&sg, (void *)chap->challenge, CHAP_CHALLENGE_LENGTH); + sg_init_one(&sg, chap->challenge, CHAP_CHALLENGE_LENGTH); ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH); if (ret < 0) { pr_err("crypto_hash_update() failed for challenge\n"); @@ -304,11 +304,11 @@ static int chap_server_compute_md5( goto out; } + /* FIXME: What happens when simple_strtoul() return 256, 257, etc.? */ if (type == HEX) - id = (unsigned char)simple_strtoul((char *)&identifier[2], - &endptr, 0); + id = simple_strtoul(&identifier[2], &endptr, 0); else - id = (unsigned char)simple_strtoul(identifier, &endptr, 0); + id = simple_strtoul(identifier, &endptr, 0); /* * RFC 1994 says Identifier is no more than octet (8 bits). */ @@ -351,7 +351,7 @@ static int chap_server_compute_md5( goto out; } - sg_init_one(&sg, (void *)&id, 1); + sg_init_one(&sg, &id, 1); ret = crypto_hash_update(&desc, &sg, 1); if (ret < 0) { pr_err("crypto_hash_update() failed for id\n"); @@ -359,7 +359,7 @@ static int chap_server_compute_md5( goto out; } - sg_init_one(&sg, (void *)auth->password_mutual, + sg_init_one(&sg, auth->password_mutual, strlen(auth->password_mutual)); ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual)); if (ret < 0) { @@ -371,7 +371,7 @@ static int chap_server_compute_md5( /* * Convert received challenge to binary hex. */ - sg_init_one(&sg, (void *)challenge_binhex, challenge_len); + sg_init_one(&sg, challenge_binhex, challenge_len); ret = crypto_hash_update(&desc, &sg, challenge_len); if (ret < 0) { pr_err("crypto_hash_update() failed for ma challenge\n"); @@ -414,7 +414,7 @@ static int chap_got_response( char *nr_out_ptr, unsigned int *nr_out_len) { - struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; + struct iscsi_chap *chap = conn->auth_protocol; switch (chap->digest_type) { case CHAP_DIGEST_MD5: @@ -437,7 +437,7 @@ u32 chap_main_loop( int *in_len, int *out_len) { - struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; + struct iscsi_chap *chap = conn->auth_protocol; if (!chap) { chap = chap_server_open(conn, auth, in_text, out_text, out_len); diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index c04d389..3468caa 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -52,8 +52,7 @@ struct iscsi_portal_group *lio_get_tpg_from_tpg_item( { struct se_portal_group *se_tpg = container_of(to_config_group(item), struct se_portal_group, tpg_group); - struct iscsi_portal_group *tpg = - (struct iscsi_portal_group *)se_tpg->se_tpg_fabric_ptr; + struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; int ret; if (!tpg) { @@ -1221,7 +1220,7 @@ struct se_portal_group *lio_target_tiqn_addtpg( ret = core_tpg_register( &lio_target_fabric_configfs->tf_ops, - wwn, &tpg->tpg_se_tpg, (void *)tpg, + wwn, &tpg->tpg_se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); if (ret < 0) return NULL; diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index d74e278e..373b0cc 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -143,7 +143,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn) list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, sess_list) { - sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess_p = se_sess->fabric_sess_ptr; spin_lock(&sess_p->conn_lock); if (atomic_read(&sess_p->session_fall_back_to_erl0) || atomic_read(&sess_p->session_logout) || @@ -151,9 +151,9 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn) spin_unlock(&sess_p->conn_lock); continue; } - if (!memcmp((void *)sess_p->isid, (void *)conn->sess->isid, 6) && - (!strcmp((void *)sess_p->sess_ops->InitiatorName, - (void *)initiatorname_param->value) && + if (!memcmp(sess_p->isid, conn->sess->isid, 6) && + (!strcmp(sess_p->sess_ops->InitiatorName, + initiatorname_param->value) && (sess_p->sess_ops->SessionType == sessiontype))) { atomic_set(&sess_p->session_reinstatement, 1); spin_unlock(&sess_p->conn_lock); @@ -229,7 +229,7 @@ static int iscsi_login_zero_tsih_s1( iscsi_login_set_conn_values(sess, conn, pdu->cid); sess->init_task_tag = pdu->itt; - memcpy((void *)&sess->isid, (void *)pdu->isid, 6); + memcpy(&sess->isid, pdu->isid, 6); sess->exp_cmd_sn = pdu->cmdsn; INIT_LIST_HEAD(&sess->sess_conn_list); INIT_LIST_HEAD(&sess->sess_ooo_cmdsn_list); @@ -440,8 +440,7 @@ static int iscsi_login_non_zero_tsih_s2( atomic_read(&sess_p->session_logout) || (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) continue; - if (!memcmp((const void *)sess_p->isid, - (const void *)pdu->isid, 6) && + if (!memcmp(sess_p->isid, pdu->isid, 6) && (sess_p->tsih == pdu->tsih)) { iscsit_inc_session_usage_count(sess_p); iscsit_stop_time2retain_timer(sess_p); @@ -654,7 +653,7 @@ static int iscsi_post_login_handler( spin_lock_bh(&se_tpg->session_lock); __transport_register_session(&sess->tpg->tpg_se_tpg, - se_sess->se_node_acl, se_sess, (void *)sess); + se_sess->se_node_acl, se_sess, sess); pr_debug("Moving to TARG_SESS_STATE_LOGGED_IN.\n"); sess->session_state = TARG_SESS_STATE_LOGGED_IN; @@ -811,7 +810,7 @@ int iscsi_target_setup_login_socket( * Setup the np->np_sockaddr from the passed sockaddr setup * in iscsi_target_configfs.c code.. */ - memcpy((void *)&np->np_sockaddr, (void *)sockaddr, + memcpy(&np->np_sockaddr, sockaddr, sizeof(struct __kernel_sockaddr_storage)); if (sockaddr->ss_family == AF_INET6) @@ -821,6 +820,7 @@ int iscsi_target_setup_login_socket( /* * Set SO_REUSEADDR, and disable Nagel Algorithm with TCP_NODELAY. */ + /* FIXME: Someone please explain why this is endian-safe */ opt = 1; if (np->np_network_transport == ISCSI_TCP) { ret = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, @@ -832,6 +832,7 @@ int iscsi_target_setup_login_socket( } } + /* FIXME: Someone please explain why this is endian-safe */ ret = kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); if (ret < 0) { @@ -1206,7 +1207,7 @@ out: int iscsi_target_login_thread(void *arg) { - struct iscsi_np *np = (struct iscsi_np *)arg; + struct iscsi_np *np = arg; int ret; allow_signal(SIGINT); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 53eea5c..e89fa74 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -732,7 +732,7 @@ static void iscsi_initiatorname_tolower( u32 iqn_size = strlen(param_buf), i; for (i = 0; i < iqn_size; i++) { - c = (char *)¶m_buf[i]; + c = ¶m_buf[i]; if (!isupper(*c)) continue; diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c index ac047d8..b3c699c 100644 --- a/drivers/target/iscsi/iscsi_target_nodeattrib.c +++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c @@ -134,7 +134,7 @@ extern int iscsit_na_nopin_timeout( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; spin_lock(&sess->conn_lock); list_for_each_entry(conn, &sess->sess_conn_list, diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 5a1aac0..421d694 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -745,7 +745,7 @@ static ssize_t iscsi_stat_sess_show_attr_node( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%u\n", sess->sess_ops->SessionType ? 0 : ISCSI_NODE_INDEX); @@ -769,7 +769,7 @@ static ssize_t iscsi_stat_sess_show_attr_indx( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%u\n", sess->session_index); @@ -793,7 +793,7 @@ static ssize_t iscsi_stat_sess_show_attr_cmd_pdus( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%u\n", sess->cmd_pdus); } @@ -816,7 +816,7 @@ static ssize_t iscsi_stat_sess_show_attr_rsp_pdus( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%u\n", sess->rsp_pdus); } @@ -839,7 +839,7 @@ static ssize_t iscsi_stat_sess_show_attr_txdata_octs( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%llu\n", (unsigned long long)sess->tx_data_octets); @@ -863,7 +863,7 @@ static ssize_t iscsi_stat_sess_show_attr_rxdata_octs( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%llu\n", (unsigned long long)sess->rx_data_octets); @@ -887,7 +887,7 @@ static ssize_t iscsi_stat_sess_show_attr_conn_digest_errors( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%u\n", sess->conn_digest_errors); @@ -911,7 +911,7 @@ static ssize_t iscsi_stat_sess_show_attr_conn_timeout_errors( spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (se_sess) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = se_sess->fabric_sess_ptr; if (sess) ret = snprintf(page, PAGE_SIZE, "%u\n", sess->conn_timeout_errors); diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index cfbcf6e..879d8d0 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -70,7 +70,7 @@ int iscsit_load_discovery_tpg(void) ret = core_tpg_register( &lio_target_fabric_configfs->tf_ops, - NULL, &tpg->tpg_se_tpg, (void *)tpg, + NULL, &tpg->tpg_se_tpg, tpg, TRANSPORT_TPG_TYPE_DISCOVERY); if (ret < 0) { kfree(tpg); diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index c1daea1..a05ca1c 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -287,7 +287,7 @@ struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr( } se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, - (void *)cmd->tmr_req, tcm_function, + cmd->tmr_req, tcm_function, GFP_KERNEL); if (!se_cmd->se_tmr_req) goto out; @@ -1064,7 +1064,7 @@ static void iscsit_handle_nopin_response_timeout(unsigned long data) if (tiqn) { spin_lock_bh(&tiqn->sess_err_stats.lock); strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name, - (void *)conn->sess->sess_ops->InitiatorName); + conn->sess->sess_ops->InitiatorName); tiqn->sess_err_stats.last_sess_failure_type = ISCSI_SESS_ERR_CXN_TIMEOUT; tiqn->sess_err_stats.cxn_timeout_errors++; diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index d427a29..572d27a 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -559,8 +559,7 @@ static char *tcm_loop_get_fabric_name(void) static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg) { - struct tcm_loop_tpg *tl_tpg = - (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; /* * tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba() @@ -587,8 +586,7 @@ static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg) static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) { - struct tcm_loop_tpg *tl_tpg = - (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; /* * Return the passed NAA identifier for the SAS Target Port */ @@ -597,8 +595,7 @@ static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg) { - struct tcm_loop_tpg *tl_tpg = - (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; /* * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83 * to represent the SCSI Target Port. @@ -618,8 +615,7 @@ static u32 tcm_loop_get_pr_transport_id( int *format_code, unsigned char *buf) { - struct tcm_loop_tpg *tl_tpg = - (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; switch (tl_hba->tl_proto_id) { @@ -648,8 +644,7 @@ static u32 tcm_loop_get_pr_transport_id_len( struct t10_pr_registration *pr_reg, int *format_code) { - struct tcm_loop_tpg *tl_tpg = - (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; switch (tl_hba->tl_proto_id) { @@ -682,8 +677,7 @@ static char *tcm_loop_parse_pr_out_transport_id( u32 *out_tid_len, char **port_nexus_ptr) { - struct tcm_loop_tpg *tl_tpg = - (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; switch (tl_hba->tl_proto_id) { diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 5921950..5c60d0a 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -116,11 +116,9 @@ target_emulate_inquiry_std(struct se_cmd *cmd) goto out; } - snprintf((unsigned char *)&buf[8], 8, "LIO-ORG"); - snprintf((unsigned char *)&buf[16], 16, "%s", - &dev->se_sub_dev->t10_wwn.model[0]); - snprintf((unsigned char *)&buf[32], 4, "%s", - &dev->se_sub_dev->t10_wwn.revision[0]); + snprintf(&buf[8], 8, "LIO-ORG"); + snprintf(&buf[16], 16, "%s", dev->se_sub_dev->t10_wwn.model); + snprintf(&buf[32], 4, "%s", dev->se_sub_dev->t10_wwn.revision); buf[4] = 31; /* Set additional length to 31 */ out: @@ -139,8 +137,7 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf) SDF_EMULATED_VPD_UNIT_SERIAL) { u32 unit_serial_len; - unit_serial_len = - strlen(&dev->se_sub_dev->t10_wwn.unit_serial[0]); + unit_serial_len = strlen(dev->se_sub_dev->t10_wwn.unit_serial); unit_serial_len++; /* For NULL Terminator */ if (((len + 4) + unit_serial_len) > cmd->data_length) { @@ -149,8 +146,8 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf) buf[3] = (len & 0xff); return 0; } - len += sprintf((unsigned char *)&buf[4], "%s", - &dev->se_sub_dev->t10_wwn.unit_serial[0]); + len += sprintf(&buf[4], "%s", + dev->se_sub_dev->t10_wwn.unit_serial); len++; /* Extra Byte for NULL Terminator */ buf[3] = len; } @@ -280,14 +277,13 @@ check_t10_vend_desc: len += (prod_len + unit_serial_len); goto check_port; } - id_len += sprintf((unsigned char *)&buf[off+12], - "%s:%s", prod, + id_len += sprintf(&buf[off+12], "%s:%s", prod, &dev->se_sub_dev->t10_wwn.unit_serial[0]); } buf[off] = 0x2; /* ASCII */ buf[off+1] = 0x1; /* T10 Vendor ID */ buf[off+2] = 0x0; - memcpy((unsigned char *)&buf[off+4], "LIO-ORG", 8); + memcpy(&buf[off+4], "LIO-ORG", 8); /* Extra Byte for NULL Terminator */ id_len++; /* Identifier Length */ diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 37b2b3d..eb62371 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1629,7 +1629,7 @@ static struct config_item_type target_core_dev_pr_cit = { static ssize_t target_core_show_dev_info(void *p, char *page) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; struct se_hba *hba = se_dev->se_dev_hba; struct se_subsystem_api *t = hba->transport; int bl = 0; @@ -1657,7 +1657,7 @@ static ssize_t target_core_store_dev_control( const char *page, size_t count) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; struct se_hba *hba = se_dev->se_dev_hba; struct se_subsystem_api *t = hba->transport; @@ -1680,7 +1680,7 @@ static struct target_core_configfs_attribute target_core_attr_dev_control = { static ssize_t target_core_show_dev_alias(void *p, char *page) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; if (!(se_dev->su_dev_flags & SDF_USING_ALIAS)) return 0; @@ -1693,7 +1693,7 @@ static ssize_t target_core_store_dev_alias( const char *page, size_t count) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; struct se_hba *hba = se_dev->se_dev_hba; ssize_t read_bytes; @@ -1726,7 +1726,7 @@ static struct target_core_configfs_attribute target_core_attr_dev_alias = { static ssize_t target_core_show_dev_udev_path(void *p, char *page) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH)) return 0; @@ -1739,7 +1739,7 @@ static ssize_t target_core_store_dev_udev_path( const char *page, size_t count) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; struct se_hba *hba = se_dev->se_dev_hba; ssize_t read_bytes; @@ -1775,7 +1775,7 @@ static ssize_t target_core_store_dev_enable( const char *page, size_t count) { - struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *se_dev = p; struct se_device *dev; struct se_hba *hba = se_dev->se_dev_hba; struct se_subsystem_api *t = hba->transport; @@ -1820,7 +1820,7 @@ static struct target_core_configfs_attribute target_core_attr_dev_enable = { static ssize_t target_core_show_alua_lu_gp(void *p, char *page) { struct se_device *dev; - struct se_subsystem_dev *su_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *su_dev = p; struct config_item *lu_ci; struct t10_alua_lu_gp *lu_gp; struct t10_alua_lu_gp_member *lu_gp_mem; @@ -1858,7 +1858,7 @@ static ssize_t target_core_store_alua_lu_gp( size_t count) { struct se_device *dev; - struct se_subsystem_dev *su_dev = (struct se_subsystem_dev *)p; + struct se_subsystem_dev *su_dev = p; struct se_hba *hba = su_dev->se_dev_hba; struct t10_alua_lu_gp *lu_gp = NULL, *lu_gp_new = NULL; struct t10_alua_lu_gp_member *lu_gp_mem; diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 32d7feb..283a36e 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -399,7 +399,7 @@ char *iscsi_parse_pr_out_transport_id( add_len = ((buf[2] >> 8) & 0xff); add_len |= (buf[3] & 0xff); - tid_len = strlen((char *)&buf[4]); + tid_len = strlen(&buf[4]); tid_len += 4; /* Add four bytes for iSCSI Transport ID header */ tid_len += 1; /* Add one byte for NULL terminator */ padding = ((-tid_len) & 3); @@ -420,11 +420,11 @@ char *iscsi_parse_pr_out_transport_id( * format. */ if (format_code == 0x40) { - p = strstr((char *)&buf[4], ",i,0x"); + p = strstr(&buf[4], ",i,0x"); if (!p) { pr_err("Unable to locate \",i,0x\" seperator" " for Initiator port identifier: %s\n", - (char *)&buf[4]); + &buf[4]); return NULL; } *p = '\0'; /* Terminate iSCSI Name */ diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 91bf11a..7ed58e2 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -85,7 +85,7 @@ static void fd_detach_hba(struct se_hba *hba) static void *fd_allocate_virtdevice(struct se_hba *hba, const char *name) { struct fd_dev *fd_dev; - struct fd_host *fd_host = (struct fd_host *) hba->hba_ptr; + struct fd_host *fd_host = hba->hba_ptr; fd_dev = kzalloc(sizeof(struct fd_dev), GFP_KERNEL); if (!fd_dev) { @@ -113,8 +113,8 @@ static struct se_device *fd_create_virtdevice( struct se_device *dev; struct se_dev_limits dev_limits; struct queue_limits *limits; - struct fd_dev *fd_dev = (struct fd_dev *) p; - struct fd_host *fd_host = (struct fd_host *) hba->hba_ptr; + struct fd_dev *fd_dev = p; + struct fd_host *fd_host = hba->hba_ptr; mm_segment_t old_fs; struct file *file; struct inode *inode = NULL; @@ -239,7 +239,7 @@ fail: */ static void fd_free_device(void *p) { - struct fd_dev *fd_dev = (struct fd_dev *) p; + struct fd_dev *fd_dev = p; if (fd_dev->fd_file) { filp_close(fd_dev->fd_file, NULL); @@ -558,7 +558,7 @@ out: static ssize_t fd_check_configfs_dev_params(struct se_hba *hba, struct se_subsystem_dev *se_dev) { - struct fd_dev *fd_dev = (struct fd_dev *) se_dev->se_dev_su_ptr; + struct fd_dev *fd_dev = se_dev->se_dev_su_ptr; if (!(fd_dev->fbd_flags & FBDF_HAS_PATH)) { pr_err("Missing fd_dev_name=\n"); diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 3c1a7aa..cc8e6b5 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -464,7 +464,7 @@ static ssize_t iblock_show_configfs_dev_params( if (bd) { bl += sprintf(b + bl, "Major: %d Minor: %d %s\n", MAJOR(bd->bd_dev), MINOR(bd->bd_dev), (!bd->bd_contains) ? - "" : (bd->bd_holder == (struct iblock_dev *)ibd) ? + "" : (bd->bd_holder == ibd) ? "CLAIMED: IBLOCK" : "CLAIMED: OS"); } else { bl += sprintf(b + bl, "Major: 0 Minor: 0\n"); diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index bb44ed4..b73a399 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -104,7 +104,7 @@ static void pscsi_detach_hba(struct se_hba *hba) static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag) { - struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)hba->hba_ptr; + struct pscsi_hba_virt *phv = hba->hba_ptr; struct Scsi_Host *sh = phv->phv_lld_host; /* * Release the struct Scsi_Host @@ -405,7 +405,7 @@ static struct se_device *pscsi_create_type_disk( __releases(sh->host_lock) { struct se_device *dev; - struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr; + struct pscsi_hba_virt *phv = pdv->pdv_se_hba->hba_ptr; struct Scsi_Host *sh = sd->host; struct block_device *bd; u32 dev_flags = 0; @@ -453,7 +453,7 @@ static struct se_device *pscsi_create_type_rom( __releases(sh->host_lock) { struct se_device *dev; - struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr; + struct pscsi_hba_virt *phv = pdv->pdv_se_hba->hba_ptr; struct Scsi_Host *sh = sd->host; u32 dev_flags = 0; @@ -488,7 +488,7 @@ static struct se_device *pscsi_create_type_other( __releases(sh->host_lock) { struct se_device *dev; - struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr; + struct pscsi_hba_virt *phv = pdv->pdv_se_hba->hba_ptr; struct Scsi_Host *sh = sd->host; u32 dev_flags = 0; @@ -509,10 +509,10 @@ static struct se_device *pscsi_create_virtdevice( struct se_subsystem_dev *se_dev, void *p) { - struct pscsi_dev_virt *pdv = (struct pscsi_dev_virt *)p; + struct pscsi_dev_virt *pdv = p; struct se_device *dev; struct scsi_device *sd; - struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)hba->hba_ptr; + struct pscsi_hba_virt *phv = hba->hba_ptr; struct Scsi_Host *sh = phv->phv_lld_host; int legacy_mode_enable = 0; @@ -1143,7 +1143,7 @@ static unsigned char *pscsi_get_sense_buffer(struct se_task *task) { struct pscsi_plugin_task *pt = PSCSI_TASK(task); - return (unsigned char *)&pt->pscsi_sense[0]; + return pt->pscsi_sense; } /* pscsi_get_device_rev(): diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index 6d8a904..f8c2d2c 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -1755,8 +1755,7 @@ static ssize_t target_stat_scsi_att_intr_port_show_attr_port_ident( /* scsiAttIntrPortName+scsiAttIntrPortIdentifier */ memset(buf, 0, 64); if (tpg->se_tpg_tfo->sess_get_initiator_sid != NULL) - tpg->se_tpg_tfo->sess_get_initiator_sid(se_sess, - (unsigned char *)&buf[0], 64); + tpg->se_tpg_tfo->sess_get_initiator_sid(se_sess, buf, 64); ret = snprintf(page, PAGE_SIZE, "%s+i+%s\n", nacl->initiatorname, buf); spin_unlock_irq(&nacl->nacl_sess_lock); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 511836e..1229256 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4161,7 +4161,7 @@ check_cond: static int transport_clear_lun_thread(void *p) { - struct se_lun *lun = (struct se_lun *)p; + struct se_lun *lun = p; __transport_clear_lun_from_sessions(lun); complete(&lun->lun_shutdown_comp); @@ -4580,7 +4580,7 @@ static int transport_processing_thread(void *param) { int ret; struct se_cmd *cmd; - struct se_device *dev = (struct se_device *) param; + struct se_device *dev = param; while (!kthread_should_stop()) { ret = wait_event_interruptible(dev->dev_queue_obj.thread_wq, -- cgit v0.10.2 From bc704fb58f507deea84a4c047a3a71351f0c55aa Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 28 Nov 2011 01:02:07 -0800 Subject: iscsi-target: fix chap identifier simple_strtoul usage This patch makes chap_server_compute_md5() use proper unsigned long usage for the CHAP_I (identifier) and check for values beyond 255 as per RFC-1994. Reported-by: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 92a2526..db0cf7c 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -165,7 +165,8 @@ static int chap_server_compute_md5( unsigned int *nr_out_len) { char *endptr; - unsigned char id, digest[MD5_SIGNATURE_SIZE]; + unsigned long id; + unsigned char digest[MD5_SIGNATURE_SIZE]; unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2]; unsigned char identifier[10], *challenge = NULL; unsigned char *challenge_binhex = NULL; @@ -304,15 +305,18 @@ static int chap_server_compute_md5( goto out; } - /* FIXME: What happens when simple_strtoul() return 256, 257, etc.? */ if (type == HEX) id = simple_strtoul(&identifier[2], &endptr, 0); else id = simple_strtoul(identifier, &endptr, 0); + if (id > 255) { + pr_err("chap identifier: %lu greater than 255\n", id); + goto out; + } /* * RFC 1994 says Identifier is no more than octet (8 bits). */ - pr_debug("[server] Got CHAP_I=%d\n", id); + pr_debug("[server] Got CHAP_I=%lu\n", id); /* * Get CHAP_C. */ -- cgit v0.10.2 From 0877eafd1618ef683e2ff2273acdeca5f1b077b9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 28 Nov 2011 13:57:25 +0100 Subject: target/configs: remove trailing newline from udev_path and alias This patch strips the trailing newline from backend device udev_path and alias attributes. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index eb62371..0955bb8 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1708,6 +1708,9 @@ static ssize_t target_core_store_dev_alias( read_bytes = snprintf(&se_dev->se_dev_alias[0], SE_DEV_ALIAS_LEN, "%s", page); + if (se_dev->se_dev_alias[read_bytes - 1] == '\n') + se_dev->se_dev_alias[read_bytes - 1] = '\0'; + pr_debug("Target_Core_ConfigFS: %s/%s set alias: %s\n", config_item_name(&hba->hba_group.cg_item), config_item_name(&se_dev->se_dev_group.cg_item), @@ -1754,6 +1757,9 @@ static ssize_t target_core_store_dev_udev_path( read_bytes = snprintf(&se_dev->se_dev_udev_path[0], SE_UDEV_PATH_LEN, "%s", page); + if (se_dev->se_dev_udev_path[read_bytes - 1] == '\n') + se_dev->se_dev_udev_path[read_bytes - 1] = '\0'; + pr_debug("Target_Core_ConfigFS: %s/%s set udev_path: %s\n", config_item_name(&hba->hba_group.cg_item), config_item_name(&se_dev->se_dev_group.cg_item), -- cgit v0.10.2 From 2e88efd3aaafa0df05593cdfa5fd17afe922781d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 29 Nov 2011 03:20:41 -0500 Subject: tcm_loop: bump max_sectors There is not reason to artifically limit max_sectors in tcm_loop, set it to UINT_MAX to allow stressing the large I/O handling in the target core using the loopback driver. Also remove various superflous defines hiding the values set in the host template. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 572d27a..c47ff7f 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -416,11 +416,11 @@ static struct scsi_host_template tcm_loop_driver_template = { .queuecommand = tcm_loop_queuecommand, .change_queue_depth = tcm_loop_change_queue_depth, .eh_device_reset_handler = tcm_loop_device_reset, - .can_queue = TL_SCSI_CAN_QUEUE, + .can_queue = 1024, .this_id = -1, - .sg_tablesize = TL_SCSI_SG_TABLESIZE, - .cmd_per_lun = TL_SCSI_CMD_PER_LUN, - .max_sectors = TL_SCSI_MAX_SECTORS, + .sg_tablesize = 256, + .cmd_per_lun = 1024, + .max_sectors = 0xFFFF, .use_clustering = DISABLE_CLUSTERING, .slave_alloc = tcm_loop_slave_alloc, .slave_configure = tcm_loop_slave_configure, diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h index 6b76c7a..15a0364 100644 --- a/drivers/target/loopback/tcm_loop.h +++ b/drivers/target/loopback/tcm_loop.h @@ -1,16 +1,7 @@ #define TCM_LOOP_VERSION "v2.1-rc1" #define TL_WWN_ADDR_LEN 256 #define TL_TPGS_PER_HBA 32 -/* - * Defaults for struct scsi_host_template tcm_loop_driver_template - * - * We use large can_queue and cmd_per_lun here and let TCM enforce - * the underlying se_device_t->queue_depth. - */ -#define TL_SCSI_CAN_QUEUE 1024 -#define TL_SCSI_CMD_PER_LUN 1024 -#define TL_SCSI_MAX_SECTORS 1024 -#define TL_SCSI_SG_TABLESIZE 256 + /* * Used in tcm_loop_driver_probe() for struct Scsi_Host->max_cmd_len */ -- cgit v0.10.2 From e0a53e70e8d519579d92c150606ece22333645cb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 29 Nov 2011 03:29:38 -0500 Subject: target: remove overagressive ____cacheline_aligned annoations If we want dynamically allocated objects to be cacheline aligned we need to tell that to the slab allocator by using the proper flags and not by liberally sprinkling annotations onto all structures. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 311f5fc..cadd4f6 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -252,7 +252,7 @@ enum tcm_tmrsp_table { struct se_obj { atomic_t obj_access_count; -} ____cacheline_aligned; +}; /* * Used by TCM Core internally to signal if ALUA emulation is enabled or @@ -299,7 +299,7 @@ struct t10_alua { struct config_group alua_tg_pt_gps_group; int (*alua_state_check)(struct se_cmd *, unsigned char *, u8 *); struct list_head tg_pt_gps_list; -} ____cacheline_aligned; +}; struct t10_alua_lu_gp { u16 lu_gp_id; @@ -310,7 +310,7 @@ struct t10_alua_lu_gp { struct config_group lu_gp_group; struct list_head lu_gp_node; struct list_head lu_gp_mem_list; -} ____cacheline_aligned; +}; struct t10_alua_lu_gp_member { bool lu_gp_assoc; @@ -319,7 +319,7 @@ struct t10_alua_lu_gp_member { struct t10_alua_lu_gp *lu_gp; struct se_device *lu_gp_mem_dev; struct list_head lu_gp_mem_list; -} ____cacheline_aligned; +}; struct t10_alua_tg_pt_gp { u16 tg_pt_gp_id; @@ -342,7 +342,7 @@ struct t10_alua_tg_pt_gp { struct config_group tg_pt_gp_group; struct list_head tg_pt_gp_list; struct list_head tg_pt_gp_mem_list; -} ____cacheline_aligned; +}; struct t10_alua_tg_pt_gp_member { bool tg_pt_gp_assoc; @@ -351,7 +351,7 @@ struct t10_alua_tg_pt_gp_member { struct t10_alua_tg_pt_gp *tg_pt_gp; struct se_port *tg_pt; struct list_head tg_pt_gp_mem_list; -} ____cacheline_aligned; +}; struct t10_vpd { unsigned char device_identifier[INQUIRY_VPD_DEVICE_IDENTIFIER_LEN]; @@ -361,7 +361,7 @@ struct t10_vpd { u32 association; u32 device_identifier_type; struct list_head vpd_list; -} ____cacheline_aligned; +}; struct t10_wwn { char vendor[8]; @@ -372,7 +372,7 @@ struct t10_wwn { struct se_subsystem_dev *t10_sub_dev; struct config_group t10_wwn_group; struct list_head t10_vpd_list; -} ____cacheline_aligned; +}; /* @@ -425,7 +425,7 @@ struct t10_pr_registration { struct list_head pr_reg_aptpl_list; struct list_head pr_reg_atp_list; struct list_head pr_reg_atp_mem_list; -} ____cacheline_aligned; +}; /* * This set of function pointer ops is set based upon SPC3_PERSISTENT_RESERVATIONS, @@ -466,20 +466,20 @@ struct t10_reservation { struct list_head registration_list; struct list_head aptpl_reg_list; struct t10_reservation_ops pr_ops; -} ____cacheline_aligned; +}; struct se_queue_req { int state; struct se_cmd *cmd; struct list_head qr_list; -} ____cacheline_aligned; +}; struct se_queue_obj { atomic_t queue_cnt; spinlock_t cmd_queue_lock; struct list_head qobj_list; wait_queue_head_t thread_wq; -} ____cacheline_aligned; +}; struct se_task { unsigned long long task_lba; @@ -496,7 +496,7 @@ struct se_task { struct list_head t_state_list; bool t_state_active; struct completion task_stop_comp; -} ____cacheline_aligned; +}; struct se_cmd { /* SAM response code being sent to initiator */ @@ -582,7 +582,7 @@ struct se_cmd { struct list_head t_task_list; u32 t_task_list_num; -} ____cacheline_aligned; +}; struct se_tmr_req { /* Task Management function to be preformed */ @@ -600,7 +600,7 @@ struct se_tmr_req { struct se_device *tmr_dev; struct se_lun *tmr_lun; struct list_head tmr_list; -} ____cacheline_aligned; +}; struct se_ua { u8 ua_asc; @@ -608,7 +608,7 @@ struct se_ua { struct se_node_acl *ua_nacl; struct list_head ua_dev_list; struct list_head ua_nacl_list; -} ____cacheline_aligned; +}; struct se_node_acl { char initiatorname[TRANSPORT_IQN_LEN]; @@ -635,7 +635,7 @@ struct se_node_acl { struct config_group *acl_default_groups[5]; struct list_head acl_list; struct list_head acl_sess_list; -} ____cacheline_aligned; +}; struct se_session { unsigned sess_tearing_down:1; @@ -648,7 +648,7 @@ struct se_session { struct list_head sess_cmd_list; struct list_head sess_wait_list; spinlock_t sess_cmd_lock; -} ____cacheline_aligned; +}; struct se_device; struct se_transform_info; @@ -668,7 +668,7 @@ struct se_lun_acl { struct list_head lacl_list; struct config_group se_lun_group; struct se_ml_stat_grps ml_stat_grps; -} ____cacheline_aligned; +}; struct se_dev_entry { bool def_pr_registered; @@ -693,7 +693,7 @@ struct se_dev_entry { struct se_lun *se_lun; struct list_head alua_port_list; struct list_head ua_list; -} ____cacheline_aligned; +}; struct se_dev_limits { /* Max supported HW queue depth */ @@ -702,7 +702,7 @@ struct se_dev_limits { u32 queue_depth; /* From include/linux/blkdev.h for the other HW/SW limits. */ struct queue_limits limits; -} ____cacheline_aligned; +}; struct se_dev_attrib { int emulate_dpo; @@ -731,7 +731,7 @@ struct se_dev_attrib { u32 unmap_granularity_alignment; struct se_subsystem_dev *da_sub_dev; struct config_group da_group; -} ____cacheline_aligned; +}; struct se_dev_stat_grps { struct config_group stat_group; @@ -764,7 +764,7 @@ struct se_subsystem_dev { struct config_group se_dev_pr_group; /* For target_core_stat.c groups */ struct se_dev_stat_grps dev_stat_grps; -} ____cacheline_aligned; +}; struct se_device { /* RELATIVE TARGET PORT IDENTIFER Counter */ @@ -830,7 +830,7 @@ struct se_device { struct se_subsystem_api *transport; /* Linked list for struct se_hba struct se_device list */ struct list_head dev_list; -} ____cacheline_aligned; +}; struct se_hba { u16 hba_tpgt; @@ -849,7 +849,7 @@ struct se_hba { struct config_group hba_group; struct mutex hba_access_mutex; struct se_subsystem_api *transport; -} ____cacheline_aligned; +}; struct se_port_stat_grps { struct config_group stat_group; @@ -875,13 +875,13 @@ struct se_lun { struct se_port *lun_sep; struct config_group lun_group; struct se_port_stat_grps port_stat_grps; -} ____cacheline_aligned; +}; struct scsi_port_stats { u64 cmd_pdus; u64 tx_data_octets; u64 rx_data_octets; -} ____cacheline_aligned; +}; struct se_port { /* RELATIVE TARGET PORT IDENTIFER */ @@ -901,12 +901,12 @@ struct se_port { struct se_portal_group *sep_tpg; struct list_head sep_alua_list; struct list_head sep_list; -} ____cacheline_aligned; +}; struct se_tpg_np { struct se_portal_group *tpg_np_parent; struct config_group tpg_np_group; -} ____cacheline_aligned; +}; struct se_portal_group { /* Type of target portal group, see transport_tpg_type_table */ @@ -939,13 +939,13 @@ struct se_portal_group { struct config_group tpg_acl_group; struct config_group tpg_attrib_group; struct config_group tpg_param_group; -} ____cacheline_aligned; +}; struct se_wwn { struct target_fabric_configfs *wwn_tf; struct config_group wwn_group; struct config_group *wwn_default_groups[2]; struct config_group fabric_stat_group; -} ____cacheline_aligned; +}; #endif /* TARGET_CORE_BASE_H */ -- cgit v0.10.2 From 77271de645631cb1dc4563d564f9dd5c214b5eed Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 28 Nov 2011 17:54:28 -0800 Subject: Documentation/target: Fix tcm_mod_builder.py build breakage This patch fixes TFO->release_cmd() and removes legacy pack_lun() usage and new_cmd_failure when generating new TCM fabric skeleton from the tcm_mod_builder.py script. Reported-by: Stefan Bergstrand Signed-off-by: Nicholas Bellinger diff --git a/Documentation/target/tcm_mod_builder.py b/Documentation/target/tcm_mod_builder.py index e2abb29..520a923 100755 --- a/Documentation/target/tcm_mod_builder.py +++ b/Documentation/target/tcm_mod_builder.py @@ -386,8 +386,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " .tpg_alloc_fabric_acl = " + fabric_mod_name + "_alloc_fabric_acl,\n" buf += " .tpg_release_fabric_acl = " + fabric_mod_name + "_release_fabric_acl,\n" buf += " .tpg_get_inst_index = " + fabric_mod_name + "_tpg_get_inst_index,\n" - buf += " .release_cmd_to_pool = " + fabric_mod_name + "_release_cmd,\n" - buf += " .release_cmd_direct = " + fabric_mod_name + "_release_cmd,\n" + buf += " .release_cmd = " + fabric_mod_name + "_release_cmd,\n" buf += " .shutdown_session = " + fabric_mod_name + "_shutdown_session,\n" buf += " .close_session = " + fabric_mod_name + "_close_session,\n" buf += " .stop_session = " + fabric_mod_name + "_stop_session,\n" @@ -400,14 +399,12 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " .set_default_node_attributes = " + fabric_mod_name + "_set_default_node_attrs,\n" buf += " .get_task_tag = " + fabric_mod_name + "_get_task_tag,\n" buf += " .get_cmd_state = " + fabric_mod_name + "_get_cmd_state,\n" - buf += " .new_cmd_failure = " + fabric_mod_name + "_new_cmd_failure,\n" buf += " .queue_data_in = " + fabric_mod_name + "_queue_data_in,\n" buf += " .queue_status = " + fabric_mod_name + "_queue_status,\n" buf += " .queue_tm_rsp = " + fabric_mod_name + "_queue_tm_rsp,\n" buf += " .get_fabric_sense_len = " + fabric_mod_name + "_get_fabric_sense_len,\n" buf += " .set_fabric_sense_len = " + fabric_mod_name + "_set_fabric_sense_len,\n" buf += " .is_state_remove = " + fabric_mod_name + "_is_state_remove,\n" - buf += " .pack_lun = " + fabric_mod_name + "_pack_lun,\n" buf += " /*\n" buf += " * Setup function pointers for generic logic in target_core_fabric_configfs.c\n" buf += " */\n" @@ -806,7 +803,7 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "}\n\n" bufi += "u32 " + fabric_mod_name + "_tpg_get_inst_index(struct se_portal_group *);\n" - if re.search('release_cmd_to_pool', fo): + if re.search('\*release_cmd\)\(', fo): buf += "void " + fabric_mod_name + "_release_cmd(struct se_cmd *se_cmd)\n" buf += "{\n" buf += " return;\n" @@ -890,13 +887,6 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "}\n\n" bufi += "int " + fabric_mod_name + "_get_cmd_state(struct se_cmd *);\n" - if re.search('new_cmd_failure\)\(', fo): - buf += "void " + fabric_mod_name + "_new_cmd_failure(struct se_cmd *se_cmd)\n" - buf += "{\n" - buf += " return;\n" - buf += "}\n\n" - bufi += "void " + fabric_mod_name + "_new_cmd_failure(struct se_cmd *);\n" - if re.search('queue_data_in\)\(', fo): buf += "int " + fabric_mod_name + "_queue_data_in(struct se_cmd *se_cmd)\n" buf += "{\n" @@ -939,15 +929,6 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "}\n\n" bufi += "int " + fabric_mod_name + "_is_state_remove(struct se_cmd *);\n" - if re.search('pack_lun\)\(', fo): - buf += "u64 " + fabric_mod_name + "_pack_lun(unsigned int lun)\n" - buf += "{\n" - buf += " WARN_ON(lun >= 256);\n" - buf += " /* Caller wants this byte-swapped */\n" - buf += " return cpu_to_le64((lun & 0xff) << 8);\n" - buf += "}\n\n" - bufi += "u64 " + fabric_mod_name + "_pack_lun(unsigned int);\n" - ret = p.write(buf) if ret: -- cgit v0.10.2 From ced38c8c00962c9b6c1fcd330572406162d150b9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 6 Dec 2011 11:20:31 +0100 Subject: target: tcm_mod_builder: small fixups This includes: - remove on _ in "__NAMELEN" in $fabric _make_tport - target_fabric_configfs_init() returns an error pointer and not NULL anymore. Consider that. - replace (!(var_name)) with (!var_name). The extra () are not required - remove #ifdef MODULE. If the code is builtin it needs an init function or the code is useless - put exit/clean functions into __exit Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Nicholas Bellinger diff --git a/Documentation/target/tcm_mod_builder.py b/Documentation/target/tcm_mod_builder.py index 520a923..6e21b8b 100755 --- a/Documentation/target/tcm_mod_builder.py +++ b/Documentation/target/tcm_mod_builder.py @@ -255,7 +255,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " /* " + fabric_mod_name + "_parse_wwn(name, &wwpn, 1) < 0)\n" buf += " return ERR_PTR(-EINVAL); */\n" buf += " se_nacl_new = " + fabric_mod_name + "_alloc_fabric_acl(se_tpg);\n" - buf += " if (!(se_nacl_new))\n" + buf += " if (!se_nacl_new)\n" buf += " return ERR_PTR(-ENOMEM);\n" buf += "//#warning FIXME: Hardcoded nexus depth in " + fabric_mod_name + "_make_nodeacl()\n" buf += " nexus_depth = 1;\n" @@ -303,7 +303,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " if (strict_strtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)\n" buf += " return ERR_PTR(-EINVAL);\n\n" buf += " tpg = kzalloc(sizeof(struct " + fabric_mod_name + "_tpg), GFP_KERNEL);\n" - buf += " if (!(tpg)) {\n" + buf += " if (!tpg) {\n" buf += " printk(KERN_ERR \"Unable to allocate struct " + fabric_mod_name + "_tpg\");\n" buf += " return ERR_PTR(-ENOMEM);\n" buf += " }\n" @@ -339,7 +339,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " /* if (" + fabric_mod_name + "_parse_wwn(name, &wwpn, 1) < 0)\n" buf += " return ERR_PTR(-EINVAL); */\n\n" buf += " " + fabric_mod_port + " = kzalloc(sizeof(struct " + fabric_mod_name + "_" + fabric_mod_port + "), GFP_KERNEL);\n" - buf += " if (!(" + fabric_mod_port + ")) {\n" + buf += " if (!" + fabric_mod_port + ") {\n" buf += " printk(KERN_ERR \"Unable to allocate struct " + fabric_mod_name + "_" + fabric_mod_port + "\");\n" buf += " return ERR_PTR(-ENOMEM);\n" buf += " }\n" @@ -347,7 +347,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): if proto_ident == "FC" or proto_ident == "SAS": buf += " " + fabric_mod_port + "->" + fabric_mod_port + "_wwpn = wwpn;\n" - buf += " /* " + fabric_mod_name + "_format_wwn(&" + fabric_mod_port + "->" + fabric_mod_port + "_name[0], " + fabric_mod_name.upper() + "__NAMELEN, wwpn); */\n\n" + buf += " /* " + fabric_mod_name + "_format_wwn(&" + fabric_mod_port + "->" + fabric_mod_port + "_name[0], " + fabric_mod_name.upper() + "_NAMELEN, wwpn); */\n\n" buf += " return &" + fabric_mod_port + "->" + fabric_mod_port + "_wwn;\n" buf += "}\n\n" buf += "static void " + fabric_mod_name + "_drop_" + fabric_mod_port + "(struct se_wwn *wwn)\n" @@ -431,9 +431,9 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " * Register the top level struct config_item_type with TCM core\n" buf += " */\n" buf += " fabric = target_fabric_configfs_init(THIS_MODULE, \"" + fabric_mod_name[4:] + "\");\n" - buf += " if (!(fabric)) {\n" + buf += " if (IS_ERR(fabric)) {\n" buf += " printk(KERN_ERR \"target_fabric_configfs_init() failed\\n\");\n" - buf += " return -ENOMEM;\n" + buf += " return PTR_ERR(fabric);\n" buf += " }\n" buf += " /*\n" buf += " * Setup fabric->tf_ops from our local " + fabric_mod_name + "_ops\n" @@ -467,9 +467,9 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " printk(KERN_INFO \"" + fabric_mod_name.upper() + "[0] - Set fabric -> " + fabric_mod_name + "_fabric_configfs\\n\");\n" buf += " return 0;\n" buf += "};\n\n" - buf += "static void " + fabric_mod_name + "_deregister_configfs(void)\n" + buf += "static void __exit " + fabric_mod_name + "_deregister_configfs(void)\n" buf += "{\n" - buf += " if (!(" + fabric_mod_name + "_fabric_configfs))\n" + buf += " if (!" + fabric_mod_name + "_fabric_configfs)\n" buf += " return;\n\n" buf += " target_fabric_configfs_deregister(" + fabric_mod_name + "_fabric_configfs);\n" buf += " " + fabric_mod_name + "_fabric_configfs = NULL;\n" @@ -484,17 +484,15 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += " return ret;\n\n" buf += " return 0;\n" buf += "};\n\n" - buf += "static void " + fabric_mod_name + "_exit(void)\n" + buf += "static void __exit " + fabric_mod_name + "_exit(void)\n" buf += "{\n" buf += " " + fabric_mod_name + "_deregister_configfs();\n" buf += "};\n\n" - buf += "#ifdef MODULE\n" buf += "MODULE_DESCRIPTION(\"" + fabric_mod_name.upper() + " series fabric driver\");\n" buf += "MODULE_LICENSE(\"GPL\");\n" buf += "module_init(" + fabric_mod_name + "_init);\n" buf += "module_exit(" + fabric_mod_name + "_exit);\n" - buf += "#endif\n" ret = p.write(buf) if ret: @@ -776,7 +774,7 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name): buf += "{\n" buf += " struct " + fabric_mod_name + "_nacl *nacl;\n\n" buf += " nacl = kzalloc(sizeof(struct " + fabric_mod_name + "_nacl), GFP_KERNEL);\n" - buf += " if (!(nacl)) {\n" + buf += " if (!nacl) {\n" buf += " printk(KERN_ERR \"Unable to alocate struct " + fabric_mod_name + "_nacl\\n\");\n" buf += " return NULL;\n" buf += " }\n\n" -- cgit v0.10.2 From ce136176fea522fc8f4c16dcae7e8ed1d890ca39 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 6 Dec 2011 10:02:09 -0800 Subject: target: Set response format in INQUIRY response Current SCSI specs say that the "response format" field in the standard INQUIRY response should be set to 2, and all the real SCSI devices I have do put 2 here. So let's do that too. Signed-off-by: Roland Dreier Cc: stable@kernel.org Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 5c60d0a..2f2235e 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -95,6 +95,18 @@ target_emulate_inquiry_std(struct se_cmd *cmd) buf[2] = dev->transport->get_device_rev(dev); /* + * NORMACA and HISUP = 0, RESPONSE DATA FORMAT = 2 + * + * SPC4 says: + * A RESPONSE DATA FORMAT field set to 2h indicates that the + * standard INQUIRY data is in the format defined in this + * standard. Response data format values less than 2h are + * obsolete. Response data format values greater than 2h are + * reserved. + */ + buf[3] = 2; + + /* * Enable SCCS and TPGS fields for Emulated ALUA */ if (dev->se_sub_dev->t10_alua.alua_type == SPC3_ALUA_EMULATED) -- cgit v0.10.2 From 7481deb413be132a22193e8a0bce88b311ecb3c2 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 12 Nov 2011 00:32:17 -0800 Subject: target: Make target_put_sess_cmd use target_release_cmd_kref This patch moves target_put_sess_cmd() to use a se_cmd->cmd_kref callback target_release_cmd_kref when performing driver release of fabric->se_cmd descriptor memory. It sets the default cmd_kref count value to '2' within target_get_sess_cmd() setup, and currently assumes TFO->check_stop_free() usage. It drops se_tfo->check_release_cmd() usage in the main transport_release_cmd codepath. Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 1229256..176f956 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3349,13 +3349,13 @@ static void transport_release_cmd(struct se_cmd *cmd) if (cmd->t_task_cdb != cmd->__t_task_cdb) kfree(cmd->t_task_cdb); /* - * Check if target_wait_for_sess_cmds() is expecting to - * release se_cmd directly here.. + * If this cmd has been setup with target_get_sess_cmd(), drop + * the kref and call ->release_cmd() in kref callback. */ - if (cmd->check_release != 0 && cmd->se_tfo->check_release_cmd) - if (cmd->se_tfo->check_release_cmd(cmd) != 0) - return; - + if (cmd->check_release != 0) { + target_put_sess_cmd(cmd->se_sess, cmd); + return; + } cmd->se_tfo->release_cmd(cmd); } @@ -3915,6 +3915,9 @@ void target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd) { unsigned long flags; + kref_init(&se_cmd->cmd_kref); + kref_get(&se_cmd->cmd_kref); + spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); se_cmd->check_release = 1; @@ -3922,30 +3925,36 @@ void target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd) } EXPORT_SYMBOL(target_get_sess_cmd); -/* target_put_sess_cmd - Check for active I/O shutdown or list delete - * @se_sess: session to reference - * @se_cmd: command descriptor to drop - */ -int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd) +static void target_release_cmd_kref(struct kref *kref) { + struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref); + struct se_session *se_sess = se_cmd->se_sess; unsigned long flags; spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); if (list_empty(&se_cmd->se_cmd_list)) { spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); WARN_ON(1); - return 0; + return; } - if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) { spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); complete(&se_cmd->cmd_wait_comp); - return 1; + return; } list_del(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); - return 0; + se_cmd->se_tfo->release_cmd(se_cmd); +} + +/* target_put_sess_cmd - Check for active I/O shutdown via kref_put + * @se_sess: session to reference + * @se_cmd: command descriptor to drop + */ +int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd) +{ + return kref_put(&se_cmd->cmd_kref, target_release_cmd_kref); } EXPORT_SYMBOL(target_put_sess_cmd); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index cadd4f6..af088a9 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -541,6 +541,7 @@ struct se_cmd { struct list_head se_queue_node; struct list_head se_cmd_list; struct completion cmd_wait_comp; + struct kref cmd_kref; struct target_core_fabric_ops *se_tfo; int (*execute_task)(struct se_task *); void (*transport_complete_callback)(struct se_cmd *); -- cgit v0.10.2 From a63607855224702ea17e6016ecf3f7d544e83625 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 18 Nov 2011 20:36:22 -0800 Subject: target: Add target_submit_cmd() for process context fabric submission This patch adds a target_submit_cmd() caller that can be used by fabrics to submit an uninitialized se_cmd descriptor to an struct se_session + unpacked_lun from workqueue process context. This call will invoke the following steps: - transport_init_se_cmd() to setup se_cmd specific pointers - Obtain se_cmd->cmd_kref references with target_get_sess_cmd() - set se_cmd->t_tasks_bidi - transport_lookup_cmd_lun() to setup struct se_cmd->se_lun from the passed unpacked_lun - transport_generic_allocate_tasks() to setup the passed *cdb, and - transport_handle_cdb_direct() handle READ dispatch or WRITE ready-to-transfer callback to fabric v2 changes from hch feedback: *) Add target_sc_flags_table for target_submit_cmd flags *) Rename bidi parameter to flags, add TARGET_SCF_BIDI_OP *) Convert checks to BUG_ON *) Add out_check_cond for transport_send_check_condition_and_sense usage v3 changes: *) Add TARGET_SCF_ACK_KREF for target_submit_cmd into target_get_sess_cmd to determine when the fabric caller is expecting a second kref_put() from fabric packet acknowledgement. Cc: Christoph Hellwig Cc: Roland Dreier Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 176f956..e4389d4 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1642,6 +1642,80 @@ int transport_handle_cdb_direct( } EXPORT_SYMBOL(transport_handle_cdb_direct); +/** + * target_submit_cmd - lookup unpacked lun and submit uninitialized se_cmd + * + * @se_cmd: command descriptor to submit + * @se_sess: associated se_sess for endpoint + * @cdb: pointer to SCSI CDB + * @sense: pointer to SCSI sense buffer + * @unpacked_lun: unpacked LUN to reference for struct se_lun + * @data_length: fabric expected data transfer length + * @task_addr: SAM task attribute + * @data_dir: DMA data direction + * @flags: flags for command submission from target_sc_flags_tables + * + * This may only be called from process context, and also currently + * assumes internal allocation of fabric payload buffer by target-core. + **/ +int target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + unsigned char *cdb, unsigned char *sense, u32 unpacked_lun, + u32 data_length, int task_attr, int data_dir, int flags) +{ + struct se_portal_group *se_tpg; + int rc; + + se_tpg = se_sess->se_tpg; + BUG_ON(!se_tpg); + BUG_ON(se_cmd->se_tfo || se_cmd->se_sess); + BUG_ON(in_interrupt()); + /* + * Initialize se_cmd for target operation. From this point + * exceptions are handled by sending exception status via + * target_core_fabric_ops->queue_status() callback + */ + transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, + data_length, data_dir, task_attr, sense); + /* + * Obtain struct se_cmd->cmd_kref reference and add new cmd to + * se_sess->sess_cmd_list. A second kref_get here is necessary + * for fabrics using TARGET_SCF_ACK_KREF that expect a second + * kref_put() to happen during fabric packet acknowledgement. + */ + target_get_sess_cmd(se_sess, se_cmd, (flags & TARGET_SCF_ACK_KREF)); + /* + * Signal bidirectional data payloads to target-core + */ + if (flags & TARGET_SCF_BIDI_OP) + se_cmd->se_cmd_flags |= SCF_BIDI; + /* + * Locate se_lun pointer and attach it to struct se_cmd + */ + if (transport_lookup_cmd_lun(se_cmd, unpacked_lun) < 0) + goto out_check_cond; + /* + * Sanitize CDBs via transport_generic_cmd_sequencer() and + * allocate the necessary tasks to complete the received CDB+data + */ + rc = transport_generic_allocate_tasks(se_cmd, cdb); + if (rc != 0) + goto out_check_cond; + /* + * Dispatch se_cmd descriptor to se_lun->lun_se_dev backend + * for immediate execution of READs, otherwise wait for + * transport_generic_handle_data() to be called for WRITEs + * when fabric has filled the incoming buffer. + */ + transport_handle_cdb_direct(se_cmd); + return 0; + +out_check_cond: + transport_send_check_condition_and_sense(se_cmd, + se_cmd->scsi_sense_reason, 0); + return 0; +} +EXPORT_SYMBOL(target_submit_cmd); + /* * Used by fabric module frontends defining a TFO->new_cmd_map() caller * to queue up a newly setup se_cmd w/ TRANSPORT_NEW_CMD_MAP in order to @@ -3910,13 +3984,21 @@ EXPORT_SYMBOL(transport_generic_free_cmd); /* target_get_sess_cmd - Add command to active ->sess_cmd_list * @se_sess: session to reference * @se_cmd: command descriptor to add + * @ack_kref: Signal that fabric will perform an ack target_put_sess_cmd() */ -void target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd) +void target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd, + bool ack_kref) { unsigned long flags; kref_init(&se_cmd->cmd_kref); - kref_get(&se_cmd->cmd_kref); + /* + * Add a second kref if the fabric caller is expecting to handle + * fabric acknowledgement that requires two target_put_sess_cmd() + * invocations before se_cmd descriptor release. + */ + if (ack_kref == true) + kref_get(&se_cmd->cmd_kref); spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index af088a9..28190dc 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -226,6 +226,11 @@ enum tcm_sense_reason_table { TCM_RESERVATION_CONFLICT = 0x10, }; +enum target_sc_flags_table { + TARGET_SCF_BIDI_OP = 0x01, + TARGET_SCF_ACK_KREF = 0x02, +}; + /* fabric independent task management function values */ enum tcm_tmreq_table { TMR_ABORT_TASK = 1, diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index d035d865..aaa26da 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -118,6 +118,8 @@ void transport_init_se_cmd(struct se_cmd *, struct target_core_fabric_ops *, struct se_session *, u32, int, int, unsigned char *); int transport_lookup_cmd_lun(struct se_cmd *, u32); int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *); +int target_submit_cmd(struct se_cmd *, struct se_session *, unsigned char *, + unsigned char *, u32, u32, int, int, int); int transport_handle_cdb_direct(struct se_cmd *); int transport_generic_handle_cdb_map(struct se_cmd *); int transport_generic_handle_data(struct se_cmd *); @@ -134,7 +136,7 @@ bool transport_wait_for_tasks(struct se_cmd *); int transport_check_aborted_status(struct se_cmd *, int); int transport_send_check_condition_and_sense(struct se_cmd *, u8, int); -void target_get_sess_cmd(struct se_session *, struct se_cmd *); +void target_get_sess_cmd(struct se_session *, struct se_cmd *, bool); int target_put_sess_cmd(struct se_session *, struct se_cmd *); void target_splice_sess_cmd_list(struct se_session *); void target_wait_for_sess_cmds(struct se_session *, int); -- cgit v0.10.2 From 4355a9110eeb2eaf1dd44fcab16ccbd1c8c5fad4 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 22 Nov 2011 21:41:54 -0800 Subject: tcm_fc: Convert ft_send_work to use target_submit_cmd This patch converts the main ft_send_work() I/O path to use target_submit_cmd() with a single se_cmd->cmd_kref reference that is released via the existing ft_check_stop_free() response path callback. It also makes ft_send_tm() use transport_init_se_cmd() and target_get_sess_cmd() to also use single se_cmd->cmd_kref reference. Cc: Christoph Hellwig Cc: Kiran Patil Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index dbbbc83..addc18f 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -363,6 +363,11 @@ static void ft_send_tm(struct ft_cmd *cmd) struct ft_sess *sess; u8 tm_func; + transport_init_se_cmd(&cmd->se_cmd, &ft_configfs->tf_ops, + cmd->sess->se_sess, 0, DMA_NONE, 0, + &cmd->ft_sense_buffer[0]); + target_get_sess_cmd(cmd->sess->se_sess, &cmd->se_cmd, false); + fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp)); switch (fcp->fc_tm_flags) { @@ -416,7 +421,6 @@ static void ft_send_tm(struct ft_cmd *cmd) sess = cmd->sess; transport_send_check_condition_and_sense(&cmd->se_cmd, cmd->se_cmd.scsi_sense_reason, 0); - transport_generic_free_cmd(&cmd->se_cmd, 0); ft_sess_put(sess); return; } @@ -532,7 +536,6 @@ static void ft_send_work(struct work_struct *work) { struct ft_cmd *cmd = container_of(work, struct ft_cmd, work); struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame); - struct se_cmd *se_cmd; struct fcp_cmnd *fcp; int data_dir = 0; u32 data_len; @@ -587,15 +590,6 @@ static void ft_send_work(struct work_struct *work) data_len = ntohl(fcp->fc_dl); cmd->cdb = fcp->fc_cdb; } - - se_cmd = &cmd->se_cmd; - /* - * Initialize struct se_cmd descriptor from target_core_mod - * infrastructure - */ - transport_init_se_cmd(se_cmd, &ft_configfs->tf_ops, cmd->sess->se_sess, - data_len, data_dir, task_attr, - &cmd->ft_sense_buffer[0]); /* * Check for FCP task management flags */ @@ -603,39 +597,20 @@ static void ft_send_work(struct work_struct *work) ft_send_tm(cmd); return; } - fc_seq_exch(cmd->seq)->lp->tt.seq_set_resp(cmd->seq, ft_recv_seq, cmd); - cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun); - ret = transport_lookup_cmd_lun(&cmd->se_cmd, cmd->lun); + /* + * Use a single se_cmd->cmd_kref as we expect to release se_cmd + * directly from ft_check_stop_free callback in response path. + */ + ret = target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, cmd->cdb, + &cmd->ft_sense_buffer[0], cmd->lun, data_len, + task_attr, data_dir, 0); + pr_debug("r_ctl %x alloc target_submit_cmd %d\n", fh->fh_r_ctl, ret); if (ret < 0) { ft_dump_cmd(cmd, __func__); - transport_send_check_condition_and_sense(&cmd->se_cmd, - cmd->se_cmd.scsi_sense_reason, 0); - return; - } - - ret = transport_generic_allocate_tasks(se_cmd, cmd->cdb); - - pr_debug("r_ctl %x alloc task ret %d\n", fh->fh_r_ctl, ret); - ft_dump_cmd(cmd, __func__); - - if (ret == -ENOMEM) { - transport_send_check_condition_and_sense(se_cmd, - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); - transport_generic_free_cmd(se_cmd, 0); - return; - } - if (ret == -EINVAL) { - if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) - ft_queue_status(se_cmd); - else - transport_send_check_condition_and_sense(se_cmd, - se_cmd->scsi_sense_reason, 0); - transport_generic_free_cmd(se_cmd, 0); return; } - transport_handle_cdb_direct(se_cmd); return; err: -- cgit v0.10.2 From ec54cc081ead14e85736c6543e36ec59be8a7d3d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 14 Dec 2011 10:50:16 +0000 Subject: target: Remove TFO->check_release_cmd() fabric API caller Remove the now unused target_core_fabric_ops->check_release_cmd() as target_core handles this directly for se_cmd->cmd_kref objects now. Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index aaa26da..523e8bc 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -52,10 +52,6 @@ struct target_core_fabric_ops { * Returning 0 will signal a descriptor has not been released. */ int (*check_stop_free)(struct se_cmd *); - /* - * Optional check for active I/O shutdown - */ - int (*check_release_cmd)(struct se_cmd *); void (*release_cmd)(struct se_cmd *); /* * Called with spin_lock_bh(struct se_portal_group->session_lock held. -- cgit v0.10.2 From 40be67f4c588fe2f3e2dbd60ae1f470abc5b6ad8 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 30 Nov 2011 00:41:20 -0800 Subject: target: Fix possible NULL pointer with __transport_execute_tasks This patch makes __transport_execute_tasks() use a local *se_dev reference to prevent direct se_cmd->se_dev access after transport_cmd_check_stop() -> transport_add_tasks_from_cmd() has been called, as in the current implementation we can expect __transport_execute_tasks() may be called from another context that may have already completed the I/O. Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index e4389d4..1cc7e92 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2076,6 +2076,7 @@ static inline int transport_execute_task_attr(struct se_cmd *cmd) static int transport_execute_tasks(struct se_cmd *cmd) { int add_tasks; + struct se_device *se_dev = cmd->se_dev; if (se_dev_check_online(cmd->se_dev) != 0) { cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -2108,7 +2109,7 @@ static int transport_execute_tasks(struct se_cmd *cmd) * storage object. */ execute_tasks: - __transport_execute_tasks(cmd->se_dev); + __transport_execute_tasks(se_dev); return 0; } -- cgit v0.10.2 From 65586d51e0986be574118286c3d0007e903a2add Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 30 Nov 2011 01:25:21 -0800 Subject: target: Drop se_device TCQ queue_depth usage from I/O path Historically, pSCSI devices have been the ones that required target-core to enforce a per se_device->depth_left. This patch changes target-core to no longer (by default) enforce a per se_device->depth_left or sleep in transport_tcq_window_closed() when we out of queue slots for all backend export cases. Cc: Christoph Hellwig Cc: Roland Dreier Cc: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 1f74de2..0c5992f 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1132,8 +1132,6 @@ int se_dev_set_emulate_rest_reord(struct se_device *dev, int flag) */ int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth) { - u32 orig_queue_depth = dev->queue_depth; - if (atomic_read(&dev->dev_export_obj.obj_access_count)) { pr_err("dev[%p]: Unable to change SE Device TCQ while" " dev_export_obj: %d count exists\n", dev, @@ -1167,11 +1165,6 @@ int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth) } dev->se_sub_dev->se_dev_attrib.queue_depth = dev->queue_depth = queue_depth; - if (queue_depth > orig_queue_depth) - atomic_add(queue_depth - orig_queue_depth, &dev->depth_left); - else if (queue_depth < orig_queue_depth) - atomic_sub(orig_queue_depth - queue_depth, &dev->depth_left); - pr_debug("dev[%p]: SE Device TCQ Depth changed to: %u\n", dev, queue_depth); return 0; diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index b73a399..d35467d 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -350,7 +350,6 @@ static struct se_device *pscsi_add_device_to_list( * scsi_device_put() and the pdv->pdv_sd cleared. */ pdv->pdv_sd = sd; - dev = transport_add_device_to_core_hba(hba, &pscsi_template, se_dev, dev_flags, pdv, &dev_limits, NULL, NULL); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 1cc7e92..7c2def7 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -691,12 +691,6 @@ void transport_complete_task(struct se_task *task, int success) struct se_cmd *cmd = task->task_se_cmd; struct se_device *dev = cmd->se_dev; unsigned long flags; -#if 0 - pr_debug("task: %p CDB: 0x%02x obj_ptr: %p\n", task, - cmd->t_task_cdb[0], dev); -#endif - if (dev) - atomic_inc(&dev->depth_left); spin_lock_irqsave(&cmd->t_state_lock, flags); task->task_flags &= ~TF_ACTIVE; @@ -971,9 +965,8 @@ void transport_dump_dev_state( break; } - *bl += sprintf(b + *bl, " Execute/Left/Max Queue Depth: %d/%d/%d", - atomic_read(&dev->execute_tasks), atomic_read(&dev->depth_left), - dev->queue_depth); + *bl += sprintf(b + *bl, " Execute/Max Queue Depth: %d/%d", + atomic_read(&dev->execute_tasks), dev->queue_depth); *bl += sprintf(b + *bl, " SectorSize: %u MaxSectors: %u\n", dev->se_sub_dev->se_dev_attrib.block_size, dev->se_sub_dev->se_dev_attrib.max_sectors); *bl += sprintf(b + *bl, " "); @@ -1328,9 +1321,6 @@ struct se_device *transport_add_device_to_core_hba( spin_lock_init(&dev->se_port_lock); spin_lock_init(&dev->se_tmr_lock); spin_lock_init(&dev->qf_cmd_lock); - - dev->queue_depth = dev_limits->queue_depth; - atomic_set(&dev->depth_left, dev->queue_depth); atomic_set(&dev->dev_ordered_id, 0); se_dev_set_default_attribs(dev, dev_limits); @@ -1982,18 +1972,6 @@ static void transport_set_supported_SAM_opcode(struct se_cmd *se_cmd) spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); } -static inline int transport_tcq_window_closed(struct se_device *dev) -{ - if (dev->dev_tcq_window_closed++ < - PYX_TRANSPORT_WINDOW_CLOSED_THRESHOLD) { - msleep(PYX_TRANSPORT_WINDOW_CLOSED_WAIT_SHORT); - } else - msleep(PYX_TRANSPORT_WINDOW_CLOSED_WAIT_LONG); - - wake_up_interruptible(&dev->dev_queue_obj.thread_wq); - return 0; -} - /* * Called from Fabric Module context from transport_execute_tasks() * @@ -2126,16 +2104,7 @@ static int __transport_execute_tasks(struct se_device *dev) struct se_task *task = NULL; unsigned long flags; - /* - * Check if there is enough room in the device and HBA queue to send - * struct se_tasks to the selected transport. - */ check_depth: - if (!atomic_read(&dev->depth_left)) - return transport_tcq_window_closed(dev); - - dev->dev_tcq_window_closed = 0; - spin_lock_irq(&dev->execute_task_lock); if (list_empty(&dev->execute_task_list)) { spin_unlock_irq(&dev->execute_task_lock); @@ -2146,10 +2115,7 @@ check_depth: __transport_remove_task_from_execute_queue(task, dev); spin_unlock_irq(&dev->execute_task_lock); - atomic_dec(&dev->depth_left); - cmd = task->task_se_cmd; - spin_lock_irqsave(&cmd->t_state_lock, flags); task->task_flags |= (TF_ACTIVE | TF_SENT); atomic_inc(&cmd->t_task_cdbs_sent); @@ -2170,7 +2136,6 @@ check_depth: spin_unlock_irqrestore(&cmd->t_state_lock, flags); atomic_set(&cmd->t_transport_sent, 0); transport_stop_tasks_for_cmd(cmd); - atomic_inc(&dev->depth_left); transport_generic_request_failure(cmd); } diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 28190dc..cd4caf3 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -798,7 +798,6 @@ struct se_device { spinlock_t stats_lock; /* Active commands on this virtual SE device */ atomic_t simple_cmds; - atomic_t depth_left; atomic_t dev_ordered_id; atomic_t execute_tasks; atomic_t dev_ordered_sync; -- cgit v0.10.2 From 4d2300ccffd22d1d0213b6a8e4d685eb6ca069c0 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 30 Nov 2011 18:18:33 -0800 Subject: target: Remove extra se_device->execute_task_lock access in fast path This patch makes __transport_execute_tasks() perform the addition of tasks to dev->execute_task_list via __transport_add_tasks_from_cmd() while holding dev->execute_task_lock during normal I/O fast path submission. It effectively removes the unnecessary re-acquire of dev->execute_task_lock during transport_execute_tasks() -> transport_add_tasks_from_cmd() ahead of calling __transport_execute_tasks() to queue tasks for the passed *se_cmd descriptor. (v2: Re-add goto check_depth usage for multi-task submission for now..) Cc: Christoph Hellwig Cc: Roland Dreier Cc: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 7c2def7..46e2511 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -68,7 +68,7 @@ struct kmem_cache *t10_alua_tg_pt_gp_mem_cache; static int transport_generic_write_pending(struct se_cmd *); static int transport_processing_thread(void *param); -static int __transport_execute_tasks(struct se_device *dev); +static int __transport_execute_tasks(struct se_device *dev, struct se_cmd *); static void transport_complete_task_attr(struct se_cmd *cmd); static void transport_handle_queue_full(struct se_cmd *cmd, struct se_device *dev); @@ -851,13 +851,11 @@ static void transport_add_tasks_to_state_queue(struct se_cmd *cmd) spin_unlock_irqrestore(&cmd->t_state_lock, flags); } -static void transport_add_tasks_from_cmd(struct se_cmd *cmd) +static void __transport_add_tasks_from_cmd(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; struct se_task *task, *task_prev = NULL; - unsigned long flags; - spin_lock_irqsave(&dev->execute_task_lock, flags); list_for_each_entry(task, &cmd->t_task_list, t_list) { if (!list_empty(&task->t_execute_list)) continue; @@ -868,6 +866,15 @@ static void transport_add_tasks_from_cmd(struct se_cmd *cmd) __transport_add_task_to_execute_queue(task, task_prev, dev); task_prev = task; } +} + +static void transport_add_tasks_from_cmd(struct se_cmd *cmd) +{ + unsigned long flags; + struct se_device *dev = cmd->se_dev; + + spin_lock_irqsave(&dev->execute_task_lock, flags); + __transport_add_tasks_from_cmd(cmd); spin_unlock_irqrestore(&dev->execute_task_lock, flags); } @@ -2075,19 +2082,16 @@ static int transport_execute_tasks(struct se_cmd *cmd) if (!add_tasks) goto execute_tasks; /* - * This calls transport_add_tasks_from_cmd() to handle - * HEAD_OF_QUEUE ordering for SAM Task Attribute emulation - * (if enabled) in __transport_add_task_to_execute_queue() and - * transport_add_task_check_sam_attr(). + * __transport_execute_tasks() -> __transport_add_tasks_from_cmd() + * adds associated se_tasks while holding dev->execute_task_lock + * before I/O dispath to avoid a double spinlock access. */ - transport_add_tasks_from_cmd(cmd); + __transport_execute_tasks(se_dev, cmd); + return 0; } - /* - * Kick the execution queue for the cmd associated struct se_device - * storage object. - */ + execute_tasks: - __transport_execute_tasks(se_dev); + __transport_execute_tasks(se_dev, NULL); return 0; } @@ -2097,7 +2101,7 @@ execute_tasks: * * Called from transport_processing_thread() */ -static int __transport_execute_tasks(struct se_device *dev) +static int __transport_execute_tasks(struct se_device *dev, struct se_cmd *new_cmd) { int error; struct se_cmd *cmd = NULL; @@ -2106,6 +2110,9 @@ static int __transport_execute_tasks(struct se_device *dev) check_depth: spin_lock_irq(&dev->execute_task_lock); + if (new_cmd != NULL) + __transport_add_tasks_from_cmd(new_cmd); + if (list_empty(&dev->execute_task_list)) { spin_unlock_irq(&dev->execute_task_lock); return 0; @@ -2139,6 +2146,7 @@ check_depth: transport_generic_request_failure(cmd); } + new_cmd = NULL; goto check_depth; return 0; @@ -4647,7 +4655,7 @@ static int transport_processing_thread(void *param) goto out; get_cmd: - __transport_execute_tasks(dev); + __transport_execute_tasks(dev, NULL); cmd = transport_get_cmd_from_queue(&dev->dev_queue_obj); if (!cmd) diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index cd4caf3..a74cb2b 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -781,7 +781,6 @@ struct se_device { u32 dev_port_count; /* See transport_device_status_table */ u32 dev_status; - u32 dev_tcq_window_closed; /* Physical device queue depth */ u32 queue_depth; /* Used for SPC-2 reservations enforce of ISIDs */ -- cgit v0.10.2 From beb55a0cc15a43be43a646b588fcf83822f0c44f Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 30 Nov 2011 18:21:24 -0800 Subject: target: Remove __transport_execute_tasks() for each processing context This patch removes the original usage of __transport_execute_tasks() ahead of every transport_get_cmd_from_queue() call in transport_processing_thread(). This helps reduce se_device->execute_task_lock contention between qla2xxx wq with target_submit_cmd() for READs and transport_processing_thread() context servicing WRITEs with full payloads for I/O submission. It also adds a __transport_execute_tasks() to kick the task queue again without a *se_cmd descriptor with existing queue full logic, but this may end up not being necessary. Cc: Christoph Hellwig Cc: Roland Dreier Cc: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 46e2511..373e01f 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4655,8 +4655,6 @@ static int transport_processing_thread(void *param) goto out; get_cmd: - __transport_execute_tasks(dev, NULL); - cmd = transport_get_cmd_from_queue(&dev->dev_queue_obj); if (!cmd) continue; -- cgit v0.10.2 From 6d5b59756033c2a029bde7262bb5f8d45f4ca952 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 30 Nov 2011 18:24:03 -0800 Subject: target: Remove legacy device status check from transport_execute_tasks This patch removes a legacy se_dev_check_online() check from within transport_execute_tasks() that should no longer be necessary as transport_lookup_cmd_lun() is already making this call. Using transport_cmd_check_stop() from transport_execute_tasks() should already be checking per se_cmd context for each descriptor upon active I/O shutdown, so no need to acquire dev->dev_status_lock again while executing se_task submission. Cc: Christoph Hellwig Cc: Roland Dreier Cc: Joern Engel Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 373e01f..b5fdacc 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2062,13 +2062,6 @@ static int transport_execute_tasks(struct se_cmd *cmd) { int add_tasks; struct se_device *se_dev = cmd->se_dev; - - if (se_dev_check_online(cmd->se_dev) != 0) { - cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - transport_generic_request_failure(cmd); - return 0; - } - /* * Call transport_cmd_check_stop() to see if a fabric exception * has occurred that prevents execution. -- cgit v0.10.2 From 895f3022523361e9b383cf48f51feb1f7d5e7e53 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 13 Dec 2011 14:55:33 -0800 Subject: target: Set additional sense length field in sense data The target code was not setting the additional sense length field in the sense data it returned, which meant that at least the Linux stack ignored the ASC/ASCQ fields. For example, without this patch, on a tcm_loop device: # sg_raw -v /dev/sda 2 0 0 0 0 0 gives cdb to send: 02 00 00 00 00 00 SCSI Status: Check Condition Sense Information: Fixed format, current; Sense key: Illegal Request Raw sense data (in hex): 70 00 05 00 00 00 00 00 while after the patch we correctly get the following (which matches what a regular disk returns): cdb to send: 02 00 00 00 00 00 SCSI Status: Check Condition Sense Information: Fixed format, current; Sense key: Illegal Request Additional sense: Invalid command operation code Raw sense data (in hex): 70 00 05 00 00 00 00 0a 00 00 00 00 20 00 00 00 00 00 Signed-off-by: Roland Dreier Cc: stable@kernel.org Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index b5fdacc..d3ddd13 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4398,6 +4398,7 @@ int transport_send_check_condition_and_sense( case TCM_NON_EXISTENT_LUN: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* LOGICAL UNIT NOT SUPPORTED */ @@ -4407,6 +4408,7 @@ int transport_send_check_condition_and_sense( case TCM_SECTOR_COUNT_TOO_MANY: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID COMMAND OPERATION CODE */ @@ -4415,6 +4417,7 @@ int transport_send_check_condition_and_sense( case TCM_UNKNOWN_MODE_PAGE: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID FIELD IN CDB */ @@ -4423,6 +4426,7 @@ int transport_send_check_condition_and_sense( case TCM_CHECK_CONDITION_ABORT_CMD: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* BUS DEVICE RESET FUNCTION OCCURRED */ @@ -4432,6 +4436,7 @@ int transport_send_check_condition_and_sense( case TCM_INCORRECT_AMOUNT_OF_DATA: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* WRITE ERROR */ @@ -4442,6 +4447,7 @@ int transport_send_check_condition_and_sense( case TCM_INVALID_CDB_FIELD: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* INVALID FIELD IN CDB */ @@ -4450,6 +4456,7 @@ int transport_send_check_condition_and_sense( case TCM_INVALID_PARAMETER_LIST: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* INVALID FIELD IN PARAMETER LIST */ @@ -4458,6 +4465,7 @@ int transport_send_check_condition_and_sense( case TCM_UNEXPECTED_UNSOLICITED_DATA: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* WRITE ERROR */ @@ -4468,6 +4476,7 @@ int transport_send_check_condition_and_sense( case TCM_SERVICE_CRC_ERROR: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* PROTOCOL SERVICE CRC ERROR */ @@ -4478,6 +4487,7 @@ int transport_send_check_condition_and_sense( case TCM_SNACK_REJECTED: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ABORTED COMMAND */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; /* READ ERROR */ @@ -4488,6 +4498,7 @@ int transport_send_check_condition_and_sense( case TCM_WRITE_PROTECTED: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* DATA PROTECT */ buffer[offset+SPC_SENSE_KEY_OFFSET] = DATA_PROTECT; /* WRITE PROTECTED */ @@ -4496,6 +4507,7 @@ int transport_send_check_condition_and_sense( case TCM_CHECK_CONDITION_UNIT_ATTENTION: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* UNIT ATTENTION */ buffer[offset+SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; core_scsi3_ua_for_check_condition(cmd, &asc, &ascq); @@ -4505,6 +4517,7 @@ int transport_send_check_condition_and_sense( case TCM_CHECK_CONDITION_NOT_READY: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* Not Ready */ buffer[offset+SPC_SENSE_KEY_OFFSET] = NOT_READY; transport_get_sense_codes(cmd, &asc, &ascq); @@ -4515,6 +4528,7 @@ int transport_send_check_condition_and_sense( default: /* CURRENT ERROR */ buffer[offset] = 0x70; + buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; /* ILLEGAL REQUEST */ buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* LOGICAL UNIT COMMUNICATION FAILURE */ diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index a74cb2b..daf532b 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -35,6 +35,7 @@ #define TRANSPORT_SENSE_BUFFER SCSI_SENSE_BUFFERSIZE /* Used by transport_send_check_condition_and_sense() */ #define SPC_SENSE_KEY_OFFSET 2 +#define SPC_ADD_SENSE_LEN_OFFSET 7 #define SPC_ASC_KEY_OFFSET 12 #define SPC_ASCQ_KEY_OFFSET 13 #define TRANSPORT_IQN_LEN 224 -- cgit v0.10.2 From a42d985bd5b234da8b61347a78dc3057bf7bb94d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 14 Oct 2011 01:30:46 +0000 Subject: ib_srpt: Initial SRP Target merge for v3.3-rc1 This patch adds the kernel module ib_srpt SCSI RDMA Protocol (SRP) target implementation conforming to the SRP r16a specification for the mainline drivers/target infrastructure. This driver was originally developed by Vu Pham and has been optimized by Bart Van Assche and merged into upstream LIO based on his srpt-lio-4.1 branch here: https://github.com/bvanassche/srpt-lio/commits/srpt-lio-4.1/ This updated patch also contains the following two changes from lio-core-2.6.git/master. One is to fix a bug with 1 >= task->task_sg[] chained mappings in ib_srpt, and the other to convert the configfs control plane to reference IB Port GUID and struct srpt_port directly following mainline v4.x target_core_fabric_configfs.c convertion for ib_srpt to work with rtslib/rtsadmin v2 code. These seperate patches can be found here: ib_srpt: Fix bug with chainged SGLs in srpt_map_sg_to_ib_sge http://www.risingtidesystems.com/git/?p=lio-core-2.6.git;a=commitdiff;h=ea485147563b6555a97dbf811825fbb586519252 ib_srpt: Convert se_wwn endpoint reference to struct srpt_port->port_wwn http://www.risingtidesystems.com/git/?p=lio-core-2.6.git;a=commitdiff;h=4e544a210acb227df1bb4ca5086e65bdf4e648ea This also includes the following recent v1 -> v2 review changes: ib_srpt: Fix potential out-of-bounds array access ib_srpt: Avoid failed multipart RDMA transfers ib_srpt: Fix srpt_alloc_fabric_acl failure case return value ib_srpt: Update comments to reference $driver/$port layout ib_srpt: Fix sport->port_guid formatting code ib_srpt: Remove legacy use_port_guid_in_session_name module parameter ib_srpt: Convert srp_max_rdma_size into per port configfs attribute ib_srpt: Convert srp_max_rsp_size into per port configfs attribute ib_srpt: Convert srpt_sq_size into per port configfs attribute and v2 -> v3 review changes: ib_srpt: Fix possible race with srp_sq_size in srpt_create_ch_ib ib_srpt: Fix possible race with srp_max_rsp_size in srpt_release_channel_work ib_srpt: Fix up MAX_SRPT_RDMA_SIZE define ib_srpt: Make srpt_map_sg_to_ib_sge() failure case return -EAGAIN ib_srpt: Convert port_guid to use subnet_prefix + interface_id formatting ib_srpt: Make srpt_check_stop_free return kref_put status ib_srpt: Make compilation with BUG=n proceed` ib_srpt: Use new target_core_fabric.h include ib_srpt: Check hex2bin() return code to silence build warning Cc: Bart Van Assche Cc: Roland Dreier Cc: Christoph Hellwig Cc: Vu Pham Cc: David Dillow Signed-off-by: Nicholas A. Bellinger diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 0f9a84c..eb0add3 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -55,6 +55,7 @@ source "drivers/infiniband/hw/nes/Kconfig" source "drivers/infiniband/ulp/ipoib/Kconfig" source "drivers/infiniband/ulp/srp/Kconfig" +source "drivers/infiniband/ulp/srpt/Kconfig" source "drivers/infiniband/ulp/iser/Kconfig" diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile index 9cc7a47..a3b2d8e 100644 --- a/drivers/infiniband/Makefile +++ b/drivers/infiniband/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_MLX4_INFINIBAND) += hw/mlx4/ obj-$(CONFIG_INFINIBAND_NES) += hw/nes/ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/ obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/ +obj-$(CONFIG_INFINIBAND_SRPT) += ulp/srpt/ obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/ diff --git a/drivers/infiniband/ulp/srpt/Kconfig b/drivers/infiniband/ulp/srpt/Kconfig new file mode 100644 index 0000000..31ee83d --- /dev/null +++ b/drivers/infiniband/ulp/srpt/Kconfig @@ -0,0 +1,12 @@ +config INFINIBAND_SRPT + tristate "InfiniBand SCSI RDMA Protocol target support" + depends on INFINIBAND && TARGET_CORE + ---help--- + + Support for the SCSI RDMA Protocol (SRP) Target driver. The + SRP protocol is a protocol that allows an initiator to access + a block storage device on another host (target) over a network + that supports the RDMA protocol. Currently the RDMA protocol is + supported by InfiniBand and by iWarp network hardware. More + information about the SRP protocol can be found on the website + of the INCITS T10 technical committee (http://www.t10.org/). diff --git a/drivers/infiniband/ulp/srpt/Makefile b/drivers/infiniband/ulp/srpt/Makefile new file mode 100644 index 0000000..e3ee4bd --- /dev/null +++ b/drivers/infiniband/ulp/srpt/Makefile @@ -0,0 +1,2 @@ +ccflags-y := -Idrivers/target +obj-$(CONFIG_INFINIBAND_SRPT) += ib_srpt.o diff --git a/drivers/infiniband/ulp/srpt/ib_dm_mad.h b/drivers/infiniband/ulp/srpt/ib_dm_mad.h new file mode 100644 index 0000000..fb1de1f --- /dev/null +++ b/drivers/infiniband/ulp/srpt/ib_dm_mad.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef IB_DM_MAD_H +#define IB_DM_MAD_H + +#include + +#include + +enum { + /* + * See also section 13.4.7 Status Field, table 115 MAD Common Status + * Field Bit Values and also section 16.3.1.1 Status Field in the + * InfiniBand Architecture Specification. + */ + DM_MAD_STATUS_UNSUP_METHOD = 0x0008, + DM_MAD_STATUS_UNSUP_METHOD_ATTR = 0x000c, + DM_MAD_STATUS_INVALID_FIELD = 0x001c, + DM_MAD_STATUS_NO_IOC = 0x0100, + + /* + * See also the Device Management chapter, section 16.3.3 Attributes, + * table 279 Device Management Attributes in the InfiniBand + * Architecture Specification. + */ + DM_ATTR_CLASS_PORT_INFO = 0x01, + DM_ATTR_IOU_INFO = 0x10, + DM_ATTR_IOC_PROFILE = 0x11, + DM_ATTR_SVC_ENTRIES = 0x12 +}; + +struct ib_dm_hdr { + u8 reserved[28]; +}; + +/* + * Structure of management datagram sent by the SRP target implementation. + * Contains a management datagram header, reliable multi-packet transaction + * protocol (RMPP) header and ib_dm_hdr. Notes: + * - The SRP target implementation does not use RMPP or ib_dm_hdr when sending + * management datagrams. + * - The header size must be exactly 64 bytes (IB_MGMT_DEVICE_HDR), since this + * is the header size that is passed to ib_create_send_mad() in ib_srpt.c. + * - The maximum supported size for a management datagram when not using RMPP + * is 256 bytes -- 64 bytes header and 192 (IB_MGMT_DEVICE_DATA) bytes data. + */ +struct ib_dm_mad { + struct ib_mad_hdr mad_hdr; + struct ib_rmpp_hdr rmpp_hdr; + struct ib_dm_hdr dm_hdr; + u8 data[IB_MGMT_DEVICE_DATA]; +}; + +/* + * IOUnitInfo as defined in section 16.3.3.3 IOUnitInfo of the InfiniBand + * Architecture Specification. + */ +struct ib_dm_iou_info { + __be16 change_id; + u8 max_controllers; + u8 op_rom; + u8 controller_list[128]; +}; + +/* + * IOControllerprofile as defined in section 16.3.3.4 IOControllerProfile of + * the InfiniBand Architecture Specification. + */ +struct ib_dm_ioc_profile { + __be64 guid; + __be32 vendor_id; + __be32 device_id; + __be16 device_version; + __be16 reserved1; + __be32 subsys_vendor_id; + __be32 subsys_device_id; + __be16 io_class; + __be16 io_subclass; + __be16 protocol; + __be16 protocol_version; + __be16 service_conn; + __be16 initiators_supported; + __be16 send_queue_depth; + u8 reserved2; + u8 rdma_read_depth; + __be32 send_size; + __be32 rdma_size; + u8 op_cap_mask; + u8 svc_cap_mask; + u8 num_svc_entries; + u8 reserved3[9]; + u8 id_string[64]; +}; + +struct ib_dm_svc_entry { + u8 name[40]; + __be64 id; +}; + +/* + * See also section 16.3.3.5 ServiceEntries in the InfiniBand Architecture + * Specification. See also section B.7, table B.8 in the T10 SRP r16a document. + */ +struct ib_dm_svc_entries { + struct ib_dm_svc_entry service_entries[4]; +}; + +#endif diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c new file mode 100644 index 0000000..cd5d05e --- /dev/null +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -0,0 +1,4073 @@ +/* + * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. + * Copyright (C) 2008 - 2011 Bart Van Assche . + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ib_srpt.h" + +/* Name of this kernel module. */ +#define DRV_NAME "ib_srpt" +#define DRV_VERSION "2.0.0" +#define DRV_RELDATE "2011-02-14" + +#define SRPT_ID_STRING "Linux SRP target" + +#undef pr_fmt +#define pr_fmt(fmt) DRV_NAME " " fmt + +MODULE_AUTHOR("Vu Pham and Bart Van Assche"); +MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol target " + "v" DRV_VERSION " (" DRV_RELDATE ")"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * Global Variables + */ + +static u64 srpt_service_guid; +static spinlock_t srpt_dev_lock; /* Protects srpt_dev_list. */ +static struct list_head srpt_dev_list; /* List of srpt_device structures. */ + +static unsigned srp_max_req_size = DEFAULT_MAX_REQ_SIZE; +module_param(srp_max_req_size, int, 0444); +MODULE_PARM_DESC(srp_max_req_size, + "Maximum size of SRP request messages in bytes."); + +static int srpt_srq_size = DEFAULT_SRPT_SRQ_SIZE; +module_param(srpt_srq_size, int, 0444); +MODULE_PARM_DESC(srpt_srq_size, + "Shared receive queue (SRQ) size."); + +static int srpt_get_u64_x(char *buffer, struct kernel_param *kp) +{ + return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg); +} +module_param_call(srpt_service_guid, NULL, srpt_get_u64_x, &srpt_service_guid, + 0444); +MODULE_PARM_DESC(srpt_service_guid, + "Using this value for ioc_guid, id_ext, and cm_listen_id" + " instead of using the node_guid of the first HCA."); + +static struct ib_client srpt_client; +static struct target_fabric_configfs *srpt_target; +static void srpt_release_channel(struct srpt_rdma_ch *ch); +static int srpt_queue_status(struct se_cmd *cmd); + +/** + * opposite_dma_dir() - Swap DMA_TO_DEVICE and DMA_FROM_DEVICE. + */ +static inline +enum dma_data_direction opposite_dma_dir(enum dma_data_direction dir) +{ + switch (dir) { + case DMA_TO_DEVICE: return DMA_FROM_DEVICE; + case DMA_FROM_DEVICE: return DMA_TO_DEVICE; + default: return dir; + } +} + +/** + * srpt_sdev_name() - Return the name associated with the HCA. + * + * Examples are ib0, ib1, ... + */ +static inline const char *srpt_sdev_name(struct srpt_device *sdev) +{ + return sdev->device->name; +} + +static enum rdma_ch_state srpt_get_ch_state(struct srpt_rdma_ch *ch) +{ + unsigned long flags; + enum rdma_ch_state state; + + spin_lock_irqsave(&ch->spinlock, flags); + state = ch->state; + spin_unlock_irqrestore(&ch->spinlock, flags); + return state; +} + +static enum rdma_ch_state +srpt_set_ch_state(struct srpt_rdma_ch *ch, enum rdma_ch_state new_state) +{ + unsigned long flags; + enum rdma_ch_state prev; + + spin_lock_irqsave(&ch->spinlock, flags); + prev = ch->state; + ch->state = new_state; + spin_unlock_irqrestore(&ch->spinlock, flags); + return prev; +} + +/** + * srpt_test_and_set_ch_state() - Test and set the channel state. + * + * Returns true if and only if the channel state has been set to the new state. + */ +static bool +srpt_test_and_set_ch_state(struct srpt_rdma_ch *ch, enum rdma_ch_state old, + enum rdma_ch_state new) +{ + unsigned long flags; + enum rdma_ch_state prev; + + spin_lock_irqsave(&ch->spinlock, flags); + prev = ch->state; + if (prev == old) + ch->state = new; + spin_unlock_irqrestore(&ch->spinlock, flags); + return prev == old; +} + +/** + * srpt_event_handler() - Asynchronous IB event callback function. + * + * Callback function called by the InfiniBand core when an asynchronous IB + * event occurs. This callback may occur in interrupt context. See also + * section 11.5.2, Set Asynchronous Event Handler in the InfiniBand + * Architecture Specification. + */ +static void srpt_event_handler(struct ib_event_handler *handler, + struct ib_event *event) +{ + struct srpt_device *sdev; + struct srpt_port *sport; + + sdev = ib_get_client_data(event->device, &srpt_client); + if (!sdev || sdev->device != event->device) + return; + + pr_debug("ASYNC event= %d on device= %s\n", event->event, + srpt_sdev_name(sdev)); + + switch (event->event) { + case IB_EVENT_PORT_ERR: + if (event->element.port_num <= sdev->device->phys_port_cnt) { + sport = &sdev->port[event->element.port_num - 1]; + sport->lid = 0; + sport->sm_lid = 0; + } + break; + case IB_EVENT_PORT_ACTIVE: + case IB_EVENT_LID_CHANGE: + case IB_EVENT_PKEY_CHANGE: + case IB_EVENT_SM_CHANGE: + case IB_EVENT_CLIENT_REREGISTER: + /* Refresh port data asynchronously. */ + if (event->element.port_num <= sdev->device->phys_port_cnt) { + sport = &sdev->port[event->element.port_num - 1]; + if (!sport->lid && !sport->sm_lid) + schedule_work(&sport->work); + } + break; + default: + printk(KERN_ERR "received unrecognized IB event %d\n", + event->event); + break; + } +} + +/** + * srpt_srq_event() - SRQ event callback function. + */ +static void srpt_srq_event(struct ib_event *event, void *ctx) +{ + printk(KERN_INFO "SRQ event %d\n", event->event); +} + +/** + * srpt_qp_event() - QP event callback function. + */ +static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) +{ + pr_debug("QP event %d on cm_id=%p sess_name=%s state=%d\n", + event->event, ch->cm_id, ch->sess_name, srpt_get_ch_state(ch)); + + switch (event->event) { + case IB_EVENT_COMM_EST: + ib_cm_notify(ch->cm_id, event->event); + break; + case IB_EVENT_QP_LAST_WQE_REACHED: + if (srpt_test_and_set_ch_state(ch, CH_DRAINING, + CH_RELEASING)) + srpt_release_channel(ch); + else + pr_debug("%s: state %d - ignored LAST_WQE.\n", + ch->sess_name, srpt_get_ch_state(ch)); + break; + default: + printk(KERN_ERR "received unrecognized IB QP event %d\n", + event->event); + break; + } +} + +/** + * srpt_set_ioc() - Helper function for initializing an IOUnitInfo structure. + * + * @slot: one-based slot number. + * @value: four-bit value. + * + * Copies the lowest four bits of value in element slot of the array of four + * bit elements called c_list (controller list). The index slot is one-based. + */ +static void srpt_set_ioc(u8 *c_list, u32 slot, u8 value) +{ + u16 id; + u8 tmp; + + id = (slot - 1) / 2; + if (slot & 0x1) { + tmp = c_list[id] & 0xf; + c_list[id] = (value << 4) | tmp; + } else { + tmp = c_list[id] & 0xf0; + c_list[id] = (value & 0xf) | tmp; + } +} + +/** + * srpt_get_class_port_info() - Copy ClassPortInfo to a management datagram. + * + * See also section 16.3.3.1 ClassPortInfo in the InfiniBand Architecture + * Specification. + */ +static void srpt_get_class_port_info(struct ib_dm_mad *mad) +{ + struct ib_class_port_info *cif; + + cif = (struct ib_class_port_info *)mad->data; + memset(cif, 0, sizeof *cif); + cif->base_version = 1; + cif->class_version = 1; + cif->resp_time_value = 20; + + mad->mad_hdr.status = 0; +} + +/** + * srpt_get_iou() - Write IOUnitInfo to a management datagram. + * + * See also section 16.3.3.3 IOUnitInfo in the InfiniBand Architecture + * Specification. See also section B.7, table B.6 in the SRP r16a document. + */ +static void srpt_get_iou(struct ib_dm_mad *mad) +{ + struct ib_dm_iou_info *ioui; + u8 slot; + int i; + + ioui = (struct ib_dm_iou_info *)mad->data; + ioui->change_id = __constant_cpu_to_be16(1); + ioui->max_controllers = 16; + + /* set present for slot 1 and empty for the rest */ + srpt_set_ioc(ioui->controller_list, 1, 1); + for (i = 1, slot = 2; i < 16; i++, slot++) + srpt_set_ioc(ioui->controller_list, slot, 0); + + mad->mad_hdr.status = 0; +} + +/** + * srpt_get_ioc() - Write IOControllerprofile to a management datagram. + * + * See also section 16.3.3.4 IOControllerProfile in the InfiniBand + * Architecture Specification. See also section B.7, table B.7 in the SRP + * r16a document. + */ +static void srpt_get_ioc(struct srpt_port *sport, u32 slot, + struct ib_dm_mad *mad) +{ + struct srpt_device *sdev = sport->sdev; + struct ib_dm_ioc_profile *iocp; + + iocp = (struct ib_dm_ioc_profile *)mad->data; + + if (!slot || slot > 16) { + mad->mad_hdr.status + = __constant_cpu_to_be16(DM_MAD_STATUS_INVALID_FIELD); + return; + } + + if (slot > 2) { + mad->mad_hdr.status + = __constant_cpu_to_be16(DM_MAD_STATUS_NO_IOC); + return; + } + + memset(iocp, 0, sizeof *iocp); + strcpy(iocp->id_string, SRPT_ID_STRING); + iocp->guid = cpu_to_be64(srpt_service_guid); + iocp->vendor_id = cpu_to_be32(sdev->dev_attr.vendor_id); + iocp->device_id = cpu_to_be32(sdev->dev_attr.vendor_part_id); + iocp->device_version = cpu_to_be16(sdev->dev_attr.hw_ver); + iocp->subsys_vendor_id = cpu_to_be32(sdev->dev_attr.vendor_id); + iocp->subsys_device_id = 0x0; + iocp->io_class = __constant_cpu_to_be16(SRP_REV16A_IB_IO_CLASS); + iocp->io_subclass = __constant_cpu_to_be16(SRP_IO_SUBCLASS); + iocp->protocol = __constant_cpu_to_be16(SRP_PROTOCOL); + iocp->protocol_version = __constant_cpu_to_be16(SRP_PROTOCOL_VERSION); + iocp->send_queue_depth = cpu_to_be16(sdev->srq_size); + iocp->rdma_read_depth = 4; + iocp->send_size = cpu_to_be32(srp_max_req_size); + iocp->rdma_size = cpu_to_be32(min(sport->port_attrib.srp_max_rdma_size, + 1U << 24)); + iocp->num_svc_entries = 1; + iocp->op_cap_mask = SRP_SEND_TO_IOC | SRP_SEND_FROM_IOC | + SRP_RDMA_READ_FROM_IOC | SRP_RDMA_WRITE_FROM_IOC; + + mad->mad_hdr.status = 0; +} + +/** + * srpt_get_svc_entries() - Write ServiceEntries to a management datagram. + * + * See also section 16.3.3.5 ServiceEntries in the InfiniBand Architecture + * Specification. See also section B.7, table B.8 in the SRP r16a document. + */ +static void srpt_get_svc_entries(u64 ioc_guid, + u16 slot, u8 hi, u8 lo, struct ib_dm_mad *mad) +{ + struct ib_dm_svc_entries *svc_entries; + + WARN_ON(!ioc_guid); + + if (!slot || slot > 16) { + mad->mad_hdr.status + = __constant_cpu_to_be16(DM_MAD_STATUS_INVALID_FIELD); + return; + } + + if (slot > 2 || lo > hi || hi > 1) { + mad->mad_hdr.status + = __constant_cpu_to_be16(DM_MAD_STATUS_NO_IOC); + return; + } + + svc_entries = (struct ib_dm_svc_entries *)mad->data; + memset(svc_entries, 0, sizeof *svc_entries); + svc_entries->service_entries[0].id = cpu_to_be64(ioc_guid); + snprintf(svc_entries->service_entries[0].name, + sizeof(svc_entries->service_entries[0].name), + "%s%016llx", + SRP_SERVICE_NAME_PREFIX, + ioc_guid); + + mad->mad_hdr.status = 0; +} + +/** + * srpt_mgmt_method_get() - Process a received management datagram. + * @sp: source port through which the MAD has been received. + * @rq_mad: received MAD. + * @rsp_mad: response MAD. + */ +static void srpt_mgmt_method_get(struct srpt_port *sp, struct ib_mad *rq_mad, + struct ib_dm_mad *rsp_mad) +{ + u16 attr_id; + u32 slot; + u8 hi, lo; + + attr_id = be16_to_cpu(rq_mad->mad_hdr.attr_id); + switch (attr_id) { + case DM_ATTR_CLASS_PORT_INFO: + srpt_get_class_port_info(rsp_mad); + break; + case DM_ATTR_IOU_INFO: + srpt_get_iou(rsp_mad); + break; + case DM_ATTR_IOC_PROFILE: + slot = be32_to_cpu(rq_mad->mad_hdr.attr_mod); + srpt_get_ioc(sp, slot, rsp_mad); + break; + case DM_ATTR_SVC_ENTRIES: + slot = be32_to_cpu(rq_mad->mad_hdr.attr_mod); + hi = (u8) ((slot >> 8) & 0xff); + lo = (u8) (slot & 0xff); + slot = (u16) ((slot >> 16) & 0xffff); + srpt_get_svc_entries(srpt_service_guid, + slot, hi, lo, rsp_mad); + break; + default: + rsp_mad->mad_hdr.status = + __constant_cpu_to_be16(DM_MAD_STATUS_UNSUP_METHOD_ATTR); + break; + } +} + +/** + * srpt_mad_send_handler() - Post MAD-send callback function. + */ +static void srpt_mad_send_handler(struct ib_mad_agent *mad_agent, + struct ib_mad_send_wc *mad_wc) +{ + ib_destroy_ah(mad_wc->send_buf->ah); + ib_free_send_mad(mad_wc->send_buf); +} + +/** + * srpt_mad_recv_handler() - MAD reception callback function. + */ +static void srpt_mad_recv_handler(struct ib_mad_agent *mad_agent, + struct ib_mad_recv_wc *mad_wc) +{ + struct srpt_port *sport = (struct srpt_port *)mad_agent->context; + struct ib_ah *ah; + struct ib_mad_send_buf *rsp; + struct ib_dm_mad *dm_mad; + + if (!mad_wc || !mad_wc->recv_buf.mad) + return; + + ah = ib_create_ah_from_wc(mad_agent->qp->pd, mad_wc->wc, + mad_wc->recv_buf.grh, mad_agent->port_num); + if (IS_ERR(ah)) + goto err; + + BUILD_BUG_ON(offsetof(struct ib_dm_mad, data) != IB_MGMT_DEVICE_HDR); + + rsp = ib_create_send_mad(mad_agent, mad_wc->wc->src_qp, + mad_wc->wc->pkey_index, 0, + IB_MGMT_DEVICE_HDR, IB_MGMT_DEVICE_DATA, + GFP_KERNEL); + if (IS_ERR(rsp)) + goto err_rsp; + + rsp->ah = ah; + + dm_mad = rsp->mad; + memcpy(dm_mad, mad_wc->recv_buf.mad, sizeof *dm_mad); + dm_mad->mad_hdr.method = IB_MGMT_METHOD_GET_RESP; + dm_mad->mad_hdr.status = 0; + + switch (mad_wc->recv_buf.mad->mad_hdr.method) { + case IB_MGMT_METHOD_GET: + srpt_mgmt_method_get(sport, mad_wc->recv_buf.mad, dm_mad); + break; + case IB_MGMT_METHOD_SET: + dm_mad->mad_hdr.status = + __constant_cpu_to_be16(DM_MAD_STATUS_UNSUP_METHOD_ATTR); + break; + default: + dm_mad->mad_hdr.status = + __constant_cpu_to_be16(DM_MAD_STATUS_UNSUP_METHOD); + break; + } + + if (!ib_post_send_mad(rsp, NULL)) { + ib_free_recv_mad(mad_wc); + /* will destroy_ah & free_send_mad in send completion */ + return; + } + + ib_free_send_mad(rsp); + +err_rsp: + ib_destroy_ah(ah); +err: + ib_free_recv_mad(mad_wc); +} + +/** + * srpt_refresh_port() - Configure a HCA port. + * + * Enable InfiniBand management datagram processing, update the cached sm_lid, + * lid and gid values, and register a callback function for processing MADs + * on the specified port. + * + * Note: It is safe to call this function more than once for the same port. + */ +static int srpt_refresh_port(struct srpt_port *sport) +{ + struct ib_mad_reg_req reg_req; + struct ib_port_modify port_modify; + struct ib_port_attr port_attr; + int ret; + + memset(&port_modify, 0, sizeof port_modify); + port_modify.set_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP; + port_modify.clr_port_cap_mask = 0; + + ret = ib_modify_port(sport->sdev->device, sport->port, 0, &port_modify); + if (ret) + goto err_mod_port; + + ret = ib_query_port(sport->sdev->device, sport->port, &port_attr); + if (ret) + goto err_query_port; + + sport->sm_lid = port_attr.sm_lid; + sport->lid = port_attr.lid; + + ret = ib_query_gid(sport->sdev->device, sport->port, 0, &sport->gid); + if (ret) + goto err_query_port; + + if (!sport->mad_agent) { + memset(®_req, 0, sizeof reg_req); + reg_req.mgmt_class = IB_MGMT_CLASS_DEVICE_MGMT; + reg_req.mgmt_class_version = IB_MGMT_BASE_VERSION; + set_bit(IB_MGMT_METHOD_GET, reg_req.method_mask); + set_bit(IB_MGMT_METHOD_SET, reg_req.method_mask); + + sport->mad_agent = ib_register_mad_agent(sport->sdev->device, + sport->port, + IB_QPT_GSI, + ®_req, 0, + srpt_mad_send_handler, + srpt_mad_recv_handler, + sport); + if (IS_ERR(sport->mad_agent)) { + ret = PTR_ERR(sport->mad_agent); + sport->mad_agent = NULL; + goto err_query_port; + } + } + + return 0; + +err_query_port: + + port_modify.set_port_cap_mask = 0; + port_modify.clr_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP; + ib_modify_port(sport->sdev->device, sport->port, 0, &port_modify); + +err_mod_port: + + return ret; +} + +/** + * srpt_unregister_mad_agent() - Unregister MAD callback functions. + * + * Note: It is safe to call this function more than once for the same device. + */ +static void srpt_unregister_mad_agent(struct srpt_device *sdev) +{ + struct ib_port_modify port_modify = { + .clr_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP, + }; + struct srpt_port *sport; + int i; + + for (i = 1; i <= sdev->device->phys_port_cnt; i++) { + sport = &sdev->port[i - 1]; + WARN_ON(sport->port != i); + if (ib_modify_port(sdev->device, i, 0, &port_modify) < 0) + printk(KERN_ERR "disabling MAD processing failed.\n"); + if (sport->mad_agent) { + ib_unregister_mad_agent(sport->mad_agent); + sport->mad_agent = NULL; + } + } +} + +/** + * srpt_alloc_ioctx() - Allocate an SRPT I/O context structure. + */ +static struct srpt_ioctx *srpt_alloc_ioctx(struct srpt_device *sdev, + int ioctx_size, int dma_size, + enum dma_data_direction dir) +{ + struct srpt_ioctx *ioctx; + + ioctx = kmalloc(ioctx_size, GFP_KERNEL); + if (!ioctx) + goto err; + + ioctx->buf = kmalloc(dma_size, GFP_KERNEL); + if (!ioctx->buf) + goto err_free_ioctx; + + ioctx->dma = ib_dma_map_single(sdev->device, ioctx->buf, dma_size, dir); + if (ib_dma_mapping_error(sdev->device, ioctx->dma)) + goto err_free_buf; + + return ioctx; + +err_free_buf: + kfree(ioctx->buf); +err_free_ioctx: + kfree(ioctx); +err: + return NULL; +} + +/** + * srpt_free_ioctx() - Free an SRPT I/O context structure. + */ +static void srpt_free_ioctx(struct srpt_device *sdev, struct srpt_ioctx *ioctx, + int dma_size, enum dma_data_direction dir) +{ + if (!ioctx) + return; + + ib_dma_unmap_single(sdev->device, ioctx->dma, dma_size, dir); + kfree(ioctx->buf); + kfree(ioctx); +} + +/** + * srpt_alloc_ioctx_ring() - Allocate a ring of SRPT I/O context structures. + * @sdev: Device to allocate the I/O context ring for. + * @ring_size: Number of elements in the I/O context ring. + * @ioctx_size: I/O context size. + * @dma_size: DMA buffer size. + * @dir: DMA data direction. + */ +static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev, + int ring_size, int ioctx_size, + int dma_size, enum dma_data_direction dir) +{ + struct srpt_ioctx **ring; + int i; + + WARN_ON(ioctx_size != sizeof(struct srpt_recv_ioctx) + && ioctx_size != sizeof(struct srpt_send_ioctx)); + + ring = kmalloc(ring_size * sizeof(ring[0]), GFP_KERNEL); + if (!ring) + goto out; + for (i = 0; i < ring_size; ++i) { + ring[i] = srpt_alloc_ioctx(sdev, ioctx_size, dma_size, dir); + if (!ring[i]) + goto err; + ring[i]->index = i; + } + goto out; + +err: + while (--i >= 0) + srpt_free_ioctx(sdev, ring[i], dma_size, dir); + kfree(ring); +out: + return ring; +} + +/** + * srpt_free_ioctx_ring() - Free the ring of SRPT I/O context structures. + */ +static void srpt_free_ioctx_ring(struct srpt_ioctx **ioctx_ring, + struct srpt_device *sdev, int ring_size, + int dma_size, enum dma_data_direction dir) +{ + int i; + + for (i = 0; i < ring_size; ++i) + srpt_free_ioctx(sdev, ioctx_ring[i], dma_size, dir); + kfree(ioctx_ring); +} + +/** + * srpt_get_cmd_state() - Get the state of a SCSI command. + */ +static enum srpt_command_state srpt_get_cmd_state(struct srpt_send_ioctx *ioctx) +{ + enum srpt_command_state state; + unsigned long flags; + + BUG_ON(!ioctx); + + spin_lock_irqsave(&ioctx->spinlock, flags); + state = ioctx->state; + spin_unlock_irqrestore(&ioctx->spinlock, flags); + return state; +} + +/** + * srpt_set_cmd_state() - Set the state of a SCSI command. + * + * Does not modify the state of aborted commands. Returns the previous command + * state. + */ +static enum srpt_command_state srpt_set_cmd_state(struct srpt_send_ioctx *ioctx, + enum srpt_command_state new) +{ + enum srpt_command_state previous; + unsigned long flags; + + BUG_ON(!ioctx); + + spin_lock_irqsave(&ioctx->spinlock, flags); + previous = ioctx->state; + if (previous != SRPT_STATE_DONE) + ioctx->state = new; + spin_unlock_irqrestore(&ioctx->spinlock, flags); + + return previous; +} + +/** + * srpt_test_and_set_cmd_state() - Test and set the state of a command. + * + * Returns true if and only if the previous command state was equal to 'old'. + */ +static bool srpt_test_and_set_cmd_state(struct srpt_send_ioctx *ioctx, + enum srpt_command_state old, + enum srpt_command_state new) +{ + enum srpt_command_state previous; + unsigned long flags; + + WARN_ON(!ioctx); + WARN_ON(old == SRPT_STATE_DONE); + WARN_ON(new == SRPT_STATE_NEW); + + spin_lock_irqsave(&ioctx->spinlock, flags); + previous = ioctx->state; + if (previous == old) + ioctx->state = new; + spin_unlock_irqrestore(&ioctx->spinlock, flags); + return previous == old; +} + +/** + * srpt_post_recv() - Post an IB receive request. + */ +static int srpt_post_recv(struct srpt_device *sdev, + struct srpt_recv_ioctx *ioctx) +{ + struct ib_sge list; + struct ib_recv_wr wr, *bad_wr; + + BUG_ON(!sdev); + wr.wr_id = encode_wr_id(SRPT_RECV, ioctx->ioctx.index); + + list.addr = ioctx->ioctx.dma; + list.length = srp_max_req_size; + list.lkey = sdev->mr->lkey; + + wr.next = NULL; + wr.sg_list = &list; + wr.num_sge = 1; + + return ib_post_srq_recv(sdev->srq, &wr, &bad_wr); +} + +/** + * srpt_post_send() - Post an IB send request. + * + * Returns zero upon success and a non-zero value upon failure. + */ +static int srpt_post_send(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx, int len) +{ + struct ib_sge list; + struct ib_send_wr wr, *bad_wr; + struct srpt_device *sdev = ch->sport->sdev; + int ret; + + atomic_inc(&ch->req_lim); + + ret = -ENOMEM; + if (unlikely(atomic_dec_return(&ch->sq_wr_avail) < 0)) { + printk(KERN_WARNING "IB send queue full (needed 1)\n"); + goto out; + } + + ib_dma_sync_single_for_device(sdev->device, ioctx->ioctx.dma, len, + DMA_TO_DEVICE); + + list.addr = ioctx->ioctx.dma; + list.length = len; + list.lkey = sdev->mr->lkey; + + wr.next = NULL; + wr.wr_id = encode_wr_id(SRPT_SEND, ioctx->ioctx.index); + wr.sg_list = &list; + wr.num_sge = 1; + wr.opcode = IB_WR_SEND; + wr.send_flags = IB_SEND_SIGNALED; + + ret = ib_post_send(ch->qp, &wr, &bad_wr); + +out: + if (ret < 0) { + atomic_inc(&ch->sq_wr_avail); + atomic_dec(&ch->req_lim); + } + return ret; +} + +/** + * srpt_get_desc_tbl() - Parse the data descriptors of an SRP_CMD request. + * @ioctx: Pointer to the I/O context associated with the request. + * @srp_cmd: Pointer to the SRP_CMD request data. + * @dir: Pointer to the variable to which the transfer direction will be + * written. + * @data_len: Pointer to the variable to which the total data length of all + * descriptors in the SRP_CMD request will be written. + * + * This function initializes ioctx->nrbuf and ioctx->r_bufs. + * + * Returns -EINVAL when the SRP_CMD request contains inconsistent descriptors; + * -ENOMEM when memory allocation fails and zero upon success. + */ +static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx, + struct srp_cmd *srp_cmd, + enum dma_data_direction *dir, u64 *data_len) +{ + struct srp_indirect_buf *idb; + struct srp_direct_buf *db; + unsigned add_cdb_offset; + int ret; + + /* + * The pointer computations below will only be compiled correctly + * if srp_cmd::add_data is declared as s8*, u8*, s8[] or u8[], so check + * whether srp_cmd::add_data has been declared as a byte pointer. + */ + BUILD_BUG_ON(!__same_type(srp_cmd->add_data[0], (s8)0) + && !__same_type(srp_cmd->add_data[0], (u8)0)); + + BUG_ON(!dir); + BUG_ON(!data_len); + + ret = 0; + *data_len = 0; + + /* + * The lower four bits of the buffer format field contain the DATA-IN + * buffer descriptor format, and the highest four bits contain the + * DATA-OUT buffer descriptor format. + */ + *dir = DMA_NONE; + if (srp_cmd->buf_fmt & 0xf) + /* DATA-IN: transfer data from target to initiator (read). */ + *dir = DMA_FROM_DEVICE; + else if (srp_cmd->buf_fmt >> 4) + /* DATA-OUT: transfer data from initiator to target (write). */ + *dir = DMA_TO_DEVICE; + + /* + * According to the SRP spec, the lower two bits of the 'ADDITIONAL + * CDB LENGTH' field are reserved and the size in bytes of this field + * is four times the value specified in bits 3..7. Hence the "& ~3". + */ + add_cdb_offset = srp_cmd->add_cdb_len & ~3; + if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_DIRECT) || + ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_DIRECT)) { + ioctx->n_rbuf = 1; + ioctx->rbufs = &ioctx->single_rbuf; + + db = (struct srp_direct_buf *)(srp_cmd->add_data + + add_cdb_offset); + memcpy(ioctx->rbufs, db, sizeof *db); + *data_len = be32_to_cpu(db->len); + } else if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_INDIRECT) || + ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_INDIRECT)) { + idb = (struct srp_indirect_buf *)(srp_cmd->add_data + + add_cdb_offset); + + ioctx->n_rbuf = be32_to_cpu(idb->table_desc.len) / sizeof *db; + + if (ioctx->n_rbuf > + (srp_cmd->data_out_desc_cnt + srp_cmd->data_in_desc_cnt)) { + printk(KERN_ERR "received unsupported SRP_CMD request" + " type (%u out + %u in != %u / %zu)\n", + srp_cmd->data_out_desc_cnt, + srp_cmd->data_in_desc_cnt, + be32_to_cpu(idb->table_desc.len), + sizeof(*db)); + ioctx->n_rbuf = 0; + ret = -EINVAL; + goto out; + } + + if (ioctx->n_rbuf == 1) + ioctx->rbufs = &ioctx->single_rbuf; + else { + ioctx->rbufs = + kmalloc(ioctx->n_rbuf * sizeof *db, GFP_ATOMIC); + if (!ioctx->rbufs) { + ioctx->n_rbuf = 0; + ret = -ENOMEM; + goto out; + } + } + + db = idb->desc_list; + memcpy(ioctx->rbufs, db, ioctx->n_rbuf * sizeof *db); + *data_len = be32_to_cpu(idb->len); + } +out: + return ret; +} + +/** + * srpt_init_ch_qp() - Initialize queue pair attributes. + * + * Initialized the attributes of queue pair 'qp' by allowing local write, + * remote read and remote write. Also transitions 'qp' to state IB_QPS_INIT. + */ +static int srpt_init_ch_qp(struct srpt_rdma_ch *ch, struct ib_qp *qp) +{ + struct ib_qp_attr *attr; + int ret; + + attr = kzalloc(sizeof *attr, GFP_KERNEL); + if (!attr) + return -ENOMEM; + + attr->qp_state = IB_QPS_INIT; + attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ | + IB_ACCESS_REMOTE_WRITE; + attr->port_num = ch->sport->port; + attr->pkey_index = 0; + + ret = ib_modify_qp(qp, attr, + IB_QP_STATE | IB_QP_ACCESS_FLAGS | IB_QP_PORT | + IB_QP_PKEY_INDEX); + + kfree(attr); + return ret; +} + +/** + * srpt_ch_qp_rtr() - Change the state of a channel to 'ready to receive' (RTR). + * @ch: channel of the queue pair. + * @qp: queue pair to change the state of. + * + * Returns zero upon success and a negative value upon failure. + * + * Note: currently a struct ib_qp_attr takes 136 bytes on a 64-bit system. + * If this structure ever becomes larger, it might be necessary to allocate + * it dynamically instead of on the stack. + */ +static int srpt_ch_qp_rtr(struct srpt_rdma_ch *ch, struct ib_qp *qp) +{ + struct ib_qp_attr qp_attr; + int attr_mask; + int ret; + + qp_attr.qp_state = IB_QPS_RTR; + ret = ib_cm_init_qp_attr(ch->cm_id, &qp_attr, &attr_mask); + if (ret) + goto out; + + qp_attr.max_dest_rd_atomic = 4; + + ret = ib_modify_qp(qp, &qp_attr, attr_mask); + +out: + return ret; +} + +/** + * srpt_ch_qp_rts() - Change the state of a channel to 'ready to send' (RTS). + * @ch: channel of the queue pair. + * @qp: queue pair to change the state of. + * + * Returns zero upon success and a negative value upon failure. + * + * Note: currently a struct ib_qp_attr takes 136 bytes on a 64-bit system. + * If this structure ever becomes larger, it might be necessary to allocate + * it dynamically instead of on the stack. + */ +static int srpt_ch_qp_rts(struct srpt_rdma_ch *ch, struct ib_qp *qp) +{ + struct ib_qp_attr qp_attr; + int attr_mask; + int ret; + + qp_attr.qp_state = IB_QPS_RTS; + ret = ib_cm_init_qp_attr(ch->cm_id, &qp_attr, &attr_mask); + if (ret) + goto out; + + qp_attr.max_rd_atomic = 4; + + ret = ib_modify_qp(qp, &qp_attr, attr_mask); + +out: + return ret; +} + +/** + * srpt_ch_qp_err() - Set the channel queue pair state to 'error'. + */ +static int srpt_ch_qp_err(struct srpt_rdma_ch *ch) +{ + struct ib_qp_attr qp_attr; + + qp_attr.qp_state = IB_QPS_ERR; + return ib_modify_qp(ch->qp, &qp_attr, IB_QP_STATE); +} + +/** + * srpt_unmap_sg_to_ib_sge() - Unmap an IB SGE list. + */ +static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx) +{ + struct scatterlist *sg; + enum dma_data_direction dir; + + BUG_ON(!ch); + BUG_ON(!ioctx); + BUG_ON(ioctx->n_rdma && !ioctx->rdma_ius); + + while (ioctx->n_rdma) + kfree(ioctx->rdma_ius[--ioctx->n_rdma].sge); + + kfree(ioctx->rdma_ius); + ioctx->rdma_ius = NULL; + + if (ioctx->mapped_sg_count) { + sg = ioctx->sg; + WARN_ON(!sg); + dir = ioctx->cmd.data_direction; + BUG_ON(dir == DMA_NONE); + ib_dma_unmap_sg(ch->sport->sdev->device, sg, ioctx->sg_cnt, + opposite_dma_dir(dir)); + ioctx->mapped_sg_count = 0; + } +} + +/** + * srpt_map_sg_to_ib_sge() - Map an SG list to an IB SGE list. + */ +static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx) +{ + struct se_cmd *cmd; + struct scatterlist *sg, *sg_orig; + int sg_cnt; + enum dma_data_direction dir; + struct rdma_iu *riu; + struct srp_direct_buf *db; + dma_addr_t dma_addr; + struct ib_sge *sge; + u64 raddr; + u32 rsize; + u32 tsize; + u32 dma_len; + int count, nrdma; + int i, j, k; + + BUG_ON(!ch); + BUG_ON(!ioctx); + cmd = &ioctx->cmd; + dir = cmd->data_direction; + BUG_ON(dir == DMA_NONE); + + transport_do_task_sg_chain(cmd); + ioctx->sg = sg = sg_orig = cmd->t_tasks_sg_chained; + ioctx->sg_cnt = sg_cnt = cmd->t_tasks_sg_chained_no; + + count = ib_dma_map_sg(ch->sport->sdev->device, sg, sg_cnt, + opposite_dma_dir(dir)); + if (unlikely(!count)) + return -EAGAIN; + + ioctx->mapped_sg_count = count; + + if (ioctx->rdma_ius && ioctx->n_rdma_ius) + nrdma = ioctx->n_rdma_ius; + else { + nrdma = (count + SRPT_DEF_SG_PER_WQE - 1) / SRPT_DEF_SG_PER_WQE + + ioctx->n_rbuf; + + ioctx->rdma_ius = kzalloc(nrdma * sizeof *riu, GFP_KERNEL); + if (!ioctx->rdma_ius) + goto free_mem; + + ioctx->n_rdma_ius = nrdma; + } + + db = ioctx->rbufs; + tsize = cmd->data_length; + dma_len = sg_dma_len(&sg[0]); + riu = ioctx->rdma_ius; + + /* + * For each remote desc - calculate the #ib_sge. + * If #ib_sge < SRPT_DEF_SG_PER_WQE per rdma operation then + * each remote desc rdma_iu is required a rdma wr; + * else + * we need to allocate extra rdma_iu to carry extra #ib_sge in + * another rdma wr + */ + for (i = 0, j = 0; + j < count && i < ioctx->n_rbuf && tsize > 0; ++i, ++riu, ++db) { + rsize = be32_to_cpu(db->len); + raddr = be64_to_cpu(db->va); + riu->raddr = raddr; + riu->rkey = be32_to_cpu(db->key); + riu->sge_cnt = 0; + + /* calculate how many sge required for this remote_buf */ + while (rsize > 0 && tsize > 0) { + + if (rsize >= dma_len) { + tsize -= dma_len; + rsize -= dma_len; + raddr += dma_len; + + if (tsize > 0) { + ++j; + if (j < count) { + sg = sg_next(sg); + dma_len = sg_dma_len(sg); + } + } + } else { + tsize -= rsize; + dma_len -= rsize; + rsize = 0; + } + + ++riu->sge_cnt; + + if (rsize > 0 && riu->sge_cnt == SRPT_DEF_SG_PER_WQE) { + ++ioctx->n_rdma; + riu->sge = + kmalloc(riu->sge_cnt * sizeof *riu->sge, + GFP_KERNEL); + if (!riu->sge) + goto free_mem; + + ++riu; + riu->sge_cnt = 0; + riu->raddr = raddr; + riu->rkey = be32_to_cpu(db->key); + } + } + + ++ioctx->n_rdma; + riu->sge = kmalloc(riu->sge_cnt * sizeof *riu->sge, + GFP_KERNEL); + if (!riu->sge) + goto free_mem; + } + + db = ioctx->rbufs; + tsize = cmd->data_length; + riu = ioctx->rdma_ius; + sg = sg_orig; + dma_len = sg_dma_len(&sg[0]); + dma_addr = sg_dma_address(&sg[0]); + + /* this second loop is really mapped sg_addres to rdma_iu->ib_sge */ + for (i = 0, j = 0; + j < count && i < ioctx->n_rbuf && tsize > 0; ++i, ++riu, ++db) { + rsize = be32_to_cpu(db->len); + sge = riu->sge; + k = 0; + + while (rsize > 0 && tsize > 0) { + sge->addr = dma_addr; + sge->lkey = ch->sport->sdev->mr->lkey; + + if (rsize >= dma_len) { + sge->length = + (tsize < dma_len) ? tsize : dma_len; + tsize -= dma_len; + rsize -= dma_len; + + if (tsize > 0) { + ++j; + if (j < count) { + sg = sg_next(sg); + dma_len = sg_dma_len(sg); + dma_addr = sg_dma_address(sg); + } + } + } else { + sge->length = (tsize < rsize) ? tsize : rsize; + tsize -= rsize; + dma_len -= rsize; + dma_addr += rsize; + rsize = 0; + } + + ++k; + if (k == riu->sge_cnt && rsize > 0 && tsize > 0) { + ++riu; + sge = riu->sge; + k = 0; + } else if (rsize > 0 && tsize > 0) + ++sge; + } + } + + return 0; + +free_mem: + srpt_unmap_sg_to_ib_sge(ch, ioctx); + + return -ENOMEM; +} + +/** + * srpt_get_send_ioctx() - Obtain an I/O context for sending to the initiator. + */ +static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch) +{ + struct srpt_send_ioctx *ioctx; + unsigned long flags; + + BUG_ON(!ch); + + ioctx = NULL; + spin_lock_irqsave(&ch->spinlock, flags); + if (!list_empty(&ch->free_list)) { + ioctx = list_first_entry(&ch->free_list, + struct srpt_send_ioctx, free_list); + list_del(&ioctx->free_list); + } + spin_unlock_irqrestore(&ch->spinlock, flags); + + if (!ioctx) + return ioctx; + + BUG_ON(ioctx->ch != ch); + kref_init(&ioctx->kref); + spin_lock_init(&ioctx->spinlock); + ioctx->state = SRPT_STATE_NEW; + ioctx->n_rbuf = 0; + ioctx->rbufs = NULL; + ioctx->n_rdma = 0; + ioctx->n_rdma_ius = 0; + ioctx->rdma_ius = NULL; + ioctx->mapped_sg_count = 0; + init_completion(&ioctx->tx_done); + ioctx->queue_status_only = false; + /* + * transport_init_se_cmd() does not initialize all fields, so do it + * here. + */ + memset(&ioctx->cmd, 0, sizeof(ioctx->cmd)); + memset(&ioctx->sense_data, 0, sizeof(ioctx->sense_data)); + + return ioctx; +} + +/** + * srpt_put_send_ioctx() - Free up resources. + */ +static void srpt_put_send_ioctx(struct srpt_send_ioctx *ioctx) +{ + struct srpt_rdma_ch *ch; + unsigned long flags; + + BUG_ON(!ioctx); + ch = ioctx->ch; + BUG_ON(!ch); + + WARN_ON(srpt_get_cmd_state(ioctx) != SRPT_STATE_DONE); + + srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx); + transport_generic_free_cmd(&ioctx->cmd, 0); + + if (ioctx->n_rbuf > 1) { + kfree(ioctx->rbufs); + ioctx->rbufs = NULL; + ioctx->n_rbuf = 0; + } + + spin_lock_irqsave(&ch->spinlock, flags); + list_add(&ioctx->free_list, &ch->free_list); + spin_unlock_irqrestore(&ch->spinlock, flags); +} + +static void srpt_put_send_ioctx_kref(struct kref *kref) +{ + srpt_put_send_ioctx(container_of(kref, struct srpt_send_ioctx, kref)); +} + +/** + * srpt_abort_cmd() - Abort a SCSI command. + * @ioctx: I/O context associated with the SCSI command. + * @context: Preferred execution context. + */ +static int srpt_abort_cmd(struct srpt_send_ioctx *ioctx) +{ + enum srpt_command_state state; + unsigned long flags; + + BUG_ON(!ioctx); + + /* + * If the command is in a state where the target core is waiting for + * the ib_srpt driver, change the state to the next state. Changing + * the state of the command from SRPT_STATE_NEED_DATA to + * SRPT_STATE_DATA_IN ensures that srpt_xmit_response() will call this + * function a second time. + */ + + spin_lock_irqsave(&ioctx->spinlock, flags); + state = ioctx->state; + switch (state) { + case SRPT_STATE_NEED_DATA: + ioctx->state = SRPT_STATE_DATA_IN; + break; + case SRPT_STATE_DATA_IN: + case SRPT_STATE_CMD_RSP_SENT: + case SRPT_STATE_MGMT_RSP_SENT: + ioctx->state = SRPT_STATE_DONE; + break; + default: + break; + } + spin_unlock_irqrestore(&ioctx->spinlock, flags); + + if (state == SRPT_STATE_DONE) + goto out; + + pr_debug("Aborting cmd with state %d and tag %lld\n", state, + ioctx->tag); + + switch (state) { + case SRPT_STATE_NEW: + case SRPT_STATE_DATA_IN: + case SRPT_STATE_MGMT: + /* + * Do nothing - defer abort processing until + * srpt_queue_response() is invoked. + */ + WARN_ON(!transport_check_aborted_status(&ioctx->cmd, false)); + break; + case SRPT_STATE_NEED_DATA: + /* DMA_TO_DEVICE (write) - RDMA read error. */ + atomic_set(&ioctx->cmd.transport_lun_stop, 1); + transport_generic_handle_data(&ioctx->cmd); + break; + case SRPT_STATE_CMD_RSP_SENT: + /* + * SRP_RSP sending failed or the SRP_RSP send completion has + * not been received in time. + */ + srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx); + atomic_set(&ioctx->cmd.transport_lun_stop, 1); + kref_put(&ioctx->kref, srpt_put_send_ioctx_kref); + break; + case SRPT_STATE_MGMT_RSP_SENT: + srpt_set_cmd_state(ioctx, SRPT_STATE_DONE); + kref_put(&ioctx->kref, srpt_put_send_ioctx_kref); + break; + default: + WARN_ON("ERROR: unexpected command state"); + break; + } + +out: + return state; +} + +/** + * srpt_handle_send_err_comp() - Process an IB_WC_SEND error completion. + */ +static void srpt_handle_send_err_comp(struct srpt_rdma_ch *ch, u64 wr_id) +{ + struct srpt_send_ioctx *ioctx; + enum srpt_command_state state; + struct se_cmd *cmd; + u32 index; + + atomic_inc(&ch->sq_wr_avail); + + index = idx_from_wr_id(wr_id); + ioctx = ch->ioctx_ring[index]; + state = srpt_get_cmd_state(ioctx); + cmd = &ioctx->cmd; + + WARN_ON(state != SRPT_STATE_CMD_RSP_SENT + && state != SRPT_STATE_MGMT_RSP_SENT + && state != SRPT_STATE_NEED_DATA + && state != SRPT_STATE_DONE); + + /* If SRP_RSP sending failed, undo the ch->req_lim change. */ + if (state == SRPT_STATE_CMD_RSP_SENT + || state == SRPT_STATE_MGMT_RSP_SENT) + atomic_dec(&ch->req_lim); + + srpt_abort_cmd(ioctx); +} + +/** + * srpt_handle_send_comp() - Process an IB send completion notification. + */ +static void srpt_handle_send_comp(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx) +{ + enum srpt_command_state state; + + atomic_inc(&ch->sq_wr_avail); + + state = srpt_set_cmd_state(ioctx, SRPT_STATE_DONE); + + if (WARN_ON(state != SRPT_STATE_CMD_RSP_SENT + && state != SRPT_STATE_MGMT_RSP_SENT + && state != SRPT_STATE_DONE)) + pr_debug("state = %d\n", state); + + if (state != SRPT_STATE_DONE) + kref_put(&ioctx->kref, srpt_put_send_ioctx_kref); + else + printk(KERN_ERR "IB completion has been received too late for" + " wr_id = %u.\n", ioctx->ioctx.index); +} + +/** + * srpt_handle_rdma_comp() - Process an IB RDMA completion notification. + * + * Note: transport_generic_handle_data() is asynchronous so unmapping the + * data that has been transferred via IB RDMA must be postponed until the + * check_stop_free() callback. + */ +static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx, + enum srpt_opcode opcode) +{ + WARN_ON(ioctx->n_rdma <= 0); + atomic_add(ioctx->n_rdma, &ch->sq_wr_avail); + + if (opcode == SRPT_RDMA_READ_LAST) { + if (srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA, + SRPT_STATE_DATA_IN)) + transport_generic_handle_data(&ioctx->cmd); + else + printk(KERN_ERR "%s[%d]: wrong state = %d\n", __func__, + __LINE__, srpt_get_cmd_state(ioctx)); + } else if (opcode == SRPT_RDMA_ABORT) { + ioctx->rdma_aborted = true; + } else { + WARN(true, "unexpected opcode %d\n", opcode); + } +} + +/** + * srpt_handle_rdma_err_comp() - Process an IB RDMA error completion. + */ +static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx, + enum srpt_opcode opcode) +{ + struct se_cmd *cmd; + enum srpt_command_state state; + + cmd = &ioctx->cmd; + state = srpt_get_cmd_state(ioctx); + switch (opcode) { + case SRPT_RDMA_READ_LAST: + if (ioctx->n_rdma <= 0) { + printk(KERN_ERR "Received invalid RDMA read" + " error completion with idx %d\n", + ioctx->ioctx.index); + break; + } + atomic_add(ioctx->n_rdma, &ch->sq_wr_avail); + if (state == SRPT_STATE_NEED_DATA) + srpt_abort_cmd(ioctx); + else + printk(KERN_ERR "%s[%d]: wrong state = %d\n", + __func__, __LINE__, state); + break; + case SRPT_RDMA_WRITE_LAST: + atomic_set(&ioctx->cmd.transport_lun_stop, 1); + break; + default: + printk(KERN_ERR "%s[%d]: opcode = %u\n", __func__, + __LINE__, opcode); + break; + } +} + +/** + * srpt_build_cmd_rsp() - Build an SRP_RSP response. + * @ch: RDMA channel through which the request has been received. + * @ioctx: I/O context associated with the SRP_CMD request. The response will + * be built in the buffer ioctx->buf points at and hence this function will + * overwrite the request data. + * @tag: tag of the request for which this response is being generated. + * @status: value for the STATUS field of the SRP_RSP information unit. + * + * Returns the size in bytes of the SRP_RSP response. + * + * An SRP_RSP response contains a SCSI status or service response. See also + * section 6.9 in the SRP r16a document for the format of an SRP_RSP + * response. See also SPC-2 for more information about sense data. + */ +static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx, u64 tag, + int status) +{ + struct srp_rsp *srp_rsp; + const u8 *sense_data; + int sense_data_len, max_sense_len; + + /* + * The lowest bit of all SAM-3 status codes is zero (see also + * paragraph 5.3 in SAM-3). + */ + WARN_ON(status & 1); + + srp_rsp = ioctx->ioctx.buf; + BUG_ON(!srp_rsp); + + sense_data = ioctx->sense_data; + sense_data_len = ioctx->cmd.scsi_sense_length; + WARN_ON(sense_data_len > sizeof(ioctx->sense_data)); + + memset(srp_rsp, 0, sizeof *srp_rsp); + srp_rsp->opcode = SRP_RSP; + srp_rsp->req_lim_delta = + __constant_cpu_to_be32(1 + atomic_xchg(&ch->req_lim_delta, 0)); + srp_rsp->tag = tag; + srp_rsp->status = status; + + if (sense_data_len) { + BUILD_BUG_ON(MIN_MAX_RSP_SIZE <= sizeof(*srp_rsp)); + max_sense_len = ch->max_ti_iu_len - sizeof(*srp_rsp); + if (sense_data_len > max_sense_len) { + printk(KERN_WARNING "truncated sense data from %d to %d" + " bytes\n", sense_data_len, max_sense_len); + sense_data_len = max_sense_len; + } + + srp_rsp->flags |= SRP_RSP_FLAG_SNSVALID; + srp_rsp->sense_data_len = cpu_to_be32(sense_data_len); + memcpy(srp_rsp + 1, sense_data, sense_data_len); + } + + return sizeof(*srp_rsp) + sense_data_len; +} + +/** + * srpt_build_tskmgmt_rsp() - Build a task management response. + * @ch: RDMA channel through which the request has been received. + * @ioctx: I/O context in which the SRP_RSP response will be built. + * @rsp_code: RSP_CODE that will be stored in the response. + * @tag: Tag of the request for which this response is being generated. + * + * Returns the size in bytes of the SRP_RSP response. + * + * An SRP_RSP response contains a SCSI status or service response. See also + * section 6.9 in the SRP r16a document for the format of an SRP_RSP + * response. + */ +static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx, + u8 rsp_code, u64 tag) +{ + struct srp_rsp *srp_rsp; + int resp_data_len; + int resp_len; + + resp_data_len = (rsp_code == SRP_TSK_MGMT_SUCCESS) ? 0 : 4; + resp_len = sizeof(*srp_rsp) + resp_data_len; + + srp_rsp = ioctx->ioctx.buf; + BUG_ON(!srp_rsp); + memset(srp_rsp, 0, sizeof *srp_rsp); + + srp_rsp->opcode = SRP_RSP; + srp_rsp->req_lim_delta = __constant_cpu_to_be32(1 + + atomic_xchg(&ch->req_lim_delta, 0)); + srp_rsp->tag = tag; + + if (rsp_code != SRP_TSK_MGMT_SUCCESS) { + srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID; + srp_rsp->resp_data_len = cpu_to_be32(resp_data_len); + srp_rsp->data[3] = rsp_code; + } + + return resp_len; +} + +#define NO_SUCH_LUN ((uint64_t)-1LL) + +/* + * SCSI LUN addressing method. See also SAM-2 and the section about + * eight byte LUNs. + */ +enum scsi_lun_addr_method { + SCSI_LUN_ADDR_METHOD_PERIPHERAL = 0, + SCSI_LUN_ADDR_METHOD_FLAT = 1, + SCSI_LUN_ADDR_METHOD_LUN = 2, + SCSI_LUN_ADDR_METHOD_EXTENDED_LUN = 3, +}; + +/* + * srpt_unpack_lun() - Convert from network LUN to linear LUN. + * + * Convert an 2-byte, 4-byte, 6-byte or 8-byte LUN structure in network byte + * order (big endian) to a linear LUN. Supports three LUN addressing methods: + * peripheral, flat and logical unit. See also SAM-2, section 4.9.4 (page 40). + */ +static uint64_t srpt_unpack_lun(const uint8_t *lun, int len) +{ + uint64_t res = NO_SUCH_LUN; + int addressing_method; + + if (unlikely(len < 2)) { + printk(KERN_ERR "Illegal LUN length %d, expected 2 bytes or " + "more", len); + goto out; + } + + switch (len) { + case 8: + if ((*((__be64 *)lun) & + __constant_cpu_to_be64(0x0000FFFFFFFFFFFFLL)) != 0) + goto out_err; + break; + case 4: + if (*((__be16 *)&lun[2]) != 0) + goto out_err; + break; + case 6: + if (*((__be32 *)&lun[2]) != 0) + goto out_err; + break; + case 2: + break; + default: + goto out_err; + } + + addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */ + switch (addressing_method) { + case SCSI_LUN_ADDR_METHOD_PERIPHERAL: + case SCSI_LUN_ADDR_METHOD_FLAT: + case SCSI_LUN_ADDR_METHOD_LUN: + res = *(lun + 1) | (((*lun) & 0x3f) << 8); + break; + + case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN: + default: + printk(KERN_ERR "Unimplemented LUN addressing method %u", + addressing_method); + break; + } + +out: + return res; + +out_err: + printk(KERN_ERR "Support for multi-level LUNs has not yet been" + " implemented"); + goto out; +} + +static int srpt_check_stop_free(struct se_cmd *cmd) +{ + struct srpt_send_ioctx *ioctx; + + ioctx = container_of(cmd, struct srpt_send_ioctx, cmd); + return kref_put(&ioctx->kref, srpt_put_send_ioctx_kref); +} + +/** + * srpt_handle_cmd() - Process SRP_CMD. + */ +static int srpt_handle_cmd(struct srpt_rdma_ch *ch, + struct srpt_recv_ioctx *recv_ioctx, + struct srpt_send_ioctx *send_ioctx) +{ + struct se_cmd *cmd; + struct srp_cmd *srp_cmd; + uint64_t unpacked_lun; + u64 data_len; + enum dma_data_direction dir; + int ret; + + BUG_ON(!send_ioctx); + + srp_cmd = recv_ioctx->ioctx.buf; + kref_get(&send_ioctx->kref); + cmd = &send_ioctx->cmd; + send_ioctx->tag = srp_cmd->tag; + + switch (srp_cmd->task_attr) { + case SRP_CMD_SIMPLE_Q: + cmd->sam_task_attr = MSG_SIMPLE_TAG; + break; + case SRP_CMD_ORDERED_Q: + default: + cmd->sam_task_attr = MSG_ORDERED_TAG; + break; + case SRP_CMD_HEAD_OF_Q: + cmd->sam_task_attr = MSG_HEAD_TAG; + break; + case SRP_CMD_ACA: + cmd->sam_task_attr = MSG_ACA_TAG; + break; + } + + ret = srpt_get_desc_tbl(send_ioctx, srp_cmd, &dir, &data_len); + if (ret) { + printk(KERN_ERR "0x%llx: parsing SRP descriptor table failed.\n", + srp_cmd->tag); + cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; + goto send_sense; + } + + cmd->data_length = data_len; + cmd->data_direction = dir; + unpacked_lun = srpt_unpack_lun((uint8_t *)&srp_cmd->lun, + sizeof(srp_cmd->lun)); + if (transport_lookup_cmd_lun(cmd, unpacked_lun) < 0) + goto send_sense; + ret = transport_generic_allocate_tasks(cmd, srp_cmd->cdb); + if (cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) + srpt_queue_status(cmd); + else if (cmd->se_cmd_flags & SCF_SCSI_CDB_EXCEPTION) + goto send_sense; + else + WARN_ON_ONCE(ret); + + transport_handle_cdb_direct(cmd); + return 0; + +send_sense: + transport_send_check_condition_and_sense(cmd, cmd->scsi_sense_reason, + 0); + return -1; +} + +/** + * srpt_rx_mgmt_fn_tag() - Process a task management function by tag. + * @ch: RDMA channel of the task management request. + * @fn: Task management function to perform. + * @req_tag: Tag of the SRP task management request. + * @mgmt_ioctx: I/O context of the task management request. + * + * Returns zero if the target core will process the task management + * request asynchronously. + * + * Note: It is assumed that the initiator serializes tag-based task management + * requests. + */ +static int srpt_rx_mgmt_fn_tag(struct srpt_send_ioctx *ioctx, u64 tag) +{ + struct srpt_device *sdev; + struct srpt_rdma_ch *ch; + struct srpt_send_ioctx *target; + int ret, i; + + ret = -EINVAL; + ch = ioctx->ch; + BUG_ON(!ch); + BUG_ON(!ch->sport); + sdev = ch->sport->sdev; + BUG_ON(!sdev); + spin_lock_irq(&sdev->spinlock); + for (i = 0; i < ch->rq_size; ++i) { + target = ch->ioctx_ring[i]; + if (target->cmd.se_lun == ioctx->cmd.se_lun && + target->tag == tag && + srpt_get_cmd_state(target) != SRPT_STATE_DONE) { + ret = 0; + /* now let the target core abort &target->cmd; */ + break; + } + } + spin_unlock_irq(&sdev->spinlock); + return ret; +} + +static int srp_tmr_to_tcm(int fn) +{ + switch (fn) { + case SRP_TSK_ABORT_TASK: + return TMR_ABORT_TASK; + case SRP_TSK_ABORT_TASK_SET: + return TMR_ABORT_TASK_SET; + case SRP_TSK_CLEAR_TASK_SET: + return TMR_CLEAR_TASK_SET; + case SRP_TSK_LUN_RESET: + return TMR_LUN_RESET; + case SRP_TSK_CLEAR_ACA: + return TMR_CLEAR_ACA; + default: + return -1; + } +} + +/** + * srpt_handle_tsk_mgmt() - Process an SRP_TSK_MGMT information unit. + * + * Returns 0 if and only if the request will be processed by the target core. + * + * For more information about SRP_TSK_MGMT information units, see also section + * 6.7 in the SRP r16a document. + */ +static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, + struct srpt_recv_ioctx *recv_ioctx, + struct srpt_send_ioctx *send_ioctx) +{ + struct srp_tsk_mgmt *srp_tsk; + struct se_cmd *cmd; + uint64_t unpacked_lun; + int tcm_tmr; + int res; + + BUG_ON(!send_ioctx); + + srp_tsk = recv_ioctx->ioctx.buf; + cmd = &send_ioctx->cmd; + + pr_debug("recv tsk_mgmt fn %d for task_tag %lld and cmd tag %lld" + " cm_id %p sess %p\n", srp_tsk->tsk_mgmt_func, + srp_tsk->task_tag, srp_tsk->tag, ch->cm_id, ch->sess); + + srpt_set_cmd_state(send_ioctx, SRPT_STATE_MGMT); + send_ioctx->tag = srp_tsk->tag; + tcm_tmr = srp_tmr_to_tcm(srp_tsk->tsk_mgmt_func); + if (tcm_tmr < 0) { + send_ioctx->cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + send_ioctx->cmd.se_tmr_req->response = + TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED; + goto process_tmr; + } + cmd->se_tmr_req = core_tmr_alloc_req(cmd, NULL, tcm_tmr, GFP_KERNEL); + if (!cmd->se_tmr_req) { + send_ioctx->cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + send_ioctx->cmd.se_tmr_req->response = TMR_FUNCTION_REJECTED; + goto process_tmr; + } + + unpacked_lun = srpt_unpack_lun((uint8_t *)&srp_tsk->lun, + sizeof(srp_tsk->lun)); + res = transport_lookup_tmr_lun(&send_ioctx->cmd, unpacked_lun); + if (res) { + pr_debug("rejecting TMR for LUN %lld\n", unpacked_lun); + send_ioctx->cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + send_ioctx->cmd.se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST; + goto process_tmr; + } + + if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK) + srpt_rx_mgmt_fn_tag(send_ioctx, srp_tsk->task_tag); + +process_tmr: + kref_get(&send_ioctx->kref); + if (!(send_ioctx->cmd.se_cmd_flags & SCF_SCSI_CDB_EXCEPTION)) + transport_generic_handle_tmr(&send_ioctx->cmd); + else + transport_send_check_condition_and_sense(cmd, + cmd->scsi_sense_reason, 0); + +} + +/** + * srpt_handle_new_iu() - Process a newly received information unit. + * @ch: RDMA channel through which the information unit has been received. + * @ioctx: SRPT I/O context associated with the information unit. + */ +static void srpt_handle_new_iu(struct srpt_rdma_ch *ch, + struct srpt_recv_ioctx *recv_ioctx, + struct srpt_send_ioctx *send_ioctx) +{ + struct srp_cmd *srp_cmd; + enum rdma_ch_state ch_state; + + BUG_ON(!ch); + BUG_ON(!recv_ioctx); + + ib_dma_sync_single_for_cpu(ch->sport->sdev->device, + recv_ioctx->ioctx.dma, srp_max_req_size, + DMA_FROM_DEVICE); + + ch_state = srpt_get_ch_state(ch); + if (unlikely(ch_state == CH_CONNECTING)) { + list_add_tail(&recv_ioctx->wait_list, &ch->cmd_wait_list); + goto out; + } + + if (unlikely(ch_state != CH_LIVE)) + goto out; + + srp_cmd = recv_ioctx->ioctx.buf; + if (srp_cmd->opcode == SRP_CMD || srp_cmd->opcode == SRP_TSK_MGMT) { + if (!send_ioctx) + send_ioctx = srpt_get_send_ioctx(ch); + if (unlikely(!send_ioctx)) { + list_add_tail(&recv_ioctx->wait_list, + &ch->cmd_wait_list); + goto out; + } + } + + transport_init_se_cmd(&send_ioctx->cmd, &srpt_target->tf_ops, ch->sess, + 0, DMA_NONE, MSG_SIMPLE_TAG, + send_ioctx->sense_data); + + switch (srp_cmd->opcode) { + case SRP_CMD: + srpt_handle_cmd(ch, recv_ioctx, send_ioctx); + break; + case SRP_TSK_MGMT: + srpt_handle_tsk_mgmt(ch, recv_ioctx, send_ioctx); + break; + case SRP_I_LOGOUT: + printk(KERN_ERR "Not yet implemented: SRP_I_LOGOUT\n"); + break; + case SRP_CRED_RSP: + pr_debug("received SRP_CRED_RSP\n"); + break; + case SRP_AER_RSP: + pr_debug("received SRP_AER_RSP\n"); + break; + case SRP_RSP: + printk(KERN_ERR "Received SRP_RSP\n"); + break; + default: + printk(KERN_ERR "received IU with unknown opcode 0x%x\n", + srp_cmd->opcode); + break; + } + + srpt_post_recv(ch->sport->sdev, recv_ioctx); +out: + return; +} + +static void srpt_process_rcv_completion(struct ib_cq *cq, + struct srpt_rdma_ch *ch, + struct ib_wc *wc) +{ + struct srpt_device *sdev = ch->sport->sdev; + struct srpt_recv_ioctx *ioctx; + u32 index; + + index = idx_from_wr_id(wc->wr_id); + if (wc->status == IB_WC_SUCCESS) { + int req_lim; + + req_lim = atomic_dec_return(&ch->req_lim); + if (unlikely(req_lim < 0)) + printk(KERN_ERR "req_lim = %d < 0\n", req_lim); + ioctx = sdev->ioctx_ring[index]; + srpt_handle_new_iu(ch, ioctx, NULL); + } else { + printk(KERN_INFO "receiving failed for idx %u with status %d\n", + index, wc->status); + } +} + +/** + * srpt_process_send_completion() - Process an IB send completion. + * + * Note: Although this has not yet been observed during tests, at least in + * theory it is possible that the srpt_get_send_ioctx() call invoked by + * srpt_handle_new_iu() fails. This is possible because the req_lim_delta + * value in each response is set to one, and it is possible that this response + * makes the initiator send a new request before the send completion for that + * response has been processed. This could e.g. happen if the call to + * srpt_put_send_iotcx() is delayed because of a higher priority interrupt or + * if IB retransmission causes generation of the send completion to be + * delayed. Incoming information units for which srpt_get_send_ioctx() fails + * are queued on cmd_wait_list. The code below processes these delayed + * requests one at a time. + */ +static void srpt_process_send_completion(struct ib_cq *cq, + struct srpt_rdma_ch *ch, + struct ib_wc *wc) +{ + struct srpt_send_ioctx *send_ioctx; + uint32_t index; + enum srpt_opcode opcode; + + index = idx_from_wr_id(wc->wr_id); + opcode = opcode_from_wr_id(wc->wr_id); + send_ioctx = ch->ioctx_ring[index]; + if (wc->status == IB_WC_SUCCESS) { + if (opcode == SRPT_SEND) + srpt_handle_send_comp(ch, send_ioctx); + else { + WARN_ON(opcode != SRPT_RDMA_ABORT && + wc->opcode != IB_WC_RDMA_READ); + srpt_handle_rdma_comp(ch, send_ioctx, opcode); + } + } else { + if (opcode == SRPT_SEND) { + printk(KERN_INFO "sending response for idx %u failed" + " with status %d\n", index, wc->status); + srpt_handle_send_err_comp(ch, wc->wr_id); + } else if (opcode != SRPT_RDMA_MID) { + printk(KERN_INFO "RDMA t %d for idx %u failed with" + " status %d", opcode, index, wc->status); + srpt_handle_rdma_err_comp(ch, send_ioctx, opcode); + } + } + + while (unlikely(opcode == SRPT_SEND + && !list_empty(&ch->cmd_wait_list) + && srpt_get_ch_state(ch) == CH_LIVE + && (send_ioctx = srpt_get_send_ioctx(ch)) != NULL)) { + struct srpt_recv_ioctx *recv_ioctx; + + recv_ioctx = list_first_entry(&ch->cmd_wait_list, + struct srpt_recv_ioctx, + wait_list); + list_del(&recv_ioctx->wait_list); + srpt_handle_new_iu(ch, recv_ioctx, send_ioctx); + } +} + +static void srpt_process_completion(struct ib_cq *cq, struct srpt_rdma_ch *ch) +{ + struct ib_wc *const wc = ch->wc; + int i, n; + + WARN_ON(cq != ch->cq); + + ib_req_notify_cq(cq, IB_CQ_NEXT_COMP); + while ((n = ib_poll_cq(cq, ARRAY_SIZE(ch->wc), wc)) > 0) { + for (i = 0; i < n; i++) { + if (opcode_from_wr_id(wc[i].wr_id) == SRPT_RECV) + srpt_process_rcv_completion(cq, ch, &wc[i]); + else + srpt_process_send_completion(cq, ch, &wc[i]); + } + } +} + +/** + * srpt_completion() - IB completion queue callback function. + * + * Notes: + * - It is guaranteed that a completion handler will never be invoked + * concurrently on two different CPUs for the same completion queue. See also + * Documentation/infiniband/core_locking.txt and the implementation of + * handle_edge_irq() in kernel/irq/chip.c. + * - When threaded IRQs are enabled, completion handlers are invoked in thread + * context instead of interrupt context. + */ +static void srpt_completion(struct ib_cq *cq, void *ctx) +{ + struct srpt_rdma_ch *ch = ctx; + + wake_up_interruptible(&ch->wait_queue); +} + +static int srpt_compl_thread(void *arg) +{ + struct srpt_rdma_ch *ch; + + /* Hibernation / freezing of the SRPT kernel thread is not supported. */ + current->flags |= PF_NOFREEZE; + + ch = arg; + BUG_ON(!ch); + printk(KERN_INFO "Session %s: kernel thread %s (PID %d) started\n", + ch->sess_name, ch->thread->comm, current->pid); + while (!kthread_should_stop()) { + wait_event_interruptible(ch->wait_queue, + (srpt_process_completion(ch->cq, ch), + kthread_should_stop())); + } + printk(KERN_INFO "Session %s: kernel thread %s (PID %d) stopped\n", + ch->sess_name, ch->thread->comm, current->pid); + return 0; +} + +/** + * srpt_create_ch_ib() - Create receive and send completion queues. + */ +static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) +{ + struct ib_qp_init_attr *qp_init; + struct srpt_port *sport = ch->sport; + struct srpt_device *sdev = sport->sdev; + u32 srp_sq_size = sport->port_attrib.srp_sq_size; + int ret; + + WARN_ON(ch->rq_size < 1); + + ret = -ENOMEM; + qp_init = kzalloc(sizeof *qp_init, GFP_KERNEL); + if (!qp_init) + goto out; + + ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch, + ch->rq_size + srp_sq_size, 0); + if (IS_ERR(ch->cq)) { + ret = PTR_ERR(ch->cq); + printk(KERN_ERR "failed to create CQ cqe= %d ret= %d\n", + ch->rq_size + srp_sq_size, ret); + goto out; + } + + qp_init->qp_context = (void *)ch; + qp_init->event_handler + = (void(*)(struct ib_event *, void*))srpt_qp_event; + qp_init->send_cq = ch->cq; + qp_init->recv_cq = ch->cq; + qp_init->srq = sdev->srq; + qp_init->sq_sig_type = IB_SIGNAL_REQ_WR; + qp_init->qp_type = IB_QPT_RC; + qp_init->cap.max_send_wr = srp_sq_size; + qp_init->cap.max_send_sge = SRPT_DEF_SG_PER_WQE; + + ch->qp = ib_create_qp(sdev->pd, qp_init); + if (IS_ERR(ch->qp)) { + ret = PTR_ERR(ch->qp); + printk(KERN_ERR "failed to create_qp ret= %d\n", ret); + goto err_destroy_cq; + } + + atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr); + + pr_debug("%s: max_cqe= %d max_sge= %d sq_size = %d cm_id= %p\n", + __func__, ch->cq->cqe, qp_init->cap.max_send_sge, + qp_init->cap.max_send_wr, ch->cm_id); + + ret = srpt_init_ch_qp(ch, ch->qp); + if (ret) + goto err_destroy_qp; + + init_waitqueue_head(&ch->wait_queue); + + pr_debug("creating thread for session %s\n", ch->sess_name); + + ch->thread = kthread_run(srpt_compl_thread, ch, "ib_srpt_compl"); + if (IS_ERR(ch->thread)) { + printk(KERN_ERR "failed to create kernel thread %ld\n", + PTR_ERR(ch->thread)); + ch->thread = NULL; + goto err_destroy_qp; + } + +out: + kfree(qp_init); + return ret; + +err_destroy_qp: + ib_destroy_qp(ch->qp); +err_destroy_cq: + ib_destroy_cq(ch->cq); + goto out; +} + +static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) +{ + if (ch->thread) + kthread_stop(ch->thread); + + ib_destroy_qp(ch->qp); + ib_destroy_cq(ch->cq); +} + +/** + * __srpt_close_ch() - Close an RDMA channel by setting the QP error state. + * + * Reset the QP and make sure all resources associated with the channel will + * be deallocated at an appropriate time. + * + * Note: The caller must hold ch->sport->sdev->spinlock. + */ +static void __srpt_close_ch(struct srpt_rdma_ch *ch) +{ + struct srpt_device *sdev; + enum rdma_ch_state prev_state; + unsigned long flags; + + sdev = ch->sport->sdev; + + spin_lock_irqsave(&ch->spinlock, flags); + prev_state = ch->state; + switch (prev_state) { + case CH_CONNECTING: + case CH_LIVE: + ch->state = CH_DISCONNECTING; + break; + default: + break; + } + spin_unlock_irqrestore(&ch->spinlock, flags); + + switch (prev_state) { + case CH_CONNECTING: + ib_send_cm_rej(ch->cm_id, IB_CM_REJ_NO_RESOURCES, NULL, 0, + NULL, 0); + /* fall through */ + case CH_LIVE: + if (ib_send_cm_dreq(ch->cm_id, NULL, 0) < 0) + printk(KERN_ERR "sending CM DREQ failed.\n"); + break; + case CH_DISCONNECTING: + break; + case CH_DRAINING: + case CH_RELEASING: + break; + } +} + +/** + * srpt_close_ch() - Close an RDMA channel. + */ +static void srpt_close_ch(struct srpt_rdma_ch *ch) +{ + struct srpt_device *sdev; + + sdev = ch->sport->sdev; + spin_lock_irq(&sdev->spinlock); + __srpt_close_ch(ch); + spin_unlock_irq(&sdev->spinlock); +} + +/** + * srpt_drain_channel() - Drain a channel by resetting the IB queue pair. + * @cm_id: Pointer to the CM ID of the channel to be drained. + * + * Note: Must be called from inside srpt_cm_handler to avoid a race between + * accessing sdev->spinlock and the call to kfree(sdev) in srpt_remove_one() + * (the caller of srpt_cm_handler holds the cm_id spinlock; srpt_remove_one() + * waits until all target sessions for the associated IB device have been + * unregistered and target session registration involves a call to + * ib_destroy_cm_id(), which locks the cm_id spinlock and hence waits until + * this function has finished). + */ +static void srpt_drain_channel(struct ib_cm_id *cm_id) +{ + struct srpt_device *sdev; + struct srpt_rdma_ch *ch; + int ret; + bool do_reset = false; + + WARN_ON_ONCE(irqs_disabled()); + + sdev = cm_id->context; + BUG_ON(!sdev); + spin_lock_irq(&sdev->spinlock); + list_for_each_entry(ch, &sdev->rch_list, list) { + if (ch->cm_id == cm_id) { + do_reset = srpt_test_and_set_ch_state(ch, + CH_CONNECTING, CH_DRAINING) || + srpt_test_and_set_ch_state(ch, + CH_LIVE, CH_DRAINING) || + srpt_test_and_set_ch_state(ch, + CH_DISCONNECTING, CH_DRAINING); + break; + } + } + spin_unlock_irq(&sdev->spinlock); + + if (do_reset) { + ret = srpt_ch_qp_err(ch); + if (ret < 0) + printk(KERN_ERR "Setting queue pair in error state" + " failed: %d\n", ret); + } +} + +/** + * srpt_find_channel() - Look up an RDMA channel. + * @cm_id: Pointer to the CM ID of the channel to be looked up. + * + * Return NULL if no matching RDMA channel has been found. + */ +static struct srpt_rdma_ch *srpt_find_channel(struct srpt_device *sdev, + struct ib_cm_id *cm_id) +{ + struct srpt_rdma_ch *ch; + bool found; + + WARN_ON_ONCE(irqs_disabled()); + BUG_ON(!sdev); + + found = false; + spin_lock_irq(&sdev->spinlock); + list_for_each_entry(ch, &sdev->rch_list, list) { + if (ch->cm_id == cm_id) { + found = true; + break; + } + } + spin_unlock_irq(&sdev->spinlock); + + return found ? ch : NULL; +} + +/** + * srpt_release_channel() - Release channel resources. + * + * Schedules the actual release because: + * - Calling the ib_destroy_cm_id() call from inside an IB CM callback would + * trigger a deadlock. + * - It is not safe to call TCM transport_* functions from interrupt context. + */ +static void srpt_release_channel(struct srpt_rdma_ch *ch) +{ + schedule_work(&ch->release_work); +} + +static void srpt_release_channel_work(struct work_struct *w) +{ + struct srpt_rdma_ch *ch; + struct srpt_device *sdev; + + ch = container_of(w, struct srpt_rdma_ch, release_work); + pr_debug("ch = %p; ch->sess = %p; release_done = %p\n", ch, ch->sess, + ch->release_done); + + sdev = ch->sport->sdev; + BUG_ON(!sdev); + + transport_deregister_session_configfs(ch->sess); + transport_deregister_session(ch->sess); + ch->sess = NULL; + + srpt_destroy_ch_ib(ch); + + srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, + ch->sport->sdev, ch->rq_size, + ch->rsp_size, DMA_TO_DEVICE); + + spin_lock_irq(&sdev->spinlock); + list_del(&ch->list); + spin_unlock_irq(&sdev->spinlock); + + ib_destroy_cm_id(ch->cm_id); + + if (ch->release_done) + complete(ch->release_done); + + wake_up(&sdev->ch_releaseQ); + + kfree(ch); +} + +static struct srpt_node_acl *__srpt_lookup_acl(struct srpt_port *sport, + u8 i_port_id[16]) +{ + struct srpt_node_acl *nacl; + + list_for_each_entry(nacl, &sport->port_acl_list, list) + if (memcmp(nacl->i_port_id, i_port_id, + sizeof(nacl->i_port_id)) == 0) + return nacl; + + return NULL; +} + +static struct srpt_node_acl *srpt_lookup_acl(struct srpt_port *sport, + u8 i_port_id[16]) +{ + struct srpt_node_acl *nacl; + + spin_lock_irq(&sport->port_acl_lock); + nacl = __srpt_lookup_acl(sport, i_port_id); + spin_unlock_irq(&sport->port_acl_lock); + + return nacl; +} + +/** + * srpt_cm_req_recv() - Process the event IB_CM_REQ_RECEIVED. + * + * Ownership of the cm_id is transferred to the target session if this + * functions returns zero. Otherwise the caller remains the owner of cm_id. + */ +static int srpt_cm_req_recv(struct ib_cm_id *cm_id, + struct ib_cm_req_event_param *param, + void *private_data) +{ + struct srpt_device *sdev = cm_id->context; + struct srpt_port *sport = &sdev->port[param->port - 1]; + struct srp_login_req *req; + struct srp_login_rsp *rsp; + struct srp_login_rej *rej; + struct ib_cm_rep_param *rep_param; + struct srpt_rdma_ch *ch, *tmp_ch; + struct srpt_node_acl *nacl; + u32 it_iu_len; + int i; + int ret = 0; + + WARN_ON_ONCE(irqs_disabled()); + + if (WARN_ON(!sdev || !private_data)) + return -EINVAL; + + req = (struct srp_login_req *)private_data; + + it_iu_len = be32_to_cpu(req->req_it_iu_len); + + printk(KERN_INFO "Received SRP_LOGIN_REQ with i_port_id 0x%llx:0x%llx," + " t_port_id 0x%llx:0x%llx and it_iu_len %d on port %d" + " (guid=0x%llx:0x%llx)\n", + be64_to_cpu(*(__be64 *)&req->initiator_port_id[0]), + be64_to_cpu(*(__be64 *)&req->initiator_port_id[8]), + be64_to_cpu(*(__be64 *)&req->target_port_id[0]), + be64_to_cpu(*(__be64 *)&req->target_port_id[8]), + it_iu_len, + param->port, + be64_to_cpu(*(__be64 *)&sdev->port[param->port - 1].gid.raw[0]), + be64_to_cpu(*(__be64 *)&sdev->port[param->port - 1].gid.raw[8])); + + rsp = kzalloc(sizeof *rsp, GFP_KERNEL); + rej = kzalloc(sizeof *rej, GFP_KERNEL); + rep_param = kzalloc(sizeof *rep_param, GFP_KERNEL); + + if (!rsp || !rej || !rep_param) { + ret = -ENOMEM; + goto out; + } + + if (it_iu_len > srp_max_req_size || it_iu_len < 64) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE); + ret = -EINVAL; + printk(KERN_ERR "rejected SRP_LOGIN_REQ because its" + " length (%d bytes) is out of range (%d .. %d)\n", + it_iu_len, 64, srp_max_req_size); + goto reject; + } + + if (!sport->enabled) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); + ret = -EINVAL; + printk(KERN_ERR "rejected SRP_LOGIN_REQ because the target port" + " has not yet been enabled\n"); + goto reject; + } + + if ((req->req_flags & SRP_MTCH_ACTION) == SRP_MULTICHAN_SINGLE) { + rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_NO_CHAN; + + spin_lock_irq(&sdev->spinlock); + + list_for_each_entry_safe(ch, tmp_ch, &sdev->rch_list, list) { + if (!memcmp(ch->i_port_id, req->initiator_port_id, 16) + && !memcmp(ch->t_port_id, req->target_port_id, 16) + && param->port == ch->sport->port + && param->listen_id == ch->sport->sdev->cm_id + && ch->cm_id) { + enum rdma_ch_state ch_state; + + ch_state = srpt_get_ch_state(ch); + if (ch_state != CH_CONNECTING + && ch_state != CH_LIVE) + continue; + + /* found an existing channel */ + pr_debug("Found existing channel %s" + " cm_id= %p state= %d\n", + ch->sess_name, ch->cm_id, ch_state); + + __srpt_close_ch(ch); + + rsp->rsp_flags = + SRP_LOGIN_RSP_MULTICHAN_TERMINATED; + } + } + + spin_unlock_irq(&sdev->spinlock); + + } else + rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_MAINTAINED; + + if (*(__be64 *)req->target_port_id != cpu_to_be64(srpt_service_guid) + || *(__be64 *)(req->target_port_id + 8) != + cpu_to_be64(srpt_service_guid)) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL); + ret = -ENOMEM; + printk(KERN_ERR "rejected SRP_LOGIN_REQ because it" + " has an invalid target port identifier.\n"); + goto reject; + } + + ch = kzalloc(sizeof *ch, GFP_KERNEL); + if (!ch) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); + printk(KERN_ERR "rejected SRP_LOGIN_REQ because no memory.\n"); + ret = -ENOMEM; + goto reject; + } + + INIT_WORK(&ch->release_work, srpt_release_channel_work); + memcpy(ch->i_port_id, req->initiator_port_id, 16); + memcpy(ch->t_port_id, req->target_port_id, 16); + ch->sport = &sdev->port[param->port - 1]; + ch->cm_id = cm_id; + /* + * Avoid QUEUE_FULL conditions by limiting the number of buffers used + * for the SRP protocol to the command queue size. + */ + ch->rq_size = SRPT_RQ_SIZE; + spin_lock_init(&ch->spinlock); + ch->state = CH_CONNECTING; + INIT_LIST_HEAD(&ch->cmd_wait_list); + ch->rsp_size = ch->sport->port_attrib.srp_max_rsp_size; + + ch->ioctx_ring = (struct srpt_send_ioctx **) + srpt_alloc_ioctx_ring(ch->sport->sdev, ch->rq_size, + sizeof(*ch->ioctx_ring[0]), + ch->rsp_size, DMA_TO_DEVICE); + if (!ch->ioctx_ring) + goto free_ch; + + INIT_LIST_HEAD(&ch->free_list); + for (i = 0; i < ch->rq_size; i++) { + ch->ioctx_ring[i]->ch = ch; + list_add_tail(&ch->ioctx_ring[i]->free_list, &ch->free_list); + } + + ret = srpt_create_ch_ib(ch); + if (ret) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); + printk(KERN_ERR "rejected SRP_LOGIN_REQ because creating" + " a new RDMA channel failed.\n"); + goto free_ring; + } + + ret = srpt_ch_qp_rtr(ch, ch->qp); + if (ret) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); + printk(KERN_ERR "rejected SRP_LOGIN_REQ because enabling" + " RTR failed (error code = %d)\n", ret); + goto destroy_ib; + } + /* + * Use the initator port identifier as the session name. + */ + snprintf(ch->sess_name, sizeof(ch->sess_name), "0x%016llx%016llx", + be64_to_cpu(*(__be64 *)ch->i_port_id), + be64_to_cpu(*(__be64 *)(ch->i_port_id + 8))); + + pr_debug("registering session %s\n", ch->sess_name); + + nacl = srpt_lookup_acl(sport, ch->i_port_id); + if (!nacl) { + printk(KERN_INFO "Rejected login because no ACL has been" + " configured yet for initiator %s.\n", ch->sess_name); + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED); + goto destroy_ib; + } + + ch->sess = transport_init_session(); + if (!ch->sess) { + rej->reason = __constant_cpu_to_be32( + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); + pr_debug("Failed to create session\n"); + goto deregister_session; + } + ch->sess->se_node_acl = &nacl->nacl; + transport_register_session(&sport->port_tpg_1, &nacl->nacl, ch->sess, ch); + + pr_debug("Establish connection sess=%p name=%s cm_id=%p\n", ch->sess, + ch->sess_name, ch->cm_id); + + /* create srp_login_response */ + rsp->opcode = SRP_LOGIN_RSP; + rsp->tag = req->tag; + rsp->max_it_iu_len = req->req_it_iu_len; + rsp->max_ti_iu_len = req->req_it_iu_len; + ch->max_ti_iu_len = it_iu_len; + rsp->buf_fmt = __constant_cpu_to_be16(SRP_BUF_FORMAT_DIRECT + | SRP_BUF_FORMAT_INDIRECT); + rsp->req_lim_delta = cpu_to_be32(ch->rq_size); + atomic_set(&ch->req_lim, ch->rq_size); + atomic_set(&ch->req_lim_delta, 0); + + /* create cm reply */ + rep_param->qp_num = ch->qp->qp_num; + rep_param->private_data = (void *)rsp; + rep_param->private_data_len = sizeof *rsp; + rep_param->rnr_retry_count = 7; + rep_param->flow_control = 1; + rep_param->failover_accepted = 0; + rep_param->srq = 1; + rep_param->responder_resources = 4; + rep_param->initiator_depth = 4; + + ret = ib_send_cm_rep(cm_id, rep_param); + if (ret) { + printk(KERN_ERR "sending SRP_LOGIN_REQ response failed" + " (error code = %d)\n", ret); + goto release_channel; + } + + spin_lock_irq(&sdev->spinlock); + list_add_tail(&ch->list, &sdev->rch_list); + spin_unlock_irq(&sdev->spinlock); + + goto out; + +release_channel: + srpt_set_ch_state(ch, CH_RELEASING); + transport_deregister_session_configfs(ch->sess); + +deregister_session: + transport_deregister_session(ch->sess); + ch->sess = NULL; + +destroy_ib: + srpt_destroy_ch_ib(ch); + +free_ring: + srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, + ch->sport->sdev, ch->rq_size, + ch->rsp_size, DMA_TO_DEVICE); +free_ch: + kfree(ch); + +reject: + rej->opcode = SRP_LOGIN_REJ; + rej->tag = req->tag; + rej->buf_fmt = __constant_cpu_to_be16(SRP_BUF_FORMAT_DIRECT + | SRP_BUF_FORMAT_INDIRECT); + + ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0, + (void *)rej, sizeof *rej); + +out: + kfree(rep_param); + kfree(rsp); + kfree(rej); + + return ret; +} + +static void srpt_cm_rej_recv(struct ib_cm_id *cm_id) +{ + printk(KERN_INFO "Received IB REJ for cm_id %p.\n", cm_id); + srpt_drain_channel(cm_id); +} + +/** + * srpt_cm_rtu_recv() - Process an IB_CM_RTU_RECEIVED or USER_ESTABLISHED event. + * + * An IB_CM_RTU_RECEIVED message indicates that the connection is established + * and that the recipient may begin transmitting (RTU = ready to use). + */ +static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id) +{ + struct srpt_rdma_ch *ch; + int ret; + + ch = srpt_find_channel(cm_id->context, cm_id); + BUG_ON(!ch); + + if (srpt_test_and_set_ch_state(ch, CH_CONNECTING, CH_LIVE)) { + struct srpt_recv_ioctx *ioctx, *ioctx_tmp; + + ret = srpt_ch_qp_rts(ch, ch->qp); + + list_for_each_entry_safe(ioctx, ioctx_tmp, &ch->cmd_wait_list, + wait_list) { + list_del(&ioctx->wait_list); + srpt_handle_new_iu(ch, ioctx, NULL); + } + if (ret) + srpt_close_ch(ch); + } +} + +static void srpt_cm_timewait_exit(struct ib_cm_id *cm_id) +{ + printk(KERN_INFO "Received IB TimeWait exit for cm_id %p.\n", cm_id); + srpt_drain_channel(cm_id); +} + +static void srpt_cm_rep_error(struct ib_cm_id *cm_id) +{ + printk(KERN_INFO "Received IB REP error for cm_id %p.\n", cm_id); + srpt_drain_channel(cm_id); +} + +/** + * srpt_cm_dreq_recv() - Process reception of a DREQ message. + */ +static void srpt_cm_dreq_recv(struct ib_cm_id *cm_id) +{ + struct srpt_rdma_ch *ch; + unsigned long flags; + bool send_drep = false; + + ch = srpt_find_channel(cm_id->context, cm_id); + BUG_ON(!ch); + + pr_debug("cm_id= %p ch->state= %d\n", cm_id, srpt_get_ch_state(ch)); + + spin_lock_irqsave(&ch->spinlock, flags); + switch (ch->state) { + case CH_CONNECTING: + case CH_LIVE: + send_drep = true; + ch->state = CH_DISCONNECTING; + break; + case CH_DISCONNECTING: + case CH_DRAINING: + case CH_RELEASING: + WARN(true, "unexpected channel state %d\n", ch->state); + break; + } + spin_unlock_irqrestore(&ch->spinlock, flags); + + if (send_drep) { + if (ib_send_cm_drep(ch->cm_id, NULL, 0) < 0) + printk(KERN_ERR "Sending IB DREP failed.\n"); + printk(KERN_INFO "Received DREQ and sent DREP for session %s.\n", + ch->sess_name); + } +} + +/** + * srpt_cm_drep_recv() - Process reception of a DREP message. + */ +static void srpt_cm_drep_recv(struct ib_cm_id *cm_id) +{ + printk(KERN_INFO "Received InfiniBand DREP message for cm_id %p.\n", + cm_id); + srpt_drain_channel(cm_id); +} + +/** + * srpt_cm_handler() - IB connection manager callback function. + * + * A non-zero return value will cause the caller destroy the CM ID. + * + * Note: srpt_cm_handler() must only return a non-zero value when transferring + * ownership of the cm_id to a channel by srpt_cm_req_recv() failed. Returning + * a non-zero value in any other case will trigger a race with the + * ib_destroy_cm_id() call in srpt_release_channel(). + */ +static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) +{ + int ret; + + ret = 0; + switch (event->event) { + case IB_CM_REQ_RECEIVED: + ret = srpt_cm_req_recv(cm_id, &event->param.req_rcvd, + event->private_data); + break; + case IB_CM_REJ_RECEIVED: + srpt_cm_rej_recv(cm_id); + break; + case IB_CM_RTU_RECEIVED: + case IB_CM_USER_ESTABLISHED: + srpt_cm_rtu_recv(cm_id); + break; + case IB_CM_DREQ_RECEIVED: + srpt_cm_dreq_recv(cm_id); + break; + case IB_CM_DREP_RECEIVED: + srpt_cm_drep_recv(cm_id); + break; + case IB_CM_TIMEWAIT_EXIT: + srpt_cm_timewait_exit(cm_id); + break; + case IB_CM_REP_ERROR: + srpt_cm_rep_error(cm_id); + break; + case IB_CM_DREQ_ERROR: + printk(KERN_INFO "Received IB DREQ ERROR event.\n"); + break; + case IB_CM_MRA_RECEIVED: + printk(KERN_INFO "Received IB MRA event\n"); + break; + default: + printk(KERN_ERR "received unrecognized IB CM event %d\n", + event->event); + break; + } + + return ret; +} + +/** + * srpt_perform_rdmas() - Perform IB RDMA. + * + * Returns zero upon success or a negative number upon failure. + */ +static int srpt_perform_rdmas(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx) +{ + struct ib_send_wr wr; + struct ib_send_wr *bad_wr; + struct rdma_iu *riu; + int i; + int ret; + int sq_wr_avail; + enum dma_data_direction dir; + const int n_rdma = ioctx->n_rdma; + + dir = ioctx->cmd.data_direction; + if (dir == DMA_TO_DEVICE) { + /* write */ + ret = -ENOMEM; + sq_wr_avail = atomic_sub_return(n_rdma, &ch->sq_wr_avail); + if (sq_wr_avail < 0) { + printk(KERN_WARNING "IB send queue full (needed %d)\n", + n_rdma); + goto out; + } + } + + ioctx->rdma_aborted = false; + ret = 0; + riu = ioctx->rdma_ius; + memset(&wr, 0, sizeof wr); + + for (i = 0; i < n_rdma; ++i, ++riu) { + if (dir == DMA_FROM_DEVICE) { + wr.opcode = IB_WR_RDMA_WRITE; + wr.wr_id = encode_wr_id(i == n_rdma - 1 ? + SRPT_RDMA_WRITE_LAST : + SRPT_RDMA_MID, + ioctx->ioctx.index); + } else { + wr.opcode = IB_WR_RDMA_READ; + wr.wr_id = encode_wr_id(i == n_rdma - 1 ? + SRPT_RDMA_READ_LAST : + SRPT_RDMA_MID, + ioctx->ioctx.index); + } + wr.next = NULL; + wr.wr.rdma.remote_addr = riu->raddr; + wr.wr.rdma.rkey = riu->rkey; + wr.num_sge = riu->sge_cnt; + wr.sg_list = riu->sge; + + /* only get completion event for the last rdma write */ + if (i == (n_rdma - 1) && dir == DMA_TO_DEVICE) + wr.send_flags = IB_SEND_SIGNALED; + + ret = ib_post_send(ch->qp, &wr, &bad_wr); + if (ret) + break; + } + + if (ret) + printk(KERN_ERR "%s[%d]: ib_post_send() returned %d for %d/%d", + __func__, __LINE__, ret, i, n_rdma); + if (ret && i > 0) { + wr.num_sge = 0; + wr.wr_id = encode_wr_id(SRPT_RDMA_ABORT, ioctx->ioctx.index); + wr.send_flags = IB_SEND_SIGNALED; + while (ch->state == CH_LIVE && + ib_post_send(ch->qp, &wr, &bad_wr) != 0) { + printk(KERN_INFO "Trying to abort failed RDMA transfer [%d]", + ioctx->ioctx.index); + msleep(1000); + } + while (ch->state != CH_RELEASING && !ioctx->rdma_aborted) { + printk(KERN_INFO "Waiting until RDMA abort finished [%d]", + ioctx->ioctx.index); + msleep(1000); + } + } +out: + if (unlikely(dir == DMA_TO_DEVICE && ret < 0)) + atomic_add(n_rdma, &ch->sq_wr_avail); + return ret; +} + +/** + * srpt_xfer_data() - Start data transfer from initiator to target. + */ +static int srpt_xfer_data(struct srpt_rdma_ch *ch, + struct srpt_send_ioctx *ioctx) +{ + int ret; + + ret = srpt_map_sg_to_ib_sge(ch, ioctx); + if (ret) { + printk(KERN_ERR "%s[%d] ret=%d\n", __func__, __LINE__, ret); + goto out; + } + + ret = srpt_perform_rdmas(ch, ioctx); + if (ret) { + if (ret == -EAGAIN || ret == -ENOMEM) + printk(KERN_INFO "%s[%d] queue full -- ret=%d\n", + __func__, __LINE__, ret); + else + printk(KERN_ERR "%s[%d] fatal error -- ret=%d\n", + __func__, __LINE__, ret); + goto out_unmap; + } + +out: + return ret; +out_unmap: + srpt_unmap_sg_to_ib_sge(ch, ioctx); + goto out; +} + +static int srpt_write_pending_status(struct se_cmd *se_cmd) +{ + struct srpt_send_ioctx *ioctx; + + ioctx = container_of(se_cmd, struct srpt_send_ioctx, cmd); + return srpt_get_cmd_state(ioctx) == SRPT_STATE_NEED_DATA; +} + +/* + * srpt_write_pending() - Start data transfer from initiator to target (write). + */ +static int srpt_write_pending(struct se_cmd *se_cmd) +{ + struct srpt_rdma_ch *ch; + struct srpt_send_ioctx *ioctx; + enum srpt_command_state new_state; + enum rdma_ch_state ch_state; + int ret; + + ioctx = container_of(se_cmd, struct srpt_send_ioctx, cmd); + + new_state = srpt_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA); + WARN_ON(new_state == SRPT_STATE_DONE); + + ch = ioctx->ch; + BUG_ON(!ch); + + ch_state = srpt_get_ch_state(ch); + switch (ch_state) { + case CH_CONNECTING: + WARN(true, "unexpected channel state %d\n", ch_state); + ret = -EINVAL; + goto out; + case CH_LIVE: + break; + case CH_DISCONNECTING: + case CH_DRAINING: + case CH_RELEASING: + pr_debug("cmd with tag %lld: channel disconnecting\n", + ioctx->tag); + srpt_set_cmd_state(ioctx, SRPT_STATE_DATA_IN); + ret = -EINVAL; + goto out; + } + ret = srpt_xfer_data(ch, ioctx); + +out: + return ret; +} + +static u8 tcm_to_srp_tsk_mgmt_status(const int tcm_mgmt_status) +{ + switch (tcm_mgmt_status) { + case TMR_FUNCTION_COMPLETE: + return SRP_TSK_MGMT_SUCCESS; + case TMR_FUNCTION_REJECTED: + return SRP_TSK_MGMT_FUNC_NOT_SUPP; + } + return SRP_TSK_MGMT_FAILED; +} + +/** + * srpt_queue_response() - Transmits the response to a SCSI command. + * + * Callback function called by the TCM core. Must not block since it can be + * invoked on the context of the IB completion handler. + */ +static int srpt_queue_response(struct se_cmd *cmd) +{ + struct srpt_rdma_ch *ch; + struct srpt_send_ioctx *ioctx; + enum srpt_command_state state; + unsigned long flags; + int ret; + enum dma_data_direction dir; + int resp_len; + u8 srp_tm_status; + + ret = 0; + + ioctx = container_of(cmd, struct srpt_send_ioctx, cmd); + ch = ioctx->ch; + BUG_ON(!ch); + + spin_lock_irqsave(&ioctx->spinlock, flags); + state = ioctx->state; + switch (state) { + case SRPT_STATE_NEW: + case SRPT_STATE_DATA_IN: + ioctx->state = SRPT_STATE_CMD_RSP_SENT; + break; + case SRPT_STATE_MGMT: + ioctx->state = SRPT_STATE_MGMT_RSP_SENT; + break; + default: + WARN(true, "ch %p; cmd %d: unexpected command state %d\n", + ch, ioctx->ioctx.index, ioctx->state); + break; + } + spin_unlock_irqrestore(&ioctx->spinlock, flags); + + if (unlikely(transport_check_aborted_status(&ioctx->cmd, false) + || WARN_ON_ONCE(state == SRPT_STATE_CMD_RSP_SENT))) { + atomic_inc(&ch->req_lim_delta); + srpt_abort_cmd(ioctx); + goto out; + } + + dir = ioctx->cmd.data_direction; + + /* For read commands, transfer the data to the initiator. */ + if (dir == DMA_FROM_DEVICE && ioctx->cmd.data_length && + !ioctx->queue_status_only) { + ret = srpt_xfer_data(ch, ioctx); + if (ret) { + printk(KERN_ERR "xfer_data failed for tag %llu\n", + ioctx->tag); + goto out; + } + } + + if (state != SRPT_STATE_MGMT) + resp_len = srpt_build_cmd_rsp(ch, ioctx, ioctx->tag, + cmd->scsi_status); + else { + srp_tm_status + = tcm_to_srp_tsk_mgmt_status(cmd->se_tmr_req->response); + resp_len = srpt_build_tskmgmt_rsp(ch, ioctx, srp_tm_status, + ioctx->tag); + } + ret = srpt_post_send(ch, ioctx, resp_len); + if (ret) { + printk(KERN_ERR "sending cmd response failed for tag %llu\n", + ioctx->tag); + srpt_unmap_sg_to_ib_sge(ch, ioctx); + srpt_set_cmd_state(ioctx, SRPT_STATE_DONE); + kref_put(&ioctx->kref, srpt_put_send_ioctx_kref); + } + +out: + return ret; +} + +static int srpt_queue_status(struct se_cmd *cmd) +{ + struct srpt_send_ioctx *ioctx; + + ioctx = container_of(cmd, struct srpt_send_ioctx, cmd); + BUG_ON(ioctx->sense_data != cmd->sense_buffer); + if (cmd->se_cmd_flags & + (SCF_TRANSPORT_TASK_SENSE | SCF_EMULATED_TASK_SENSE)) + WARN_ON(cmd->scsi_status != SAM_STAT_CHECK_CONDITION); + ioctx->queue_status_only = true; + return srpt_queue_response(cmd); +} + +static void srpt_refresh_port_work(struct work_struct *work) +{ + struct srpt_port *sport = container_of(work, struct srpt_port, work); + + srpt_refresh_port(sport); +} + +static int srpt_ch_list_empty(struct srpt_device *sdev) +{ + int res; + + spin_lock_irq(&sdev->spinlock); + res = list_empty(&sdev->rch_list); + spin_unlock_irq(&sdev->spinlock); + + return res; +} + +/** + * srpt_release_sdev() - Free the channel resources associated with a target. + */ +static int srpt_release_sdev(struct srpt_device *sdev) +{ + struct srpt_rdma_ch *ch, *tmp_ch; + int res; + + WARN_ON_ONCE(irqs_disabled()); + + BUG_ON(!sdev); + + spin_lock_irq(&sdev->spinlock); + list_for_each_entry_safe(ch, tmp_ch, &sdev->rch_list, list) + __srpt_close_ch(ch); + spin_unlock_irq(&sdev->spinlock); + + res = wait_event_interruptible(sdev->ch_releaseQ, + srpt_ch_list_empty(sdev)); + if (res) + printk(KERN_ERR "%s: interrupted.\n", __func__); + + return 0; +} + +static struct srpt_port *__srpt_lookup_port(const char *name) +{ + struct ib_device *dev; + struct srpt_device *sdev; + struct srpt_port *sport; + int i; + + list_for_each_entry(sdev, &srpt_dev_list, list) { + dev = sdev->device; + if (!dev) + continue; + + for (i = 0; i < dev->phys_port_cnt; i++) { + sport = &sdev->port[i]; + + if (!strcmp(sport->port_guid, name)) + return sport; + } + } + + return NULL; +} + +static struct srpt_port *srpt_lookup_port(const char *name) +{ + struct srpt_port *sport; + + spin_lock(&srpt_dev_lock); + sport = __srpt_lookup_port(name); + spin_unlock(&srpt_dev_lock); + + return sport; +} + +/** + * srpt_add_one() - Infiniband device addition callback function. + */ +static void srpt_add_one(struct ib_device *device) +{ + struct srpt_device *sdev; + struct srpt_port *sport; + struct ib_srq_init_attr srq_attr; + int i; + + pr_debug("device = %p, device->dma_ops = %p\n", device, + device->dma_ops); + + sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + if (!sdev) + goto err; + + sdev->device = device; + INIT_LIST_HEAD(&sdev->rch_list); + init_waitqueue_head(&sdev->ch_releaseQ); + spin_lock_init(&sdev->spinlock); + + if (ib_query_device(device, &sdev->dev_attr)) + goto free_dev; + + sdev->pd = ib_alloc_pd(device); + if (IS_ERR(sdev->pd)) + goto free_dev; + + sdev->mr = ib_get_dma_mr(sdev->pd, IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(sdev->mr)) + goto err_pd; + + sdev->srq_size = min(srpt_srq_size, sdev->dev_attr.max_srq_wr); + + srq_attr.event_handler = srpt_srq_event; + srq_attr.srq_context = (void *)sdev; + srq_attr.attr.max_wr = sdev->srq_size; + srq_attr.attr.max_sge = 1; + srq_attr.attr.srq_limit = 0; + + sdev->srq = ib_create_srq(sdev->pd, &srq_attr); + if (IS_ERR(sdev->srq)) + goto err_mr; + + pr_debug("%s: create SRQ #wr= %d max_allow=%d dev= %s\n", + __func__, sdev->srq_size, sdev->dev_attr.max_srq_wr, + device->name); + + if (!srpt_service_guid) + srpt_service_guid = be64_to_cpu(device->node_guid); + + sdev->cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev); + if (IS_ERR(sdev->cm_id)) + goto err_srq; + + /* print out target login information */ + pr_debug("Target login info: id_ext=%016llx,ioc_guid=%016llx," + "pkey=ffff,service_id=%016llx\n", srpt_service_guid, + srpt_service_guid, srpt_service_guid); + + /* + * We do not have a consistent service_id (ie. also id_ext of target_id) + * to identify this target. We currently use the guid of the first HCA + * in the system as service_id; therefore, the target_id will change + * if this HCA is gone bad and replaced by different HCA + */ + if (ib_cm_listen(sdev->cm_id, cpu_to_be64(srpt_service_guid), 0, NULL)) + goto err_cm; + + INIT_IB_EVENT_HANDLER(&sdev->event_handler, sdev->device, + srpt_event_handler); + if (ib_register_event_handler(&sdev->event_handler)) + goto err_cm; + + sdev->ioctx_ring = (struct srpt_recv_ioctx **) + srpt_alloc_ioctx_ring(sdev, sdev->srq_size, + sizeof(*sdev->ioctx_ring[0]), + srp_max_req_size, DMA_FROM_DEVICE); + if (!sdev->ioctx_ring) + goto err_event; + + for (i = 0; i < sdev->srq_size; ++i) + srpt_post_recv(sdev, sdev->ioctx_ring[i]); + + WARN_ON(sdev->device->phys_port_cnt + > sizeof(sdev->port)/sizeof(sdev->port[0])); + + for (i = 1; i <= sdev->device->phys_port_cnt; i++) { + sport = &sdev->port[i - 1]; + sport->sdev = sdev; + sport->port = i; + sport->port_attrib.srp_max_rdma_size = DEFAULT_MAX_RDMA_SIZE; + sport->port_attrib.srp_max_rsp_size = DEFAULT_MAX_RSP_SIZE; + sport->port_attrib.srp_sq_size = DEF_SRPT_SQ_SIZE; + INIT_WORK(&sport->work, srpt_refresh_port_work); + INIT_LIST_HEAD(&sport->port_acl_list); + spin_lock_init(&sport->port_acl_lock); + + if (srpt_refresh_port(sport)) { + printk(KERN_ERR "MAD registration failed for %s-%d.\n", + srpt_sdev_name(sdev), i); + goto err_ring; + } + snprintf(sport->port_guid, sizeof(sport->port_guid), + "0x%016llx%016llx", + be64_to_cpu(sport->gid.global.subnet_prefix), + be64_to_cpu(sport->gid.global.interface_id)); + } + + spin_lock(&srpt_dev_lock); + list_add_tail(&sdev->list, &srpt_dev_list); + spin_unlock(&srpt_dev_lock); + +out: + ib_set_client_data(device, &srpt_client, sdev); + pr_debug("added %s.\n", device->name); + return; + +err_ring: + srpt_free_ioctx_ring((struct srpt_ioctx **)sdev->ioctx_ring, sdev, + sdev->srq_size, srp_max_req_size, + DMA_FROM_DEVICE); +err_event: + ib_unregister_event_handler(&sdev->event_handler); +err_cm: + ib_destroy_cm_id(sdev->cm_id); +err_srq: + ib_destroy_srq(sdev->srq); +err_mr: + ib_dereg_mr(sdev->mr); +err_pd: + ib_dealloc_pd(sdev->pd); +free_dev: + kfree(sdev); +err: + sdev = NULL; + printk(KERN_INFO "%s(%s) failed.\n", __func__, device->name); + goto out; +} + +/** + * srpt_remove_one() - InfiniBand device removal callback function. + */ +static void srpt_remove_one(struct ib_device *device) +{ + struct srpt_device *sdev; + int i; + + sdev = ib_get_client_data(device, &srpt_client); + if (!sdev) { + printk(KERN_INFO "%s(%s): nothing to do.\n", __func__, + device->name); + return; + } + + srpt_unregister_mad_agent(sdev); + + ib_unregister_event_handler(&sdev->event_handler); + + /* Cancel any work queued by the just unregistered IB event handler. */ + for (i = 0; i < sdev->device->phys_port_cnt; i++) + cancel_work_sync(&sdev->port[i].work); + + ib_destroy_cm_id(sdev->cm_id); + + /* + * Unregistering a target must happen after destroying sdev->cm_id + * such that no new SRP_LOGIN_REQ information units can arrive while + * destroying the target. + */ + spin_lock(&srpt_dev_lock); + list_del(&sdev->list); + spin_unlock(&srpt_dev_lock); + srpt_release_sdev(sdev); + + ib_destroy_srq(sdev->srq); + ib_dereg_mr(sdev->mr); + ib_dealloc_pd(sdev->pd); + + srpt_free_ioctx_ring((struct srpt_ioctx **)sdev->ioctx_ring, sdev, + sdev->srq_size, srp_max_req_size, DMA_FROM_DEVICE); + sdev->ioctx_ring = NULL; + kfree(sdev); +} + +static struct ib_client srpt_client = { + .name = DRV_NAME, + .add = srpt_add_one, + .remove = srpt_remove_one +}; + +static int srpt_check_true(struct se_portal_group *se_tpg) +{ + return 1; +} + +static int srpt_check_false(struct se_portal_group *se_tpg) +{ + return 0; +} + +static char *srpt_get_fabric_name(void) +{ + return "srpt"; +} + +static u8 srpt_get_fabric_proto_ident(struct se_portal_group *se_tpg) +{ + return SCSI_TRANSPORTID_PROTOCOLID_SRP; +} + +static char *srpt_get_fabric_wwn(struct se_portal_group *tpg) +{ + struct srpt_port *sport = container_of(tpg, struct srpt_port, port_tpg_1); + + return sport->port_guid; +} + +static u16 srpt_get_tag(struct se_portal_group *tpg) +{ + return 1; +} + +static u32 srpt_get_default_depth(struct se_portal_group *se_tpg) +{ + return 1; +} + +static u32 srpt_get_pr_transport_id(struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code, unsigned char *buf) +{ + struct srpt_node_acl *nacl; + struct spc_rdma_transport_id *tr_id; + + nacl = container_of(se_nacl, struct srpt_node_acl, nacl); + tr_id = (void *)buf; + tr_id->protocol_identifier = SCSI_TRANSPORTID_PROTOCOLID_SRP; + memcpy(tr_id->i_port_id, nacl->i_port_id, sizeof(tr_id->i_port_id)); + return sizeof(*tr_id); +} + +static u32 srpt_get_pr_transport_id_len(struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code) +{ + *format_code = 0; + return sizeof(struct spc_rdma_transport_id); +} + +static char *srpt_parse_pr_out_transport_id(struct se_portal_group *se_tpg, + const char *buf, u32 *out_tid_len, + char **port_nexus_ptr) +{ + struct spc_rdma_transport_id *tr_id; + + *port_nexus_ptr = NULL; + *out_tid_len = sizeof(struct spc_rdma_transport_id); + tr_id = (void *)buf; + return (char *)tr_id->i_port_id; +} + +static struct se_node_acl *srpt_alloc_fabric_acl(struct se_portal_group *se_tpg) +{ + struct srpt_node_acl *nacl; + + nacl = kzalloc(sizeof(struct srpt_node_acl), GFP_KERNEL); + if (!nacl) { + printk(KERN_ERR "Unable to alocate struct srpt_node_acl\n"); + return NULL; + } + + return &nacl->nacl; +} + +static void srpt_release_fabric_acl(struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl) +{ + struct srpt_node_acl *nacl; + + nacl = container_of(se_nacl, struct srpt_node_acl, nacl); + kfree(nacl); +} + +static u32 srpt_tpg_get_inst_index(struct se_portal_group *se_tpg) +{ + return 1; +} + +static void srpt_release_cmd(struct se_cmd *se_cmd) +{ +} + +/** + * srpt_shutdown_session() - Whether or not a session may be shut down. + */ +static int srpt_shutdown_session(struct se_session *se_sess) +{ + return true; +} + +/** + * srpt_close_session() - Forcibly close a session. + * + * Callback function invoked by the TCM core to clean up sessions associated + * with a node ACL when the user invokes + * rmdir /sys/kernel/config/target/$driver/$port/$tpg/acls/$i_port_id + */ +static void srpt_close_session(struct se_session *se_sess) +{ + DECLARE_COMPLETION_ONSTACK(release_done); + struct srpt_rdma_ch *ch; + struct srpt_device *sdev; + int res; + + ch = se_sess->fabric_sess_ptr; + WARN_ON(ch->sess != se_sess); + + pr_debug("ch %p state %d\n", ch, srpt_get_ch_state(ch)); + + sdev = ch->sport->sdev; + spin_lock_irq(&sdev->spinlock); + BUG_ON(ch->release_done); + ch->release_done = &release_done; + __srpt_close_ch(ch); + spin_unlock_irq(&sdev->spinlock); + + res = wait_for_completion_timeout(&release_done, 60 * HZ); + WARN_ON(res <= 0); +} + +/** + * To do: Find out whether stop_session() has a meaning for transports + * other than iSCSI. + */ +static void srpt_stop_session(struct se_session *se_sess, int sess_sleep, + int conn_sleep) +{ +} + +static void srpt_reset_nexus(struct se_session *sess) +{ + printk(KERN_ERR "This is the SRP protocol, not iSCSI\n"); +} + +static int srpt_sess_logged_in(struct se_session *se_sess) +{ + return true; +} + +/** + * srpt_sess_get_index() - Return the value of scsiAttIntrPortIndex (SCSI-MIB). + * + * A quote from RFC 4455 (SCSI-MIB) about this MIB object: + * This object represents an arbitrary integer used to uniquely identify a + * particular attached remote initiator port to a particular SCSI target port + * within a particular SCSI target device within a particular SCSI instance. + */ +static u32 srpt_sess_get_index(struct se_session *se_sess) +{ + return 0; +} + +static void srpt_set_default_node_attrs(struct se_node_acl *nacl) +{ +} + +static u32 srpt_get_task_tag(struct se_cmd *se_cmd) +{ + struct srpt_send_ioctx *ioctx; + + ioctx = container_of(se_cmd, struct srpt_send_ioctx, cmd); + return ioctx->tag; +} + +/* Note: only used from inside debug printk's by the TCM core. */ +static int srpt_get_tcm_cmd_state(struct se_cmd *se_cmd) +{ + struct srpt_send_ioctx *ioctx; + + ioctx = container_of(se_cmd, struct srpt_send_ioctx, cmd); + return srpt_get_cmd_state(ioctx); +} + +static u16 srpt_set_fabric_sense_len(struct se_cmd *cmd, u32 sense_length) +{ + return 0; +} + +static u16 srpt_get_fabric_sense_len(void) +{ + return 0; +} + +static int srpt_is_state_remove(struct se_cmd *se_cmd) +{ + return 0; +} + +/** + * srpt_parse_i_port_id() - Parse an initiator port ID. + * @name: ASCII representation of a 128-bit initiator port ID. + * @i_port_id: Binary 128-bit port ID. + */ +static int srpt_parse_i_port_id(u8 i_port_id[16], const char *name) +{ + const char *p; + unsigned len, count, leading_zero_bytes; + int ret, rc; + + p = name; + if (strnicmp(p, "0x", 2) == 0) + p += 2; + ret = -EINVAL; + len = strlen(p); + if (len % 2) + goto out; + count = min(len / 2, 16U); + leading_zero_bytes = 16 - count; + memset(i_port_id, 0, leading_zero_bytes); + rc = hex2bin(i_port_id + leading_zero_bytes, p, count); + if (rc < 0) + pr_debug("hex2bin failed for srpt_parse_i_port_id: %d\n", rc); + ret = 0; +out: + return ret; +} + +/* + * configfs callback function invoked for + * mkdir /sys/kernel/config/target/$driver/$port/$tpg/acls/$i_port_id + */ +static struct se_node_acl *srpt_make_nodeacl(struct se_portal_group *tpg, + struct config_group *group, + const char *name) +{ + struct srpt_port *sport = container_of(tpg, struct srpt_port, port_tpg_1); + struct se_node_acl *se_nacl, *se_nacl_new; + struct srpt_node_acl *nacl; + int ret = 0; + u32 nexus_depth = 1; + u8 i_port_id[16]; + + if (srpt_parse_i_port_id(i_port_id, name) < 0) { + printk(KERN_ERR "invalid initiator port ID %s\n", name); + ret = -EINVAL; + goto err; + } + + se_nacl_new = srpt_alloc_fabric_acl(tpg); + if (!se_nacl_new) { + ret = -ENOMEM; + goto err; + } + /* + * nacl_new may be released by core_tpg_add_initiator_node_acl() + * when converting a node ACL from demo mode to explict + */ + se_nacl = core_tpg_add_initiator_node_acl(tpg, se_nacl_new, name, + nexus_depth); + if (IS_ERR(se_nacl)) { + ret = PTR_ERR(se_nacl); + goto err; + } + /* Locate our struct srpt_node_acl and set sdev and i_port_id. */ + nacl = container_of(se_nacl, struct srpt_node_acl, nacl); + memcpy(&nacl->i_port_id[0], &i_port_id[0], 16); + nacl->sport = sport; + + spin_lock_irq(&sport->port_acl_lock); + list_add_tail(&nacl->list, &sport->port_acl_list); + spin_unlock_irq(&sport->port_acl_lock); + + return se_nacl; +err: + return ERR_PTR(ret); +} + +/* + * configfs callback function invoked for + * rmdir /sys/kernel/config/target/$driver/$port/$tpg/acls/$i_port_id + */ +static void srpt_drop_nodeacl(struct se_node_acl *se_nacl) +{ + struct srpt_node_acl *nacl; + struct srpt_device *sdev; + struct srpt_port *sport; + + nacl = container_of(se_nacl, struct srpt_node_acl, nacl); + sport = nacl->sport; + sdev = sport->sdev; + spin_lock_irq(&sport->port_acl_lock); + list_del(&nacl->list); + spin_unlock_irq(&sport->port_acl_lock); + core_tpg_del_initiator_node_acl(&sport->port_tpg_1, se_nacl, 1); + srpt_release_fabric_acl(NULL, se_nacl); +} + +static ssize_t srpt_tpg_attrib_show_srp_max_rdma_size( + struct se_portal_group *se_tpg, + char *page) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + + return sprintf(page, "%u\n", sport->port_attrib.srp_max_rdma_size); +} + +static ssize_t srpt_tpg_attrib_store_srp_max_rdma_size( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + unsigned long val; + int ret; + + ret = strict_strtoul(page, 0, &val); + if (ret < 0) { + pr_err("strict_strtoul() failed with ret: %d\n", ret); + return -EINVAL; + } + if (val > MAX_SRPT_RDMA_SIZE) { + pr_err("val: %lu exceeds MAX_SRPT_RDMA_SIZE: %d\n", val, + MAX_SRPT_RDMA_SIZE); + return -EINVAL; + } + if (val < DEFAULT_MAX_RDMA_SIZE) { + pr_err("val: %lu smaller than DEFAULT_MAX_RDMA_SIZE: %d\n", + val, DEFAULT_MAX_RDMA_SIZE); + return -EINVAL; + } + sport->port_attrib.srp_max_rdma_size = val; + + return count; +} + +TF_TPG_ATTRIB_ATTR(srpt, srp_max_rdma_size, S_IRUGO | S_IWUSR); + +static ssize_t srpt_tpg_attrib_show_srp_max_rsp_size( + struct se_portal_group *se_tpg, + char *page) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + + return sprintf(page, "%u\n", sport->port_attrib.srp_max_rsp_size); +} + +static ssize_t srpt_tpg_attrib_store_srp_max_rsp_size( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + unsigned long val; + int ret; + + ret = strict_strtoul(page, 0, &val); + if (ret < 0) { + pr_err("strict_strtoul() failed with ret: %d\n", ret); + return -EINVAL; + } + if (val > MAX_SRPT_RSP_SIZE) { + pr_err("val: %lu exceeds MAX_SRPT_RSP_SIZE: %d\n", val, + MAX_SRPT_RSP_SIZE); + return -EINVAL; + } + if (val < MIN_MAX_RSP_SIZE) { + pr_err("val: %lu smaller than MIN_MAX_RSP_SIZE: %d\n", val, + MIN_MAX_RSP_SIZE); + return -EINVAL; + } + sport->port_attrib.srp_max_rsp_size = val; + + return count; +} + +TF_TPG_ATTRIB_ATTR(srpt, srp_max_rsp_size, S_IRUGO | S_IWUSR); + +static ssize_t srpt_tpg_attrib_show_srp_sq_size( + struct se_portal_group *se_tpg, + char *page) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + + return sprintf(page, "%u\n", sport->port_attrib.srp_sq_size); +} + +static ssize_t srpt_tpg_attrib_store_srp_sq_size( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + unsigned long val; + int ret; + + ret = strict_strtoul(page, 0, &val); + if (ret < 0) { + pr_err("strict_strtoul() failed with ret: %d\n", ret); + return -EINVAL; + } + if (val > MAX_SRPT_SRQ_SIZE) { + pr_err("val: %lu exceeds MAX_SRPT_SRQ_SIZE: %d\n", val, + MAX_SRPT_SRQ_SIZE); + return -EINVAL; + } + if (val < MIN_SRPT_SRQ_SIZE) { + pr_err("val: %lu smaller than MIN_SRPT_SRQ_SIZE: %d\n", val, + MIN_SRPT_SRQ_SIZE); + return -EINVAL; + } + sport->port_attrib.srp_sq_size = val; + + return count; +} + +TF_TPG_ATTRIB_ATTR(srpt, srp_sq_size, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *srpt_tpg_attrib_attrs[] = { + &srpt_tpg_attrib_srp_max_rdma_size.attr, + &srpt_tpg_attrib_srp_max_rsp_size.attr, + &srpt_tpg_attrib_srp_sq_size.attr, + NULL, +}; + +static ssize_t srpt_tpg_show_enable( + struct se_portal_group *se_tpg, + char *page) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + + return snprintf(page, PAGE_SIZE, "%d\n", (sport->enabled) ? 1: 0); +} + +static ssize_t srpt_tpg_store_enable( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + unsigned long tmp; + int ret; + + ret = strict_strtoul(page, 0, &tmp); + if (ret < 0) { + printk(KERN_ERR "Unable to extract srpt_tpg_store_enable\n"); + return -EINVAL; + } + + if ((tmp != 0) && (tmp != 1)) { + printk(KERN_ERR "Illegal value for srpt_tpg_store_enable: %lu\n", tmp); + return -EINVAL; + } + if (tmp == 1) + sport->enabled = true; + else + sport->enabled = false; + + return count; +} + +TF_TPG_BASE_ATTR(srpt, enable, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *srpt_tpg_attrs[] = { + &srpt_tpg_enable.attr, + NULL, +}; + +/** + * configfs callback invoked for + * mkdir /sys/kernel/config/target/$driver/$port/$tpg + */ +static struct se_portal_group *srpt_make_tpg(struct se_wwn *wwn, + struct config_group *group, + const char *name) +{ + struct srpt_port *sport = container_of(wwn, struct srpt_port, port_wwn); + int res; + + /* Initialize sport->port_wwn and sport->port_tpg_1 */ + res = core_tpg_register(&srpt_target->tf_ops, &sport->port_wwn, + &sport->port_tpg_1, sport, TRANSPORT_TPG_TYPE_NORMAL); + if (res) + return ERR_PTR(res); + + return &sport->port_tpg_1; +} + +/** + * configfs callback invoked for + * rmdir /sys/kernel/config/target/$driver/$port/$tpg + */ +static void srpt_drop_tpg(struct se_portal_group *tpg) +{ + struct srpt_port *sport = container_of(tpg, + struct srpt_port, port_tpg_1); + + sport->enabled = false; + core_tpg_deregister(&sport->port_tpg_1); +} + +/** + * configfs callback invoked for + * mkdir /sys/kernel/config/target/$driver/$port + */ +static struct se_wwn *srpt_make_tport(struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) +{ + struct srpt_port *sport; + int ret; + + sport = srpt_lookup_port(name); + pr_debug("make_tport(%s)\n", name); + ret = -EINVAL; + if (!sport) + goto err; + + return &sport->port_wwn; + +err: + return ERR_PTR(ret); +} + +/** + * configfs callback invoked for + * rmdir /sys/kernel/config/target/$driver/$port + */ +static void srpt_drop_tport(struct se_wwn *wwn) +{ + struct srpt_port *sport = container_of(wwn, struct srpt_port, port_wwn); + + pr_debug("drop_tport(%s\n", config_item_name(&sport->port_wwn.wwn_group.cg_item)); +} + +static ssize_t srpt_wwn_show_attr_version(struct target_fabric_configfs *tf, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", DRV_VERSION); +} + +TF_WWN_ATTR_RO(srpt, version); + +static struct configfs_attribute *srpt_wwn_attrs[] = { + &srpt_wwn_version.attr, + NULL, +}; + +static struct target_core_fabric_ops srpt_template = { + .get_fabric_name = srpt_get_fabric_name, + .get_fabric_proto_ident = srpt_get_fabric_proto_ident, + .tpg_get_wwn = srpt_get_fabric_wwn, + .tpg_get_tag = srpt_get_tag, + .tpg_get_default_depth = srpt_get_default_depth, + .tpg_get_pr_transport_id = srpt_get_pr_transport_id, + .tpg_get_pr_transport_id_len = srpt_get_pr_transport_id_len, + .tpg_parse_pr_out_transport_id = srpt_parse_pr_out_transport_id, + .tpg_check_demo_mode = srpt_check_false, + .tpg_check_demo_mode_cache = srpt_check_true, + .tpg_check_demo_mode_write_protect = srpt_check_true, + .tpg_check_prod_mode_write_protect = srpt_check_false, + .tpg_alloc_fabric_acl = srpt_alloc_fabric_acl, + .tpg_release_fabric_acl = srpt_release_fabric_acl, + .tpg_get_inst_index = srpt_tpg_get_inst_index, + .release_cmd = srpt_release_cmd, + .check_stop_free = srpt_check_stop_free, + .shutdown_session = srpt_shutdown_session, + .close_session = srpt_close_session, + .stop_session = srpt_stop_session, + .fall_back_to_erl0 = srpt_reset_nexus, + .sess_logged_in = srpt_sess_logged_in, + .sess_get_index = srpt_sess_get_index, + .sess_get_initiator_sid = NULL, + .write_pending = srpt_write_pending, + .write_pending_status = srpt_write_pending_status, + .set_default_node_attributes = srpt_set_default_node_attrs, + .get_task_tag = srpt_get_task_tag, + .get_cmd_state = srpt_get_tcm_cmd_state, + .queue_data_in = srpt_queue_response, + .queue_status = srpt_queue_status, + .queue_tm_rsp = srpt_queue_response, + .get_fabric_sense_len = srpt_get_fabric_sense_len, + .set_fabric_sense_len = srpt_set_fabric_sense_len, + .is_state_remove = srpt_is_state_remove, + /* + * Setup function pointers for generic logic in + * target_core_fabric_configfs.c + */ + .fabric_make_wwn = srpt_make_tport, + .fabric_drop_wwn = srpt_drop_tport, + .fabric_make_tpg = srpt_make_tpg, + .fabric_drop_tpg = srpt_drop_tpg, + .fabric_post_link = NULL, + .fabric_pre_unlink = NULL, + .fabric_make_np = NULL, + .fabric_drop_np = NULL, + .fabric_make_nodeacl = srpt_make_nodeacl, + .fabric_drop_nodeacl = srpt_drop_nodeacl, +}; + +/** + * srpt_init_module() - Kernel module initialization. + * + * Note: Since ib_register_client() registers callback functions, and since at + * least one of these callback functions (srpt_add_one()) calls target core + * functions, this driver must be registered with the target core before + * ib_register_client() is called. + */ +static int __init srpt_init_module(void) +{ + int ret; + + ret = -EINVAL; + if (srp_max_req_size < MIN_MAX_REQ_SIZE) { + printk(KERN_ERR "invalid value %d for kernel module parameter" + " srp_max_req_size -- must be at least %d.\n", + srp_max_req_size, MIN_MAX_REQ_SIZE); + goto out; + } + + if (srpt_srq_size < MIN_SRPT_SRQ_SIZE + || srpt_srq_size > MAX_SRPT_SRQ_SIZE) { + printk(KERN_ERR "invalid value %d for kernel module parameter" + " srpt_srq_size -- must be in the range [%d..%d].\n", + srpt_srq_size, MIN_SRPT_SRQ_SIZE, MAX_SRPT_SRQ_SIZE); + goto out; + } + + spin_lock_init(&srpt_dev_lock); + INIT_LIST_HEAD(&srpt_dev_list); + + ret = -ENODEV; + srpt_target = target_fabric_configfs_init(THIS_MODULE, "srpt"); + if (!srpt_target) { + printk(KERN_ERR "couldn't register\n"); + goto out; + } + + srpt_target->tf_ops = srpt_template; + + /* Enable SG chaining */ + srpt_target->tf_ops.task_sg_chaining = true; + + /* + * Set up default attribute lists. + */ + srpt_target->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = srpt_wwn_attrs; + srpt_target->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = srpt_tpg_attrs; + srpt_target->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = srpt_tpg_attrib_attrs; + srpt_target->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; + srpt_target->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; + srpt_target->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; + srpt_target->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; + srpt_target->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; + srpt_target->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; + + ret = target_fabric_configfs_register(srpt_target); + if (ret < 0) { + printk(KERN_ERR "couldn't register\n"); + goto out_free_target; + } + + ret = ib_register_client(&srpt_client); + if (ret) { + printk(KERN_ERR "couldn't register IB client\n"); + goto out_unregister_target; + } + + return 0; + +out_unregister_target: + target_fabric_configfs_deregister(srpt_target); + srpt_target = NULL; +out_free_target: + if (srpt_target) + target_fabric_configfs_free(srpt_target); +out: + return ret; +} + +static void __exit srpt_cleanup_module(void) +{ + ib_unregister_client(&srpt_client); + target_fabric_configfs_deregister(srpt_target); + srpt_target = NULL; +} + +module_init(srpt_init_module); +module_exit(srpt_cleanup_module); diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h new file mode 100644 index 0000000..b4b4bbc --- /dev/null +++ b/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. + * Copyright (C) 2009 - 2010 Bart Van Assche . + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef IB_SRPT_H +#define IB_SRPT_H + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "ib_dm_mad.h" + +/* + * The prefix the ServiceName field must start with in the device management + * ServiceEntries attribute pair. See also the SRP specification. + */ +#define SRP_SERVICE_NAME_PREFIX "SRP.T10:" + +enum { + /* + * SRP IOControllerProfile attributes for SRP target ports that have + * not been defined in . Source: section B.7, table B.7 + * in the SRP specification. + */ + SRP_PROTOCOL = 0x0108, + SRP_PROTOCOL_VERSION = 0x0001, + SRP_IO_SUBCLASS = 0x609e, + SRP_SEND_TO_IOC = 0x01, + SRP_SEND_FROM_IOC = 0x02, + SRP_RDMA_READ_FROM_IOC = 0x08, + SRP_RDMA_WRITE_FROM_IOC = 0x20, + + /* + * srp_login_cmd.req_flags bitmasks. See also table 9 in the SRP + * specification. + */ + SRP_MTCH_ACTION = 0x03, /* MULTI-CHANNEL ACTION */ + SRP_LOSOLNT = 0x10, /* logout solicited notification */ + SRP_CRSOLNT = 0x20, /* credit request solicited notification */ + SRP_AESOLNT = 0x40, /* asynchronous event solicited notification */ + + /* + * srp_cmd.sol_nt / srp_tsk_mgmt.sol_not bitmasks. See also tables + * 18 and 20 in the SRP specification. + */ + SRP_SCSOLNT = 0x02, /* SCSOLNT = successful solicited notification */ + SRP_UCSOLNT = 0x04, /* UCSOLNT = unsuccessful solicited notification */ + + /* + * srp_rsp.sol_not / srp_t_logout.sol_not bitmasks. See also tables + * 16 and 22 in the SRP specification. + */ + SRP_SOLNT = 0x01, /* SOLNT = solicited notification */ + + /* See also table 24 in the SRP specification. */ + SRP_TSK_MGMT_SUCCESS = 0x00, + SRP_TSK_MGMT_FUNC_NOT_SUPP = 0x04, + SRP_TSK_MGMT_FAILED = 0x05, + + /* See also table 21 in the SRP specification. */ + SRP_CMD_SIMPLE_Q = 0x0, + SRP_CMD_HEAD_OF_Q = 0x1, + SRP_CMD_ORDERED_Q = 0x2, + SRP_CMD_ACA = 0x4, + + SRP_LOGIN_RSP_MULTICHAN_NO_CHAN = 0x0, + SRP_LOGIN_RSP_MULTICHAN_TERMINATED = 0x1, + SRP_LOGIN_RSP_MULTICHAN_MAINTAINED = 0x2, + + SRPT_DEF_SG_TABLESIZE = 128, + SRPT_DEF_SG_PER_WQE = 16, + + MIN_SRPT_SQ_SIZE = 16, + DEF_SRPT_SQ_SIZE = 4096, + SRPT_RQ_SIZE = 128, + MIN_SRPT_SRQ_SIZE = 4, + DEFAULT_SRPT_SRQ_SIZE = 4095, + MAX_SRPT_SRQ_SIZE = 65535, + MAX_SRPT_RDMA_SIZE = 1U << 24, + MAX_SRPT_RSP_SIZE = 1024, + + MIN_MAX_REQ_SIZE = 996, + DEFAULT_MAX_REQ_SIZE + = sizeof(struct srp_cmd)/*48*/ + + sizeof(struct srp_indirect_buf)/*20*/ + + 128 * sizeof(struct srp_direct_buf)/*16*/, + + MIN_MAX_RSP_SIZE = sizeof(struct srp_rsp)/*36*/ + 4, + DEFAULT_MAX_RSP_SIZE = 256, /* leaves 220 bytes for sense data */ + + DEFAULT_MAX_RDMA_SIZE = 65536, +}; + +enum srpt_opcode { + SRPT_RECV, + SRPT_SEND, + SRPT_RDMA_MID, + SRPT_RDMA_ABORT, + SRPT_RDMA_READ_LAST, + SRPT_RDMA_WRITE_LAST, +}; + +static inline u64 encode_wr_id(u8 opcode, u32 idx) +{ + return ((u64)opcode << 32) | idx; +} +static inline enum srpt_opcode opcode_from_wr_id(u64 wr_id) +{ + return wr_id >> 32; +} +static inline u32 idx_from_wr_id(u64 wr_id) +{ + return (u32)wr_id; +} + +struct rdma_iu { + u64 raddr; + u32 rkey; + struct ib_sge *sge; + u32 sge_cnt; + int mem_id; +}; + +/** + * enum srpt_command_state - SCSI command state managed by SRPT. + * @SRPT_STATE_NEW: New command arrived and is being processed. + * @SRPT_STATE_NEED_DATA: Processing a write or bidir command and waiting + * for data arrival. + * @SRPT_STATE_DATA_IN: Data for the write or bidir command arrived and is + * being processed. + * @SRPT_STATE_CMD_RSP_SENT: SRP_RSP for SRP_CMD has been sent. + * @SRPT_STATE_MGMT: Processing a SCSI task management command. + * @SRPT_STATE_MGMT_RSP_SENT: SRP_RSP for SRP_TSK_MGMT has been sent. + * @SRPT_STATE_DONE: Command processing finished successfully, command + * processing has been aborted or command processing + * failed. + */ +enum srpt_command_state { + SRPT_STATE_NEW = 0, + SRPT_STATE_NEED_DATA = 1, + SRPT_STATE_DATA_IN = 2, + SRPT_STATE_CMD_RSP_SENT = 3, + SRPT_STATE_MGMT = 4, + SRPT_STATE_MGMT_RSP_SENT = 5, + SRPT_STATE_DONE = 6, +}; + +/** + * struct srpt_ioctx - Shared SRPT I/O context information. + * @buf: Pointer to the buffer. + * @dma: DMA address of the buffer. + * @index: Index of the I/O context in its ioctx_ring array. + */ +struct srpt_ioctx { + void *buf; + dma_addr_t dma; + uint32_t index; +}; + +/** + * struct srpt_recv_ioctx - SRPT receive I/O context. + * @ioctx: See above. + * @wait_list: Node for insertion in srpt_rdma_ch.cmd_wait_list. + */ +struct srpt_recv_ioctx { + struct srpt_ioctx ioctx; + struct list_head wait_list; +}; + +/** + * struct srpt_send_ioctx - SRPT send I/O context. + * @ioctx: See above. + * @ch: Channel pointer. + * @free_list: Node in srpt_rdma_ch.free_list. + * @n_rbuf: Number of data buffers in the received SRP command. + * @rbufs: Pointer to SRP data buffer array. + * @single_rbuf: SRP data buffer if the command has only a single buffer. + * @sg: Pointer to sg-list associated with this I/O context. + * @sg_cnt: SG-list size. + * @mapped_sg_count: ib_dma_map_sg() return value. + * @n_rdma_ius: Number of elements in the rdma_ius array. + * @rdma_ius: Array with information about the RDMA mapping. + * @tag: Tag of the received SRP information unit. + * @spinlock: Protects 'state'. + * @state: I/O context state. + * @rdma_aborted: If initiating a multipart RDMA transfer failed, whether + * the already initiated transfers have finished. + * @cmd: Target core command data structure. + * @sense_data: SCSI sense data. + */ +struct srpt_send_ioctx { + struct srpt_ioctx ioctx; + struct srpt_rdma_ch *ch; + struct kref kref; + struct rdma_iu *rdma_ius; + struct srp_direct_buf *rbufs; + struct srp_direct_buf single_rbuf; + struct scatterlist *sg; + struct list_head free_list; + spinlock_t spinlock; + enum srpt_command_state state; + bool rdma_aborted; + struct se_cmd cmd; + struct completion tx_done; + u64 tag; + int sg_cnt; + int mapped_sg_count; + u16 n_rdma_ius; + u8 n_rdma; + u8 n_rbuf; + bool queue_status_only; + u8 sense_data[SCSI_SENSE_BUFFERSIZE]; +}; + +/** + * enum rdma_ch_state - SRP channel state. + * @CH_CONNECTING: QP is in RTR state; waiting for RTU. + * @CH_LIVE: QP is in RTS state. + * @CH_DISCONNECTING: DREQ has been received; waiting for DREP + * or DREQ has been send and waiting for DREP + * or . + * @CH_DRAINING: QP is in ERR state; waiting for last WQE event. + * @CH_RELEASING: Last WQE event has been received; releasing resources. + */ +enum rdma_ch_state { + CH_CONNECTING, + CH_LIVE, + CH_DISCONNECTING, + CH_DRAINING, + CH_RELEASING +}; + +/** + * struct srpt_rdma_ch - RDMA channel. + * @wait_queue: Allows the kernel thread to wait for more work. + * @thread: Kernel thread that processes the IB queues associated with + * the channel. + * @cm_id: IB CM ID associated with the channel. + * @qp: IB queue pair used for communicating over this channel. + * @cq: IB completion queue for this channel. + * @rq_size: IB receive queue size. + * @rsp_size IB response message size in bytes. + * @sq_wr_avail: number of work requests available in the send queue. + * @sport: pointer to the information of the HCA port used by this + * channel. + * @i_port_id: 128-bit initiator port identifier copied from SRP_LOGIN_REQ. + * @t_port_id: 128-bit target port identifier copied from SRP_LOGIN_REQ. + * @max_ti_iu_len: maximum target-to-initiator information unit length. + * @req_lim: request limit: maximum number of requests that may be sent + * by the initiator without having received a response. + * @req_lim_delta: Number of credits not yet sent back to the initiator. + * @spinlock: Protects free_list and state. + * @free_list: Head of list with free send I/O contexts. + * @state: channel state. See also enum rdma_ch_state. + * @ioctx_ring: Send ring. + * @wc: IB work completion array for srpt_process_completion(). + * @list: Node for insertion in the srpt_device.rch_list list. + * @cmd_wait_list: List of SCSI commands that arrived before the RTU event. This + * list contains struct srpt_ioctx elements and is protected + * against concurrent modification by the cm_id spinlock. + * @sess: Session information associated with this SRP channel. + * @sess_name: Session name. + * @release_work: Allows scheduling of srpt_release_channel(). + * @release_done: Enables waiting for srpt_release_channel() completion. + */ +struct srpt_rdma_ch { + wait_queue_head_t wait_queue; + struct task_struct *thread; + struct ib_cm_id *cm_id; + struct ib_qp *qp; + struct ib_cq *cq; + int rq_size; + u32 rsp_size; + atomic_t sq_wr_avail; + struct srpt_port *sport; + u8 i_port_id[16]; + u8 t_port_id[16]; + int max_ti_iu_len; + atomic_t req_lim; + atomic_t req_lim_delta; + spinlock_t spinlock; + struct list_head free_list; + enum rdma_ch_state state; + struct srpt_send_ioctx **ioctx_ring; + struct ib_wc wc[16]; + struct list_head list; + struct list_head cmd_wait_list; + struct se_session *sess; + u8 sess_name[36]; + struct work_struct release_work; + struct completion *release_done; +}; + +/** + * struct srpt_port_attib - Attributes for SRPT port + * @srp_max_rdma_size: Maximum size of SRP RDMA transfers for new connections. + * @srp_max_rsp_size: Maximum size of SRP response messages in bytes. + * @srp_sq_size: Shared receive queue (SRQ) size. + */ +struct srpt_port_attrib { + u32 srp_max_rdma_size; + u32 srp_max_rsp_size; + u32 srp_sq_size; +}; + +/** + * struct srpt_port - Information associated by SRPT with a single IB port. + * @sdev: backpointer to the HCA information. + * @mad_agent: per-port management datagram processing information. + * @enabled: Whether or not this target port is enabled. + * @port_guid: ASCII representation of Port GUID + * @port: one-based port number. + * @sm_lid: cached value of the port's sm_lid. + * @lid: cached value of the port's lid. + * @gid: cached value of the port's gid. + * @port_acl_lock spinlock for port_acl_list: + * @work: work structure for refreshing the aforementioned cached values. + * @port_tpg_1 Target portal group = 1 data. + * @port_wwn: Target core WWN data. + * @port_acl_list: Head of the list with all node ACLs for this port. + */ +struct srpt_port { + struct srpt_device *sdev; + struct ib_mad_agent *mad_agent; + bool enabled; + u8 port_guid[64]; + u8 port; + u16 sm_lid; + u16 lid; + union ib_gid gid; + spinlock_t port_acl_lock; + struct work_struct work; + struct se_portal_group port_tpg_1; + struct se_wwn port_wwn; + struct list_head port_acl_list; + struct srpt_port_attrib port_attrib; +}; + +/** + * struct srpt_device - Information associated by SRPT with a single HCA. + * @device: Backpointer to the struct ib_device managed by the IB core. + * @pd: IB protection domain. + * @mr: L_Key (local key) with write access to all local memory. + * @srq: Per-HCA SRQ (shared receive queue). + * @cm_id: Connection identifier. + * @dev_attr: Attributes of the InfiniBand device as obtained during the + * ib_client.add() callback. + * @srq_size: SRQ size. + * @ioctx_ring: Per-HCA SRQ. + * @rch_list: Per-device channel list -- see also srpt_rdma_ch.list. + * @ch_releaseQ: Enables waiting for removal from rch_list. + * @spinlock: Protects rch_list and tpg. + * @port: Information about the ports owned by this HCA. + * @event_handler: Per-HCA asynchronous IB event handler. + * @list: Node in srpt_dev_list. + */ +struct srpt_device { + struct ib_device *device; + struct ib_pd *pd; + struct ib_mr *mr; + struct ib_srq *srq; + struct ib_cm_id *cm_id; + struct ib_device_attr dev_attr; + int srq_size; + struct srpt_recv_ioctx **ioctx_ring; + struct list_head rch_list; + wait_queue_head_t ch_releaseQ; + spinlock_t spinlock; + struct srpt_port port[2]; + struct ib_event_handler event_handler; + struct list_head list; +}; + +/** + * struct srpt_node_acl - Per-initiator ACL data (managed via configfs). + * @i_port_id: 128-bit SRP initiator port ID. + * @sport: port information. + * @nacl: Target core node ACL information. + * @list: Element of the per-HCA ACL list. + */ +struct srpt_node_acl { + u8 i_port_id[16]; + struct srpt_port *sport; + struct se_node_acl nacl; + struct list_head list; +}; + +/* + * SRP-releated SCSI persistent reservation definitions. + * + * See also SPC4r28, section 7.6.1 (Protocol specific parameters introduction). + * See also SPC4r28, section 7.6.4.5 (TransportID for initiator ports using + * SCSI over an RDMA interface). + */ + +enum { + SCSI_TRANSPORTID_PROTOCOLID_SRP = 4, +}; + +struct spc_rdma_transport_id { + uint8_t protocol_identifier; + uint8_t reserved[7]; + uint8_t i_port_id[16]; +}; + +#endif /* IB_SRPT_H */ -- cgit v0.10.2 From 5db0276014b80484689eb6c1bf7b94af1c7d5b1a Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Tue, 1 Nov 2011 17:04:16 +0100 Subject: Btrfs: add optional integrity check code The two files added in this patch contain all the code that is required to implement the integrity checks. Signed-off-by: Stefan Behrens diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c new file mode 100644 index 0000000..ad0b3ba --- /dev/null +++ b/fs/btrfs/check-integrity.c @@ -0,0 +1,3068 @@ +/* + * Copyright (C) STRATO AG 2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +/* + * This module can be used to catch cases when the btrfs kernel + * code executes write requests to the disk that bring the file + * system in an inconsistent state. In such a state, a power-loss + * or kernel panic event would cause that the data on disk is + * lost or at least damaged. + * + * Code is added that examines all block write requests during + * runtime (including writes of the super block). Three rules + * are verified and an error is printed on violation of the + * rules: + * 1. It is not allowed to write a disk block which is + * currently referenced by the super block (either directly + * or indirectly). + * 2. When a super block is written, it is verified that all + * referenced (directly or indirectly) blocks fulfill the + * following requirements: + * 2a. All referenced blocks have either been present when + * the file system was mounted, (i.e., they have been + * referenced by the super block) or they have been + * written since then and the write completion callback + * was called and a FLUSH request to the device where + * these blocks are located was received and completed. + * 2b. All referenced blocks need to have a generation + * number which is equal to the parent's number. + * + * One issue that was found using this module was that the log + * tree on disk became temporarily corrupted because disk blocks + * that had been in use for the log tree had been freed and + * reused too early, while being referenced by the written super + * block. + * + * The search term in the kernel log that can be used to filter + * on the existence of detected integrity issues is + * "btrfs: attempt". + * + * The integrity check is enabled via mount options. These + * mount options are only supported if the integrity check + * tool is compiled by defining BTRFS_FS_CHECK_INTEGRITY. + * + * Example #1, apply integrity checks to all metadata: + * mount /dev/sdb1 /mnt -o check_int + * + * Example #2, apply integrity checks to all metadata and + * to data extents: + * mount /dev/sdb1 /mnt -o check_int_data + * + * Example #3, apply integrity checks to all metadata and dump + * the tree that the super block references to kernel messages + * each time after a super block was written: + * mount /dev/sdb1 /mnt -o check_int,check_int_print_mask=263 + * + * If the integrity check tool is included and activated in + * the mount options, plenty of kernel memory is used, and + * plenty of additional CPU cycles are spent. Enabling this + * functionality is not intended for normal use. In most + * cases, unless you are a btrfs developer who needs to verify + * the integrity of (super)-block write requests, do not + * enable the config option BTRFS_FS_CHECK_INTEGRITY to + * include and compile the integrity check tool. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ctree.h" +#include "disk-io.h" +#include "transaction.h" +#include "extent_io.h" +#include "disk-io.h" +#include "volumes.h" +#include "print-tree.h" +#include "locking.h" +#include "check-integrity.h" + +#define BTRFSIC_BLOCK_HASHTABLE_SIZE 0x10000 +#define BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE 0x10000 +#define BTRFSIC_DEV2STATE_HASHTABLE_SIZE 0x100 +#define BTRFSIC_BLOCK_MAGIC_NUMBER 0x14491051 +#define BTRFSIC_BLOCK_LINK_MAGIC_NUMBER 0x11070807 +#define BTRFSIC_DEV2STATE_MAGIC_NUMBER 0x20111530 +#define BTRFSIC_BLOCK_STACK_FRAME_MAGIC_NUMBER 20111300 +#define BTRFSIC_TREE_DUMP_MAX_INDENT_LEVEL (200 - 6) /* in characters, + * excluding " [...]" */ +#define BTRFSIC_BLOCK_SIZE PAGE_SIZE + +#define BTRFSIC_GENERATION_UNKNOWN ((u64)-1) + +/* + * The definition of the bitmask fields for the print_mask. + * They are specified with the mount option check_integrity_print_mask. + */ +#define BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE 0x00000001 +#define BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION 0x00000002 +#define BTRFSIC_PRINT_MASK_TREE_AFTER_SB_WRITE 0x00000004 +#define BTRFSIC_PRINT_MASK_TREE_BEFORE_SB_WRITE 0x00000008 +#define BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH 0x00000010 +#define BTRFSIC_PRINT_MASK_END_IO_BIO_BH 0x00000020 +#define BTRFSIC_PRINT_MASK_VERBOSE 0x00000040 +#define BTRFSIC_PRINT_MASK_VERY_VERBOSE 0x00000080 +#define BTRFSIC_PRINT_MASK_INITIAL_TREE 0x00000100 +#define BTRFSIC_PRINT_MASK_INITIAL_ALL_TREES 0x00000200 +#define BTRFSIC_PRINT_MASK_INITIAL_DATABASE 0x00000400 +#define BTRFSIC_PRINT_MASK_NUM_COPIES 0x00000800 +#define BTRFSIC_PRINT_MASK_TREE_WITH_ALL_MIRRORS 0x00001000 + +struct btrfsic_dev_state; +struct btrfsic_state; + +struct btrfsic_block { + u32 magic_num; /* only used for debug purposes */ + unsigned int is_metadata:1; /* if it is meta-data, not data-data */ + unsigned int is_superblock:1; /* if it is one of the superblocks */ + unsigned int is_iodone:1; /* if is done by lower subsystem */ + unsigned int iodone_w_error:1; /* error was indicated to endio */ + unsigned int never_written:1; /* block was added because it was + * referenced, not because it was + * written */ + unsigned int mirror_num:2; /* large enough to hold + * BTRFS_SUPER_MIRROR_MAX */ + struct btrfsic_dev_state *dev_state; + u64 dev_bytenr; /* key, physical byte num on disk */ + u64 logical_bytenr; /* logical byte num on disk */ + u64 generation; + struct btrfs_disk_key disk_key; /* extra info to print in case of + * issues, will not always be correct */ + struct list_head collision_resolving_node; /* list node */ + struct list_head all_blocks_node; /* list node */ + + /* the following two lists contain block_link items */ + struct list_head ref_to_list; /* list */ + struct list_head ref_from_list; /* list */ + struct btrfsic_block *next_in_same_bio; + void *orig_bio_bh_private; + union { + bio_end_io_t *bio; + bh_end_io_t *bh; + } orig_bio_bh_end_io; + int submit_bio_bh_rw; + u64 flush_gen; /* only valid if !never_written */ +}; + +/* + * Elements of this type are allocated dynamically and required because + * each block object can refer to and can be ref from multiple blocks. + * The key to lookup them in the hashtable is the dev_bytenr of + * the block ref to plus the one from the block refered from. + * The fact that they are searchable via a hashtable and that a + * ref_cnt is maintained is not required for the btrfs integrity + * check algorithm itself, it is only used to make the output more + * beautiful in case that an error is detected (an error is defined + * as a write operation to a block while that block is still referenced). + */ +struct btrfsic_block_link { + u32 magic_num; /* only used for debug purposes */ + u32 ref_cnt; + struct list_head node_ref_to; /* list node */ + struct list_head node_ref_from; /* list node */ + struct list_head collision_resolving_node; /* list node */ + struct btrfsic_block *block_ref_to; + struct btrfsic_block *block_ref_from; + u64 parent_generation; +}; + +struct btrfsic_dev_state { + u32 magic_num; /* only used for debug purposes */ + struct block_device *bdev; + struct btrfsic_state *state; + struct list_head collision_resolving_node; /* list node */ + struct btrfsic_block dummy_block_for_bio_bh_flush; + u64 last_flush_gen; + char name[BDEVNAME_SIZE]; +}; + +struct btrfsic_block_hashtable { + struct list_head table[BTRFSIC_BLOCK_HASHTABLE_SIZE]; +}; + +struct btrfsic_block_link_hashtable { + struct list_head table[BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE]; +}; + +struct btrfsic_dev_state_hashtable { + struct list_head table[BTRFSIC_DEV2STATE_HASHTABLE_SIZE]; +}; + +struct btrfsic_block_data_ctx { + u64 start; /* virtual bytenr */ + u64 dev_bytenr; /* physical bytenr on device */ + u32 len; + struct btrfsic_dev_state *dev; + char *data; + struct buffer_head *bh; /* do not use if set to NULL */ +}; + +/* This structure is used to implement recursion without occupying + * any stack space, refer to btrfsic_process_metablock() */ +struct btrfsic_stack_frame { + u32 magic; + u32 nr; + int error; + int i; + int limit_nesting; + int num_copies; + int mirror_num; + struct btrfsic_block *block; + struct btrfsic_block_data_ctx *block_ctx; + struct btrfsic_block *next_block; + struct btrfsic_block_data_ctx next_block_ctx; + struct btrfs_header *hdr; + struct btrfsic_stack_frame *prev; +}; + +/* Some state per mounted filesystem */ +struct btrfsic_state { + u32 print_mask; + int include_extent_data; + int csum_size; + struct list_head all_blocks_list; + struct btrfsic_block_hashtable block_hashtable; + struct btrfsic_block_link_hashtable block_link_hashtable; + struct btrfs_root *root; + u64 max_superblock_generation; + struct btrfsic_block *latest_superblock; +}; + +static void btrfsic_block_init(struct btrfsic_block *b); +static struct btrfsic_block *btrfsic_block_alloc(void); +static void btrfsic_block_free(struct btrfsic_block *b); +static void btrfsic_block_link_init(struct btrfsic_block_link *n); +static struct btrfsic_block_link *btrfsic_block_link_alloc(void); +static void btrfsic_block_link_free(struct btrfsic_block_link *n); +static void btrfsic_dev_state_init(struct btrfsic_dev_state *ds); +static struct btrfsic_dev_state *btrfsic_dev_state_alloc(void); +static void btrfsic_dev_state_free(struct btrfsic_dev_state *ds); +static void btrfsic_block_hashtable_init(struct btrfsic_block_hashtable *h); +static void btrfsic_block_hashtable_add(struct btrfsic_block *b, + struct btrfsic_block_hashtable *h); +static void btrfsic_block_hashtable_remove(struct btrfsic_block *b); +static struct btrfsic_block *btrfsic_block_hashtable_lookup( + struct block_device *bdev, + u64 dev_bytenr, + struct btrfsic_block_hashtable *h); +static void btrfsic_block_link_hashtable_init( + struct btrfsic_block_link_hashtable *h); +static void btrfsic_block_link_hashtable_add( + struct btrfsic_block_link *l, + struct btrfsic_block_link_hashtable *h); +static void btrfsic_block_link_hashtable_remove(struct btrfsic_block_link *l); +static struct btrfsic_block_link *btrfsic_block_link_hashtable_lookup( + struct block_device *bdev_ref_to, + u64 dev_bytenr_ref_to, + struct block_device *bdev_ref_from, + u64 dev_bytenr_ref_from, + struct btrfsic_block_link_hashtable *h); +static void btrfsic_dev_state_hashtable_init( + struct btrfsic_dev_state_hashtable *h); +static void btrfsic_dev_state_hashtable_add( + struct btrfsic_dev_state *ds, + struct btrfsic_dev_state_hashtable *h); +static void btrfsic_dev_state_hashtable_remove(struct btrfsic_dev_state *ds); +static struct btrfsic_dev_state *btrfsic_dev_state_hashtable_lookup( + struct block_device *bdev, + struct btrfsic_dev_state_hashtable *h); +static struct btrfsic_stack_frame *btrfsic_stack_frame_alloc(void); +static void btrfsic_stack_frame_free(struct btrfsic_stack_frame *sf); +static int btrfsic_process_superblock(struct btrfsic_state *state, + struct btrfs_fs_devices *fs_devices); +static int btrfsic_process_metablock(struct btrfsic_state *state, + struct btrfsic_block *block, + struct btrfsic_block_data_ctx *block_ctx, + struct btrfs_header *hdr, + int limit_nesting, int force_iodone_flag); +static int btrfsic_create_link_to_next_block( + struct btrfsic_state *state, + struct btrfsic_block *block, + struct btrfsic_block_data_ctx + *block_ctx, u64 next_bytenr, + int limit_nesting, + struct btrfsic_block_data_ctx *next_block_ctx, + struct btrfsic_block **next_blockp, + int force_iodone_flag, + int *num_copiesp, int *mirror_nump, + struct btrfs_disk_key *disk_key, + u64 parent_generation); +static int btrfsic_handle_extent_data(struct btrfsic_state *state, + struct btrfsic_block *block, + struct btrfsic_block_data_ctx *block_ctx, + u32 item_offset, int force_iodone_flag); +static int btrfsic_map_block(struct btrfsic_state *state, u64 bytenr, u32 len, + struct btrfsic_block_data_ctx *block_ctx_out, + int mirror_num); +static int btrfsic_map_superblock(struct btrfsic_state *state, u64 bytenr, + u32 len, struct block_device *bdev, + struct btrfsic_block_data_ctx *block_ctx_out); +static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx); +static int btrfsic_read_block(struct btrfsic_state *state, + struct btrfsic_block_data_ctx *block_ctx); +static void btrfsic_dump_database(struct btrfsic_state *state); +static int btrfsic_test_for_metadata(struct btrfsic_state *state, + const u8 *data, unsigned int size); +static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state, + u64 dev_bytenr, u8 *mapped_data, + unsigned int len, struct bio *bio, + int *bio_is_patched, + struct buffer_head *bh, + int submit_bio_bh_rw); +static int btrfsic_process_written_superblock( + struct btrfsic_state *state, + struct btrfsic_block *const block, + struct btrfs_super_block *const super_hdr); +static void btrfsic_bio_end_io(struct bio *bp, int bio_error_status); +static void btrfsic_bh_end_io(struct buffer_head *bh, int uptodate); +static int btrfsic_is_block_ref_by_superblock(const struct btrfsic_state *state, + const struct btrfsic_block *block, + int recursion_level); +static int btrfsic_check_all_ref_blocks(struct btrfsic_state *state, + struct btrfsic_block *const block, + int recursion_level); +static void btrfsic_print_add_link(const struct btrfsic_state *state, + const struct btrfsic_block_link *l); +static void btrfsic_print_rem_link(const struct btrfsic_state *state, + const struct btrfsic_block_link *l); +static char btrfsic_get_block_type(const struct btrfsic_state *state, + const struct btrfsic_block *block); +static void btrfsic_dump_tree(const struct btrfsic_state *state); +static void btrfsic_dump_tree_sub(const struct btrfsic_state *state, + const struct btrfsic_block *block, + int indent_level); +static struct btrfsic_block_link *btrfsic_block_link_lookup_or_add( + struct btrfsic_state *state, + struct btrfsic_block_data_ctx *next_block_ctx, + struct btrfsic_block *next_block, + struct btrfsic_block *from_block, + u64 parent_generation); +static struct btrfsic_block *btrfsic_block_lookup_or_add( + struct btrfsic_state *state, + struct btrfsic_block_data_ctx *block_ctx, + const char *additional_string, + int is_metadata, + int is_iodone, + int never_written, + int mirror_num, + int *was_created); +static int btrfsic_process_superblock_dev_mirror( + struct btrfsic_state *state, + struct btrfsic_dev_state *dev_state, + struct btrfs_device *device, + int superblock_mirror_num, + struct btrfsic_dev_state **selected_dev_state, + struct btrfs_super_block *selected_super); +static struct btrfsic_dev_state *btrfsic_dev_state_lookup( + struct block_device *bdev); +static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state, + u64 bytenr, + struct btrfsic_dev_state *dev_state, + u64 dev_bytenr, char *data); + +static struct mutex btrfsic_mutex; +static int btrfsic_is_initialized; +static struct btrfsic_dev_state_hashtable btrfsic_dev_state_hashtable; + + +static void btrfsic_block_init(struct btrfsic_block *b) +{ + b->magic_num = BTRFSIC_BLOCK_MAGIC_NUMBER; + b->dev_state = NULL; + b->dev_bytenr = 0; + b->logical_bytenr = 0; + b->generation = BTRFSIC_GENERATION_UNKNOWN; + b->disk_key.objectid = 0; + b->disk_key.type = 0; + b->disk_key.offset = 0; + b->is_metadata = 0; + b->is_superblock = 0; + b->is_iodone = 0; + b->iodone_w_error = 0; + b->never_written = 0; + b->mirror_num = 0; + b->next_in_same_bio = NULL; + b->orig_bio_bh_private = NULL; + b->orig_bio_bh_end_io.bio = NULL; + INIT_LIST_HEAD(&b->collision_resolving_node); + INIT_LIST_HEAD(&b->all_blocks_node); + INIT_LIST_HEAD(&b->ref_to_list); + INIT_LIST_HEAD(&b->ref_from_list); + b->submit_bio_bh_rw = 0; + b->flush_gen = 0; +} + +static struct btrfsic_block *btrfsic_block_alloc(void) +{ + struct btrfsic_block *b; + + b = kzalloc(sizeof(*b), GFP_NOFS); + if (NULL != b) + btrfsic_block_init(b); + + return b; +} + +static void btrfsic_block_free(struct btrfsic_block *b) +{ + BUG_ON(!(NULL == b || BTRFSIC_BLOCK_MAGIC_NUMBER == b->magic_num)); + kfree(b); +} + +static void btrfsic_block_link_init(struct btrfsic_block_link *l) +{ + l->magic_num = BTRFSIC_BLOCK_LINK_MAGIC_NUMBER; + l->ref_cnt = 1; + INIT_LIST_HEAD(&l->node_ref_to); + INIT_LIST_HEAD(&l->node_ref_from); + INIT_LIST_HEAD(&l->collision_resolving_node); + l->block_ref_to = NULL; + l->block_ref_from = NULL; +} + +static struct btrfsic_block_link *btrfsic_block_link_alloc(void) +{ + struct btrfsic_block_link *l; + + l = kzalloc(sizeof(*l), GFP_NOFS); + if (NULL != l) + btrfsic_block_link_init(l); + + return l; +} + +static void btrfsic_block_link_free(struct btrfsic_block_link *l) +{ + BUG_ON(!(NULL == l || BTRFSIC_BLOCK_LINK_MAGIC_NUMBER == l->magic_num)); + kfree(l); +} + +static void btrfsic_dev_state_init(struct btrfsic_dev_state *ds) +{ + ds->magic_num = BTRFSIC_DEV2STATE_MAGIC_NUMBER; + ds->bdev = NULL; + ds->state = NULL; + ds->name[0] = '\0'; + INIT_LIST_HEAD(&ds->collision_resolving_node); + ds->last_flush_gen = 0; + btrfsic_block_init(&ds->dummy_block_for_bio_bh_flush); + ds->dummy_block_for_bio_bh_flush.is_iodone = 1; + ds->dummy_block_for_bio_bh_flush.dev_state = ds; +} + +static struct btrfsic_dev_state *btrfsic_dev_state_alloc(void) +{ + struct btrfsic_dev_state *ds; + + ds = kzalloc(sizeof(*ds), GFP_NOFS); + if (NULL != ds) + btrfsic_dev_state_init(ds); + + return ds; +} + +static void btrfsic_dev_state_free(struct btrfsic_dev_state *ds) +{ + BUG_ON(!(NULL == ds || + BTRFSIC_DEV2STATE_MAGIC_NUMBER == ds->magic_num)); + kfree(ds); +} + +static void btrfsic_block_hashtable_init(struct btrfsic_block_hashtable *h) +{ + int i; + + for (i = 0; i < BTRFSIC_BLOCK_HASHTABLE_SIZE; i++) + INIT_LIST_HEAD(h->table + i); +} + +static void btrfsic_block_hashtable_add(struct btrfsic_block *b, + struct btrfsic_block_hashtable *h) +{ + const unsigned int hashval = + (((unsigned int)(b->dev_bytenr >> 16)) ^ + ((unsigned int)((uintptr_t)b->dev_state->bdev))) & + (BTRFSIC_BLOCK_HASHTABLE_SIZE - 1); + + list_add(&b->collision_resolving_node, h->table + hashval); +} + +static void btrfsic_block_hashtable_remove(struct btrfsic_block *b) +{ + list_del(&b->collision_resolving_node); +} + +static struct btrfsic_block *btrfsic_block_hashtable_lookup( + struct block_device *bdev, + u64 dev_bytenr, + struct btrfsic_block_hashtable *h) +{ + const unsigned int hashval = + (((unsigned int)(dev_bytenr >> 16)) ^ + ((unsigned int)((uintptr_t)bdev))) & + (BTRFSIC_BLOCK_HASHTABLE_SIZE - 1); + struct list_head *elem; + + list_for_each(elem, h->table + hashval) { + struct btrfsic_block *const b = + list_entry(elem, struct btrfsic_block, + collision_resolving_node); + + if (b->dev_state->bdev == bdev && b->dev_bytenr == dev_bytenr) + return b; + } + + return NULL; +} + +static void btrfsic_block_link_hashtable_init( + struct btrfsic_block_link_hashtable *h) +{ + int i; + + for (i = 0; i < BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE; i++) + INIT_LIST_HEAD(h->table + i); +} + +static void btrfsic_block_link_hashtable_add( + struct btrfsic_block_link *l, + struct btrfsic_block_link_hashtable *h) +{ + const unsigned int hashval = + (((unsigned int)(l->block_ref_to->dev_bytenr >> 16)) ^ + ((unsigned int)(l->block_ref_from->dev_bytenr >> 16)) ^ + ((unsigned int)((uintptr_t)l->block_ref_to->dev_state->bdev)) ^ + ((unsigned int)((uintptr_t)l->block_ref_from->dev_state->bdev))) + & (BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE - 1); + + BUG_ON(NULL == l->block_ref_to); + BUG_ON(NULL == l->block_ref_from); + list_add(&l->collision_resolving_node, h->table + hashval); +} + +static void btrfsic_block_link_hashtable_remove(struct btrfsic_block_link *l) +{ + list_del(&l->collision_resolving_node); +} + +static struct btrfsic_block_link *btrfsic_block_link_hashtable_lookup( + struct block_device *bdev_ref_to, + u64 dev_bytenr_ref_to, + struct block_device *bdev_ref_from, + u64 dev_bytenr_ref_from, + struct btrfsic_block_link_hashtable *h) +{ + const unsigned int hashval = + (((unsigned int)(dev_bytenr_ref_to >> 16)) ^ + ((unsigned int)(dev_bytenr_ref_from >> 16)) ^ + ((unsigned int)((uintptr_t)bdev_ref_to)) ^ + ((unsigned int)((uintptr_t)bdev_ref_from))) & + (BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE - 1); + struct list_head *elem; + + list_for_each(elem, h->table + hashval) { + struct btrfsic_block_link *const l = + list_entry(elem, struct btrfsic_block_link, + collision_resolving_node); + + BUG_ON(NULL == l->block_ref_to); + BUG_ON(NULL == l->block_ref_from); + if (l->block_ref_to->dev_state->bdev == bdev_ref_to && + l->block_ref_to->dev_bytenr == dev_bytenr_ref_to && + l->block_ref_from->dev_state->bdev == bdev_ref_from && + l->block_ref_from->dev_bytenr == dev_bytenr_ref_from) + return l; + } + + return NULL; +} + +static void btrfsic_dev_state_hashtable_init( + struct btrfsic_dev_state_hashtable *h) +{ + int i; + + for (i = 0; i < BTRFSIC_DEV2STATE_HASHTABLE_SIZE; i++) + INIT_LIST_HEAD(h->table + i); +} + +static void btrfsic_dev_state_hashtable_add( + struct btrfsic_dev_state *ds, + struct btrfsic_dev_state_hashtable *h) +{ + const unsigned int hashval = + (((unsigned int)((uintptr_t)ds->bdev)) & + (BTRFSIC_DEV2STATE_HASHTABLE_SIZE - 1)); + + list_add(&ds->collision_resolving_node, h->table + hashval); +} + +static void btrfsic_dev_state_hashtable_remove(struct btrfsic_dev_state *ds) +{ + list_del(&ds->collision_resolving_node); +} + +static struct btrfsic_dev_state *btrfsic_dev_state_hashtable_lookup( + struct block_device *bdev, + struct btrfsic_dev_state_hashtable *h) +{ + const unsigned int hashval = + (((unsigned int)((uintptr_t)bdev)) & + (BTRFSIC_DEV2STATE_HASHTABLE_SIZE - 1)); + struct list_head *elem; + + list_for_each(elem, h->table + hashval) { + struct btrfsic_dev_state *const ds = + list_entry(elem, struct btrfsic_dev_state, + collision_resolving_node); + + if (ds->bdev == bdev) + return ds; + } + + return NULL; +} + +static int btrfsic_process_superblock(struct btrfsic_state *state, + struct btrfs_fs_devices *fs_devices) +{ + int ret; + struct btrfs_super_block *selected_super; + struct list_head *dev_head = &fs_devices->devices; + struct btrfs_device *device; + struct btrfsic_dev_state *selected_dev_state = NULL; + int pass; + + BUG_ON(NULL == state); + selected_super = kmalloc(sizeof(*selected_super), GFP_NOFS); + if (NULL == selected_super) { + printk(KERN_INFO "btrfsic: error, kmalloc failed!\n"); + return -1; + } + + list_for_each_entry(device, dev_head, dev_list) { + int i; + struct btrfsic_dev_state *dev_state; + + if (!device->bdev || !device->name) + continue; + + dev_state = btrfsic_dev_state_lookup(device->bdev); + BUG_ON(NULL == dev_state); + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + ret = btrfsic_process_superblock_dev_mirror( + state, dev_state, device, i, + &selected_dev_state, selected_super); + if (0 != ret && 0 == i) { + kfree(selected_super); + return ret; + } + } + } + + if (NULL == state->latest_superblock) { + printk(KERN_INFO "btrfsic: no superblock found!\n"); + kfree(selected_super); + return -1; + } + + state->csum_size = btrfs_super_csum_size(selected_super); + + for (pass = 0; pass < 3; pass++) { + int num_copies; + int mirror_num; + u64 next_bytenr; + + switch (pass) { + case 0: + next_bytenr = btrfs_super_root(selected_super); + if (state->print_mask & + BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION) + printk(KERN_INFO "root@%llu\n", + (unsigned long long)next_bytenr); + break; + case 1: + next_bytenr = btrfs_super_chunk_root(selected_super); + if (state->print_mask & + BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION) + printk(KERN_INFO "chunk@%llu\n", + (unsigned long long)next_bytenr); + break; + case 2: + next_bytenr = btrfs_super_log_root(selected_super); + if (0 == next_bytenr) + continue; + if (state->print_mask & + BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION) + printk(KERN_INFO "log@%llu\n", + (unsigned long long)next_bytenr); + break; + } + + num_copies = + btrfs_num_copies(&state->root->fs_info->mapping_tree, + next_bytenr, PAGE_SIZE); + if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES) + printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n", + (unsigned long long)next_bytenr, num_copies); + + for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) { + struct btrfsic_block *next_block; + struct btrfsic_block_data_ctx tmp_next_block_ctx; + struct btrfsic_block_link *l; + struct btrfs_header *hdr; + + ret = btrfsic_map_block(state, next_bytenr, PAGE_SIZE, + &tmp_next_block_ctx, + mirror_num); + if (ret) { + printk(KERN_INFO "btrfsic:" + " btrfsic_map_block(root @%llu," + " mirror %d) failed!\n", + (unsigned long long)next_bytenr, + mirror_num); + kfree(selected_super); + return -1; + } + + next_block = btrfsic_block_hashtable_lookup( + tmp_next_block_ctx.dev->bdev, + tmp_next_block_ctx.dev_bytenr, + &state->block_hashtable); + BUG_ON(NULL == next_block); + + l = btrfsic_block_link_hashtable_lookup( + tmp_next_block_ctx.dev->bdev, + tmp_next_block_ctx.dev_bytenr, + state->latest_superblock->dev_state-> + bdev, + state->latest_superblock->dev_bytenr, + &state->block_link_hashtable); + BUG_ON(NULL == l); + + ret = btrfsic_read_block(state, &tmp_next_block_ctx); + if (ret < (int)BTRFSIC_BLOCK_SIZE) { + printk(KERN_INFO + "btrfsic: read @logical %llu failed!\n", + (unsigned long long) + tmp_next_block_ctx.start); + btrfsic_release_block_ctx(&tmp_next_block_ctx); + kfree(selected_super); + return -1; + } + + hdr = (struct btrfs_header *)tmp_next_block_ctx.data; + ret = btrfsic_process_metablock(state, + next_block, + &tmp_next_block_ctx, + hdr, + BTRFS_MAX_LEVEL + 3, 1); + btrfsic_release_block_ctx(&tmp_next_block_ctx); + } + } + + kfree(selected_super); + return ret; +} + +static int btrfsic_process_superblock_dev_mirror( + struct btrfsic_state *state, + struct btrfsic_dev_state *dev_state, + struct btrfs_device *device, + int superblock_mirror_num, + struct btrfsic_dev_state **selected_dev_state, + struct btrfs_super_block *selected_super) +{ + struct btrfs_super_block *super_tmp; + u64 dev_bytenr; + struct buffer_head *bh; + struct btrfsic_block *superblock_tmp; + int pass; + struct block_device *const superblock_bdev = device->bdev; + + /* super block bytenr is always the unmapped device bytenr */ + dev_bytenr = btrfs_sb_offset(superblock_mirror_num); + bh = __bread(superblock_bdev, dev_bytenr / 4096, 4096); + if (NULL == bh) + return -1; + super_tmp = (struct btrfs_super_block *) + (bh->b_data + (dev_bytenr & 4095)); + + if (btrfs_super_bytenr(super_tmp) != dev_bytenr || + strncmp((char *)(&(super_tmp->magic)), BTRFS_MAGIC, + sizeof(super_tmp->magic)) || + memcmp(device->uuid, super_tmp->dev_item.uuid, BTRFS_UUID_SIZE)) { + brelse(bh); + return 0; + } + + superblock_tmp = + btrfsic_block_hashtable_lookup(superblock_bdev, + dev_bytenr, + &state->block_hashtable); + if (NULL == superblock_tmp) { + superblock_tmp = btrfsic_block_alloc(); + if (NULL == superblock_tmp) { + printk(KERN_INFO "btrfsic: error, kmalloc failed!\n"); + brelse(bh); + return -1; + } + /* for superblock, only the dev_bytenr makes sense */ + superblock_tmp->dev_bytenr = dev_bytenr; + superblock_tmp->dev_state = dev_state; + superblock_tmp->logical_bytenr = dev_bytenr; + superblock_tmp->generation = btrfs_super_generation(super_tmp); + superblock_tmp->is_metadata = 1; + superblock_tmp->is_superblock = 1; + superblock_tmp->is_iodone = 1; + superblock_tmp->never_written = 0; + superblock_tmp->mirror_num = 1 + superblock_mirror_num; + if (state->print_mask & BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE) + printk(KERN_INFO "New initial S-block (bdev %p, %s)" + " @%llu (%s/%llu/%d)\n", + superblock_bdev, device->name, + (unsigned long long)dev_bytenr, + dev_state->name, + (unsigned long long)dev_bytenr, + superblock_mirror_num); + list_add(&superblock_tmp->all_blocks_node, + &state->all_blocks_list); + btrfsic_block_hashtable_add(superblock_tmp, + &state->block_hashtable); + } + + /* select the one with the highest generation field */ + if (btrfs_super_generation(super_tmp) > + state->max_superblock_generation || + 0 == state->max_superblock_generation) { + memcpy(selected_super, super_tmp, sizeof(*selected_super)); + *selected_dev_state = dev_state; + state->max_superblock_generation = + btrfs_super_generation(super_tmp); + state->latest_superblock = superblock_tmp; + } + + for (pass = 0; pass < 3; pass++) { + u64 next_bytenr; + int num_copies; + int mirror_num; + const char *additional_string = NULL; + struct btrfs_disk_key tmp_disk_key; + + tmp_disk_key.type = BTRFS_ROOT_ITEM_KEY; + tmp_disk_key.offset = 0; + switch (pass) { + case 0: + tmp_disk_key.objectid = + cpu_to_le64(BTRFS_ROOT_TREE_OBJECTID); + additional_string = "initial root "; + next_bytenr = btrfs_super_root(super_tmp); + break; + case 1: + tmp_disk_key.objectid = + cpu_to_le64(BTRFS_CHUNK_TREE_OBJECTID); + additional_string = "initial chunk "; + next_bytenr = btrfs_super_chunk_root(super_tmp); + break; + case 2: + tmp_disk_key.objectid = + cpu_to_le64(BTRFS_TREE_LOG_OBJECTID); + additional_string = "initial log "; + next_bytenr = btrfs_super_log_root(super_tmp); + if (0 == next_bytenr) + continue; + break; + } + + num_copies = + btrfs_num_copies(&state->root->fs_info->mapping_tree, + next_bytenr, PAGE_SIZE); + if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES) + printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n", + (unsigned long long)next_bytenr, num_copies); + for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) { + struct btrfsic_block *next_block; + struct btrfsic_block_data_ctx tmp_next_block_ctx; + struct btrfsic_block_link *l; + + if (btrfsic_map_block(state, next_bytenr, PAGE_SIZE, + &tmp_next_block_ctx, + mirror_num)) { + printk(KERN_INFO "btrfsic: btrfsic_map_block(" + "bytenr @%llu, mirror %d) failed!\n", + (unsigned long long)next_bytenr, + mirror_num); + brelse(bh); + return -1; + } + + next_block = btrfsic_block_lookup_or_add( + state, &tmp_next_block_ctx, + additional_string, 1, 1, 0, + mirror_num, NULL); + if (NULL == next_block) { + btrfsic_release_block_ctx(&tmp_next_block_ctx); + brelse(bh); + return -1; + } + + next_block->disk_key = tmp_disk_key; + next_block->generation = BTRFSIC_GENERATION_UNKNOWN; + l = btrfsic_block_link_lookup_or_add( + state, &tmp_next_block_ctx, + next_block, superblock_tmp, + BTRFSIC_GENERATION_UNKNOWN); + btrfsic_release_block_ctx(&tmp_next_block_ctx); + if (NULL == l) { + brelse(bh); + return -1; + } + } + } + if (state->print_mask & BTRFSIC_PRINT_MASK_INITIAL_ALL_TREES) + btrfsic_dump_tree_sub(state, superblock_tmp, 0); + + brelse(bh); + return 0; +} + +static struct btrfsic_stack_frame *btrfsic_stack_frame_alloc(void) +{ + struct btrfsic_stack_frame *sf; + + sf = kzalloc(sizeof(*sf), GFP_NOFS); + if (NULL == sf) + printk(KERN_INFO "btrfsic: alloc memory failed!\n"); + else + sf->magic = BTRFSIC_BLOCK_STACK_FRAME_MAGIC_NUMBER; + return sf; +} + +static void btrfsic_stack_frame_free(struct btrfsic_stack_frame *sf) +{ + BUG_ON(!(NULL == sf || + BTRFSIC_BLOCK_STACK_FRAME_MAGIC_NUMBER == sf->magic)); + kfree(sf); +} + +static int btrfsic_process_metablock( + struct btrfsic_state *state, + struct btrfsic_block *const first_block, + struct btrfsic_block_data_ctx *const first_block_ctx, + struct btrfs_header *const first_hdr, + int first_limit_nesting, int force_iodone_flag) +{ + struct btrfsic_stack_frame initial_stack_frame = { 0 }; + struct btrfsic_stack_frame *sf; + struct btrfsic_stack_frame *next_stack; + + sf = &initial_stack_frame; + sf->error = 0; + sf->i = -1; + sf->limit_nesting = first_limit_nesting; + sf->block = first_block; + sf->block_ctx = first_block_ctx; + sf->next_block = NULL; + sf->hdr = first_hdr; + sf->prev = NULL; + +continue_with_new_stack_frame: + sf->block->generation = le64_to_cpu(sf->hdr->generation); + if (0 == sf->hdr->level) { + struct btrfs_leaf *const leafhdr = + (struct btrfs_leaf *)sf->hdr; + + if (-1 == sf->i) { + sf->nr = le32_to_cpu(leafhdr->header.nritems); + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "leaf %llu items %d generation %llu" + " owner %llu\n", + (unsigned long long) + sf->block_ctx->start, + sf->nr, + (unsigned long long) + le64_to_cpu(leafhdr->header.generation), + (unsigned long long) + le64_to_cpu(leafhdr->header.owner)); + } + +continue_with_current_leaf_stack_frame: + if (0 == sf->num_copies || sf->mirror_num > sf->num_copies) { + sf->i++; + sf->num_copies = 0; + } + + if (sf->i < sf->nr) { + struct btrfs_item *disk_item = leafhdr->items + sf->i; + struct btrfs_disk_key *disk_key = &disk_item->key; + u8 type; + const u32 item_offset = le32_to_cpu(disk_item->offset); + + type = disk_key->type; + + if (BTRFS_ROOT_ITEM_KEY == type) { + const struct btrfs_root_item *const root_item = + (struct btrfs_root_item *) + (sf->block_ctx->data + + offsetof(struct btrfs_leaf, items) + + item_offset); + const u64 next_bytenr = + le64_to_cpu(root_item->bytenr); + + sf->error = + btrfsic_create_link_to_next_block( + state, + sf->block, + sf->block_ctx, + next_bytenr, + sf->limit_nesting, + &sf->next_block_ctx, + &sf->next_block, + force_iodone_flag, + &sf->num_copies, + &sf->mirror_num, + disk_key, + le64_to_cpu(root_item-> + generation)); + if (sf->error) + goto one_stack_frame_backwards; + + if (NULL != sf->next_block) { + struct btrfs_header *const next_hdr = + (struct btrfs_header *) + sf->next_block_ctx.data; + + next_stack = + btrfsic_stack_frame_alloc(); + if (NULL == next_stack) { + btrfsic_release_block_ctx( + &sf-> + next_block_ctx); + goto one_stack_frame_backwards; + } + + next_stack->i = -1; + next_stack->block = sf->next_block; + next_stack->block_ctx = + &sf->next_block_ctx; + next_stack->next_block = NULL; + next_stack->hdr = next_hdr; + next_stack->limit_nesting = + sf->limit_nesting - 1; + next_stack->prev = sf; + sf = next_stack; + goto continue_with_new_stack_frame; + } + } else if (BTRFS_EXTENT_DATA_KEY == type && + state->include_extent_data) { + sf->error = btrfsic_handle_extent_data( + state, + sf->block, + sf->block_ctx, + item_offset, + force_iodone_flag); + if (sf->error) + goto one_stack_frame_backwards; + } + + goto continue_with_current_leaf_stack_frame; + } + } else { + struct btrfs_node *const nodehdr = (struct btrfs_node *)sf->hdr; + + if (-1 == sf->i) { + sf->nr = le32_to_cpu(nodehdr->header.nritems); + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO "node %llu level %d items %d" + " generation %llu owner %llu\n", + (unsigned long long) + sf->block_ctx->start, + nodehdr->header.level, sf->nr, + (unsigned long long) + le64_to_cpu(nodehdr->header.generation), + (unsigned long long) + le64_to_cpu(nodehdr->header.owner)); + } + +continue_with_current_node_stack_frame: + if (0 == sf->num_copies || sf->mirror_num > sf->num_copies) { + sf->i++; + sf->num_copies = 0; + } + + if (sf->i < sf->nr) { + struct btrfs_key_ptr *disk_key_ptr = + nodehdr->ptrs + sf->i; + const u64 next_bytenr = + le64_to_cpu(disk_key_ptr->blockptr); + + sf->error = btrfsic_create_link_to_next_block( + state, + sf->block, + sf->block_ctx, + next_bytenr, + sf->limit_nesting, + &sf->next_block_ctx, + &sf->next_block, + force_iodone_flag, + &sf->num_copies, + &sf->mirror_num, + &disk_key_ptr->key, + le64_to_cpu(disk_key_ptr->generation)); + if (sf->error) + goto one_stack_frame_backwards; + + if (NULL != sf->next_block) { + struct btrfs_header *const next_hdr = + (struct btrfs_header *) + sf->next_block_ctx.data; + + next_stack = btrfsic_stack_frame_alloc(); + if (NULL == next_stack) + goto one_stack_frame_backwards; + + next_stack->i = -1; + next_stack->block = sf->next_block; + next_stack->block_ctx = &sf->next_block_ctx; + next_stack->next_block = NULL; + next_stack->hdr = next_hdr; + next_stack->limit_nesting = + sf->limit_nesting - 1; + next_stack->prev = sf; + sf = next_stack; + goto continue_with_new_stack_frame; + } + + goto continue_with_current_node_stack_frame; + } + } + +one_stack_frame_backwards: + if (NULL != sf->prev) { + struct btrfsic_stack_frame *const prev = sf->prev; + + /* the one for the initial block is freed in the caller */ + btrfsic_release_block_ctx(sf->block_ctx); + + if (sf->error) { + prev->error = sf->error; + btrfsic_stack_frame_free(sf); + sf = prev; + goto one_stack_frame_backwards; + } + + btrfsic_stack_frame_free(sf); + sf = prev; + goto continue_with_new_stack_frame; + } else { + BUG_ON(&initial_stack_frame != sf); + } + + return sf->error; +} + +static int btrfsic_create_link_to_next_block( + struct btrfsic_state *state, + struct btrfsic_block *block, + struct btrfsic_block_data_ctx *block_ctx, + u64 next_bytenr, + int limit_nesting, + struct btrfsic_block_data_ctx *next_block_ctx, + struct btrfsic_block **next_blockp, + int force_iodone_flag, + int *num_copiesp, int *mirror_nump, + struct btrfs_disk_key *disk_key, + u64 parent_generation) +{ + struct btrfsic_block *next_block = NULL; + int ret; + struct btrfsic_block_link *l; + int did_alloc_block_link; + int block_was_created; + + *next_blockp = NULL; + if (0 == *num_copiesp) { + *num_copiesp = + btrfs_num_copies(&state->root->fs_info->mapping_tree, + next_bytenr, PAGE_SIZE); + if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES) + printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n", + (unsigned long long)next_bytenr, *num_copiesp); + *mirror_nump = 1; + } + + if (*mirror_nump > *num_copiesp) + return 0; + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "btrfsic_create_link_to_next_block(mirror_num=%d)\n", + *mirror_nump); + ret = btrfsic_map_block(state, next_bytenr, + BTRFSIC_BLOCK_SIZE, + next_block_ctx, *mirror_nump); + if (ret) { + printk(KERN_INFO + "btrfsic: btrfsic_map_block(@%llu, mirror=%d) failed!\n", + (unsigned long long)next_bytenr, *mirror_nump); + btrfsic_release_block_ctx(next_block_ctx); + *next_blockp = NULL; + return -1; + } + + next_block = btrfsic_block_lookup_or_add(state, + next_block_ctx, "referenced ", + 1, force_iodone_flag, + !force_iodone_flag, + *mirror_nump, + &block_was_created); + if (NULL == next_block) { + btrfsic_release_block_ctx(next_block_ctx); + *next_blockp = NULL; + return -1; + } + if (block_was_created) { + l = NULL; + next_block->generation = BTRFSIC_GENERATION_UNKNOWN; + } else { + if (next_block->logical_bytenr != next_bytenr && + !(!next_block->is_metadata && + 0 == next_block->logical_bytenr)) { + printk(KERN_INFO + "Referenced block @%llu (%s/%llu/%d)" + " found in hash table, %c," + " bytenr mismatch (!= stored %llu).\n", + (unsigned long long)next_bytenr, + next_block_ctx->dev->name, + (unsigned long long)next_block_ctx->dev_bytenr, + *mirror_nump, + btrfsic_get_block_type(state, next_block), + (unsigned long long)next_block->logical_bytenr); + } else if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "Referenced block @%llu (%s/%llu/%d)" + " found in hash table, %c.\n", + (unsigned long long)next_bytenr, + next_block_ctx->dev->name, + (unsigned long long)next_block_ctx->dev_bytenr, + *mirror_nump, + btrfsic_get_block_type(state, next_block)); + next_block->logical_bytenr = next_bytenr; + + next_block->mirror_num = *mirror_nump; + l = btrfsic_block_link_hashtable_lookup( + next_block_ctx->dev->bdev, + next_block_ctx->dev_bytenr, + block_ctx->dev->bdev, + block_ctx->dev_bytenr, + &state->block_link_hashtable); + } + + next_block->disk_key = *disk_key; + if (NULL == l) { + l = btrfsic_block_link_alloc(); + if (NULL == l) { + printk(KERN_INFO "btrfsic: error, kmalloc failed!\n"); + btrfsic_release_block_ctx(next_block_ctx); + *next_blockp = NULL; + return -1; + } + + did_alloc_block_link = 1; + l->block_ref_to = next_block; + l->block_ref_from = block; + l->ref_cnt = 1; + l->parent_generation = parent_generation; + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + btrfsic_print_add_link(state, l); + + list_add(&l->node_ref_to, &block->ref_to_list); + list_add(&l->node_ref_from, &next_block->ref_from_list); + + btrfsic_block_link_hashtable_add(l, + &state->block_link_hashtable); + } else { + did_alloc_block_link = 0; + if (0 == limit_nesting) { + l->ref_cnt++; + l->parent_generation = parent_generation; + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + btrfsic_print_add_link(state, l); + } + } + + if (limit_nesting > 0 && did_alloc_block_link) { + ret = btrfsic_read_block(state, next_block_ctx); + if (ret < (int)BTRFSIC_BLOCK_SIZE) { + printk(KERN_INFO + "btrfsic: read block @logical %llu failed!\n", + (unsigned long long)next_bytenr); + btrfsic_release_block_ctx(next_block_ctx); + *next_blockp = NULL; + return -1; + } + + *next_blockp = next_block; + } else { + *next_blockp = NULL; + } + (*mirror_nump)++; + + return 0; +} + +static int btrfsic_handle_extent_data( + struct btrfsic_state *state, + struct btrfsic_block *block, + struct btrfsic_block_data_ctx *block_ctx, + u32 item_offset, int force_iodone_flag) +{ + int ret; + struct btrfs_file_extent_item *file_extent_item = + (struct btrfs_file_extent_item *)(block_ctx->data + + offsetof(struct btrfs_leaf, + items) + item_offset); + u64 next_bytenr = + le64_to_cpu(file_extent_item->disk_bytenr) + + le64_to_cpu(file_extent_item->offset); + u64 num_bytes = le64_to_cpu(file_extent_item->num_bytes); + u64 generation = le64_to_cpu(file_extent_item->generation); + struct btrfsic_block_link *l; + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERY_VERBOSE) + printk(KERN_INFO "extent_data: type %u, disk_bytenr = %llu," + " offset = %llu, num_bytes = %llu\n", + file_extent_item->type, + (unsigned long long) + le64_to_cpu(file_extent_item->disk_bytenr), + (unsigned long long) + le64_to_cpu(file_extent_item->offset), + (unsigned long long) + le64_to_cpu(file_extent_item->num_bytes)); + if (BTRFS_FILE_EXTENT_REG != file_extent_item->type || + ((u64)0) == le64_to_cpu(file_extent_item->disk_bytenr)) + return 0; + while (num_bytes > 0) { + u32 chunk_len; + int num_copies; + int mirror_num; + + if (num_bytes > BTRFSIC_BLOCK_SIZE) + chunk_len = BTRFSIC_BLOCK_SIZE; + else + chunk_len = num_bytes; + + num_copies = + btrfs_num_copies(&state->root->fs_info->mapping_tree, + next_bytenr, PAGE_SIZE); + if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES) + printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n", + (unsigned long long)next_bytenr, num_copies); + for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) { + struct btrfsic_block_data_ctx next_block_ctx; + struct btrfsic_block *next_block; + int block_was_created; + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO "btrfsic_handle_extent_data(" + "mirror_num=%d)\n", mirror_num); + if (state->print_mask & BTRFSIC_PRINT_MASK_VERY_VERBOSE) + printk(KERN_INFO + "\tdisk_bytenr = %llu, num_bytes %u\n", + (unsigned long long)next_bytenr, + chunk_len); + ret = btrfsic_map_block(state, next_bytenr, + chunk_len, &next_block_ctx, + mirror_num); + if (ret) { + printk(KERN_INFO + "btrfsic: btrfsic_map_block(@%llu," + " mirror=%d) failed!\n", + (unsigned long long)next_bytenr, + mirror_num); + return -1; + } + + next_block = btrfsic_block_lookup_or_add( + state, + &next_block_ctx, + "referenced ", + 0, + force_iodone_flag, + !force_iodone_flag, + mirror_num, + &block_was_created); + if (NULL == next_block) { + printk(KERN_INFO + "btrfsic: error, kmalloc failed!\n"); + btrfsic_release_block_ctx(&next_block_ctx); + return -1; + } + if (!block_was_created) { + if (next_block->logical_bytenr != next_bytenr && + !(!next_block->is_metadata && + 0 == next_block->logical_bytenr)) { + printk(KERN_INFO + "Referenced block" + " @%llu (%s/%llu/%d)" + " found in hash table, D," + " bytenr mismatch" + " (!= stored %llu).\n", + (unsigned long long)next_bytenr, + next_block_ctx.dev->name, + (unsigned long long) + next_block_ctx.dev_bytenr, + mirror_num, + (unsigned long long) + next_block->logical_bytenr); + } + next_block->logical_bytenr = next_bytenr; + next_block->mirror_num = mirror_num; + } + + l = btrfsic_block_link_lookup_or_add(state, + &next_block_ctx, + next_block, block, + generation); + btrfsic_release_block_ctx(&next_block_ctx); + if (NULL == l) + return -1; + } + + next_bytenr += chunk_len; + num_bytes -= chunk_len; + } + + return 0; +} + +static int btrfsic_map_block(struct btrfsic_state *state, u64 bytenr, u32 len, + struct btrfsic_block_data_ctx *block_ctx_out, + int mirror_num) +{ + int ret; + u64 length; + struct btrfs_bio *multi = NULL; + struct btrfs_device *device; + + length = len; + ret = btrfs_map_block(&state->root->fs_info->mapping_tree, READ, + bytenr, &length, &multi, mirror_num); + + device = multi->stripes[0].dev; + block_ctx_out->dev = btrfsic_dev_state_lookup(device->bdev); + block_ctx_out->dev_bytenr = multi->stripes[0].physical; + block_ctx_out->start = bytenr; + block_ctx_out->len = len; + block_ctx_out->data = NULL; + block_ctx_out->bh = NULL; + + if (0 == ret) + kfree(multi); + if (NULL == block_ctx_out->dev) { + ret = -ENXIO; + printk(KERN_INFO "btrfsic: error, cannot lookup dev (#1)!\n"); + } + + return ret; +} + +static int btrfsic_map_superblock(struct btrfsic_state *state, u64 bytenr, + u32 len, struct block_device *bdev, + struct btrfsic_block_data_ctx *block_ctx_out) +{ + block_ctx_out->dev = btrfsic_dev_state_lookup(bdev); + block_ctx_out->dev_bytenr = bytenr; + block_ctx_out->start = bytenr; + block_ctx_out->len = len; + block_ctx_out->data = NULL; + block_ctx_out->bh = NULL; + if (NULL != block_ctx_out->dev) { + return 0; + } else { + printk(KERN_INFO "btrfsic: error, cannot lookup dev (#2)!\n"); + return -ENXIO; + } +} + +static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx) +{ + if (NULL != block_ctx->bh) { + brelse(block_ctx->bh); + block_ctx->bh = NULL; + } +} + +static int btrfsic_read_block(struct btrfsic_state *state, + struct btrfsic_block_data_ctx *block_ctx) +{ + block_ctx->bh = NULL; + if (block_ctx->dev_bytenr & 4095) { + printk(KERN_INFO + "btrfsic: read_block() with unaligned bytenr %llu\n", + (unsigned long long)block_ctx->dev_bytenr); + return -1; + } + if (block_ctx->len > 4096) { + printk(KERN_INFO + "btrfsic: read_block() with too huge size %d\n", + block_ctx->len); + return -1; + } + + block_ctx->bh = __bread(block_ctx->dev->bdev, + block_ctx->dev_bytenr >> 12, 4096); + if (NULL == block_ctx->bh) + return -1; + block_ctx->data = block_ctx->bh->b_data; + + return block_ctx->len; +} + +static void btrfsic_dump_database(struct btrfsic_state *state) +{ + struct list_head *elem_all; + + BUG_ON(NULL == state); + + printk(KERN_INFO "all_blocks_list:\n"); + list_for_each(elem_all, &state->all_blocks_list) { + const struct btrfsic_block *const b_all = + list_entry(elem_all, struct btrfsic_block, + all_blocks_node); + struct list_head *elem_ref_to; + struct list_head *elem_ref_from; + + printk(KERN_INFO "%c-block @%llu (%s/%llu/%d)\n", + btrfsic_get_block_type(state, b_all), + (unsigned long long)b_all->logical_bytenr, + b_all->dev_state->name, + (unsigned long long)b_all->dev_bytenr, + b_all->mirror_num); + + list_for_each(elem_ref_to, &b_all->ref_to_list) { + const struct btrfsic_block_link *const l = + list_entry(elem_ref_to, + struct btrfsic_block_link, + node_ref_to); + + printk(KERN_INFO " %c @%llu (%s/%llu/%d)" + " refers %u* to" + " %c @%llu (%s/%llu/%d)\n", + btrfsic_get_block_type(state, b_all), + (unsigned long long)b_all->logical_bytenr, + b_all->dev_state->name, + (unsigned long long)b_all->dev_bytenr, + b_all->mirror_num, + l->ref_cnt, + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long) + l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num); + } + + list_for_each(elem_ref_from, &b_all->ref_from_list) { + const struct btrfsic_block_link *const l = + list_entry(elem_ref_from, + struct btrfsic_block_link, + node_ref_from); + + printk(KERN_INFO " %c @%llu (%s/%llu/%d)" + " is ref %u* from" + " %c @%llu (%s/%llu/%d)\n", + btrfsic_get_block_type(state, b_all), + (unsigned long long)b_all->logical_bytenr, + b_all->dev_state->name, + (unsigned long long)b_all->dev_bytenr, + b_all->mirror_num, + l->ref_cnt, + btrfsic_get_block_type(state, l->block_ref_from), + (unsigned long long) + l->block_ref_from->logical_bytenr, + l->block_ref_from->dev_state->name, + (unsigned long long) + l->block_ref_from->dev_bytenr, + l->block_ref_from->mirror_num); + } + + printk(KERN_INFO "\n"); + } +} + +/* + * Test whether the disk block contains a tree block (leaf or node) + * (note that this test fails for the super block) + */ +static int btrfsic_test_for_metadata(struct btrfsic_state *state, + const u8 *data, unsigned int size) +{ + struct btrfs_header *h; + u8 csum[BTRFS_CSUM_SIZE]; + u32 crc = ~(u32)0; + int fail = 0; + int crc_fail = 0; + + h = (struct btrfs_header *)data; + + if (memcmp(h->fsid, state->root->fs_info->fsid, BTRFS_UUID_SIZE)) + fail++; + + crc = crc32c(crc, data + BTRFS_CSUM_SIZE, PAGE_SIZE - BTRFS_CSUM_SIZE); + btrfs_csum_final(crc, csum); + if (memcmp(csum, h->csum, state->csum_size)) + crc_fail++; + + return fail || crc_fail; +} + +static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state, + u64 dev_bytenr, + u8 *mapped_data, unsigned int len, + struct bio *bio, + int *bio_is_patched, + struct buffer_head *bh, + int submit_bio_bh_rw) +{ + int is_metadata; + struct btrfsic_block *block; + struct btrfsic_block_data_ctx block_ctx; + int ret; + struct btrfsic_state *state = dev_state->state; + struct block_device *bdev = dev_state->bdev; + + WARN_ON(len > PAGE_SIZE); + is_metadata = (0 == btrfsic_test_for_metadata(state, mapped_data, len)); + if (NULL != bio_is_patched) + *bio_is_patched = 0; + + block = btrfsic_block_hashtable_lookup(bdev, dev_bytenr, + &state->block_hashtable); + if (NULL != block) { + u64 bytenr; + struct list_head *elem_ref_to; + struct list_head *tmp_ref_to; + + if (block->is_superblock) { + bytenr = le64_to_cpu(((struct btrfs_super_block *) + mapped_data)->bytenr); + is_metadata = 1; + if (state->print_mask & + BTRFSIC_PRINT_MASK_TREE_BEFORE_SB_WRITE) { + printk(KERN_INFO + "[before new superblock is written]:\n"); + btrfsic_dump_tree_sub(state, block, 0); + } + } + if (is_metadata) { + if (!block->is_superblock) { + bytenr = le64_to_cpu(((struct btrfs_header *) + mapped_data)->bytenr); + btrfsic_cmp_log_and_dev_bytenr(state, bytenr, + dev_state, + dev_bytenr, + mapped_data); + } + if (block->logical_bytenr != bytenr) { + printk(KERN_INFO + "Written block @%llu (%s/%llu/%d)" + " found in hash table, %c," + " bytenr mismatch" + " (!= stored %llu).\n", + (unsigned long long)bytenr, + dev_state->name, + (unsigned long long)dev_bytenr, + block->mirror_num, + btrfsic_get_block_type(state, block), + (unsigned long long) + block->logical_bytenr); + block->logical_bytenr = bytenr; + } else if (state->print_mask & + BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "Written block @%llu (%s/%llu/%d)" + " found in hash table, %c.\n", + (unsigned long long)bytenr, + dev_state->name, + (unsigned long long)dev_bytenr, + block->mirror_num, + btrfsic_get_block_type(state, block)); + } else { + bytenr = block->logical_bytenr; + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "Written block @%llu (%s/%llu/%d)" + " found in hash table, %c.\n", + (unsigned long long)bytenr, + dev_state->name, + (unsigned long long)dev_bytenr, + block->mirror_num, + btrfsic_get_block_type(state, block)); + } + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "ref_to_list: %cE, ref_from_list: %cE\n", + list_empty(&block->ref_to_list) ? ' ' : '!', + list_empty(&block->ref_from_list) ? ' ' : '!'); + if (btrfsic_is_block_ref_by_superblock(state, block, 0)) { + printk(KERN_INFO "btrfs: attempt to overwrite %c-block" + " @%llu (%s/%llu/%d), old(gen=%llu," + " objectid=%llu, type=%d, offset=%llu)," + " new(gen=%llu)," + " which is referenced by most recent superblock" + " (superblockgen=%llu)!\n", + btrfsic_get_block_type(state, block), + (unsigned long long)bytenr, + dev_state->name, + (unsigned long long)dev_bytenr, + block->mirror_num, + (unsigned long long)block->generation, + (unsigned long long) + le64_to_cpu(block->disk_key.objectid), + block->disk_key.type, + (unsigned long long) + le64_to_cpu(block->disk_key.offset), + (unsigned long long) + le64_to_cpu(((struct btrfs_header *) + mapped_data)->generation), + (unsigned long long) + state->max_superblock_generation); + btrfsic_dump_tree(state); + } + + if (!block->is_iodone && !block->never_written) { + printk(KERN_INFO "btrfs: attempt to overwrite %c-block" + " @%llu (%s/%llu/%d), oldgen=%llu, newgen=%llu," + " which is not yet iodone!\n", + btrfsic_get_block_type(state, block), + (unsigned long long)bytenr, + dev_state->name, + (unsigned long long)dev_bytenr, + block->mirror_num, + (unsigned long long)block->generation, + (unsigned long long) + le64_to_cpu(((struct btrfs_header *) + mapped_data)->generation)); + /* it would not be safe to go on */ + btrfsic_dump_tree(state); + return; + } + + /* + * Clear all references of this block. Do not free + * the block itself even if is not referenced anymore + * because it still carries valueable information + * like whether it was ever written and IO completed. + */ + list_for_each_safe(elem_ref_to, tmp_ref_to, + &block->ref_to_list) { + struct btrfsic_block_link *const l = + list_entry(elem_ref_to, + struct btrfsic_block_link, + node_ref_to); + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + btrfsic_print_rem_link(state, l); + l->ref_cnt--; + if (0 == l->ref_cnt) { + list_del(&l->node_ref_to); + list_del(&l->node_ref_from); + btrfsic_block_link_hashtable_remove(l); + btrfsic_block_link_free(l); + } + } + + if (block->is_superblock) + ret = btrfsic_map_superblock(state, bytenr, len, + bdev, &block_ctx); + else + ret = btrfsic_map_block(state, bytenr, len, + &block_ctx, 0); + if (ret) { + printk(KERN_INFO + "btrfsic: btrfsic_map_block(root @%llu)" + " failed!\n", (unsigned long long)bytenr); + return; + } + block_ctx.data = mapped_data; + /* the following is required in case of writes to mirrors, + * use the same that was used for the lookup */ + block_ctx.dev = dev_state; + block_ctx.dev_bytenr = dev_bytenr; + + if (is_metadata || state->include_extent_data) { + block->never_written = 0; + block->iodone_w_error = 0; + if (NULL != bio) { + block->is_iodone = 0; + BUG_ON(NULL == bio_is_patched); + if (!*bio_is_patched) { + block->orig_bio_bh_private = + bio->bi_private; + block->orig_bio_bh_end_io.bio = + bio->bi_end_io; + block->next_in_same_bio = NULL; + bio->bi_private = block; + bio->bi_end_io = btrfsic_bio_end_io; + *bio_is_patched = 1; + } else { + struct btrfsic_block *chained_block = + (struct btrfsic_block *) + bio->bi_private; + + BUG_ON(NULL == chained_block); + block->orig_bio_bh_private = + chained_block->orig_bio_bh_private; + block->orig_bio_bh_end_io.bio = + chained_block->orig_bio_bh_end_io. + bio; + block->next_in_same_bio = chained_block; + bio->bi_private = block; + } + } else if (NULL != bh) { + block->is_iodone = 0; + block->orig_bio_bh_private = bh->b_private; + block->orig_bio_bh_end_io.bh = bh->b_end_io; + block->next_in_same_bio = NULL; + bh->b_private = block; + bh->b_end_io = btrfsic_bh_end_io; + } else { + block->is_iodone = 1; + block->orig_bio_bh_private = NULL; + block->orig_bio_bh_end_io.bio = NULL; + block->next_in_same_bio = NULL; + } + } + + block->flush_gen = dev_state->last_flush_gen + 1; + block->submit_bio_bh_rw = submit_bio_bh_rw; + if (is_metadata) { + block->logical_bytenr = bytenr; + block->is_metadata = 1; + if (block->is_superblock) { + ret = btrfsic_process_written_superblock( + state, + block, + (struct btrfs_super_block *) + mapped_data); + if (state->print_mask & + BTRFSIC_PRINT_MASK_TREE_AFTER_SB_WRITE) { + printk(KERN_INFO + "[after new superblock is written]:\n"); + btrfsic_dump_tree_sub(state, block, 0); + } + } else { + block->mirror_num = 0; /* unknown */ + ret = btrfsic_process_metablock( + state, + block, + &block_ctx, + (struct btrfs_header *) + block_ctx.data, + 0, 0); + } + if (ret) + printk(KERN_INFO + "btrfsic: btrfsic_process_metablock" + "(root @%llu) failed!\n", + (unsigned long long)dev_bytenr); + } else { + block->is_metadata = 0; + block->mirror_num = 0; /* unknown */ + block->generation = BTRFSIC_GENERATION_UNKNOWN; + if (!state->include_extent_data + && list_empty(&block->ref_from_list)) { + /* + * disk block is overwritten with extent + * data (not meta data) and we are configured + * to not include extent data: take the + * chance and free the block's memory + */ + btrfsic_block_hashtable_remove(block); + list_del(&block->all_blocks_node); + btrfsic_block_free(block); + } + } + btrfsic_release_block_ctx(&block_ctx); + } else { + /* block has not been found in hash table */ + u64 bytenr; + + if (!is_metadata) { + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO "Written block (%s/%llu/?)" + " !found in hash table, D.\n", + dev_state->name, + (unsigned long long)dev_bytenr); + if (!state->include_extent_data) + return; /* ignore that written D block */ + + /* this is getting ugly for the + * include_extent_data case... */ + bytenr = 0; /* unknown */ + block_ctx.start = bytenr; + block_ctx.len = len; + block_ctx.bh = NULL; + } else { + bytenr = le64_to_cpu(((struct btrfs_header *) + mapped_data)->bytenr); + btrfsic_cmp_log_and_dev_bytenr(state, bytenr, dev_state, + dev_bytenr, + mapped_data); + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "Written block @%llu (%s/%llu/?)" + " !found in hash table, M.\n", + (unsigned long long)bytenr, + dev_state->name, + (unsigned long long)dev_bytenr); + + ret = btrfsic_map_block(state, bytenr, len, &block_ctx, + 0); + if (ret) { + printk(KERN_INFO + "btrfsic: btrfsic_map_block(root @%llu)" + " failed!\n", + (unsigned long long)dev_bytenr); + return; + } + } + block_ctx.data = mapped_data; + /* the following is required in case of writes to mirrors, + * use the same that was used for the lookup */ + block_ctx.dev = dev_state; + block_ctx.dev_bytenr = dev_bytenr; + + block = btrfsic_block_alloc(); + if (NULL == block) { + printk(KERN_INFO "btrfsic: error, kmalloc failed!\n"); + btrfsic_release_block_ctx(&block_ctx); + return; + } + block->dev_state = dev_state; + block->dev_bytenr = dev_bytenr; + block->logical_bytenr = bytenr; + block->is_metadata = is_metadata; + block->never_written = 0; + block->iodone_w_error = 0; + block->mirror_num = 0; /* unknown */ + block->flush_gen = dev_state->last_flush_gen + 1; + block->submit_bio_bh_rw = submit_bio_bh_rw; + if (NULL != bio) { + block->is_iodone = 0; + BUG_ON(NULL == bio_is_patched); + if (!*bio_is_patched) { + block->orig_bio_bh_private = bio->bi_private; + block->orig_bio_bh_end_io.bio = bio->bi_end_io; + block->next_in_same_bio = NULL; + bio->bi_private = block; + bio->bi_end_io = btrfsic_bio_end_io; + *bio_is_patched = 1; + } else { + struct btrfsic_block *chained_block = + (struct btrfsic_block *) + bio->bi_private; + + BUG_ON(NULL == chained_block); + block->orig_bio_bh_private = + chained_block->orig_bio_bh_private; + block->orig_bio_bh_end_io.bio = + chained_block->orig_bio_bh_end_io.bio; + block->next_in_same_bio = chained_block; + bio->bi_private = block; + } + } else if (NULL != bh) { + block->is_iodone = 0; + block->orig_bio_bh_private = bh->b_private; + block->orig_bio_bh_end_io.bh = bh->b_end_io; + block->next_in_same_bio = NULL; + bh->b_private = block; + bh->b_end_io = btrfsic_bh_end_io; + } else { + block->is_iodone = 1; + block->orig_bio_bh_private = NULL; + block->orig_bio_bh_end_io.bio = NULL; + block->next_in_same_bio = NULL; + } + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "New written %c-block @%llu (%s/%llu/%d)\n", + is_metadata ? 'M' : 'D', + (unsigned long long)block->logical_bytenr, + block->dev_state->name, + (unsigned long long)block->dev_bytenr, + block->mirror_num); + list_add(&block->all_blocks_node, &state->all_blocks_list); + btrfsic_block_hashtable_add(block, &state->block_hashtable); + + if (is_metadata) { + ret = btrfsic_process_metablock(state, block, + &block_ctx, + (struct btrfs_header *) + block_ctx.data, 0, 0); + if (ret) + printk(KERN_INFO + "btrfsic: process_metablock(root @%llu)" + " failed!\n", + (unsigned long long)dev_bytenr); + } + btrfsic_release_block_ctx(&block_ctx); + } +} + +static void btrfsic_bio_end_io(struct bio *bp, int bio_error_status) +{ + struct btrfsic_block *block = (struct btrfsic_block *)bp->bi_private; + int iodone_w_error; + + /* mutex is not held! This is not save if IO is not yet completed + * on umount */ + iodone_w_error = 0; + if (bio_error_status) + iodone_w_error = 1; + + BUG_ON(NULL == block); + bp->bi_private = block->orig_bio_bh_private; + bp->bi_end_io = block->orig_bio_bh_end_io.bio; + + do { + struct btrfsic_block *next_block; + struct btrfsic_dev_state *const dev_state = block->dev_state; + + if ((dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_END_IO_BIO_BH)) + printk(KERN_INFO + "bio_end_io(err=%d) for %c @%llu (%s/%llu/%d)\n", + bio_error_status, + btrfsic_get_block_type(dev_state->state, block), + (unsigned long long)block->logical_bytenr, + dev_state->name, + (unsigned long long)block->dev_bytenr, + block->mirror_num); + next_block = block->next_in_same_bio; + block->iodone_w_error = iodone_w_error; + if (block->submit_bio_bh_rw & REQ_FLUSH) { + dev_state->last_flush_gen++; + if ((dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_END_IO_BIO_BH)) + printk(KERN_INFO + "bio_end_io() new %s flush_gen=%llu\n", + dev_state->name, + (unsigned long long) + dev_state->last_flush_gen); + } + if (block->submit_bio_bh_rw & REQ_FUA) + block->flush_gen = 0; /* FUA completed means block is + * on disk */ + block->is_iodone = 1; /* for FLUSH, this releases the block */ + block = next_block; + } while (NULL != block); + + bp->bi_end_io(bp, bio_error_status); +} + +static void btrfsic_bh_end_io(struct buffer_head *bh, int uptodate) +{ + struct btrfsic_block *block = (struct btrfsic_block *)bh->b_private; + int iodone_w_error = !uptodate; + struct btrfsic_dev_state *dev_state; + + BUG_ON(NULL == block); + dev_state = block->dev_state; + if ((dev_state->state->print_mask & BTRFSIC_PRINT_MASK_END_IO_BIO_BH)) + printk(KERN_INFO + "bh_end_io(error=%d) for %c @%llu (%s/%llu/%d)\n", + iodone_w_error, + btrfsic_get_block_type(dev_state->state, block), + (unsigned long long)block->logical_bytenr, + block->dev_state->name, + (unsigned long long)block->dev_bytenr, + block->mirror_num); + + block->iodone_w_error = iodone_w_error; + if (block->submit_bio_bh_rw & REQ_FLUSH) { + dev_state->last_flush_gen++; + if ((dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_END_IO_BIO_BH)) + printk(KERN_INFO + "bh_end_io() new %s flush_gen=%llu\n", + dev_state->name, + (unsigned long long)dev_state->last_flush_gen); + } + if (block->submit_bio_bh_rw & REQ_FUA) + block->flush_gen = 0; /* FUA completed means block is on disk */ + + bh->b_private = block->orig_bio_bh_private; + bh->b_end_io = block->orig_bio_bh_end_io.bh; + block->is_iodone = 1; /* for FLUSH, this releases the block */ + bh->b_end_io(bh, uptodate); +} + +static int btrfsic_process_written_superblock( + struct btrfsic_state *state, + struct btrfsic_block *const superblock, + struct btrfs_super_block *const super_hdr) +{ + int pass; + + superblock->generation = btrfs_super_generation(super_hdr); + if (!(superblock->generation > state->max_superblock_generation || + 0 == state->max_superblock_generation)) { + if (state->print_mask & BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE) + printk(KERN_INFO + "btrfsic: superblock @%llu (%s/%llu/%d)" + " with old gen %llu <= %llu\n", + (unsigned long long)superblock->logical_bytenr, + superblock->dev_state->name, + (unsigned long long)superblock->dev_bytenr, + superblock->mirror_num, + (unsigned long long) + btrfs_super_generation(super_hdr), + (unsigned long long) + state->max_superblock_generation); + } else { + if (state->print_mask & BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE) + printk(KERN_INFO + "btrfsic: got new superblock @%llu (%s/%llu/%d)" + " with new gen %llu > %llu\n", + (unsigned long long)superblock->logical_bytenr, + superblock->dev_state->name, + (unsigned long long)superblock->dev_bytenr, + superblock->mirror_num, + (unsigned long long) + btrfs_super_generation(super_hdr), + (unsigned long long) + state->max_superblock_generation); + + state->max_superblock_generation = + btrfs_super_generation(super_hdr); + state->latest_superblock = superblock; + } + + for (pass = 0; pass < 3; pass++) { + int ret; + u64 next_bytenr; + struct btrfsic_block *next_block; + struct btrfsic_block_data_ctx tmp_next_block_ctx; + struct btrfsic_block_link *l; + int num_copies; + int mirror_num; + const char *additional_string = NULL; + struct btrfs_disk_key tmp_disk_key; + + tmp_disk_key.type = BTRFS_ROOT_ITEM_KEY; + tmp_disk_key.offset = 0; + + switch (pass) { + case 0: + tmp_disk_key.objectid = + cpu_to_le64(BTRFS_ROOT_TREE_OBJECTID); + additional_string = "root "; + next_bytenr = btrfs_super_root(super_hdr); + if (state->print_mask & + BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION) + printk(KERN_INFO "root@%llu\n", + (unsigned long long)next_bytenr); + break; + case 1: + tmp_disk_key.objectid = + cpu_to_le64(BTRFS_CHUNK_TREE_OBJECTID); + additional_string = "chunk "; + next_bytenr = btrfs_super_chunk_root(super_hdr); + if (state->print_mask & + BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION) + printk(KERN_INFO "chunk@%llu\n", + (unsigned long long)next_bytenr); + break; + case 2: + tmp_disk_key.objectid = + cpu_to_le64(BTRFS_TREE_LOG_OBJECTID); + additional_string = "log "; + next_bytenr = btrfs_super_log_root(super_hdr); + if (0 == next_bytenr) + continue; + if (state->print_mask & + BTRFSIC_PRINT_MASK_ROOT_CHUNK_LOG_TREE_LOCATION) + printk(KERN_INFO "log@%llu\n", + (unsigned long long)next_bytenr); + break; + } + + num_copies = + btrfs_num_copies(&state->root->fs_info->mapping_tree, + next_bytenr, PAGE_SIZE); + if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES) + printk(KERN_INFO "num_copies(log_bytenr=%llu) = %d\n", + (unsigned long long)next_bytenr, num_copies); + for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) { + int was_created; + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "btrfsic_process_written_superblock(" + "mirror_num=%d)\n", mirror_num); + ret = btrfsic_map_block(state, next_bytenr, PAGE_SIZE, + &tmp_next_block_ctx, + mirror_num); + if (ret) { + printk(KERN_INFO + "btrfsic: btrfsic_map_block(@%llu," + " mirror=%d) failed!\n", + (unsigned long long)next_bytenr, + mirror_num); + return -1; + } + + next_block = btrfsic_block_lookup_or_add( + state, + &tmp_next_block_ctx, + additional_string, + 1, 0, 1, + mirror_num, + &was_created); + if (NULL == next_block) { + printk(KERN_INFO + "btrfsic: error, kmalloc failed!\n"); + btrfsic_release_block_ctx(&tmp_next_block_ctx); + return -1; + } + + next_block->disk_key = tmp_disk_key; + if (was_created) + next_block->generation = + BTRFSIC_GENERATION_UNKNOWN; + l = btrfsic_block_link_lookup_or_add( + state, + &tmp_next_block_ctx, + next_block, + superblock, + BTRFSIC_GENERATION_UNKNOWN); + btrfsic_release_block_ctx(&tmp_next_block_ctx); + if (NULL == l) + return -1; + } + } + + if (-1 == btrfsic_check_all_ref_blocks(state, superblock, 0)) { + WARN_ON(1); + btrfsic_dump_tree(state); + } + + return 0; +} + +static int btrfsic_check_all_ref_blocks(struct btrfsic_state *state, + struct btrfsic_block *const block, + int recursion_level) +{ + struct list_head *elem_ref_to; + int ret = 0; + + if (recursion_level >= 3 + BTRFS_MAX_LEVEL) { + /* + * Note that this situation can happen and does not + * indicate an error in regular cases. It happens + * when disk blocks are freed and later reused. + * The check-integrity module is not aware of any + * block free operations, it just recognizes block + * write operations. Therefore it keeps the linkage + * information for a block until a block is + * rewritten. This can temporarily cause incorrect + * and even circular linkage informations. This + * causes no harm unless such blocks are referenced + * by the most recent super block. + */ + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "btrfsic: abort cyclic linkage (case 1).\n"); + + return ret; + } + + /* + * This algorithm is recursive because the amount of used stack + * space is very small and the max recursion depth is limited. + */ + list_for_each(elem_ref_to, &block->ref_to_list) { + const struct btrfsic_block_link *const l = + list_entry(elem_ref_to, struct btrfsic_block_link, + node_ref_to); + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "rl=%d, %c @%llu (%s/%llu/%d)" + " %u* refers to %c @%llu (%s/%llu/%d)\n", + recursion_level, + btrfsic_get_block_type(state, block), + (unsigned long long)block->logical_bytenr, + block->dev_state->name, + (unsigned long long)block->dev_bytenr, + block->mirror_num, + l->ref_cnt, + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long) + l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num); + if (l->block_ref_to->never_written) { + printk(KERN_INFO "btrfs: attempt to write superblock" + " which references block %c @%llu (%s/%llu/%d)" + " which is never written!\n", + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long) + l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num); + ret = -1; + } else if (!l->block_ref_to->is_iodone) { + printk(KERN_INFO "btrfs: attempt to write superblock" + " which references block %c @%llu (%s/%llu/%d)" + " which is not yet iodone!\n", + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long) + l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num); + ret = -1; + } else if (l->parent_generation != + l->block_ref_to->generation && + BTRFSIC_GENERATION_UNKNOWN != + l->parent_generation && + BTRFSIC_GENERATION_UNKNOWN != + l->block_ref_to->generation) { + printk(KERN_INFO "btrfs: attempt to write superblock" + " which references block %c @%llu (%s/%llu/%d)" + " with generation %llu !=" + " parent generation %llu!\n", + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long) + l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num, + (unsigned long long)l->block_ref_to->generation, + (unsigned long long)l->parent_generation); + ret = -1; + } else if (l->block_ref_to->flush_gen > + l->block_ref_to->dev_state->last_flush_gen) { + printk(KERN_INFO "btrfs: attempt to write superblock" + " which references block %c @%llu (%s/%llu/%d)" + " which is not flushed out of disk's write cache" + " (block flush_gen=%llu," + " dev->flush_gen=%llu)!\n", + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long) + l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num, + (unsigned long long)block->flush_gen, + (unsigned long long) + l->block_ref_to->dev_state->last_flush_gen); + ret = -1; + } else if (-1 == btrfsic_check_all_ref_blocks(state, + l->block_ref_to, + recursion_level + + 1)) { + ret = -1; + } + } + + return ret; +} + +static int btrfsic_is_block_ref_by_superblock( + const struct btrfsic_state *state, + const struct btrfsic_block *block, + int recursion_level) +{ + struct list_head *elem_ref_from; + + if (recursion_level >= 3 + BTRFS_MAX_LEVEL) { + /* refer to comment at "abort cyclic linkage (case 1)" */ + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "btrfsic: abort cyclic linkage (case 2).\n"); + + return 0; + } + + /* + * This algorithm is recursive because the amount of used stack space + * is very small and the max recursion depth is limited. + */ + list_for_each(elem_ref_from, &block->ref_from_list) { + const struct btrfsic_block_link *const l = + list_entry(elem_ref_from, struct btrfsic_block_link, + node_ref_from); + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "rl=%d, %c @%llu (%s/%llu/%d)" + " is ref %u* from %c @%llu (%s/%llu/%d)\n", + recursion_level, + btrfsic_get_block_type(state, block), + (unsigned long long)block->logical_bytenr, + block->dev_state->name, + (unsigned long long)block->dev_bytenr, + block->mirror_num, + l->ref_cnt, + btrfsic_get_block_type(state, l->block_ref_from), + (unsigned long long) + l->block_ref_from->logical_bytenr, + l->block_ref_from->dev_state->name, + (unsigned long long) + l->block_ref_from->dev_bytenr, + l->block_ref_from->mirror_num); + if (l->block_ref_from->is_superblock && + state->latest_superblock->dev_bytenr == + l->block_ref_from->dev_bytenr && + state->latest_superblock->dev_state->bdev == + l->block_ref_from->dev_state->bdev) + return 1; + else if (btrfsic_is_block_ref_by_superblock(state, + l->block_ref_from, + recursion_level + + 1)) + return 1; + } + + return 0; +} + +static void btrfsic_print_add_link(const struct btrfsic_state *state, + const struct btrfsic_block_link *l) +{ + printk(KERN_INFO + "Add %u* link from %c @%llu (%s/%llu/%d)" + " to %c @%llu (%s/%llu/%d).\n", + l->ref_cnt, + btrfsic_get_block_type(state, l->block_ref_from), + (unsigned long long)l->block_ref_from->logical_bytenr, + l->block_ref_from->dev_state->name, + (unsigned long long)l->block_ref_from->dev_bytenr, + l->block_ref_from->mirror_num, + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long)l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num); +} + +static void btrfsic_print_rem_link(const struct btrfsic_state *state, + const struct btrfsic_block_link *l) +{ + printk(KERN_INFO + "Rem %u* link from %c @%llu (%s/%llu/%d)" + " to %c @%llu (%s/%llu/%d).\n", + l->ref_cnt, + btrfsic_get_block_type(state, l->block_ref_from), + (unsigned long long)l->block_ref_from->logical_bytenr, + l->block_ref_from->dev_state->name, + (unsigned long long)l->block_ref_from->dev_bytenr, + l->block_ref_from->mirror_num, + btrfsic_get_block_type(state, l->block_ref_to), + (unsigned long long)l->block_ref_to->logical_bytenr, + l->block_ref_to->dev_state->name, + (unsigned long long)l->block_ref_to->dev_bytenr, + l->block_ref_to->mirror_num); +} + +static char btrfsic_get_block_type(const struct btrfsic_state *state, + const struct btrfsic_block *block) +{ + if (block->is_superblock && + state->latest_superblock->dev_bytenr == block->dev_bytenr && + state->latest_superblock->dev_state->bdev == block->dev_state->bdev) + return 'S'; + else if (block->is_superblock) + return 's'; + else if (block->is_metadata) + return 'M'; + else + return 'D'; +} + +static void btrfsic_dump_tree(const struct btrfsic_state *state) +{ + btrfsic_dump_tree_sub(state, state->latest_superblock, 0); +} + +static void btrfsic_dump_tree_sub(const struct btrfsic_state *state, + const struct btrfsic_block *block, + int indent_level) +{ + struct list_head *elem_ref_to; + int indent_add; + static char buf[80]; + int cursor_position; + + /* + * Should better fill an on-stack buffer with a complete line and + * dump it at once when it is time to print a newline character. + */ + + /* + * This algorithm is recursive because the amount of used stack space + * is very small and the max recursion depth is limited. + */ + indent_add = sprintf(buf, "%c-%llu(%s/%llu/%d)", + btrfsic_get_block_type(state, block), + (unsigned long long)block->logical_bytenr, + block->dev_state->name, + (unsigned long long)block->dev_bytenr, + block->mirror_num); + if (indent_level + indent_add > BTRFSIC_TREE_DUMP_MAX_INDENT_LEVEL) { + printk("[...]\n"); + return; + } + printk(buf); + indent_level += indent_add; + if (list_empty(&block->ref_to_list)) { + printk("\n"); + return; + } + if (block->mirror_num > 1 && + !(state->print_mask & BTRFSIC_PRINT_MASK_TREE_WITH_ALL_MIRRORS)) { + printk(" [...]\n"); + return; + } + + cursor_position = indent_level; + list_for_each(elem_ref_to, &block->ref_to_list) { + const struct btrfsic_block_link *const l = + list_entry(elem_ref_to, struct btrfsic_block_link, + node_ref_to); + + while (cursor_position < indent_level) { + printk(" "); + cursor_position++; + } + if (l->ref_cnt > 1) + indent_add = sprintf(buf, " %d*--> ", l->ref_cnt); + else + indent_add = sprintf(buf, " --> "); + if (indent_level + indent_add > + BTRFSIC_TREE_DUMP_MAX_INDENT_LEVEL) { + printk("[...]\n"); + cursor_position = 0; + continue; + } + + printk(buf); + + btrfsic_dump_tree_sub(state, l->block_ref_to, + indent_level + indent_add); + cursor_position = 0; + } +} + +static struct btrfsic_block_link *btrfsic_block_link_lookup_or_add( + struct btrfsic_state *state, + struct btrfsic_block_data_ctx *next_block_ctx, + struct btrfsic_block *next_block, + struct btrfsic_block *from_block, + u64 parent_generation) +{ + struct btrfsic_block_link *l; + + l = btrfsic_block_link_hashtable_lookup(next_block_ctx->dev->bdev, + next_block_ctx->dev_bytenr, + from_block->dev_state->bdev, + from_block->dev_bytenr, + &state->block_link_hashtable); + if (NULL == l) { + l = btrfsic_block_link_alloc(); + if (NULL == l) { + printk(KERN_INFO + "btrfsic: error, kmalloc" " failed!\n"); + return NULL; + } + + l->block_ref_to = next_block; + l->block_ref_from = from_block; + l->ref_cnt = 1; + l->parent_generation = parent_generation; + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + btrfsic_print_add_link(state, l); + + list_add(&l->node_ref_to, &from_block->ref_to_list); + list_add(&l->node_ref_from, &next_block->ref_from_list); + + btrfsic_block_link_hashtable_add(l, + &state->block_link_hashtable); + } else { + l->ref_cnt++; + l->parent_generation = parent_generation; + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + btrfsic_print_add_link(state, l); + } + + return l; +} + +static struct btrfsic_block *btrfsic_block_lookup_or_add( + struct btrfsic_state *state, + struct btrfsic_block_data_ctx *block_ctx, + const char *additional_string, + int is_metadata, + int is_iodone, + int never_written, + int mirror_num, + int *was_created) +{ + struct btrfsic_block *block; + + block = btrfsic_block_hashtable_lookup(block_ctx->dev->bdev, + block_ctx->dev_bytenr, + &state->block_hashtable); + if (NULL == block) { + struct btrfsic_dev_state *dev_state; + + block = btrfsic_block_alloc(); + if (NULL == block) { + printk(KERN_INFO "btrfsic: error, kmalloc failed!\n"); + return NULL; + } + dev_state = btrfsic_dev_state_lookup(block_ctx->dev->bdev); + if (NULL == dev_state) { + printk(KERN_INFO + "btrfsic: error, lookup dev_state failed!\n"); + btrfsic_block_free(block); + return NULL; + } + block->dev_state = dev_state; + block->dev_bytenr = block_ctx->dev_bytenr; + block->logical_bytenr = block_ctx->start; + block->is_metadata = is_metadata; + block->is_iodone = is_iodone; + block->never_written = never_written; + block->mirror_num = mirror_num; + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + printk(KERN_INFO + "New %s%c-block @%llu (%s/%llu/%d)\n", + additional_string, + btrfsic_get_block_type(state, block), + (unsigned long long)block->logical_bytenr, + dev_state->name, + (unsigned long long)block->dev_bytenr, + mirror_num); + list_add(&block->all_blocks_node, &state->all_blocks_list); + btrfsic_block_hashtable_add(block, &state->block_hashtable); + if (NULL != was_created) + *was_created = 1; + } else { + if (NULL != was_created) + *was_created = 0; + } + + return block; +} + +static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state, + u64 bytenr, + struct btrfsic_dev_state *dev_state, + u64 dev_bytenr, char *data) +{ + int num_copies; + int mirror_num; + int ret; + struct btrfsic_block_data_ctx block_ctx; + int match = 0; + + num_copies = btrfs_num_copies(&state->root->fs_info->mapping_tree, + bytenr, PAGE_SIZE); + + for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) { + ret = btrfsic_map_block(state, bytenr, PAGE_SIZE, + &block_ctx, mirror_num); + if (ret) { + printk(KERN_INFO "btrfsic:" + " btrfsic_map_block(logical @%llu," + " mirror %d) failed!\n", + (unsigned long long)bytenr, mirror_num); + continue; + } + + if (dev_state->bdev == block_ctx.dev->bdev && + dev_bytenr == block_ctx.dev_bytenr) { + match++; + btrfsic_release_block_ctx(&block_ctx); + break; + } + btrfsic_release_block_ctx(&block_ctx); + } + + if (!match) { + printk(KERN_INFO "btrfs: attempt to write M-block which contains logical bytenr that doesn't map to dev+physical bytenr of submit_bio," + " buffer->log_bytenr=%llu, submit_bio(bdev=%s," + " phys_bytenr=%llu)!\n", + (unsigned long long)bytenr, dev_state->name, + (unsigned long long)dev_bytenr); + for (mirror_num = 1; mirror_num <= num_copies; mirror_num++) { + ret = btrfsic_map_block(state, bytenr, PAGE_SIZE, + &block_ctx, mirror_num); + if (ret) + continue; + + printk(KERN_INFO "Read logical bytenr @%llu maps to" + " (%s/%llu/%d)\n", + (unsigned long long)bytenr, + block_ctx.dev->name, + (unsigned long long)block_ctx.dev_bytenr, + mirror_num); + } + WARN_ON(1); + } +} + +static struct btrfsic_dev_state *btrfsic_dev_state_lookup( + struct block_device *bdev) +{ + struct btrfsic_dev_state *ds; + + ds = btrfsic_dev_state_hashtable_lookup(bdev, + &btrfsic_dev_state_hashtable); + return ds; +} + +int btrfsic_submit_bh(int rw, struct buffer_head *bh) +{ + struct btrfsic_dev_state *dev_state; + + if (!btrfsic_is_initialized) + return submit_bh(rw, bh); + + mutex_lock(&btrfsic_mutex); + /* since btrfsic_submit_bh() might also be called before + * btrfsic_mount(), this might return NULL */ + dev_state = btrfsic_dev_state_lookup(bh->b_bdev); + + /* Only called to write the superblock (incl. FLUSH/FUA) */ + if (NULL != dev_state && + (rw & WRITE) && bh->b_size > 0) { + u64 dev_bytenr; + + dev_bytenr = 4096 * bh->b_blocknr; + if (dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH) + printk(KERN_INFO + "submit_bh(rw=0x%x, blocknr=%lu (bytenr %llu)," + " size=%lu, data=%p, bdev=%p)\n", + rw, bh->b_blocknr, + (unsigned long long)dev_bytenr, bh->b_size, + bh->b_data, bh->b_bdev); + btrfsic_process_written_block(dev_state, dev_bytenr, + bh->b_data, bh->b_size, NULL, + NULL, bh, rw); + } else if (NULL != dev_state && (rw & REQ_FLUSH)) { + if (dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH) + printk(KERN_INFO + "submit_bh(rw=0x%x) FLUSH, bdev=%p)\n", + rw, bh->b_bdev); + if (!dev_state->dummy_block_for_bio_bh_flush.is_iodone) { + if ((dev_state->state->print_mask & + (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH | + BTRFSIC_PRINT_MASK_VERBOSE))) + printk(KERN_INFO + "btrfsic_submit_bh(%s) with FLUSH" + " but dummy block already in use" + " (ignored)!\n", + dev_state->name); + } else { + struct btrfsic_block *const block = + &dev_state->dummy_block_for_bio_bh_flush; + + block->is_iodone = 0; + block->never_written = 0; + block->iodone_w_error = 0; + block->flush_gen = dev_state->last_flush_gen + 1; + block->submit_bio_bh_rw = rw; + block->orig_bio_bh_private = bh->b_private; + block->orig_bio_bh_end_io.bh = bh->b_end_io; + block->next_in_same_bio = NULL; + bh->b_private = block; + bh->b_end_io = btrfsic_bh_end_io; + } + } + mutex_unlock(&btrfsic_mutex); + return submit_bh(rw, bh); +} + +void btrfsic_submit_bio(int rw, struct bio *bio) +{ + struct btrfsic_dev_state *dev_state; + + if (!btrfsic_is_initialized) { + submit_bio(rw, bio); + return; + } + + mutex_lock(&btrfsic_mutex); + /* since btrfsic_submit_bio() is also called before + * btrfsic_mount(), this might return NULL */ + dev_state = btrfsic_dev_state_lookup(bio->bi_bdev); + if (NULL != dev_state && + (rw & WRITE) && NULL != bio->bi_io_vec) { + unsigned int i; + u64 dev_bytenr; + int bio_is_patched; + + dev_bytenr = 512 * bio->bi_sector; + bio_is_patched = 0; + if (dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH) + printk(KERN_INFO + "submit_bio(rw=0x%x, bi_vcnt=%u," + " bi_sector=%lu (bytenr %llu), bi_bdev=%p)\n", + rw, bio->bi_vcnt, bio->bi_sector, + (unsigned long long)dev_bytenr, + bio->bi_bdev); + + for (i = 0; i < bio->bi_vcnt; i++) { + u8 *mapped_data; + + mapped_data = kmap(bio->bi_io_vec[i].bv_page); + if ((BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH | + BTRFSIC_PRINT_MASK_VERBOSE) == + (dev_state->state->print_mask & + (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH | + BTRFSIC_PRINT_MASK_VERBOSE))) + printk(KERN_INFO + "#%u: page=%p, mapped=%p, len=%u," + " offset=%u\n", + i, bio->bi_io_vec[i].bv_page, + mapped_data, + bio->bi_io_vec[i].bv_len, + bio->bi_io_vec[i].bv_offset); + btrfsic_process_written_block(dev_state, dev_bytenr, + mapped_data, + bio->bi_io_vec[i].bv_len, + bio, &bio_is_patched, + NULL, rw); + kunmap(bio->bi_io_vec[i].bv_page); + dev_bytenr += bio->bi_io_vec[i].bv_len; + } + } else if (NULL != dev_state && (rw & REQ_FLUSH)) { + if (dev_state->state->print_mask & + BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH) + printk(KERN_INFO + "submit_bio(rw=0x%x) FLUSH, bdev=%p)\n", + rw, bio->bi_bdev); + if (!dev_state->dummy_block_for_bio_bh_flush.is_iodone) { + if ((dev_state->state->print_mask & + (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH | + BTRFSIC_PRINT_MASK_VERBOSE))) + printk(KERN_INFO + "btrfsic_submit_bio(%s) with FLUSH" + " but dummy block already in use" + " (ignored)!\n", + dev_state->name); + } else { + struct btrfsic_block *const block = + &dev_state->dummy_block_for_bio_bh_flush; + + block->is_iodone = 0; + block->never_written = 0; + block->iodone_w_error = 0; + block->flush_gen = dev_state->last_flush_gen + 1; + block->submit_bio_bh_rw = rw; + block->orig_bio_bh_private = bio->bi_private; + block->orig_bio_bh_end_io.bio = bio->bi_end_io; + block->next_in_same_bio = NULL; + bio->bi_private = block; + bio->bi_end_io = btrfsic_bio_end_io; + } + } + mutex_unlock(&btrfsic_mutex); + + submit_bio(rw, bio); +} + +int btrfsic_mount(struct btrfs_root *root, + struct btrfs_fs_devices *fs_devices, + int including_extent_data, u32 print_mask) +{ + int ret; + struct btrfsic_state *state; + struct list_head *dev_head = &fs_devices->devices; + struct btrfs_device *device; + + state = kzalloc(sizeof(*state), GFP_NOFS); + if (NULL == state) { + printk(KERN_INFO "btrfs check-integrity: kmalloc() failed!\n"); + return -1; + } + + if (!btrfsic_is_initialized) { + mutex_init(&btrfsic_mutex); + btrfsic_dev_state_hashtable_init(&btrfsic_dev_state_hashtable); + btrfsic_is_initialized = 1; + } + mutex_lock(&btrfsic_mutex); + state->root = root; + state->print_mask = print_mask; + state->include_extent_data = including_extent_data; + state->csum_size = 0; + INIT_LIST_HEAD(&state->all_blocks_list); + btrfsic_block_hashtable_init(&state->block_hashtable); + btrfsic_block_link_hashtable_init(&state->block_link_hashtable); + state->max_superblock_generation = 0; + state->latest_superblock = NULL; + + list_for_each_entry(device, dev_head, dev_list) { + struct btrfsic_dev_state *ds; + char *p; + + if (!device->bdev || !device->name) + continue; + + ds = btrfsic_dev_state_alloc(); + if (NULL == ds) { + printk(KERN_INFO + "btrfs check-integrity: kmalloc() failed!\n"); + mutex_unlock(&btrfsic_mutex); + return -1; + } + ds->bdev = device->bdev; + ds->state = state; + bdevname(ds->bdev, ds->name); + ds->name[BDEVNAME_SIZE - 1] = '\0'; + for (p = ds->name; *p != '\0'; p++); + while (p > ds->name && *p != '/') + p--; + if (*p == '/') + p++; + strlcpy(ds->name, p, sizeof(ds->name)); + btrfsic_dev_state_hashtable_add(ds, + &btrfsic_dev_state_hashtable); + } + + ret = btrfsic_process_superblock(state, fs_devices); + if (0 != ret) { + mutex_unlock(&btrfsic_mutex); + btrfsic_unmount(root, fs_devices); + return ret; + } + + if (state->print_mask & BTRFSIC_PRINT_MASK_INITIAL_DATABASE) + btrfsic_dump_database(state); + if (state->print_mask & BTRFSIC_PRINT_MASK_INITIAL_TREE) + btrfsic_dump_tree(state); + + mutex_unlock(&btrfsic_mutex); + return 0; +} + +void btrfsic_unmount(struct btrfs_root *root, + struct btrfs_fs_devices *fs_devices) +{ + struct list_head *elem_all; + struct list_head *tmp_all; + struct btrfsic_state *state; + struct list_head *dev_head = &fs_devices->devices; + struct btrfs_device *device; + + if (!btrfsic_is_initialized) + return; + + mutex_lock(&btrfsic_mutex); + + state = NULL; + list_for_each_entry(device, dev_head, dev_list) { + struct btrfsic_dev_state *ds; + + if (!device->bdev || !device->name) + continue; + + ds = btrfsic_dev_state_hashtable_lookup( + device->bdev, + &btrfsic_dev_state_hashtable); + if (NULL != ds) { + state = ds->state; + btrfsic_dev_state_hashtable_remove(ds); + btrfsic_dev_state_free(ds); + } + } + + if (NULL == state) { + printk(KERN_INFO + "btrfsic: error, cannot find state information" + " on umount!\n"); + mutex_unlock(&btrfsic_mutex); + return; + } + + /* + * Don't care about keeping the lists' state up to date, + * just free all memory that was allocated dynamically. + * Free the blocks and the block_links. + */ + list_for_each_safe(elem_all, tmp_all, &state->all_blocks_list) { + struct btrfsic_block *const b_all = + list_entry(elem_all, struct btrfsic_block, + all_blocks_node); + struct list_head *elem_ref_to; + struct list_head *tmp_ref_to; + + list_for_each_safe(elem_ref_to, tmp_ref_to, + &b_all->ref_to_list) { + struct btrfsic_block_link *const l = + list_entry(elem_ref_to, + struct btrfsic_block_link, + node_ref_to); + + if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + btrfsic_print_rem_link(state, l); + + l->ref_cnt--; + if (0 == l->ref_cnt) + btrfsic_block_link_free(l); + } + + if (b_all->is_iodone) + btrfsic_block_free(b_all); + else + printk(KERN_INFO "btrfs: attempt to free %c-block" + " @%llu (%s/%llu/%d) on umount which is" + " not yet iodone!\n", + btrfsic_get_block_type(state, b_all), + (unsigned long long)b_all->logical_bytenr, + b_all->dev_state->name, + (unsigned long long)b_all->dev_bytenr, + b_all->mirror_num); + } + + mutex_unlock(&btrfsic_mutex); + + kfree(state); +} diff --git a/fs/btrfs/check-integrity.h b/fs/btrfs/check-integrity.h new file mode 100644 index 0000000..8b59175 --- /dev/null +++ b/fs/btrfs/check-integrity.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) STRATO AG 2011. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#if !defined(__BTRFS_CHECK_INTEGRITY__) +#define __BTRFS_CHECK_INTEGRITY__ + +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY +int btrfsic_submit_bh(int rw, struct buffer_head *bh); +void btrfsic_submit_bio(int rw, struct bio *bio); +#else +#define btrfsic_submit_bh submit_bh +#define btrfsic_submit_bio submit_bio +#endif + +int btrfsic_mount(struct btrfs_root *root, + struct btrfs_fs_devices *fs_devices, + int including_extent_data, u32 print_mask); +void btrfsic_unmount(struct btrfs_root *root, + struct btrfs_fs_devices *fs_devices); + +#endif -- cgit v0.10.2 From c975dd469d748ce619c510050d4fb407c2398591 Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Tue, 1 Nov 2011 17:06:04 +0100 Subject: Btrfs: add config option to enable btrfs integrity check Added the BTRFS_FS_CHECK_INTEGRITY option to Kconfig. It depends on BTRFS_FS. Signed-off-by: Stefan Behrens diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index ecb9fd3..d33f01c 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -31,3 +31,22 @@ config BTRFS_FS_POSIX_ACL Linux website . If you don't know what Access Control Lists are, say N + +config BTRFS_FS_CHECK_INTEGRITY + bool "Btrfs with integrity check tool compiled in (DANGEROUS)" + depends on BTRFS_FS + help + Adds code that examines all block write requests (including + writes of the super block). The goal is to verify that the + state of the filesystem on disk is always consistent, i.e., + after a power-loss or kernel panic event the filesystem is + in a consistent state. + + If the integrity check tool is included and activated in + the mount options, plenty of kernel memory is used, and + plenty of additional CPU cycles are spent. Enabling this + functionality is not intended for normal use. + + In most cases, unless you are a btrfs developer who needs + to verify the integrity of (super)-block write requests + during the run of a regression test, say N -- cgit v0.10.2 From f11e4d7f533249ddfa110116200c5c3a509f9218 Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Tue, 1 Nov 2011 17:06:39 +0100 Subject: Btrfs: Makefile changes to optionally include btrfs integrity check If the btrfs integrity check is enabled, the files required to implement the checks are included in the build. Signed-off-by: Stefan Behrens diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index c0ddfd2..bc5b355 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -11,3 +11,4 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ reada.o backref.o btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o +btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o -- cgit v0.10.2 From 21adbd5cbb5344a3fca6bb7ddb2ab6cb03c44546 Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Wed, 9 Nov 2011 13:44:05 +0100 Subject: Btrfs: integrate integrity check module into btrfs This is the last part of the patch series. It modifies the btrfs code to use the integrity check module if configured to do so with the define BTRFS_FS_CHECK_INTEGRITY. If this define is not set, the only effective change is that code is added that handles the mount option to activate the integrity check. If the mount option is set and the define BTRFS_FS_CHECK_INTEGRITY is not set, that code complains in the log and the mount fails with EINVAL. Add the mount option to activate the usage of the integrity check code. Add invocation of btrfs integrity check code init and cleanup function on mount and umount, respectively. Add hook to call btrfs integrity check code version of submit_bh/submit_bio. Signed-off-by: Stefan Behrens diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6738503..39f6188 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -971,7 +971,7 @@ struct btrfs_fs_info { * is required instead of the faster short fsync log commits */ u64 last_trans_log_full_commit; - unsigned long mount_opt:20; + unsigned long mount_opt:21; unsigned long compress_type:4; u64 max_inline; u64 alloc_start; @@ -1155,6 +1155,10 @@ struct btrfs_fs_info { int scrub_workers_refcnt; struct btrfs_workers scrub_workers; +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + u32 check_integrity_print_mask; +#endif + /* filesystem state */ u64 fs_state; @@ -1413,6 +1417,8 @@ struct btrfs_ioctl_defrag_range_args { #define BTRFS_MOUNT_AUTO_DEFRAG (1 << 16) #define BTRFS_MOUNT_INODE_MAP_CACHE (1 << 17) #define BTRFS_MOUNT_RECOVERY (1 << 18) +#define BTRFS_MOUNT_CHECK_INTEGRITY (1 << 19) +#define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 20) #define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt) #define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 3f9d555..f363c6d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -43,6 +43,7 @@ #include "tree-log.h" #include "free-space-cache.h" #include "inode-map.h" +#include "check-integrity.h" static struct extent_io_ops btree_extent_io_ops; static void end_workqueue_fn(struct btrfs_work *work); @@ -2001,6 +2002,9 @@ struct btrfs_root *open_ctree(struct super_block *sb, init_waitqueue_head(&fs_info->scrub_pause_wait); init_rwsem(&fs_info->scrub_super_lock); fs_info->scrub_workers_refcnt = 0; +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + fs_info->check_integrity_print_mask = 0; +#endif sb->s_blocksize = 4096; sb->s_blocksize_bits = blksize_bits(4096); @@ -2356,6 +2360,19 @@ retry_root_backup: btrfs_set_opt(fs_info->mount_opt, SSD); } +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + if (btrfs_test_opt(tree_root, CHECK_INTEGRITY)) { + ret = btrfsic_mount(tree_root, fs_devices, + btrfs_test_opt(tree_root, + CHECK_INTEGRITY_INCLUDING_EXTENT_DATA) ? + 1 : 0, + fs_info->check_integrity_print_mask); + if (ret) + printk(KERN_WARNING "btrfs: failed to initialize" + " integrity check module %s\n", sb->s_id); + } +#endif + /* do not make disk changes in broken FS */ if (btrfs_super_log_root(disk_super) != 0 && !(fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)) { @@ -2634,7 +2651,7 @@ static int write_dev_supers(struct btrfs_device *device, * we fua the first super. The others we allow * to go down lazy. */ - ret = submit_bh(WRITE_FUA, bh); + ret = btrfsic_submit_bh(WRITE_FUA, bh); if (ret) errors++; } @@ -2711,7 +2728,7 @@ static int write_dev_flush(struct btrfs_device *device, int wait) device->flush_bio = bio; bio_get(bio); - submit_bio(WRITE_FLUSH, bio); + btrfsic_submit_bio(WRITE_FLUSH, bio); return 0; } @@ -3057,6 +3074,11 @@ int close_ctree(struct btrfs_root *root) btrfs_stop_workers(&fs_info->caching_workers); btrfs_stop_workers(&fs_info->readahead_workers); +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + if (btrfs_test_opt(root, CHECK_INTEGRITY)) + btrfsic_unmount(root, fs_info->fs_devices); +#endif + btrfs_close_devices(fs_info->fs_devices); btrfs_mapping_tree_free(&fs_info->mapping_tree); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 49f3c9d..2466692 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -18,6 +18,7 @@ #include "ctree.h" #include "btrfs_inode.h" #include "volumes.h" +#include "check-integrity.h" static struct kmem_cache *extent_state_cache; static struct kmem_cache *extent_buffer_cache; @@ -1895,7 +1896,7 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start, } bio->bi_bdev = dev->bdev; bio_add_page(bio, page, length, start-page_offset(page)); - submit_bio(WRITE_SYNC, bio); + btrfsic_submit_bio(WRITE_SYNC, bio); wait_for_completion(&compl); if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { @@ -2393,7 +2394,7 @@ static int submit_one_bio(int rw, struct bio *bio, int mirror_num, ret = tree->ops->submit_bio_hook(page->mapping->host, rw, bio, mirror_num, bio_flags, start); else - submit_bio(rw, bio); + btrfsic_submit_bio(rw, bio); if (bio_flagged(bio, BIO_EOPNOTSUPP)) ret = -EOPNOTSUPP; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index ddf2c90..567e148 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -25,6 +25,7 @@ #include "transaction.h" #include "backref.h" #include "extent_io.h" +#include "check-integrity.h" /* * This is only the first step towards a full-features scrub. It reads all @@ -732,7 +733,7 @@ static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector, bio_add_page(bio, page, PAGE_SIZE, 0); bio->bi_end_io = scrub_fixup_end_io; bio->bi_private = &complete; - submit_bio(rw, bio); + btrfsic_submit_bio(rw, bio); /* this will also unplug the queue */ wait_for_completion(&complete); @@ -958,7 +959,7 @@ static int scrub_submit(struct scrub_dev *sdev) sdev->curr = -1; atomic_inc(&sdev->in_flight); - submit_bio(READ, sbio->bio); + btrfsic_submit_bio(READ, sbio->bio); return 0; } diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 34a8b61..22a2015 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -165,7 +165,10 @@ enum { Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_discard, Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed, Opt_enospc_debug, Opt_subvolrootid, Opt_defrag, - Opt_inode_cache, Opt_no_space_cache, Opt_recovery, Opt_err, + Opt_inode_cache, Opt_no_space_cache, Opt_recovery, + Opt_check_integrity, Opt_check_integrity_including_extent_data, + Opt_check_integrity_print_mask, + Opt_err, }; static match_table_t tokens = { @@ -200,6 +203,9 @@ static match_table_t tokens = { {Opt_inode_cache, "inode_cache"}, {Opt_no_space_cache, "nospace_cache"}, {Opt_recovery, "recovery"}, + {Opt_check_integrity, "check_int"}, + {Opt_check_integrity_including_extent_data, "check_int_data"}, + {Opt_check_integrity_print_mask, "check_int_print_mask=%d"}, {Opt_err, NULL}, }; @@ -398,6 +404,37 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) printk(KERN_INFO "btrfs: enabling auto recovery"); btrfs_set_opt(info->mount_opt, RECOVERY); break; +#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY + case Opt_check_integrity_including_extent_data: + printk(KERN_INFO "btrfs: enabling check integrity" + " including extent data\n"); + btrfs_set_opt(info->mount_opt, + CHECK_INTEGRITY_INCLUDING_EXTENT_DATA); + btrfs_set_opt(info->mount_opt, CHECK_INTEGRITY); + break; + case Opt_check_integrity: + printk(KERN_INFO "btrfs: enabling check integrity\n"); + btrfs_set_opt(info->mount_opt, CHECK_INTEGRITY); + break; + case Opt_check_integrity_print_mask: + intarg = 0; + match_int(&args[0], &intarg); + if (intarg) { + info->check_integrity_print_mask = intarg; + printk(KERN_INFO "btrfs:" + " check_integrity_print_mask 0x%x\n", + info->check_integrity_print_mask); + } + break; +#else + case Opt_check_integrity_including_extent_data: + case Opt_check_integrity: + case Opt_check_integrity_print_mask: + printk(KERN_ERR "btrfs: support for check_integrity*" + " not compiled in!\n"); + ret = -EINVAL; + goto out; +#endif case Opt_err: printk(KERN_INFO "btrfs: unrecognized mount option " "'%s'\n", p); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f4b839f..821334f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -32,6 +32,7 @@ #include "print-tree.h" #include "volumes.h" #include "async-thread.h" +#include "check-integrity.h" static int init_first_rw_device(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -246,7 +247,7 @@ loop_lock: sync_pending = 0; } - submit_bio(cur->bi_rw, cur); + btrfsic_submit_bio(cur->bi_rw, cur); num_run++; batch_run++; if (need_resched()) @@ -3304,7 +3305,7 @@ static noinline int schedule_bio(struct btrfs_root *root, /* don't bother with additional async steps for reads, right now */ if (!(rw & REQ_WRITE)) { bio_get(bio); - submit_bio(rw, bio); + btrfsic_submit_bio(rw, bio); bio_put(bio); return 0; } @@ -3399,7 +3400,7 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio, if (async_submit) schedule_bio(root, dev, rw, bio); else - submit_bio(rw, bio); + btrfsic_submit_bio(rw, bio); } else { bio->bi_bdev = root->fs_info->fs_devices->latest_bdev; bio->bi_sector = logical >> 9; -- cgit v0.10.2 From da5c81356426c476112f2b59fe64bdb1b37f079d Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Tue, 13 Sep 2011 12:29:12 +0200 Subject: Btrfs: generic data structure to build unique lists ulist is a generic data structures to hold a collection of unique u64 values. The only operations it supports is adding to the list and enumerating it. It is possible to store an auxiliary value along with the key. The implementation is preliminary and can probably be sped up significantly. It is used by btrfs_find_all_roots() quota to translate recursions into iterative loops. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index c0ddfd2..7079840 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -8,6 +8,6 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \ export.o tree-log.o free-space-cache.o zlib.o lzo.o \ compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ - reada.o backref.o + reada.o backref.o ulist.o btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o diff --git a/fs/btrfs/ulist.c b/fs/btrfs/ulist.c new file mode 100644 index 0000000..12f5147 --- /dev/null +++ b/fs/btrfs/ulist.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2011 STRATO AG + * written by Arne Jansen + * Distributed under the GNU GPL license version 2. + */ + +#include +#include +#include "ulist.h" + +/* + * ulist is a generic data structure to hold a collection of unique u64 + * values. The only operations it supports is adding to the list and + * enumerating it. + * It is possible to store an auxiliary value along with the key. + * + * The implementation is preliminary and can probably be sped up + * significantly. A first step would be to store the values in an rbtree + * as soon as ULIST_SIZE is exceeded. + * + * A sample usage for ulists is the enumeration of directed graphs without + * visiting a node twice. The pseudo-code could look like this: + * + * ulist = ulist_alloc(); + * ulist_add(ulist, root); + * elem = NULL; + * + * while ((elem = ulist_next(ulist, elem)) { + * for (all child nodes n in elem) + * ulist_add(ulist, n); + * do something useful with the node; + * } + * ulist_free(ulist); + * + * This assumes the graph nodes are adressable by u64. This stems from the + * usage for tree enumeration in btrfs, where the logical addresses are + * 64 bit. + * + * It is also useful for tree enumeration which could be done elegantly + * recursively, but is not possible due to kernel stack limitations. The + * loop would be similar to the above. + */ + +/** + * ulist_init - freshly initialize a ulist + * @ulist: the ulist to initialize + * + * Note: don't use this function to init an already used ulist, use + * ulist_reinit instead. + */ +void ulist_init(struct ulist *ulist) +{ + ulist->nnodes = 0; + ulist->nodes = ulist->int_nodes; + ulist->nodes_alloced = ULIST_SIZE; +} +EXPORT_SYMBOL(ulist_init); + +/** + * ulist_fini - free up additionally allocated memory for the ulist + * @ulist: the ulist from which to free the additional memory + * + * This is useful in cases where the base 'struct ulist' has been statically + * allocated. + */ +void ulist_fini(struct ulist *ulist) +{ + /* + * The first ULIST_SIZE elements are stored inline in struct ulist. + * Only if more elements are alocated they need to be freed. + */ + if (ulist->nodes_alloced > ULIST_SIZE) + kfree(ulist->nodes); + ulist->nodes_alloced = 0; /* in case ulist_fini is called twice */ +} +EXPORT_SYMBOL(ulist_fini); + +/** + * ulist_reinit - prepare a ulist for reuse + * @ulist: ulist to be reused + * + * Free up all additional memory allocated for the list elements and reinit + * the ulist. + */ +void ulist_reinit(struct ulist *ulist) +{ + ulist_fini(ulist); + ulist_init(ulist); +} +EXPORT_SYMBOL(ulist_reinit); + +/** + * ulist_alloc - dynamically allocate a ulist + * @gfp_mask: allocation flags to for base allocation + * + * The allocated ulist will be returned in an initialized state. + */ +struct ulist *ulist_alloc(unsigned long gfp_mask) +{ + struct ulist *ulist = kmalloc(sizeof(*ulist), gfp_mask); + + if (!ulist) + return NULL; + + ulist_init(ulist); + + return ulist; +} +EXPORT_SYMBOL(ulist_alloc); + +/** + * ulist_free - free dynamically allocated ulist + * @ulist: ulist to free + * + * It is not necessary to call ulist_fini before. + */ +void ulist_free(struct ulist *ulist) +{ + if (!ulist) + return; + ulist_fini(ulist); + kfree(ulist); +} +EXPORT_SYMBOL(ulist_free); + +/** + * ulist_add - add an element to the ulist + * @ulist: ulist to add the element to + * @val: value to add to ulist + * @aux: auxiliary value to store along with val + * @gfp_mask: flags to use for allocation + * + * Note: locking must be provided by the caller. In case of rwlocks write + * locking is needed + * + * Add an element to a ulist. The @val will only be added if it doesn't + * already exist. If it is added, the auxiliary value @aux is stored along with + * it. In case @val already exists in the ulist, @aux is ignored, even if + * it differs from the already stored value. + * + * ulist_add returns 0 if @val already exists in ulist and 1 if @val has been + * inserted. + * In case of allocation failure -ENOMEM is returned and the ulist stays + * unaltered. + */ +int ulist_add(struct ulist *ulist, u64 val, unsigned long aux, + unsigned long gfp_mask) +{ + int i; + + for (i = 0; i < ulist->nnodes; ++i) { + if (ulist->nodes[i].val == val) + return 0; + } + + if (ulist->nnodes >= ulist->nodes_alloced) { + u64 new_alloced = ulist->nodes_alloced + 128; + struct ulist_node *new_nodes; + void *old = NULL; + + /* + * if nodes_alloced == ULIST_SIZE no memory has been allocated + * yet, so pass NULL to krealloc + */ + if (ulist->nodes_alloced > ULIST_SIZE) + old = ulist->nodes; + + new_nodes = krealloc(old, sizeof(*new_nodes) * new_alloced, + gfp_mask); + if (!new_nodes) + return -ENOMEM; + + if (!old) + memcpy(new_nodes, ulist->int_nodes, + sizeof(ulist->int_nodes)); + + ulist->nodes = new_nodes; + ulist->nodes_alloced = new_alloced; + } + ulist->nodes[ulist->nnodes].val = val; + ulist->nodes[ulist->nnodes].aux = aux; + ++ulist->nnodes; + + return 1; +} +EXPORT_SYMBOL(ulist_add); + +/** + * ulist_next - iterate ulist + * @ulist: ulist to iterate + * @prev: previously returned element or %NULL to start iteration + * + * Note: locking must be provided by the caller. In case of rwlocks only read + * locking is needed + * + * This function is used to iterate an ulist. The iteration is started with + * @prev = %NULL. It returns the next element from the ulist or %NULL when the + * end is reached. No guarantee is made with respect to the order in which + * the elements are returned. They might neither be returned in order of + * addition nor in ascending order. + * It is allowed to call ulist_add during an enumeration. Newly added items + * are guaranteed to show up in the running enumeration. + */ +struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_node *prev) +{ + int next; + + if (ulist->nnodes == 0) + return NULL; + + if (!prev) + return &ulist->nodes[0]; + + next = (prev - ulist->nodes) + 1; + if (next < 0 || next >= ulist->nnodes) + return NULL; + + return &ulist->nodes[next]; +} +EXPORT_SYMBOL(ulist_next); diff --git a/fs/btrfs/ulist.h b/fs/btrfs/ulist.h new file mode 100644 index 0000000..2e25dec --- /dev/null +++ b/fs/btrfs/ulist.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 STRATO AG + * written by Arne Jansen + * Distributed under the GNU GPL license version 2. + * + */ + +#ifndef __ULIST__ +#define __ULIST__ + +/* + * ulist is a generic data structure to hold a collection of unique u64 + * values. The only operations it supports is adding to the list and + * enumerating it. + * It is possible to store an auxiliary value along with the key. + * + * The implementation is preliminary and can probably be sped up + * significantly. A first step would be to store the values in an rbtree + * as soon as ULIST_SIZE is exceeded. + */ + +/* + * number of elements statically allocated inside struct ulist + */ +#define ULIST_SIZE 16 + +/* + * element of the list + */ +struct ulist_node { + u64 val; /* value to store */ + unsigned long aux; /* auxiliary value saved along with the val */ +}; + +struct ulist { + /* + * number of elements stored in list + */ + unsigned long nnodes; + + /* + * number of nodes we already have room for + */ + unsigned long nodes_alloced; + + /* + * pointer to the array storing the elements. The first ULIST_SIZE + * elements are stored inline. In this case the it points to int_nodes. + * After exceeding ULIST_SIZE, dynamic memory is allocated. + */ + struct ulist_node *nodes; + + /* + * inline storage space for the first ULIST_SIZE entries + */ + struct ulist_node int_nodes[ULIST_SIZE]; +}; + +void ulist_init(struct ulist *ulist); +void ulist_fini(struct ulist *ulist); +void ulist_reinit(struct ulist *ulist); +struct ulist *ulist_alloc(unsigned long gfp_mask); +void ulist_free(struct ulist *ulist); +int ulist_add(struct ulist *ulist, u64 val, unsigned long aux, + unsigned long gfp_mask); +struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_node *prev); + +#endif -- cgit v0.10.2 From c7d22a3c3cdb73d8a0151e2ccc8cf4a48c48310b Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Tue, 22 Nov 2011 15:14:33 +0100 Subject: Btrfs: added helper btrfs_next_item() btrfs_next_item() makes the btrfs path point to the next item, crossing leaf boundaries if needed. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 50634abe..3e4a07b 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2482,6 +2482,13 @@ static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, } int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); +static inline int btrfs_next_item(struct btrfs_root *root, struct btrfs_path *p) +{ + ++p->slots[0]; + if (p->slots[0] >= btrfs_header_nritems(p->nodes[0])) + return btrfs_next_leaf(root, p); + return 0; +} int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf); void btrfs_drop_snapshot(struct btrfs_root *root, -- cgit v0.10.2 From 66d7e7f09f77456fe68683247d77721032a00ee5 Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Mon, 12 Sep 2011 15:26:38 +0200 Subject: Btrfs: mark delayed refs as for cow Add a for_cow parameter to add_delayed_*_ref and pass the appropriate value from every call site. The for_cow parameter will later on be used to determine if a ref will change anything with respect to qgroups. Delayed refs coming from relocation are always counted as for_cow, as they don't change subvol quota. Also pass in the fs_info for later use. btrfs_find_all_roots() will use this as an optimization, as changes that are for_cow will not change anything with respect to which root points to a certain leaf. Thus, we don't need to add the current sequence number to those delayed refs. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index dede441..0639a55 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -240,7 +240,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, cow = btrfs_alloc_free_block(trans, root, buf->len, 0, new_root_objectid, &disk_key, level, - buf->start, 0); + buf->start, 0, 1); if (IS_ERR(cow)) return PTR_ERR(cow); @@ -261,9 +261,9 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, WARN_ON(btrfs_header_generation(buf) > trans->transid); if (new_root_objectid == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); + ret = btrfs_inc_ref(trans, root, cow, 1, 1); else - ret = btrfs_inc_ref(trans, root, cow, 0); + ret = btrfs_inc_ref(trans, root, cow, 0, 1); if (ret) return ret; @@ -350,14 +350,14 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if ((owner == root->root_key.objectid || root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) && !(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) { - ret = btrfs_inc_ref(trans, root, buf, 1); + ret = btrfs_inc_ref(trans, root, buf, 1, 1); BUG_ON(ret); if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { - ret = btrfs_dec_ref(trans, root, buf, 0); + ret = btrfs_dec_ref(trans, root, buf, 0, 1); BUG_ON(ret); - ret = btrfs_inc_ref(trans, root, cow, 1); + ret = btrfs_inc_ref(trans, root, cow, 1, 1); BUG_ON(ret); } new_flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF; @@ -365,9 +365,9 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); + ret = btrfs_inc_ref(trans, root, cow, 1, 1); else - ret = btrfs_inc_ref(trans, root, cow, 0); + ret = btrfs_inc_ref(trans, root, cow, 0, 1); BUG_ON(ret); } if (new_flags != 0) { @@ -381,11 +381,11 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) { if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) - ret = btrfs_inc_ref(trans, root, cow, 1); + ret = btrfs_inc_ref(trans, root, cow, 1, 1); else - ret = btrfs_inc_ref(trans, root, cow, 0); + ret = btrfs_inc_ref(trans, root, cow, 0, 1); BUG_ON(ret); - ret = btrfs_dec_ref(trans, root, buf, 1); + ret = btrfs_dec_ref(trans, root, buf, 1, 1); BUG_ON(ret); } clean_tree_block(trans, root, buf); @@ -446,7 +446,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, cow = btrfs_alloc_free_block(trans, root, buf->len, parent_start, root->root_key.objectid, &disk_key, - level, search_start, empty_size); + level, search_start, empty_size, 1); if (IS_ERR(cow)) return PTR_ERR(cow); @@ -484,7 +484,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, rcu_assign_pointer(root->node, cow); btrfs_free_tree_block(trans, root, buf, parent_start, - last_ref); + last_ref, 1); free_extent_buffer(buf); add_root_to_dirty_list(root); } else { @@ -500,7 +500,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, trans->transid); btrfs_mark_buffer_dirty(parent); btrfs_free_tree_block(trans, root, buf, parent_start, - last_ref); + last_ref, 1); } if (unlock_orig) btrfs_tree_unlock(buf); @@ -957,7 +957,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, free_extent_buffer(mid); root_sub_used(root, mid->len); - btrfs_free_tree_block(trans, root, mid, 0, 1); + btrfs_free_tree_block(trans, root, mid, 0, 1, 0); /* once for the root ptr */ free_extent_buffer(mid); return 0; @@ -1015,7 +1015,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, if (wret) ret = wret; root_sub_used(root, right->len); - btrfs_free_tree_block(trans, root, right, 0, 1); + btrfs_free_tree_block(trans, root, right, 0, 1, 0); free_extent_buffer(right); right = NULL; } else { @@ -1055,7 +1055,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, if (wret) ret = wret; root_sub_used(root, mid->len); - btrfs_free_tree_block(trans, root, mid, 0, 1); + btrfs_free_tree_block(trans, root, mid, 0, 1, 0); free_extent_buffer(mid); mid = NULL; } else { @@ -2089,7 +2089,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans, c = btrfs_alloc_free_block(trans, root, root->nodesize, 0, root->root_key.objectid, &lower_key, - level, root->node->start, 0); + level, root->node->start, 0, 0); if (IS_ERR(c)) return PTR_ERR(c); @@ -2216,7 +2216,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans, split = btrfs_alloc_free_block(trans, root, root->nodesize, 0, root->root_key.objectid, - &disk_key, level, c->start, 0); + &disk_key, level, c->start, 0, 0); if (IS_ERR(split)) return PTR_ERR(split); @@ -2970,7 +2970,7 @@ again: right = btrfs_alloc_free_block(trans, root, root->leafsize, 0, root->root_key.objectid, - &disk_key, 0, l->start, 0); + &disk_key, 0, l->start, 0, 0); if (IS_ERR(right)) return PTR_ERR(right); @@ -3781,7 +3781,7 @@ static noinline int btrfs_del_leaf(struct btrfs_trans_handle *trans, root_sub_used(root, leaf->len); - btrfs_free_tree_block(trans, root, leaf, 0, 1); + btrfs_free_tree_block(trans, root, leaf, 0, 1, 0); return 0; } /* diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3e4a07b..543f60b 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2277,11 +2277,11 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, u32 blocksize, u64 parent, u64 root_objectid, struct btrfs_disk_key *key, int level, - u64 hint, u64 empty_size); + u64 hint, u64 empty_size, int for_cow); void btrfs_free_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, - u64 parent, int last_ref); + u64 parent, int last_ref, int for_cow); struct extent_buffer *btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u32 blocksize, @@ -2301,17 +2301,17 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, u64 search_end, struct btrfs_key *ins, u64 data); int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref); + struct extent_buffer *buf, int full_backref, int for_cow); int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref); + struct extent_buffer *buf, int full_backref, int for_cow); int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 flags, int is_data); int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset); + u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, + u64 owner, u64 offset, int for_cow); int btrfs_free_reserved_extent(struct btrfs_root *root, u64 start, u64 len); int btrfs_free_and_pin_reserved_extent(struct btrfs_root *root, @@ -2323,7 +2323,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset); + u64 root_objectid, u64 owner, u64 offset, int for_cow); int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, struct btrfs_root *root); @@ -2492,7 +2492,8 @@ static inline int btrfs_next_item(struct btrfs_root *root, struct btrfs_path *p) int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf); void btrfs_drop_snapshot(struct btrfs_root *root, - struct btrfs_block_rsv *block_rsv, int update_ref); + struct btrfs_block_rsv *block_rsv, int update_ref, + int for_reloc); int btrfs_drop_subtree(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *node, diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 125cf76..3a0f0ab 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -390,7 +390,8 @@ update_existing_head_ref(struct btrfs_delayed_ref_node *existing, * this does all the dirty work in terms of maintaining the correct * overall modification count. */ -static noinline int add_delayed_ref_head(struct btrfs_trans_handle *trans, +static noinline int add_delayed_ref_head(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *ref, u64 bytenr, u64 num_bytes, int action, int is_data) @@ -468,10 +469,12 @@ static noinline int add_delayed_ref_head(struct btrfs_trans_handle *trans, /* * helper to insert a delayed tree ref into the rbtree. */ -static noinline int add_delayed_tree_ref(struct btrfs_trans_handle *trans, +static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *ref, u64 bytenr, u64 num_bytes, u64 parent, - u64 ref_root, int level, int action) + u64 ref_root, int level, int action, + int for_cow) { struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_tree_ref *full_ref; @@ -522,11 +525,12 @@ static noinline int add_delayed_tree_ref(struct btrfs_trans_handle *trans, /* * helper to insert a delayed data ref into the rbtree. */ -static noinline int add_delayed_data_ref(struct btrfs_trans_handle *trans, +static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *ref, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, u64 owner, u64 offset, - int action) + int action, int for_cow) { struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_data_ref *full_ref; @@ -554,6 +558,7 @@ static noinline int add_delayed_data_ref(struct btrfs_trans_handle *trans, full_ref->root = ref_root; ref->type = BTRFS_EXTENT_DATA_REF_KEY; } + full_ref->objectid = owner; full_ref->offset = offset; @@ -580,10 +585,12 @@ static noinline int add_delayed_data_ref(struct btrfs_trans_handle *trans, * to make sure the delayed ref is eventually processed before this * transaction commits. */ -int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, +int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, int level, int action, - struct btrfs_delayed_extent_op *extent_op) + struct btrfs_delayed_extent_op *extent_op, + int for_cow) { struct btrfs_delayed_tree_ref *ref; struct btrfs_delayed_ref_head *head_ref; @@ -610,12 +617,13 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, * insert both the head node and the new ref without dropping * the spin lock */ - ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, num_bytes, - action, 0); + ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr, + num_bytes, action, 0); BUG_ON(ret); - ret = add_delayed_tree_ref(trans, &ref->node, bytenr, num_bytes, - parent, ref_root, level, action); + ret = add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr, + num_bytes, parent, ref_root, level, action, + for_cow); BUG_ON(ret); spin_unlock(&delayed_refs->lock); return 0; @@ -624,11 +632,13 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, /* * add a delayed data ref. it's similar to btrfs_add_delayed_tree_ref. */ -int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, +int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, u64 owner, u64 offset, int action, - struct btrfs_delayed_extent_op *extent_op) + struct btrfs_delayed_extent_op *extent_op, + int for_cow) { struct btrfs_delayed_data_ref *ref; struct btrfs_delayed_ref_head *head_ref; @@ -655,18 +665,20 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, * insert both the head node and the new ref without dropping * the spin lock */ - ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, num_bytes, - action, 1); + ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr, + num_bytes, action, 1); BUG_ON(ret); - ret = add_delayed_data_ref(trans, &ref->node, bytenr, num_bytes, - parent, ref_root, owner, offset, action); + ret = add_delayed_data_ref(fs_info, trans, &ref->node, bytenr, + num_bytes, parent, ref_root, owner, offset, + action, for_cow); BUG_ON(ret); spin_unlock(&delayed_refs->lock); return 0; } -int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans, +int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, struct btrfs_delayed_extent_op *extent_op) { @@ -683,7 +695,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans, delayed_refs = &trans->transaction->delayed_refs; spin_lock(&delayed_refs->lock); - ret = add_delayed_ref_head(trans, &head_ref->node, bytenr, + ret = add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr, num_bytes, BTRFS_UPDATE_DELAYED_HEAD, extent_op->is_data); BUG_ON(ret); diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index e287e3b..8316bff 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -151,16 +151,21 @@ static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref) } } -int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, +int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, int level, int action, - struct btrfs_delayed_extent_op *extent_op); -int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, + struct btrfs_delayed_extent_op *extent_op, + int for_cow); +int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root, u64 owner, u64 offset, int action, - struct btrfs_delayed_extent_op *extent_op); -int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans, + struct btrfs_delayed_extent_op *extent_op, + int for_cow); +int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, + struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, struct btrfs_delayed_extent_op *extent_op); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 94abc25..6f8cd17 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1243,7 +1243,8 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans, root->ref_cows = 0; leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0, - BTRFS_TREE_LOG_OBJECTID, NULL, 0, 0, 0); + BTRFS_TREE_LOG_OBJECTID, NULL, + 0, 0, 0, 0); if (IS_ERR(leaf)) { kfree(root); return ERR_CAST(leaf); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 813c6bb..dc8b9a8 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -1872,20 +1872,24 @@ static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset) + u64 root_objectid, u64 owner, u64 offset, int for_cow) { int ret; + struct btrfs_fs_info *fs_info = root->fs_info; + BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID && root_objectid == BTRFS_TREE_LOG_OBJECTID); if (owner < BTRFS_FIRST_FREE_OBJECTID) { - ret = btrfs_add_delayed_tree_ref(trans, bytenr, num_bytes, + ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr, + num_bytes, parent, root_objectid, (int)owner, - BTRFS_ADD_DELAYED_REF, NULL); + BTRFS_ADD_DELAYED_REF, NULL, for_cow); } else { - ret = btrfs_add_delayed_data_ref(trans, bytenr, num_bytes, + ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr, + num_bytes, parent, root_objectid, owner, offset, - BTRFS_ADD_DELAYED_REF, NULL); + BTRFS_ADD_DELAYED_REF, NULL, for_cow); } return ret; } @@ -2405,7 +2409,8 @@ int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans, extent_op->update_key = 0; extent_op->is_data = is_data ? 1 : 0; - ret = btrfs_add_delayed_extent_op(trans, bytenr, num_bytes, extent_op); + ret = btrfs_add_delayed_extent_op(root->fs_info, trans, bytenr, + num_bytes, extent_op); if (ret) kfree(extent_op); return ret; @@ -2590,7 +2595,7 @@ out: static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, - int full_backref, int inc) + int full_backref, int inc, int for_cow) { u64 bytenr; u64 num_bytes; @@ -2603,7 +2608,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, int level; int ret = 0; int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *, - u64, u64, u64, u64, u64, u64); + u64, u64, u64, u64, u64, u64, int); ref_root = btrfs_header_owner(buf); nritems = btrfs_header_nritems(buf); @@ -2640,14 +2645,15 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans, key.offset -= btrfs_file_extent_offset(buf, fi); ret = process_func(trans, root, bytenr, num_bytes, parent, ref_root, key.objectid, - key.offset); + key.offset, for_cow); if (ret) goto fail; } else { bytenr = btrfs_node_blockptr(buf, i); num_bytes = btrfs_level_size(root, level - 1); ret = process_func(trans, root, bytenr, num_bytes, - parent, ref_root, level - 1, 0); + parent, ref_root, level - 1, 0, + for_cow); if (ret) goto fail; } @@ -2659,15 +2665,15 @@ fail: } int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref) + struct extent_buffer *buf, int full_backref, int for_cow) { - return __btrfs_mod_ref(trans, root, buf, full_backref, 1); + return __btrfs_mod_ref(trans, root, buf, full_backref, 1, for_cow); } int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct extent_buffer *buf, int full_backref) + struct extent_buffer *buf, int full_backref, int for_cow) { - return __btrfs_mod_ref(trans, root, buf, full_backref, 0); + return __btrfs_mod_ref(trans, root, buf, full_backref, 0, for_cow); } static int write_one_cache_group(struct btrfs_trans_handle *trans, @@ -4937,16 +4943,17 @@ out: void btrfs_free_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, - u64 parent, int last_ref) + u64 parent, int last_ref, int for_cow) { struct btrfs_block_group_cache *cache = NULL; int ret; if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { - ret = btrfs_add_delayed_tree_ref(trans, buf->start, buf->len, - parent, root->root_key.objectid, - btrfs_header_level(buf), - BTRFS_DROP_DELAYED_REF, NULL); + ret = btrfs_add_delayed_tree_ref(root->fs_info, trans, + buf->start, buf->len, + parent, root->root_key.objectid, + btrfs_header_level(buf), + BTRFS_DROP_DELAYED_REF, NULL, for_cow); BUG_ON(ret); } @@ -4981,12 +4988,12 @@ out: btrfs_put_block_group(cache); } -int btrfs_free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, - u64 root_objectid, u64 owner, u64 offset) +int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, + u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, + u64 owner, u64 offset, int for_cow) { int ret; + struct btrfs_fs_info *fs_info = root->fs_info; /* * tree log blocks never actually go into the extent allocation @@ -4998,14 +5005,17 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, btrfs_pin_extent(root, bytenr, num_bytes, 1); ret = 0; } else if (owner < BTRFS_FIRST_FREE_OBJECTID) { - ret = btrfs_add_delayed_tree_ref(trans, bytenr, num_bytes, + ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr, + num_bytes, parent, root_objectid, (int)owner, - BTRFS_DROP_DELAYED_REF, NULL); + BTRFS_DROP_DELAYED_REF, NULL, for_cow); BUG_ON(ret); } else { - ret = btrfs_add_delayed_data_ref(trans, bytenr, num_bytes, - parent, root_objectid, owner, - offset, BTRFS_DROP_DELAYED_REF, NULL); + ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr, + num_bytes, + parent, root_objectid, owner, + offset, BTRFS_DROP_DELAYED_REF, + NULL, for_cow); BUG_ON(ret); } return ret; @@ -5826,9 +5836,10 @@ int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, BUG_ON(root_objectid == BTRFS_TREE_LOG_OBJECTID); - ret = btrfs_add_delayed_data_ref(trans, ins->objectid, ins->offset, - 0, root_objectid, owner, offset, - BTRFS_ADD_DELAYED_EXTENT, NULL); + ret = btrfs_add_delayed_data_ref(root->fs_info, trans, ins->objectid, + ins->offset, 0, + root_objectid, owner, offset, + BTRFS_ADD_DELAYED_EXTENT, NULL, 0); return ret; } @@ -5998,7 +6009,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, u32 blocksize, u64 parent, u64 root_objectid, struct btrfs_disk_key *key, int level, - u64 hint, u64 empty_size) + u64 hint, u64 empty_size, int for_cow) { struct btrfs_key ins; struct btrfs_block_rsv *block_rsv; @@ -6042,10 +6053,11 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, extent_op->update_flags = 1; extent_op->is_data = 0; - ret = btrfs_add_delayed_tree_ref(trans, ins.objectid, + ret = btrfs_add_delayed_tree_ref(root->fs_info, trans, + ins.objectid, ins.offset, parent, root_objectid, level, BTRFS_ADD_DELAYED_EXTENT, - extent_op); + extent_op, for_cow); BUG_ON(ret); } return buf; @@ -6062,6 +6074,7 @@ struct walk_control { int keep_locks; int reada_slot; int reada_count; + int for_reloc; }; #define DROP_REFERENCE 1 @@ -6200,9 +6213,9 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, /* wc->stage == UPDATE_BACKREF */ if (!(wc->flags[level] & flag)) { BUG_ON(!path->locks[level]); - ret = btrfs_inc_ref(trans, root, eb, 1); + ret = btrfs_inc_ref(trans, root, eb, 1, wc->for_reloc); BUG_ON(ret); - ret = btrfs_dec_ref(trans, root, eb, 0); + ret = btrfs_dec_ref(trans, root, eb, 0, wc->for_reloc); BUG_ON(ret); ret = btrfs_set_disk_extent_flags(trans, root, eb->start, eb->len, flag, 0); @@ -6346,7 +6359,7 @@ skip: } ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, - root->root_key.objectid, level - 1, 0); + root->root_key.objectid, level - 1, 0, 0); BUG_ON(ret); } btrfs_tree_unlock(next); @@ -6420,9 +6433,11 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, if (wc->refs[level] == 1) { if (level == 0) { if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) - ret = btrfs_dec_ref(trans, root, eb, 1); + ret = btrfs_dec_ref(trans, root, eb, 1, + wc->for_reloc); else - ret = btrfs_dec_ref(trans, root, eb, 0); + ret = btrfs_dec_ref(trans, root, eb, 0, + wc->for_reloc); BUG_ON(ret); } /* make block locked assertion in clean_tree_block happy */ @@ -6449,7 +6464,7 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, btrfs_header_owner(path->nodes[level + 1])); } - btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1); + btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1, 0); out: wc->refs[level] = 0; wc->flags[level] = 0; @@ -6533,7 +6548,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans, * blocks are properly updated. */ void btrfs_drop_snapshot(struct btrfs_root *root, - struct btrfs_block_rsv *block_rsv, int update_ref) + struct btrfs_block_rsv *block_rsv, int update_ref, + int for_reloc) { struct btrfs_path *path; struct btrfs_trans_handle *trans; @@ -6621,6 +6637,7 @@ void btrfs_drop_snapshot(struct btrfs_root *root, wc->stage = DROP_REFERENCE; wc->update_ref = update_ref; wc->keep_locks = 0; + wc->for_reloc = for_reloc; wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root); while (1) { @@ -6705,6 +6722,7 @@ out: * drop subtree rooted at tree block 'node'. * * NOTE: this function will unlock and release tree block 'node' + * only used by relocation code */ int btrfs_drop_subtree(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -6749,6 +6767,7 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans, wc->stage = DROP_REFERENCE; wc->update_ref = 0; wc->keep_locks = 1; + wc->for_reloc = 1; wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root); while (1) { diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index f2e9282..d2b60ed 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -678,7 +678,7 @@ next_slot: disk_bytenr, num_bytes, 0, root->root_key.objectid, new_key.objectid, - start - extent_offset); + start - extent_offset, 0); BUG_ON(ret); *hint_byte = disk_bytenr; } @@ -753,7 +753,7 @@ next_slot: disk_bytenr, num_bytes, 0, root->root_key.objectid, key.objectid, key.offset - - extent_offset); + extent_offset, 0); BUG_ON(ret); inode_sub_bytes(inode, extent_end - key.offset); @@ -962,7 +962,7 @@ again: ret = btrfs_inc_extent_ref(trans, root, bytenr, num_bytes, 0, root->root_key.objectid, - ino, orig_offset); + ino, orig_offset, 0); BUG_ON(ret); if (split == start) { @@ -989,7 +989,7 @@ again: del_nr++; ret = btrfs_free_extent(trans, root, bytenr, num_bytes, 0, root->root_key.objectid, - ino, orig_offset); + ino, orig_offset, 0); BUG_ON(ret); } other_start = 0; @@ -1006,7 +1006,7 @@ again: del_nr++; ret = btrfs_free_extent(trans, root, bytenr, num_bytes, 0, root->root_key.objectid, - ino, orig_offset); + ino, orig_offset, 0); BUG_ON(ret); } if (del_nr == 0) { diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c5ccec2..ea81938 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3139,7 +3139,7 @@ delete: ret = btrfs_free_extent(trans, root, extent_start, extent_num_bytes, 0, btrfs_header_owner(leaf), - ino, extent_offset); + ino, extent_offset, 0); BUG_ON(ret); } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 72d4616..c48f2e9 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -358,7 +358,7 @@ static noinline int create_subvol(struct btrfs_root *root, return PTR_ERR(trans); leaf = btrfs_alloc_free_block(trans, root, root->leafsize, - 0, objectid, NULL, 0, 0, 0); + 0, objectid, NULL, 0, 0, 0, 0); if (IS_ERR(leaf)) { ret = PTR_ERR(leaf); goto fail; @@ -2425,7 +2425,8 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, disko, diskl, 0, root->root_key.objectid, btrfs_ino(inode), - new_key.offset - datao); + new_key.offset - datao, + 0); BUG_ON(ret); } } else if (type == BTRFS_FILE_EXTENT_INLINE) { diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index dff29d5..8c1aae2 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1604,12 +1604,12 @@ int replace_file_extents(struct btrfs_trans_handle *trans, ret = btrfs_inc_extent_ref(trans, root, new_bytenr, num_bytes, parent, btrfs_header_owner(leaf), - key.objectid, key.offset); + key.objectid, key.offset, 1); BUG_ON(ret); ret = btrfs_free_extent(trans, root, bytenr, num_bytes, parent, btrfs_header_owner(leaf), - key.objectid, key.offset); + key.objectid, key.offset, 1); BUG_ON(ret); } if (dirty) @@ -1778,21 +1778,23 @@ again: ret = btrfs_inc_extent_ref(trans, src, old_bytenr, blocksize, path->nodes[level]->start, - src->root_key.objectid, level - 1, 0); + src->root_key.objectid, level - 1, 0, + 1); BUG_ON(ret); ret = btrfs_inc_extent_ref(trans, dest, new_bytenr, blocksize, 0, dest->root_key.objectid, level - 1, - 0); + 0, 1); BUG_ON(ret); ret = btrfs_free_extent(trans, src, new_bytenr, blocksize, path->nodes[level]->start, - src->root_key.objectid, level - 1, 0); + src->root_key.objectid, level - 1, 0, + 1); BUG_ON(ret); ret = btrfs_free_extent(trans, dest, old_bytenr, blocksize, 0, dest->root_key.objectid, level - 1, - 0); + 0, 1); BUG_ON(ret); btrfs_unlock_up_safe(path, 0); @@ -2244,7 +2246,7 @@ again: } else { list_del_init(&reloc_root->root_list); } - btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0); + btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1); } if (found) { @@ -2558,7 +2560,7 @@ static int do_relocation(struct btrfs_trans_handle *trans, node->eb->start, blocksize, upper->eb->start, btrfs_header_owner(upper->eb), - node->level, 0); + node->level, 0, 1); BUG_ON(ret); ret = btrfs_drop_subtree(trans, root, eb, upper->eb); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 81376d9..a2bfedc 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1393,9 +1393,9 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root) if (btrfs_header_backref_rev(root->node) < BTRFS_MIXED_BACKREF_REV) - btrfs_drop_snapshot(root, NULL, 0); + btrfs_drop_snapshot(root, NULL, 0, 0); else - btrfs_drop_snapshot(root, NULL, 1); + btrfs_drop_snapshot(root, NULL, 1, 0); } return 0; } diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index f4d81c0..fce7b9e 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -589,7 +589,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, ret = btrfs_inc_extent_ref(trans, root, ins.objectid, ins.offset, 0, root->root_key.objectid, - key->objectid, offset); + key->objectid, offset, 0); BUG_ON(ret); } else { /* -- cgit v0.10.2 From eebe063b7f916087cd5c61de57b20a3a30894a96 Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Wed, 14 Sep 2011 14:01:24 +0200 Subject: Btrfs: always save ref_root in delayed refs For consistent backref walking and (later) qgroup calculation the information to which root a delayed ref belongs is useful even for shared refs. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 3a0f0ab..babd37b 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -495,13 +495,12 @@ static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info, ref->in_tree = 1; full_ref = btrfs_delayed_node_to_tree_ref(ref); - if (parent) { - full_ref->parent = parent; + full_ref->parent = parent; + full_ref->root = ref_root; + if (parent) ref->type = BTRFS_SHARED_BLOCK_REF_KEY; - } else { - full_ref->root = ref_root; + else ref->type = BTRFS_TREE_BLOCK_REF_KEY; - } full_ref->level = level; trace_btrfs_delayed_tree_ref(ref, full_ref, action); @@ -551,13 +550,12 @@ static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info, ref->in_tree = 1; full_ref = btrfs_delayed_node_to_data_ref(ref); - if (parent) { - full_ref->parent = parent; + full_ref->parent = parent; + full_ref->root = ref_root; + if (parent) ref->type = BTRFS_SHARED_DATA_REF_KEY; - } else { - full_ref->root = ref_root; + else ref->type = BTRFS_EXTENT_DATA_REF_KEY; - } full_ref->objectid = owner; full_ref->offset = offset; diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 8316bff..a5fb2bc 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -98,19 +98,15 @@ struct btrfs_delayed_ref_head { struct btrfs_delayed_tree_ref { struct btrfs_delayed_ref_node node; - union { - u64 root; - u64 parent; - }; + u64 root; + u64 parent; int level; }; struct btrfs_delayed_data_ref { struct btrfs_delayed_ref_node node; - union { - u64 root; - u64 parent; - }; + u64 root; + u64 parent; u64 objectid; u64 offset; }; -- cgit v0.10.2 From 400312201b0cf4e4deaf75842f5e95212b382e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Thu, 8 Dec 2011 09:15:41 +0100 Subject: dma: mxs-dma: fix a typo in comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lothar Waßmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index bdf4672..d04662e 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -268,7 +268,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) /* * When both completion and error of termination bits set at the * same time, we do not take it as an error. IOW, it only becomes - * an error we need to handler here in case of ether it's (1) an bus + * an error we need to handle here in case of either it's (1) a bus * error or (2) a termination error with no completion. */ stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) | /* (1) */ -- cgit v0.10.2 From feb397de65c3f76e40ef70e264f2cdf688c850c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Thu, 8 Dec 2011 09:15:42 +0100 Subject: dma: mxs-dma: Always leave mxs_dma_init() with the clock disabled. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to have the clock enabled all the time the driver is loaded. It will be enabled anyway in mxs_dma_alloc_chan_resources() when a channel is actually going to be used. Signed-off-by: Lothar Waßmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index d04662e..b0e6ac3 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -580,7 +580,7 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) ret = clk_enable(mxs_dma->clk); if (ret) - goto err_out; + return ret; ret = mxs_reset_block(mxs_dma->base); if (ret) @@ -604,11 +604,8 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS, mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR); - clk_disable(mxs_dma->clk); - - return 0; - err_out: + clk_disable(mxs_dma->clk); return ret; } -- cgit v0.10.2 From 6d23ea4b1906f28f5d99ad6aeef7207c48be6bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Thu, 8 Dec 2011 09:15:43 +0100 Subject: dma: mxs-dma: make mxs_dma_prep_slave_sg() multi user safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using a static variable for counting the number of CCWs attached to a DMA channel when appending a new descriptor is not multi user safe. Signed-off-by: Lothar Waßmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index b0e6ac3..1b4c6be 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -111,6 +111,7 @@ struct mxs_dma_chan { int chan_irq; struct mxs_dma_ccw *ccw; dma_addr_t ccw_phys; + int desc_count; dma_cookie_t last_completed; enum dma_status status; unsigned int flags; @@ -386,7 +387,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct scatterlist *sg; int i, j; u32 *pio; - static int idx; + int idx = append ? mxs_chan->desc_count : 0; if (mxs_chan->status == DMA_IN_PROGRESS && !append) return NULL; @@ -462,6 +463,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( } } } + mxs_chan->desc_count = idx; return &mxs_chan->desc; @@ -523,6 +525,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( i++; } + mxs_chan->desc_count = i; return &mxs_chan->desc; -- cgit v0.10.2 From 7ad7a345a4f17c08a1bb9bfdbb62f7793d84aa36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Thu, 8 Dec 2011 09:15:44 +0100 Subject: dma: mxs-dma: Don't use CLKGATE bits in CTRL0 to disable DMA channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is how the original Freescale code (unintentionally) worked, because the code path which would have asserted the CLKGATE bit was never actually reached in their code. This fixes the nefarious "DMA timout" bug when multiple DMA channels (e.g. GPMI NAND and MMC) are used at the same time. If a better fix for this problem should be found, the clkgate handling could be reinstated. See http://lists.infradead.org/pipermail/linux-arm-kernel/2011-September/065228.html Also reverse the order of mxs_dma_disable_chan() and mxs_dma_reset_chan() in mxs_dma_control() because mxs_dma_reset_chan() can only work when the DMA channel is enabled. Signed-off-by: Lothar Waßmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 1b4c6be..6548595 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -44,7 +44,6 @@ #define HW_APBHX_CTRL0 0x000 #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) #define BM_APBH_CTRL0_APB_BURST_EN (1 << 28) -#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8 #define BP_APBH_CTRL0_RESET_CHANNEL 16 #define HW_APBHX_CTRL1 0x010 #define HW_APBHX_CTRL2 0x020 @@ -131,23 +130,6 @@ struct mxs_dma_engine { struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; }; -static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable) -{ - struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - int chan_id = mxs_chan->chan.chan_id; - int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR; - - /* enable apbh channel clock */ - if (dma_is_apbh()) { - if (apbh_is_old()) - writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL), - mxs_dma->base + HW_APBHX_CTRL0 + set_clr); - else - writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + set_clr); - } -} - static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) { struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -166,9 +148,6 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; - /* clkgate needs to be enabled before writing other registers */ - mxs_dma_clkgate(mxs_chan, 1); - /* set cmd_addr up */ writel(mxs_chan->ccw_phys, mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); @@ -179,9 +158,6 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) { - /* disable apbh channel clock */ - mxs_dma_clkgate(mxs_chan, 0); - mxs_chan->status = DMA_SUCCESS; } @@ -339,10 +315,7 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) if (ret) goto err_clk; - /* clkgate needs to be enabled for reset to finish */ - mxs_dma_clkgate(mxs_chan, 1); mxs_dma_reset_chan(mxs_chan); - mxs_dma_clkgate(mxs_chan, 0); dma_async_tx_descriptor_init(&mxs_chan->desc, chan); mxs_chan->desc.tx_submit = mxs_dma_tx_submit; @@ -542,8 +515,8 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: - mxs_dma_disable_chan(mxs_chan); mxs_dma_reset_chan(mxs_chan); + mxs_dma_disable_chan(mxs_chan); break; case DMA_PAUSE: mxs_dma_pause_chan(mxs_chan); -- cgit v0.10.2 From 62268ce9170c5466332c046ff6ddafcb67751502 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 13 Dec 2011 23:48:03 +0800 Subject: dmaengine: add DMA_TRANS_NONE to dma_transfer_direction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before dma_transfer_direction was introduced to replace dma_data_direction, some dmaengine device uses DMA_NONE of dma_data_direction for some talk with its client drivers. The mxs-dma and its clients mxs-mmc and gpmi-nand are such case. This patch adds DMA_TRANS_NONE to dma_transfer_direction and migrate the DMA_NONE use in mxs-dma to it. It also fixes the compile warning below. CC drivers/dma/mxs-dma.o drivers/dma/mxs-dma.c: In function ‘mxs_dma_prep_slave_sg’: drivers/dma/mxs-dma.c:420:16: warning: comparison between ‘enum dma_transfer_direction’ and ‘enum dma_data_direction’ Signed-off-by: Shawn Guo Signed-off-by: Vinod Koul diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 6548595..493af2f 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -391,7 +391,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( idx = 0; } - if (direction == DMA_NONE) { + if (direction == DMA_TRANS_NONE) { ccw = &mxs_chan->ccw[idx++]; pio = (u32 *) sgl; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 5532bb8..679b349 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -88,6 +88,7 @@ enum dma_transfer_direction { DMA_MEM_TO_DEV, DMA_DEV_TO_MEM, DMA_DEV_TO_DEV, + DMA_TRANS_NONE, }; /** -- cgit v0.10.2 From a4e3e86d66ffc640ac54acffa29450e327a80ae2 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 13 Dec 2011 23:48:04 +0800 Subject: mmc: mxs-mmc: fix the dma_transfer_direction migration The commit 05f5799 (mmc-host: move to dma_transfer_direction) left out the DMA_NONE, in turn breaks the driver as below. [ 0.650000] mxs-mmc mxs-mmc.0: initialized [ 0.650000] mxs-mmc mxs-mmc.1: initialized [ 0.690000] mxs-dma mxs-dma-apbh: maximum bytes for sg entry exceeded: -55906 7475 > 65280 [ 0.690000] mxs-mmc mxs-mmc.0: mxs_mmc_ac: failed to prep dma Signed-off-by: Shawn Guo Signed-off-by: Vinod Koul diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 0a878b1..94d7358 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -357,6 +357,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host) host->ssp_pio_words[1] = cmd0; host->ssp_pio_words[2] = cmd1; host->dma_dir = DMA_NONE; + host->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, 0); if (!desc) goto out; @@ -396,6 +397,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) host->ssp_pio_words[1] = cmd0; host->ssp_pio_words[2] = cmd1; host->dma_dir = DMA_NONE; + host->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, 0); if (!desc) goto out; @@ -514,6 +516,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) host->ssp_pio_words[1] = cmd0; host->ssp_pio_words[2] = cmd1; host->dma_dir = DMA_NONE; + host->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, 0); if (!desc) goto out; -- cgit v0.10.2 From 4e82786f7039cb707c031dcf0dc84c025e149487 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Tue, 13 Dec 2011 23:48:05 +0800 Subject: mtd: fix compile error for gpmi-nand The driver gpmi-nand should compile at least. This patch adds the missing gpmi-nand.h to fix the compile error below. CC drivers/mtd/nand/gpmi-nand/gpmi-nand.o CC drivers/mtd/nand/gpmi-nand/gpmi-lib.o drivers/mtd/nand/gpmi-nand/gpmi-nand.c:25:33: fatal error: linux/mtd/gpmi-nand.h: No such file or directory drivers/mtd/nand/gpmi-nand/gpmi-lib.c:21:33: fatal error: linux/mtd/gpmi-nand.h: No such file or directory This header is grabbed from patch below, which has not been postponed for merging. [PATCH v8 1/4] ARM: mxs: add GPMI-NAND support for imx23/imx28 http://permalink.gmane.org/gmane.linux.drivers.mtd/37338 Signed-off-by: Huang Shijie Signed-off-by: Shawn Guo Signed-off-by: Vinod Koul diff --git a/include/linux/mtd/gpmi-nand.h b/include/linux/mtd/gpmi-nand.h new file mode 100644 index 0000000..69b6dbf --- /dev/null +++ b/include/linux/mtd/gpmi-nand.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __MACH_MXS_GPMI_NAND_H__ +#define __MACH_MXS_GPMI_NAND_H__ + +/* The size of the resources is fixed. */ +#define GPMI_NAND_RES_SIZE 6 + +/* Resource names for the GPMI NAND driver. */ +#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "GPMI NAND GPMI Registers" +#define GPMI_NAND_GPMI_INTERRUPT_RES_NAME "GPMI NAND GPMI Interrupt" +#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "GPMI NAND BCH Registers" +#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "GPMI NAND BCH Interrupt" +#define GPMI_NAND_DMA_CHANNELS_RES_NAME "GPMI NAND DMA Channels" +#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "GPMI NAND DMA Interrupt" + +/** + * struct gpmi_nand_platform_data - GPMI NAND driver platform data. + * + * This structure communicates platform-specific information to the GPMI NAND + * driver that can't be expressed as resources. + * + * @platform_init: A pointer to a function the driver will call to + * initialize the platform (e.g., set up the pin mux). + * @min_prop_delay_in_ns: Minimum propagation delay of GPMI signals to and + * from the NAND Flash device, in nanoseconds. + * @max_prop_delay_in_ns: Maximum propagation delay of GPMI signals to and + * from the NAND Flash device, in nanoseconds. + * @max_chip_count: The maximum number of chips for which the driver + * should configure the hardware. This value most + * likely reflects the number of pins that are + * connected to a NAND Flash device. If this is + * greater than the SoC hardware can support, the + * driver will print a message and fail to initialize. + * @partitions: An optional pointer to an array of partition + * descriptions. + * @partition_count: The number of elements in the partitions array. + */ +struct gpmi_nand_platform_data { + /* SoC hardware information. */ + int (*platform_init)(void); + + /* NAND Flash information. */ + unsigned int min_prop_delay_in_ns; + unsigned int max_prop_delay_in_ns; + unsigned int max_chip_count; + + /* Medium information. */ + struct mtd_partition *partitions; + unsigned partition_count; +}; +#endif -- cgit v0.10.2 From 0ef7e206d6a5a5de0cd84ed4925a4f688c62e732 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 13 Dec 2011 23:48:06 +0800 Subject: mtd: gpmi-nand: move to dma_transfer_direction This patch fixes usage of dma direction to adopt dma_transfer_direction. Signed-off-by: Shawn Guo Signed-off-by: Vinod Koul diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index de4db76..c4c4d6d 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -827,7 +827,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) pio[1] = pio[2] = 0; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 1 error\n"); return -1; @@ -839,7 +839,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) sg_init_one(sgl, this->cmd_buffer, this->command_length); dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); desc = channel->device->device_prep_slave_sg(channel, - sgl, 1, DMA_TO_DEVICE, 1); + sgl, 1, DMA_MEM_TO_DEV, 1); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -872,7 +872,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) pio[1] = 0; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 1 error\n"); return -1; @@ -881,7 +881,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) /* [2] send DMA request */ prepare_data_dma(this, DMA_TO_DEVICE); desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_TO_DEVICE, 1); + 1, DMA_MEM_TO_DEV, 1); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -908,7 +908,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) pio[1] = 0; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 1 error\n"); return -1; @@ -917,7 +917,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) /* [2] : send DMA request */ prepare_data_dma(this, DMA_FROM_DEVICE); desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_FROM_DEVICE, 1); + 1, DMA_DEV_TO_MEM, 1); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -964,7 +964,7 @@ int gpmi_send_page(struct gpmi_nand_data *this, desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -998,7 +998,8 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_XFER_COUNT(0); pio[1] = 0; desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, 2, DMA_NONE, 0); + (struct scatterlist *)pio, 2, + DMA_TRANS_NONE, 0); if (!desc) { pr_err("step 1 error\n"); return -1; @@ -1027,7 +1028,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[5] = auxiliary; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_NONE, 1); + ARRAY_SIZE(pio), DMA_TRANS_NONE, 1); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -1045,7 +1046,8 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); pio[1] = 0; desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, 2, DMA_NONE, 1); + (struct scatterlist *)pio, 2, + DMA_TRANS_NONE, 1); if (!desc) { pr_err("step 3 error\n"); return -1; -- cgit v0.10.2 From 1fded07513ea57b5ee128958ff119e05588b7227 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 16 Dec 2011 11:01:38 +0200 Subject: dmaengine: intel_mid_dma: locking and freeing fixes Two issues are fixed: 1. DMA descriptors are reused so when freeing lli structures that are linked to them, the pointer must be nulled. 2. midc_scan_descriptors() must be called with the channel lock held. Signed-off-by: Adrian Hunter Signed-off-by: Vinod Koul diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 01929ed..d4b9616 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -280,7 +280,8 @@ static void midc_dostart(struct intel_mid_dma_chan *midc, * callbacks but must be called with the lock held. */ static void midc_descriptor_complete(struct intel_mid_dma_chan *midc, - struct intel_mid_dma_desc *desc) + struct intel_mid_dma_desc *desc) + __releases(&midc->lock) __acquires(&midc->lock) { struct dma_async_tx_descriptor *txd = &desc->txd; dma_async_tx_callback callback_txd = NULL; @@ -311,6 +312,7 @@ static void midc_descriptor_complete(struct intel_mid_dma_chan *midc, pci_pool_free(desc->lli_pool, desc->lli, desc->lli_phys); pci_pool_destroy(desc->lli_pool); + desc->lli = NULL; } list_move(&desc->desc_node, &midc->free_list); midc->busy = false; @@ -490,7 +492,9 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret != DMA_SUCCESS) { + spin_lock_bh(&midc->lock); midc_scan_descriptors(to_middma_device(chan->device), midc); + spin_unlock_bh(&midc->lock); last_complete = midc->completed; last_used = chan->cookie; @@ -566,6 +570,7 @@ static int intel_mid_dma_device_control(struct dma_chan *chan, pci_pool_free(desc->lli_pool, desc->lli, desc->lli_phys); pci_pool_destroy(desc->lli_pool); + desc->lli = NULL; } list_move(&desc->desc_node, &midc->free_list); } -- cgit v0.10.2 From 2a0ff7a602714990b10a8749e908dad48401138e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 16 Dec 2011 11:01:39 +0200 Subject: dmaengine: intel_mid_dma: error path fix Add missing iounmap in error path. Signed-off-by: Adrian Hunter Signed-off-by: Vinod Koul diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index d4b9616..bbcd470 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -1104,7 +1104,8 @@ static int mid_setup_dma(struct pci_dev *pdev) LNW_PERIPHRAL_MASK_SIZE); if (dma->mask_reg == NULL) { pr_err("ERR_MDMA:Can't map periphral intr space !!\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_ioremap; } } else dma->mask_reg = NULL; @@ -1201,6 +1202,9 @@ static int mid_setup_dma(struct pci_dev *pdev) err_engine: free_irq(pdev->irq, dma); err_irq: + if (dma->mask_reg) + iounmap(dma->mask_reg); +err_ioremap: pci_pool_destroy(dma->dma_pool); err_dma_pool: pr_err("ERR_MDMA:setup_dma failed: %d\n", err); -- cgit v0.10.2 From d6077c92b6c51299eac7c2673631d6394ee2191a Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 14 Dec 2011 15:47:46 +0800 Subject: ASoC: mxs: correct 'direction' of device_prep_dma_cyclic The commit 49920bc (dmaengine: add new enum dma_transfer_direction) changes the type of parameter 'direction' of device_prep_dma_cyclic from dma_data_direction to dma_transfer_direction. Signed-off-by: Shawn Guo Signed-off-by: Vinod Koul diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index dea5aa4..4b48a16 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -136,7 +136,7 @@ static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream, iprtd->period_bytes * iprtd->periods, iprtd->period_bytes, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); + DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); if (!iprtd->desc) { dev_err(&chan->dev->device, "cannot prepare slave dma\n"); return -EINVAL; -- cgit v0.10.2 From 8730790b971d747dd4508d22fbc1a4f9422c2803 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Fri, 16 Dec 2011 11:01:40 +0200 Subject: dmaengine: intel_mid_dma: remove legacy pm interface Drivers should not support both legacy pm and new framework. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Vinod Koul diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index bbcd470..74f70aa 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -873,7 +873,7 @@ static int intel_mid_dma_alloc_chan_resources(struct dma_chan *chan) pm_runtime_get_sync(&mid->pdev->dev); if (mid->state == SUSPENDED) { - if (dma_resume(mid->pdev)) { + if (dma_resume(&mid->pdev->dev)) { pr_err("ERR_MDMA: resume failed"); return -EFAULT; } @@ -1346,8 +1346,9 @@ static void __devexit intel_mid_dma_remove(struct pci_dev *pdev) * * This function is called by OS when a power event occurs */ -int dma_suspend(struct pci_dev *pci, pm_message_t state) +static int dma_suspend(struct device *dev) { + struct pci_dev *pci = to_pci_dev(dev); int i; struct middma_device *device = pci_get_drvdata(pci); pr_debug("MDMA: dma_suspend called\n"); @@ -1371,8 +1372,9 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state) * * This function is called by OS when a power event occurs */ -int dma_resume(struct pci_dev *pci) +int dma_resume(struct device *dev) { + struct pci_dev *pci = to_pci_dev(dev); int ret; struct middma_device *device = pci_get_drvdata(pci); @@ -1438,6 +1440,8 @@ static const struct dev_pm_ops intel_mid_dma_pm = { .runtime_suspend = dma_runtime_suspend, .runtime_resume = dma_runtime_resume, .runtime_idle = dma_runtime_idle, + .suspend = dma_suspend, + .resume = dma_resume, }; static struct pci_driver intel_mid_dma_pci_driver = { @@ -1446,8 +1450,6 @@ static struct pci_driver intel_mid_dma_pci_driver = { .probe = intel_mid_dma_probe, .remove = __devexit_p(intel_mid_dma_remove), #ifdef CONFIG_PM - .suspend = dma_suspend, - .resume = dma_resume, .driver = { .pm = &intel_mid_dma_pm, }, diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h index c6de919..c83d35b 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h @@ -296,6 +296,6 @@ static inline struct intel_mid_dma_slave *to_intel_mid_dma_slave } -int dma_resume(struct pci_dev *pci); +int dma_resume(struct device *dev); #endif /*__INTEL_MID_DMAC_REGS_H__*/ -- cgit v0.10.2 From e9f3a49c9698da6f6b606d5e048ddce308438763 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 28 Dec 2011 11:27:40 +0530 Subject: dmaengine/ste_dma40: clear LNK on channel startup Otherwise if a previously physical channel is used as a logical channel, the LNK may have old values which affect the operation of the logical channel since the LNK register has different semantics between physical and logical channels. Signed-off-by: Rabin Vincent Reviewed-by: Jonas ABERG Signed-off-by: Narayanan G Acked-by: Linus Walleij Signed-off-by: Vinod Koul diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 972dc35..cc5ecbc 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1104,6 +1104,10 @@ static void d40_config_write(struct d40_chan *d40c) /* Set LIDX for lcla */ writel(lidx, chanbase + D40_CHAN_REG_SSELT); writel(lidx, chanbase + D40_CHAN_REG_SDELT); + + /* Clear LNK which will be used by d40_chan_has_events() */ + writel(0, chanbase + D40_CHAN_REG_SSLNK); + writel(0, chanbase + D40_CHAN_REG_SDLNK); } } -- cgit v0.10.2 From 5b25f70f4200766355cdabda604e131d2fb6010d Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Tue, 13 Sep 2011 10:55:48 +0200 Subject: Btrfs: add nested locking mode for paths This patch adds the possibilty to read-lock an extent even if it is already write-locked from the same thread. btrfs_find_all_roots() needs this capability. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index be1bf62..dd8d140 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3571,6 +3571,7 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree, atomic_set(&eb->blocking_writers, 0); atomic_set(&eb->spinning_readers, 0); atomic_set(&eb->spinning_writers, 0); + eb->lock_nested = 0; init_waitqueue_head(&eb->write_lock_wq); init_waitqueue_head(&eb->read_lock_wq); diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 7604c30..bc6a042cb 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -129,6 +129,7 @@ struct extent_buffer { struct list_head leak_list; struct rcu_head rcu_head; atomic_t refs; + pid_t lock_owner; /* count of read lock holders on the extent buffer */ atomic_t write_locks; @@ -137,6 +138,7 @@ struct extent_buffer { atomic_t blocking_readers; atomic_t spinning_readers; atomic_t spinning_writers; + int lock_nested; /* protects write locks */ rwlock_t lock; diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index d77b67c..5e178d8 100644 --- a/fs/btrfs/locking.c +++ b/fs/btrfs/locking.c @@ -33,6 +33,14 @@ void btrfs_assert_tree_read_locked(struct extent_buffer *eb); */ void btrfs_set_lock_blocking_rw(struct extent_buffer *eb, int rw) { + if (eb->lock_nested) { + read_lock(&eb->lock); + if (eb->lock_nested && current->pid == eb->lock_owner) { + read_unlock(&eb->lock); + return; + } + read_unlock(&eb->lock); + } if (rw == BTRFS_WRITE_LOCK) { if (atomic_read(&eb->blocking_writers) == 0) { WARN_ON(atomic_read(&eb->spinning_writers) != 1); @@ -57,6 +65,14 @@ void btrfs_set_lock_blocking_rw(struct extent_buffer *eb, int rw) */ void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw) { + if (eb->lock_nested) { + read_lock(&eb->lock); + if (&eb->lock_nested && current->pid == eb->lock_owner) { + read_unlock(&eb->lock); + return; + } + read_unlock(&eb->lock); + } if (rw == BTRFS_WRITE_LOCK_BLOCKING) { BUG_ON(atomic_read(&eb->blocking_writers) != 1); write_lock(&eb->lock); @@ -81,12 +97,25 @@ void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw) void btrfs_tree_read_lock(struct extent_buffer *eb) { again: + read_lock(&eb->lock); + if (atomic_read(&eb->blocking_writers) && + current->pid == eb->lock_owner) { + /* + * This extent is already write-locked by our thread. We allow + * an additional read lock to be added because it's for the same + * thread. btrfs_find_all_roots() depends on this as it may be + * called on a partly (write-)locked tree. + */ + BUG_ON(eb->lock_nested); + eb->lock_nested = 1; + read_unlock(&eb->lock); + return; + } + read_unlock(&eb->lock); wait_event(eb->write_lock_wq, atomic_read(&eb->blocking_writers) == 0); read_lock(&eb->lock); if (atomic_read(&eb->blocking_writers)) { read_unlock(&eb->lock); - wait_event(eb->write_lock_wq, - atomic_read(&eb->blocking_writers) == 0); goto again; } atomic_inc(&eb->read_locks); @@ -129,6 +158,7 @@ int btrfs_try_tree_write_lock(struct extent_buffer *eb) } atomic_inc(&eb->write_locks); atomic_inc(&eb->spinning_writers); + eb->lock_owner = current->pid; return 1; } @@ -137,6 +167,15 @@ int btrfs_try_tree_write_lock(struct extent_buffer *eb) */ void btrfs_tree_read_unlock(struct extent_buffer *eb) { + if (eb->lock_nested) { + read_lock(&eb->lock); + if (eb->lock_nested && current->pid == eb->lock_owner) { + eb->lock_nested = 0; + read_unlock(&eb->lock); + return; + } + read_unlock(&eb->lock); + } btrfs_assert_tree_read_locked(eb); WARN_ON(atomic_read(&eb->spinning_readers) == 0); atomic_dec(&eb->spinning_readers); @@ -149,6 +188,15 @@ void btrfs_tree_read_unlock(struct extent_buffer *eb) */ void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb) { + if (eb->lock_nested) { + read_lock(&eb->lock); + if (eb->lock_nested && current->pid == eb->lock_owner) { + eb->lock_nested = 0; + read_unlock(&eb->lock); + return; + } + read_unlock(&eb->lock); + } btrfs_assert_tree_read_locked(eb); WARN_ON(atomic_read(&eb->blocking_readers) == 0); if (atomic_dec_and_test(&eb->blocking_readers)) @@ -181,6 +229,7 @@ again: WARN_ON(atomic_read(&eb->spinning_writers)); atomic_inc(&eb->spinning_writers); atomic_inc(&eb->write_locks); + eb->lock_owner = current->pid; return 0; } -- cgit v0.10.2 From 00f04b88791ff49dc64ada18819d40a5b0671709 Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Wed, 14 Sep 2011 12:37:00 +0200 Subject: Btrfs: add sequence numbers to delayed refs Sequence numbers are needed to reconstruct the backrefs of a given extent to a certain point in time. The total set of backrefs consist of the set of backrefs recorded on disk plus the enqueued delayed refs for it that existed at that moment. This patch also adds a list that records all delayed refs which are currently in the process of being added. When walking all refs of an extent in btrfs_find_all_roots(), we freeze the current state of delayed refs, honor anythinh up to this point and prevent processing newer delayed refs to assert consistency. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index babd37b..a405db0 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -101,6 +101,11 @@ static int comp_entry(struct btrfs_delayed_ref_node *ref2, return -1; if (ref1->type > ref2->type) return 1; + /* merging of sequenced refs is not allowed */ + if (ref1->seq < ref2->seq) + return -1; + if (ref1->seq > ref2->seq) + return 1; if (ref1->type == BTRFS_TREE_BLOCK_REF_KEY || ref1->type == BTRFS_SHARED_BLOCK_REF_KEY) { return comp_tree_refs(btrfs_delayed_node_to_tree_ref(ref2), @@ -209,6 +214,24 @@ int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans, return 0; } +int btrfs_check_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs, + u64 seq) +{ + struct seq_list *elem; + + assert_spin_locked(&delayed_refs->lock); + if (list_empty(&delayed_refs->seq_head)) + return 0; + + elem = list_first_entry(&delayed_refs->seq_head, struct seq_list, list); + if (seq >= elem->seq) { + pr_debug("holding back delayed_ref %llu, lowest is %llu (%p)\n", + seq, elem->seq, delayed_refs); + return 1; + } + return 0; +} + int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans, struct list_head *cluster, u64 start) { @@ -438,6 +461,7 @@ static noinline int add_delayed_ref_head(struct btrfs_fs_info *fs_info, ref->action = 0; ref->is_head = 1; ref->in_tree = 1; + ref->seq = 0; head_ref = btrfs_delayed_node_to_head(ref); head_ref->must_insert_reserved = must_insert_reserved; @@ -479,6 +503,7 @@ static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_tree_ref *full_ref; struct btrfs_delayed_ref_root *delayed_refs; + u64 seq = 0; if (action == BTRFS_ADD_DELAYED_EXTENT) action = BTRFS_ADD_DELAYED_REF; @@ -494,6 +519,10 @@ static noinline int add_delayed_tree_ref(struct btrfs_fs_info *fs_info, ref->is_head = 0; ref->in_tree = 1; + if (need_ref_seq(for_cow, ref_root)) + seq = inc_delayed_seq(delayed_refs); + ref->seq = seq; + full_ref = btrfs_delayed_node_to_tree_ref(ref); full_ref->parent = parent; full_ref->root = ref_root; @@ -534,6 +563,7 @@ static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_data_ref *full_ref; struct btrfs_delayed_ref_root *delayed_refs; + u64 seq = 0; if (action == BTRFS_ADD_DELAYED_EXTENT) action = BTRFS_ADD_DELAYED_REF; @@ -549,6 +579,10 @@ static noinline int add_delayed_data_ref(struct btrfs_fs_info *fs_info, ref->is_head = 0; ref->in_tree = 1; + if (need_ref_seq(for_cow, ref_root)) + seq = inc_delayed_seq(delayed_refs); + ref->seq = seq; + full_ref = btrfs_delayed_node_to_data_ref(ref); full_ref->parent = parent; full_ref->root = ref_root; diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index a5fb2bc..174416f 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -33,6 +33,9 @@ struct btrfs_delayed_ref_node { /* the size of the extent */ u64 num_bytes; + /* seq number to keep track of insertion order */ + u64 seq; + /* ref count on this data structure */ atomic_t refs; @@ -136,6 +139,20 @@ struct btrfs_delayed_ref_root { int flushing; u64 run_delayed_start; + + /* + * seq number of delayed refs. We need to know if a backref was being + * added before the currently processed ref or afterwards. + */ + u64 seq; + + /* + * seq_list holds a list of all seq numbers that are currently being + * added to the list. While walking backrefs (btrfs_find_all_roots, + * qgroups), which might take some time, no newer ref must be processed, + * as it might influence the outcome of the walk. + */ + struct list_head seq_head; }; static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref) @@ -171,6 +188,59 @@ int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_head *head); int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans, struct list_head *cluster, u64 search_start); + +struct seq_list { + struct list_head list; + u64 seq; +}; + +static inline u64 inc_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs) +{ + assert_spin_locked(&delayed_refs->lock); + ++delayed_refs->seq; + return delayed_refs->seq; +} + +static inline void +btrfs_get_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs, + struct seq_list *elem) +{ + assert_spin_locked(&delayed_refs->lock); + elem->seq = delayed_refs->seq; + list_add_tail(&elem->list, &delayed_refs->seq_head); +} + +static inline void +btrfs_put_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs, + struct seq_list *elem) +{ + spin_lock(&delayed_refs->lock); + list_del(&elem->list); + spin_unlock(&delayed_refs->lock); +} + +int btrfs_check_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs, + u64 seq); + +/* + * delayed refs with a ref_seq > 0 must be held back during backref walking. + * this only applies to items in one of the fs-trees. for_cow items never need + * to be held back, so they won't get a ref_seq number. + */ +static inline int need_ref_seq(int for_cow, u64 rootid) +{ + if (for_cow) + return 0; + + if (rootid == BTRFS_FS_TREE_OBJECTID) + return 1; + + if ((s64)rootid >= (s64)BTRFS_FIRST_FREE_OBJECTID) + return 1; + + return 0; +} + /* * a node might live in a head or a regular ref, this lets you * test for the proper type to use. diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index a2bfedc..31a7393 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -36,6 +36,8 @@ static noinline void put_transaction(struct btrfs_transaction *transaction) WARN_ON(atomic_read(&transaction->use_count) == 0); if (atomic_dec_and_test(&transaction->use_count)) { BUG_ON(!list_empty(&transaction->list)); + WARN_ON(transaction->delayed_refs.root.rb_node); + WARN_ON(!list_empty(&transaction->delayed_refs.seq_head)); memset(transaction, 0, sizeof(*transaction)); kmem_cache_free(btrfs_transaction_cachep, transaction); } @@ -108,8 +110,10 @@ loop: cur_trans->delayed_refs.num_heads = 0; cur_trans->delayed_refs.flushing = 0; cur_trans->delayed_refs.run_delayed_start = 0; + cur_trans->delayed_refs.seq = 1; spin_lock_init(&cur_trans->commit_lock); spin_lock_init(&cur_trans->delayed_refs.lock); + INIT_LIST_HEAD(&cur_trans->delayed_refs.seq_head); INIT_LIST_HEAD(&cur_trans->pending_snapshots); list_add_tail(&cur_trans->list, &root->fs_info->trans_list); -- cgit v0.10.2 From d1270cd91f308c9d22b2804720c36ccd32dbc35e Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Tue, 13 Sep 2011 15:16:43 +0200 Subject: Btrfs: put back delayed refs that are too new When processing a delayed ref, first check if there are still old refs in the process of being added. If so, put this ref back to the tree. To avoid looping on this ref, choose a newer one in the next loop. btrfs_find_ref_cluster has to take care of that. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index a405db0..ee18198 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -155,16 +155,22 @@ static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root, /* * find an head entry based on bytenr. This returns the delayed ref - * head if it was able to find one, or NULL if nothing was in that spot + * head if it was able to find one, or NULL if nothing was in that spot. + * If return_bigger is given, the next bigger entry is returned if no exact + * match is found. */ static struct btrfs_delayed_ref_node *find_ref_head(struct rb_root *root, u64 bytenr, - struct btrfs_delayed_ref_node **last) + struct btrfs_delayed_ref_node **last, + int return_bigger) { - struct rb_node *n = root->rb_node; + struct rb_node *n; struct btrfs_delayed_ref_node *entry; - int cmp; + int cmp = 0; +again: + n = root->rb_node; + entry = NULL; while (n) { entry = rb_entry(n, struct btrfs_delayed_ref_node, rb_node); WARN_ON(!entry->in_tree); @@ -187,6 +193,19 @@ static struct btrfs_delayed_ref_node *find_ref_head(struct rb_root *root, else return entry; } + if (entry && return_bigger) { + if (cmp > 0) { + n = rb_next(&entry->rb_node); + if (!n) + n = rb_first(root); + entry = rb_entry(n, struct btrfs_delayed_ref_node, + rb_node); + bytenr = entry->bytenr; + return_bigger = 0; + goto again; + } + return entry; + } return NULL; } @@ -246,20 +265,8 @@ int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans, node = rb_first(&delayed_refs->root); } else { ref = NULL; - find_ref_head(&delayed_refs->root, start, &ref); + find_ref_head(&delayed_refs->root, start + 1, &ref, 1); if (ref) { - struct btrfs_delayed_ref_node *tmp; - - node = rb_prev(&ref->rb_node); - while (node) { - tmp = rb_entry(node, - struct btrfs_delayed_ref_node, - rb_node); - if (tmp->bytenr < start) - break; - ref = tmp; - node = rb_prev(&ref->rb_node); - } node = &ref->rb_node; } else node = rb_first(&delayed_refs->root); @@ -748,7 +755,7 @@ btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr) struct btrfs_delayed_ref_root *delayed_refs; delayed_refs = &trans->transaction->delayed_refs; - ref = find_ref_head(&delayed_refs->root, bytenr, NULL); + ref = find_ref_head(&delayed_refs->root, bytenr, NULL, 0); if (ref) return btrfs_delayed_node_to_head(ref); return NULL; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index dc8b9a8..bbcca12 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2237,6 +2237,28 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, } /* + * locked_ref is the head node, so we have to go one + * node back for any delayed ref updates + */ + ref = select_delayed_ref(locked_ref); + + if (ref && ref->seq && + btrfs_check_delayed_seq(delayed_refs, ref->seq)) { + /* + * there are still refs with lower seq numbers in the + * process of being added. Don't run this ref yet. + */ + list_del_init(&locked_ref->cluster); + mutex_unlock(&locked_ref->mutex); + locked_ref = NULL; + delayed_refs->num_heads_ready++; + spin_unlock(&delayed_refs->lock); + cond_resched(); + spin_lock(&delayed_refs->lock); + continue; + } + + /* * record the must insert reserved flag before we * drop the spin lock. */ @@ -2246,11 +2268,6 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, extent_op = locked_ref->extent_op; locked_ref->extent_op = NULL; - /* - * locked_ref is the head node, so we have to go one - * node back for any delayed ref updates - */ - ref = select_delayed_ref(locked_ref); if (!ref) { /* All delayed refs have been processed, Go ahead * and send the head node to run_one_delayed_ref, -- cgit v0.10.2 From a168650c08300434e1456abe7b6451f1448230d3 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 12 Dec 2011 16:10:07 +0100 Subject: Btrfs: add waitqueue instead of doing busy waiting for more delayed refs Now that we may be holding back delayed refs for a limited period, we might end up having no runnable delayed refs. Without this commit, we'd do busy waiting in that thread until another (runnable) ref arives. Instead, we're detecting this situation and use a waitqueue, such that we only try to run more refs after a) another runnable ref was added or b) delayed refs are no longer held back Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index ee18198..66e4f29 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -664,6 +664,9 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, num_bytes, parent, ref_root, level, action, for_cow); BUG_ON(ret); + if (!need_ref_seq(for_cow, ref_root) && + waitqueue_active(&delayed_refs->seq_wait)) + wake_up(&delayed_refs->seq_wait); spin_unlock(&delayed_refs->lock); return 0; } @@ -712,6 +715,9 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, num_bytes, parent, ref_root, owner, offset, action, for_cow); BUG_ON(ret); + if (!need_ref_seq(for_cow, ref_root) && + waitqueue_active(&delayed_refs->seq_wait)) + wake_up(&delayed_refs->seq_wait); spin_unlock(&delayed_refs->lock); return 0; } @@ -739,6 +745,8 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, extent_op->is_data); BUG_ON(ret); + if (waitqueue_active(&delayed_refs->seq_wait)) + wake_up(&delayed_refs->seq_wait); spin_unlock(&delayed_refs->lock); return 0; } diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 174416f..d8f244d 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -153,6 +153,12 @@ struct btrfs_delayed_ref_root { * as it might influence the outcome of the walk. */ struct list_head seq_head; + + /* + * when the only refs we have in the list must not be processed, we want + * to wait for more refs to show up or for the end of backref walking. + */ + wait_queue_head_t seq_wait; }; static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref) @@ -216,6 +222,7 @@ btrfs_put_delayed_seq(struct btrfs_delayed_ref_root *delayed_refs, { spin_lock(&delayed_refs->lock); list_del(&elem->list); + wake_up(&delayed_refs->seq_wait); spin_unlock(&delayed_refs->lock); } diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index bbcca12..0a435e2 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2300,7 +2300,12 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, ref->in_tree = 0; rb_erase(&ref->rb_node, &delayed_refs->root); delayed_refs->num_entries--; - + /* + * we modified num_entries, but as we're currently running + * delayed refs, skip + * wake_up(&delayed_refs->seq_wait); + * here. + */ spin_unlock(&delayed_refs->lock); ret = run_one_delayed_ref(trans, root, ref, extent_op, @@ -2317,6 +2322,23 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, return count; } + +static void wait_for_more_refs(struct btrfs_delayed_ref_root *delayed_refs, + unsigned long num_refs) +{ + struct list_head *first_seq = delayed_refs->seq_head.next; + + spin_unlock(&delayed_refs->lock); + pr_debug("waiting for more refs (num %ld, first %p)\n", + num_refs, first_seq); + wait_event(delayed_refs->seq_wait, + num_refs != delayed_refs->num_entries || + delayed_refs->seq_head.next != first_seq); + pr_debug("done waiting for more refs (num %ld, first %p)\n", + delayed_refs->num_entries, delayed_refs->seq_head.next); + spin_lock(&delayed_refs->lock); +} + /* * this starts processing the delayed reference count updates and * extent insertions we have queued up so far. count can be @@ -2332,8 +2354,11 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *ref; struct list_head cluster; int ret; + u64 delayed_start; int run_all = count == (unsigned long)-1; int run_most = 0; + unsigned long num_refs = 0; + int consider_waiting; if (root == root->fs_info->extent_root) root = root->fs_info->tree_root; @@ -2341,6 +2366,7 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, delayed_refs = &trans->transaction->delayed_refs; INIT_LIST_HEAD(&cluster); again: + consider_waiting = 0; spin_lock(&delayed_refs->lock); if (count == 0) { count = delayed_refs->num_entries * 2; @@ -2357,11 +2383,35 @@ again: * of refs to process starting at the first one we are able to * lock */ + delayed_start = delayed_refs->run_delayed_start; ret = btrfs_find_ref_cluster(trans, &cluster, delayed_refs->run_delayed_start); if (ret) break; + if (delayed_start >= delayed_refs->run_delayed_start) { + if (consider_waiting == 0) { + /* + * btrfs_find_ref_cluster looped. let's do one + * more cycle. if we don't run any delayed ref + * during that cycle (because we can't because + * all of them are blocked) and if the number of + * refs doesn't change, we avoid busy waiting. + */ + consider_waiting = 1; + num_refs = delayed_refs->num_entries; + } else { + wait_for_more_refs(delayed_refs, num_refs); + /* + * after waiting, things have changed. we + * dropped the lock and someone else might have + * run some refs, built new clusters and so on. + * therefore, we restart staleness detection. + */ + consider_waiting = 0; + } + } + ret = run_clustered_refs(trans, root, &cluster); BUG_ON(ret < 0); @@ -2369,6 +2419,11 @@ again: if (count == 0) break; + + if (ret || delayed_refs->run_delayed_start == 0) { + /* refs were run, let's reset staleness detection */ + consider_waiting = 0; + } } if (run_all) { @@ -4933,6 +4988,8 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, rb_erase(&head->node.rb_node, &delayed_refs->root); delayed_refs->num_entries--; + if (waitqueue_active(&delayed_refs->seq_wait)) + wake_up(&delayed_refs->seq_wait); /* * we don't take a ref on the node because we're removing it from the diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 31a7393..04c5c7c 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -111,6 +111,7 @@ loop: cur_trans->delayed_refs.flushing = 0; cur_trans->delayed_refs.run_delayed_start = 0; cur_trans->delayed_refs.seq = 1; + init_waitqueue_head(&cur_trans->delayed_refs.seq_wait); spin_lock_init(&cur_trans->commit_lock); spin_lock_init(&cur_trans->delayed_refs.lock); INIT_LIST_HEAD(&cur_trans->delayed_refs.seq_head); -- cgit v0.10.2 From 8da6d5815c592b713ecaf4f4f8b631f8359c96c4 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 23 Nov 2011 18:55:04 +0100 Subject: Btrfs: added btrfs_find_all_roots() This function gets a byte number (a data extent), collects all the leafs pointing to it and walks up the trees to find all fs roots pointing to those leafs. It also returns the list of all leafs pointing to that extent. It does proper locking for the involved trees, can be used on busy file systems and honors delayed refs. Signed-off-by: Arne Jansen Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 22c64ff..03c30a1 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -19,6 +19,9 @@ #include "ctree.h" #include "disk-io.h" #include "backref.h" +#include "ulist.h" +#include "transaction.h" +#include "delayed-ref.h" struct __data_ref { struct list_head list; @@ -32,6 +35,786 @@ struct __shared_ref { u64 disk_byte; }; +/* + * this structure records all encountered refs on the way up to the root + */ +struct __prelim_ref { + struct list_head list; + u64 root_id; + struct btrfs_key key; + int level; + int count; + u64 parent; + u64 wanted_disk_byte; +}; + +static int __add_prelim_ref(struct list_head *head, u64 root_id, + struct btrfs_key *key, int level, u64 parent, + u64 wanted_disk_byte, int count) +{ + struct __prelim_ref *ref; + + /* in case we're adding delayed refs, we're holding the refs spinlock */ + ref = kmalloc(sizeof(*ref), GFP_ATOMIC); + if (!ref) + return -ENOMEM; + + ref->root_id = root_id; + if (key) + ref->key = *key; + else + memset(&ref->key, 0, sizeof(ref->key)); + + ref->level = level; + ref->count = count; + ref->parent = parent; + ref->wanted_disk_byte = wanted_disk_byte; + list_add_tail(&ref->list, head); + + return 0; +} + +static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, + struct ulist *parents, + struct extent_buffer *eb, int level, + u64 wanted_objectid, u64 wanted_disk_byte) +{ + int ret; + int slot; + struct btrfs_file_extent_item *fi; + struct btrfs_key key; + u64 disk_byte; + +add_parent: + ret = ulist_add(parents, eb->start, 0, GFP_NOFS); + if (ret < 0) + return ret; + + if (level != 0) + return 0; + + /* + * if the current leaf is full with EXTENT_DATA items, we must + * check the next one if that holds a reference as well. + * ref->count cannot be used to skip this check. + * repeat this until we don't find any additional EXTENT_DATA items. + */ + while (1) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) + return ret; + if (ret) + return 0; + + eb = path->nodes[0]; + for (slot = 0; slot < btrfs_header_nritems(eb); ++slot) { + btrfs_item_key_to_cpu(eb, &key, slot); + if (key.objectid != wanted_objectid || + key.type != BTRFS_EXTENT_DATA_KEY) + return 0; + fi = btrfs_item_ptr(eb, slot, + struct btrfs_file_extent_item); + disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); + if (disk_byte == wanted_disk_byte) + goto add_parent; + } + } + + return 0; +} + +/* + * resolve an indirect backref in the form (root_id, key, level) + * to a logical address + */ +static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, + struct __prelim_ref *ref, + struct ulist *parents) +{ + struct btrfs_path *path; + struct btrfs_root *root; + struct btrfs_key root_key; + struct btrfs_key key = {0}; + struct extent_buffer *eb; + int ret = 0; + int root_level; + int level = ref->level; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + root_key.objectid = ref->root_id; + root_key.type = BTRFS_ROOT_ITEM_KEY; + root_key.offset = (u64)-1; + root = btrfs_read_fs_root_no_name(fs_info, &root_key); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto out; + } + + rcu_read_lock(); + root_level = btrfs_header_level(root->node); + rcu_read_unlock(); + + if (root_level + 1 == level) + goto out; + + path->lowest_level = level; + ret = btrfs_search_slot(NULL, root, &ref->key, path, 0, 0); + pr_debug("search slot in root %llu (level %d, ref count %d) returned " + "%d for key (%llu %u %llu)\n", + (unsigned long long)ref->root_id, level, ref->count, ret, + (unsigned long long)ref->key.objectid, ref->key.type, + (unsigned long long)ref->key.offset); + if (ret < 0) + goto out; + + eb = path->nodes[level]; + if (!eb) { + WARN_ON(1); + ret = 1; + goto out; + } + + if (level == 0) { + if (ret == 1 && path->slots[0] >= btrfs_header_nritems(eb)) { + ret = btrfs_next_leaf(root, path); + if (ret) + goto out; + eb = path->nodes[0]; + } + + btrfs_item_key_to_cpu(eb, &key, path->slots[0]); + } + + /* the last two parameters will only be used for level == 0 */ + ret = add_all_parents(root, path, parents, eb, level, key.objectid, + ref->wanted_disk_byte); +out: + btrfs_free_path(path); + return ret; +} + +/* + * resolve all indirect backrefs from the list + */ +static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, + struct list_head *head) +{ + int err; + int ret = 0; + struct __prelim_ref *ref; + struct __prelim_ref *ref_safe; + struct __prelim_ref *new_ref; + struct ulist *parents; + struct ulist_node *node; + + parents = ulist_alloc(GFP_NOFS); + if (!parents) + return -ENOMEM; + + /* + * _safe allows us to insert directly after the current item without + * iterating over the newly inserted items. + * we're also allowed to re-assign ref during iteration. + */ + list_for_each_entry_safe(ref, ref_safe, head, list) { + if (ref->parent) /* already direct */ + continue; + if (ref->count == 0) + continue; + err = __resolve_indirect_ref(fs_info, ref, parents); + if (err) { + if (ret == 0) + ret = err; + continue; + } + + /* we put the first parent into the ref at hand */ + node = ulist_next(parents, NULL); + ref->parent = node ? node->val : 0; + + /* additional parents require new refs being added here */ + while ((node = ulist_next(parents, node))) { + new_ref = kmalloc(sizeof(*new_ref), GFP_NOFS); + if (!new_ref) { + ret = -ENOMEM; + break; + } + memcpy(new_ref, ref, sizeof(*ref)); + new_ref->parent = node->val; + list_add(&new_ref->list, &ref->list); + } + ulist_reinit(parents); + } + + ulist_free(parents); + return ret; +} + +/* + * merge two lists of backrefs and adjust counts accordingly + * + * mode = 1: merge identical keys, if key is set + * mode = 2: merge identical parents + */ +static int __merge_refs(struct list_head *head, int mode) +{ + struct list_head *pos1; + + list_for_each(pos1, head) { + struct list_head *n2; + struct list_head *pos2; + struct __prelim_ref *ref1; + + ref1 = list_entry(pos1, struct __prelim_ref, list); + + if (mode == 1 && ref1->key.type == 0) + continue; + for (pos2 = pos1->next, n2 = pos2->next; pos2 != head; + pos2 = n2, n2 = pos2->next) { + struct __prelim_ref *ref2; + + ref2 = list_entry(pos2, struct __prelim_ref, list); + + if (mode == 1) { + if (memcmp(&ref1->key, &ref2->key, + sizeof(ref1->key)) || + ref1->level != ref2->level || + ref1->root_id != ref2->root_id) + continue; + ref1->count += ref2->count; + } else { + if (ref1->parent != ref2->parent) + continue; + ref1->count += ref2->count; + } + list_del(&ref2->list); + kfree(ref2); + } + + } + return 0; +} + +/* + * add all currently queued delayed refs from this head whose seq nr is + * smaller or equal that seq to the list + */ +static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq, + struct btrfs_key *info_key, + struct list_head *prefs) +{ + struct btrfs_delayed_extent_op *extent_op = head->extent_op; + struct rb_node *n = &head->node.rb_node; + int sgn; + int ret; + + if (extent_op && extent_op->update_key) + btrfs_disk_key_to_cpu(info_key, &extent_op->key); + + while ((n = rb_prev(n))) { + struct btrfs_delayed_ref_node *node; + node = rb_entry(n, struct btrfs_delayed_ref_node, + rb_node); + if (node->bytenr != head->node.bytenr) + break; + WARN_ON(node->is_head); + + if (node->seq > seq) + continue; + + switch (node->action) { + case BTRFS_ADD_DELAYED_EXTENT: + case BTRFS_UPDATE_DELAYED_HEAD: + WARN_ON(1); + continue; + case BTRFS_ADD_DELAYED_REF: + sgn = 1; + break; + case BTRFS_DROP_DELAYED_REF: + sgn = -1; + break; + default: + BUG_ON(1); + } + switch (node->type) { + case BTRFS_TREE_BLOCK_REF_KEY: { + struct btrfs_delayed_tree_ref *ref; + + ref = btrfs_delayed_node_to_tree_ref(node); + ret = __add_prelim_ref(prefs, ref->root, info_key, + ref->level + 1, 0, node->bytenr, + node->ref_mod * sgn); + break; + } + case BTRFS_SHARED_BLOCK_REF_KEY: { + struct btrfs_delayed_tree_ref *ref; + + ref = btrfs_delayed_node_to_tree_ref(node); + ret = __add_prelim_ref(prefs, ref->root, info_key, + ref->level + 1, ref->parent, + node->bytenr, + node->ref_mod * sgn); + break; + } + case BTRFS_EXTENT_DATA_REF_KEY: { + struct btrfs_delayed_data_ref *ref; + struct btrfs_key key; + + ref = btrfs_delayed_node_to_data_ref(node); + + key.objectid = ref->objectid; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = ref->offset; + ret = __add_prelim_ref(prefs, ref->root, &key, 0, 0, + node->bytenr, + node->ref_mod * sgn); + break; + } + case BTRFS_SHARED_DATA_REF_KEY: { + struct btrfs_delayed_data_ref *ref; + struct btrfs_key key; + + ref = btrfs_delayed_node_to_data_ref(node); + + key.objectid = ref->objectid; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = ref->offset; + ret = __add_prelim_ref(prefs, ref->root, &key, 0, + ref->parent, node->bytenr, + node->ref_mod * sgn); + break; + } + default: + WARN_ON(1); + } + BUG_ON(ret); + } + + return 0; +} + +/* + * add all inline backrefs for bytenr to the list + */ +static int __add_inline_refs(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, u64 bytenr, + struct btrfs_key *info_key, int *info_level, + struct list_head *prefs) +{ + int ret; + int slot; + struct extent_buffer *leaf; + struct btrfs_key key; + unsigned long ptr; + unsigned long end; + struct btrfs_extent_item *ei; + u64 flags; + u64 item_size; + + /* + * enumerate all inline refs + */ + leaf = path->nodes[0]; + slot = path->slots[0] - 1; + + item_size = btrfs_item_size_nr(leaf, slot); + BUG_ON(item_size < sizeof(*ei)); + + ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); + flags = btrfs_extent_flags(leaf, ei); + + ptr = (unsigned long)(ei + 1); + end = (unsigned long)ei + item_size; + + if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { + struct btrfs_tree_block_info *info; + struct btrfs_disk_key disk_key; + + info = (struct btrfs_tree_block_info *)ptr; + *info_level = btrfs_tree_block_level(leaf, info); + btrfs_tree_block_key(leaf, info, &disk_key); + btrfs_disk_key_to_cpu(info_key, &disk_key); + ptr += sizeof(struct btrfs_tree_block_info); + BUG_ON(ptr > end); + } else { + BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA)); + } + + while (ptr < end) { + struct btrfs_extent_inline_ref *iref; + u64 offset; + int type; + + iref = (struct btrfs_extent_inline_ref *)ptr; + type = btrfs_extent_inline_ref_type(leaf, iref); + offset = btrfs_extent_inline_ref_offset(leaf, iref); + + switch (type) { + case BTRFS_SHARED_BLOCK_REF_KEY: + ret = __add_prelim_ref(prefs, 0, info_key, + *info_level + 1, offset, + bytenr, 1); + break; + case BTRFS_SHARED_DATA_REF_KEY: { + struct btrfs_shared_data_ref *sdref; + int count; + + sdref = (struct btrfs_shared_data_ref *)(iref + 1); + count = btrfs_shared_data_ref_count(leaf, sdref); + ret = __add_prelim_ref(prefs, 0, NULL, 0, offset, + bytenr, count); + break; + } + case BTRFS_TREE_BLOCK_REF_KEY: + ret = __add_prelim_ref(prefs, offset, info_key, + *info_level + 1, 0, bytenr, 1); + break; + case BTRFS_EXTENT_DATA_REF_KEY: { + struct btrfs_extent_data_ref *dref; + int count; + u64 root; + + dref = (struct btrfs_extent_data_ref *)(&iref->offset); + count = btrfs_extent_data_ref_count(leaf, dref); + key.objectid = btrfs_extent_data_ref_objectid(leaf, + dref); + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = btrfs_extent_data_ref_offset(leaf, dref); + root = btrfs_extent_data_ref_root(leaf, dref); + ret = __add_prelim_ref(prefs, root, &key, 0, 0, bytenr, + count); + break; + } + default: + WARN_ON(1); + } + BUG_ON(ret); + ptr += btrfs_extent_inline_ref_size(type); + } + + return 0; +} + +/* + * add all non-inline backrefs for bytenr to the list + */ +static int __add_keyed_refs(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, u64 bytenr, + struct btrfs_key *info_key, int info_level, + struct list_head *prefs) +{ + struct btrfs_root *extent_root = fs_info->extent_root; + int ret; + int slot; + struct extent_buffer *leaf; + struct btrfs_key key; + + while (1) { + ret = btrfs_next_item(extent_root, path); + if (ret < 0) + break; + if (ret) { + ret = 0; + break; + } + + slot = path->slots[0]; + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + + if (key.objectid != bytenr) + break; + if (key.type < BTRFS_TREE_BLOCK_REF_KEY) + continue; + if (key.type > BTRFS_SHARED_DATA_REF_KEY) + break; + + switch (key.type) { + case BTRFS_SHARED_BLOCK_REF_KEY: + ret = __add_prelim_ref(prefs, 0, info_key, + info_level + 1, key.offset, + bytenr, 1); + break; + case BTRFS_SHARED_DATA_REF_KEY: { + struct btrfs_shared_data_ref *sdref; + int count; + + sdref = btrfs_item_ptr(leaf, slot, + struct btrfs_shared_data_ref); + count = btrfs_shared_data_ref_count(leaf, sdref); + ret = __add_prelim_ref(prefs, 0, NULL, 0, key.offset, + bytenr, count); + break; + } + case BTRFS_TREE_BLOCK_REF_KEY: + ret = __add_prelim_ref(prefs, key.offset, info_key, + info_level + 1, 0, bytenr, 1); + break; + case BTRFS_EXTENT_DATA_REF_KEY: { + struct btrfs_extent_data_ref *dref; + int count; + u64 root; + + dref = btrfs_item_ptr(leaf, slot, + struct btrfs_extent_data_ref); + count = btrfs_extent_data_ref_count(leaf, dref); + key.objectid = btrfs_extent_data_ref_objectid(leaf, + dref); + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = btrfs_extent_data_ref_offset(leaf, dref); + root = btrfs_extent_data_ref_root(leaf, dref); + ret = __add_prelim_ref(prefs, root, &key, 0, 0, + bytenr, count); + break; + } + default: + WARN_ON(1); + } + BUG_ON(ret); + } + + return ret; +} + +/* + * this adds all existing backrefs (inline backrefs, backrefs and delayed + * refs) for the given bytenr to the refs list, merges duplicates and resolves + * indirect refs to their parent bytenr. + * When roots are found, they're added to the roots list + * + * FIXME some caching might speed things up + */ +static int find_parent_nodes(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, u64 bytenr, + u64 seq, struct ulist *refs, struct ulist *roots) +{ + struct btrfs_key key; + struct btrfs_path *path; + struct btrfs_key info_key = { 0 }; + struct btrfs_delayed_ref_root *delayed_refs = NULL; + struct btrfs_delayed_ref_head *head = NULL; + int info_level = 0; + int ret; + struct list_head prefs_delayed; + struct list_head prefs; + struct __prelim_ref *ref; + + INIT_LIST_HEAD(&prefs); + INIT_LIST_HEAD(&prefs_delayed); + + key.objectid = bytenr; + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = (u64)-1; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* + * grab both a lock on the path and a lock on the delayed ref head. + * We need both to get a consistent picture of how the refs look + * at a specified point in time + */ +again: + ret = btrfs_search_slot(trans, fs_info->extent_root, &key, path, 0, 0); + if (ret < 0) + goto out; + BUG_ON(ret == 0); + + /* + * look if there are updates for this ref queued and lock the head + */ + delayed_refs = &trans->transaction->delayed_refs; + spin_lock(&delayed_refs->lock); + head = btrfs_find_delayed_ref_head(trans, bytenr); + if (head) { + if (!mutex_trylock(&head->mutex)) { + atomic_inc(&head->node.refs); + spin_unlock(&delayed_refs->lock); + + btrfs_release_path(path); + + /* + * Mutex was contended, block until it's + * released and try again + */ + mutex_lock(&head->mutex); + mutex_unlock(&head->mutex); + btrfs_put_delayed_ref(&head->node); + goto again; + } + ret = __add_delayed_refs(head, seq, &info_key, &prefs_delayed); + if (ret) + goto out; + } + spin_unlock(&delayed_refs->lock); + + if (path->slots[0]) { + struct extent_buffer *leaf; + int slot; + + leaf = path->nodes[0]; + slot = path->slots[0] - 1; + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid == bytenr && + key.type == BTRFS_EXTENT_ITEM_KEY) { + ret = __add_inline_refs(fs_info, path, bytenr, + &info_key, &info_level, &prefs); + if (ret) + goto out; + ret = __add_keyed_refs(fs_info, path, bytenr, &info_key, + info_level, &prefs); + if (ret) + goto out; + } + } + btrfs_release_path(path); + + /* + * when adding the delayed refs above, the info_key might not have + * been known yet. Go over the list and replace the missing keys + */ + list_for_each_entry(ref, &prefs_delayed, list) { + if ((ref->key.offset | ref->key.type | ref->key.objectid) == 0) + memcpy(&ref->key, &info_key, sizeof(ref->key)); + } + list_splice_init(&prefs_delayed, &prefs); + + ret = __merge_refs(&prefs, 1); + if (ret) + goto out; + + ret = __resolve_indirect_refs(fs_info, &prefs); + if (ret) + goto out; + + ret = __merge_refs(&prefs, 2); + if (ret) + goto out; + + while (!list_empty(&prefs)) { + ref = list_first_entry(&prefs, struct __prelim_ref, list); + list_del(&ref->list); + if (ref->count < 0) + WARN_ON(1); + if (ref->count && ref->root_id && ref->parent == 0) { + /* no parent == root of tree */ + ret = ulist_add(roots, ref->root_id, 0, GFP_NOFS); + BUG_ON(ret < 0); + } + if (ref->count && ref->parent) { + ret = ulist_add(refs, ref->parent, 0, GFP_NOFS); + BUG_ON(ret < 0); + } + kfree(ref); + } + +out: + if (head) + mutex_unlock(&head->mutex); + btrfs_free_path(path); + while (!list_empty(&prefs)) { + ref = list_first_entry(&prefs, struct __prelim_ref, list); + list_del(&ref->list); + kfree(ref); + } + while (!list_empty(&prefs_delayed)) { + ref = list_first_entry(&prefs_delayed, struct __prelim_ref, + list); + list_del(&ref->list); + kfree(ref); + } + + return ret; +} + +/* + * Finds all leafs with a reference to the specified combination of bytenr and + * offset. key_list_head will point to a list of corresponding keys (caller must + * free each list element). The leafs will be stored in the leafs ulist, which + * must be freed with ulist_free. + * + * returns 0 on success, <0 on error + */ +static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, u64 bytenr, + u64 num_bytes, u64 seq, struct ulist **leafs) +{ + struct ulist *tmp; + int ret; + + tmp = ulist_alloc(GFP_NOFS); + if (!tmp) + return -ENOMEM; + *leafs = ulist_alloc(GFP_NOFS); + if (!*leafs) { + ulist_free(tmp); + return -ENOMEM; + } + + ret = find_parent_nodes(trans, fs_info, bytenr, seq, *leafs, tmp); + ulist_free(tmp); + + if (ret < 0 && ret != -ENOENT) { + ulist_free(*leafs); + return ret; + } + + return 0; +} + +/* + * walk all backrefs for a given extent to find all roots that reference this + * extent. Walking a backref means finding all extents that reference this + * extent and in turn walk the backrefs of those, too. Naturally this is a + * recursive process, but here it is implemented in an iterative fashion: We + * find all referencing extents for the extent in question and put them on a + * list. In turn, we find all referencing extents for those, further appending + * to the list. The way we iterate the list allows adding more elements after + * the current while iterating. The process stops when we reach the end of the + * list. Found roots are added to the roots list. + * + * returns 0 on success, < 0 on error. + */ +int btrfs_find_all_roots(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, u64 bytenr, + u64 num_bytes, u64 seq, struct ulist **roots) +{ + struct ulist *tmp; + struct ulist_node *node = NULL; + int ret; + + tmp = ulist_alloc(GFP_NOFS); + if (!tmp) + return -ENOMEM; + *roots = ulist_alloc(GFP_NOFS); + if (!*roots) { + ulist_free(tmp); + return -ENOMEM; + } + + while (1) { + ret = find_parent_nodes(trans, fs_info, bytenr, seq, + tmp, *roots); + if (ret < 0 && ret != -ENOENT) { + ulist_free(tmp); + ulist_free(*roots); + return ret; + } + node = ulist_next(tmp, node); + if (!node) + break; + bytenr = node->val; + } + + ulist_free(tmp); + return 0; +} + + static int __inode_info(u64 inum, u64 ioff, u8 key_type, struct btrfs_root *fs_root, struct btrfs_path *path, struct btrfs_key *found_key) diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index 9261883..d00dfa9 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h @@ -20,6 +20,7 @@ #define __BTRFS_BACKREF__ #include "ioctl.h" +#include "ulist.h" struct inode_fs_paths { struct btrfs_path *btrfs_path; @@ -54,6 +55,10 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, int paths_from_inode(u64 inum, struct inode_fs_paths *ipath); +int btrfs_find_all_roots(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, u64 bytenr, + u64 num_bytes, u64 seq, struct ulist **roots); + struct btrfs_data_container *init_data_container(u32 total_bytes); struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root, struct btrfs_path *path); -- cgit v0.10.2 From 4692cf58aa7b81f721c1653d48db99ea41421d58 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 2 Dec 2011 14:56:41 +0100 Subject: Btrfs: new backref walking code The old backref iteration code could only safely be used on commit roots. Besides this limitation, it had bugs in finding the roots for these references. This commit replaces large parts of it by btrfs_find_all_roots() which a) really finds all roots and the correct roots, b) works correctly under heavy file system load, c) considers delayed refs. Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 03c30a1..b9a8432 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -23,18 +23,6 @@ #include "transaction.h" #include "delayed-ref.h" -struct __data_ref { - struct list_head list; - u64 inum; - u64 root; - u64 extent_data_item_offset; -}; - -struct __shared_ref { - struct list_head list; - u64 disk_byte; -}; - /* * this structure records all encountered refs on the way up to the root */ @@ -964,8 +952,11 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]); if (found_key->type != BTRFS_EXTENT_ITEM_KEY || found_key->objectid > logical || - found_key->objectid + found_key->offset <= logical) + found_key->objectid + found_key->offset <= logical) { + pr_debug("logical %llu is not within any extent\n", + (unsigned long long)logical); return -ENOENT; + } eb = path->nodes[0]; item_size = btrfs_item_size_nr(eb, path->slots[0]); @@ -974,6 +965,13 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); flags = btrfs_extent_flags(eb, ei); + pr_debug("logical %llu is at position %llu within the extent (%llu " + "EXTENT_ITEM %llu) flags %#llx size %u\n", + (unsigned long long)logical, + (unsigned long long)(logical - found_key->objectid), + (unsigned long long)found_key->objectid, + (unsigned long long)found_key->offset, + (unsigned long long)flags, item_size); if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) return BTRFS_EXTENT_FLAG_TREE_BLOCK; if (flags & BTRFS_EXTENT_FLAG_DATA) @@ -1070,128 +1068,11 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, return 0; } -static int __data_list_add(struct list_head *head, u64 inum, - u64 extent_data_item_offset, u64 root) -{ - struct __data_ref *ref; - - ref = kmalloc(sizeof(*ref), GFP_NOFS); - if (!ref) - return -ENOMEM; - - ref->inum = inum; - ref->extent_data_item_offset = extent_data_item_offset; - ref->root = root; - list_add_tail(&ref->list, head); - - return 0; -} - -static int __data_list_add_eb(struct list_head *head, struct extent_buffer *eb, - struct btrfs_extent_data_ref *dref) -{ - return __data_list_add(head, btrfs_extent_data_ref_objectid(eb, dref), - btrfs_extent_data_ref_offset(eb, dref), - btrfs_extent_data_ref_root(eb, dref)); -} - -static int __shared_list_add(struct list_head *head, u64 disk_byte) -{ - struct __shared_ref *ref; - - ref = kmalloc(sizeof(*ref), GFP_NOFS); - if (!ref) - return -ENOMEM; - - ref->disk_byte = disk_byte; - list_add_tail(&ref->list, head); - - return 0; -} - -static int __iter_shared_inline_ref_inodes(struct btrfs_fs_info *fs_info, - u64 logical, u64 inum, - u64 extent_data_item_offset, - u64 extent_offset, - struct btrfs_path *path, - struct list_head *data_refs, - iterate_extent_inodes_t *iterate, - void *ctx) -{ - u64 ref_root; - u32 item_size; - struct btrfs_key key; - struct extent_buffer *eb; - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *eiref; - struct __data_ref *ref; - int ret; - int type; - int last; - unsigned long ptr = 0; - - WARN_ON(!list_empty(data_refs)); - ret = extent_from_logical(fs_info, logical, path, &key); - if (ret & BTRFS_EXTENT_FLAG_DATA) - ret = -EIO; - if (ret < 0) - goto out; - - eb = path->nodes[0]; - ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); - item_size = btrfs_item_size_nr(eb, path->slots[0]); - - ret = 0; - ref_root = 0; - /* - * as done in iterate_extent_inodes, we first build a list of refs to - * iterate, then free the path and then iterate them to avoid deadlocks. - */ - do { - last = __get_extent_inline_ref(&ptr, eb, ei, item_size, - &eiref, &type); - if (last < 0) { - ret = last; - goto out; - } - if (type == BTRFS_TREE_BLOCK_REF_KEY || - type == BTRFS_SHARED_BLOCK_REF_KEY) { - ref_root = btrfs_extent_inline_ref_offset(eb, eiref); - ret = __data_list_add(data_refs, inum, - extent_data_item_offset, - ref_root); - } - } while (!ret && !last); - - btrfs_release_path(path); - - if (ref_root == 0) { - printk(KERN_ERR "btrfs: failed to find tree block ref " - "for shared data backref %llu\n", logical); - WARN_ON(1); - ret = -EIO; - } - -out: - while (!list_empty(data_refs)) { - ref = list_first_entry(data_refs, struct __data_ref, list); - list_del(&ref->list); - if (!ret) - ret = iterate(ref->inum, extent_offset + - ref->extent_data_item_offset, - ref->root, ctx); - kfree(ref); - } - - return ret; -} - -static int __iter_shared_inline_ref(struct btrfs_fs_info *fs_info, - u64 logical, u64 orig_extent_item_objectid, - u64 extent_offset, struct btrfs_path *path, - struct list_head *data_refs, - iterate_extent_inodes_t *iterate, - void *ctx) +static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, + struct btrfs_path *path, u64 logical, + u64 orig_extent_item_objectid, + u64 extent_item_pos, u64 root, + iterate_extent_inodes_t *iterate, void *ctx) { u64 disk_byte; struct btrfs_key key; @@ -1199,8 +1080,10 @@ static int __iter_shared_inline_ref(struct btrfs_fs_info *fs_info, struct extent_buffer *eb; int slot; int nritems; - int ret; - int found = 0; + int ret = 0; + int extent_type; + u64 data_offset; + u64 data_len; eb = read_tree_block(fs_info->tree_root, logical, fs_info->tree_root->leafsize, 0); @@ -1218,149 +1101,99 @@ static int __iter_shared_inline_ref(struct btrfs_fs_info *fs_info, if (key.type != BTRFS_EXTENT_DATA_KEY) continue; fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); - if (!fi) { - free_extent_buffer(eb); - return -EIO; - } + extent_type = btrfs_file_extent_type(eb, fi); + if (extent_type == BTRFS_FILE_EXTENT_INLINE) + continue; + /* don't skip BTRFS_FILE_EXTENT_PREALLOC, we can handle that */ disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); - if (disk_byte != orig_extent_item_objectid) { - if (found) - break; - else - continue; - } - ++found; - ret = __iter_shared_inline_ref_inodes(fs_info, logical, - key.objectid, - key.offset, - extent_offset, path, - data_refs, - iterate, ctx); - if (ret) - break; - } + if (disk_byte != orig_extent_item_objectid) + continue; - if (!found) { - printk(KERN_ERR "btrfs: failed to follow shared data backref " - "to parent %llu\n", logical); - WARN_ON(1); - ret = -EIO; + data_offset = btrfs_file_extent_offset(eb, fi); + data_len = btrfs_file_extent_num_bytes(eb, fi); + + if (extent_item_pos < data_offset || + extent_item_pos >= data_offset + data_len) + continue; + + pr_debug("ref for %llu resolved, key (%llu EXTEND_DATA %llu), " + "root %llu\n", orig_extent_item_objectid, + key.objectid, key.offset, root); + ret = iterate(key.objectid, + key.offset + (extent_item_pos - data_offset), + root, ctx); + if (ret) { + pr_debug("stopping iteration because ret=%d\n", ret); + break; + } } free_extent_buffer(eb); + return ret; } /* * calls iterate() for every inode that references the extent identified by - * the given parameters. will use the path given as a parameter and return it - * released. + * the given parameters. * when the iterator function returns a non-zero value, iteration stops. + * path is guaranteed to be in released state when iterate() is called. */ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, struct btrfs_path *path, - u64 extent_item_objectid, - u64 extent_offset, + u64 extent_item_objectid, u64 extent_item_pos, iterate_extent_inodes_t *iterate, void *ctx) { - unsigned long ptr = 0; - int last; int ret; - int type; - u64 logical; - u32 item_size; - struct btrfs_extent_inline_ref *eiref; - struct btrfs_extent_data_ref *dref; - struct extent_buffer *eb; - struct btrfs_extent_item *ei; - struct btrfs_key key; struct list_head data_refs = LIST_HEAD_INIT(data_refs); struct list_head shared_refs = LIST_HEAD_INIT(shared_refs); - struct __data_ref *ref_d; - struct __shared_ref *ref_s; + struct btrfs_trans_handle *trans; + struct ulist *refs; + struct ulist *roots; + struct ulist_node *ref_node = NULL; + struct ulist_node *root_node = NULL; + struct seq_list seq_elem; + struct btrfs_delayed_ref_root *delayed_refs; - eb = path->nodes[0]; - ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); - item_size = btrfs_item_size_nr(eb, path->slots[0]); - - /* first we iterate the inline refs, ... */ - do { - last = __get_extent_inline_ref(&ptr, eb, ei, item_size, - &eiref, &type); - if (last == -ENOENT) { - ret = 0; - break; - } - if (last < 0) { - ret = last; - break; - } + trans = btrfs_join_transaction(fs_info->extent_root); + if (IS_ERR(trans)) + return PTR_ERR(trans); - if (type == BTRFS_EXTENT_DATA_REF_KEY) { - dref = (struct btrfs_extent_data_ref *)(&eiref->offset); - ret = __data_list_add_eb(&data_refs, eb, dref); - } else if (type == BTRFS_SHARED_DATA_REF_KEY) { - logical = btrfs_extent_inline_ref_offset(eb, eiref); - ret = __shared_list_add(&shared_refs, logical); - } - } while (!ret && !last); + pr_debug("resolving all inodes for extent %llu\n", + extent_item_objectid); - /* ... then we proceed to in-tree references and ... */ - while (!ret) { - ++path->slots[0]; - if (path->slots[0] > btrfs_header_nritems(eb)) { - ret = btrfs_next_leaf(fs_info->extent_root, path); - if (ret) { - if (ret == 1) - ret = 0; /* we're done */ - break; - } - eb = path->nodes[0]; - } - btrfs_item_key_to_cpu(eb, &key, path->slots[0]); - if (key.objectid != extent_item_objectid) - break; - if (key.type == BTRFS_EXTENT_DATA_REF_KEY) { - dref = btrfs_item_ptr(eb, path->slots[0], - struct btrfs_extent_data_ref); - ret = __data_list_add_eb(&data_refs, eb, dref); - } else if (key.type == BTRFS_SHARED_DATA_REF_KEY) { - ret = __shared_list_add(&shared_refs, key.offset); - } - } + delayed_refs = &trans->transaction->delayed_refs; + spin_lock(&delayed_refs->lock); + btrfs_get_delayed_seq(delayed_refs, &seq_elem); + spin_unlock(&delayed_refs->lock); - btrfs_release_path(path); + ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid, + extent_item_pos, seq_elem.seq, + &refs); - /* - * ... only at the very end we can process the refs we found. this is - * because the iterator function we call is allowed to make tree lookups - * and we have to avoid deadlocks. additionally, we need more tree - * lookups ourselves for shared data refs. - */ - while (!list_empty(&data_refs)) { - ref_d = list_first_entry(&data_refs, struct __data_ref, list); - list_del(&ref_d->list); - if (!ret) - ret = iterate(ref_d->inum, extent_offset + - ref_d->extent_data_item_offset, - ref_d->root, ctx); - kfree(ref_d); - } + if (ret) + goto out; - while (!list_empty(&shared_refs)) { - ref_s = list_first_entry(&shared_refs, struct __shared_ref, - list); - list_del(&ref_s->list); - if (!ret) - ret = __iter_shared_inline_ref(fs_info, - ref_s->disk_byte, - extent_item_objectid, - extent_offset, path, - &data_refs, - iterate, ctx); - kfree(ref_s); + while (!ret && (ref_node = ulist_next(refs, ref_node))) { + ret = btrfs_find_all_roots(trans, fs_info, ref_node->val, -1, + seq_elem.seq, &roots); + if (ret) + break; + while (!ret && (root_node = ulist_next(roots, root_node))) { + pr_debug("root %llu references leaf %llu\n", + root_node->val, ref_node->val); + ret = iterate_leaf_refs(fs_info, path, ref_node->val, + extent_item_objectid, + extent_item_pos, root_node->val, + iterate, ctx); + } } + ulist_free(refs); + ulist_free(roots); +out: + btrfs_put_delayed_seq(delayed_refs, &seq_elem); + btrfs_end_transaction(trans, fs_info->extent_root); return ret; } @@ -1369,19 +1202,20 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, iterate_extent_inodes_t *iterate, void *ctx) { int ret; - u64 offset; + u64 extent_item_pos; struct btrfs_key found_key; ret = extent_from_logical(fs_info, logical, path, &found_key); + btrfs_release_path(path); if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) ret = -EINVAL; if (ret < 0) return ret; - offset = logical - found_key.objectid; + extent_item_pos = logical - found_key.objectid; ret = iterate_extent_inodes(fs_info, path, found_key.objectid, - offset, iterate, ctx); + extent_item_pos, iterate, ctx); return ret; } @@ -1426,6 +1260,10 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, for (cur = 0; cur < btrfs_item_size(eb, item); cur += len) { name_len = btrfs_inode_ref_name_len(eb, iref); /* path must be released before calling iterate()! */ + pr_debug("following ref at offset %u for inode %llu in " + "tree %llu\n", cur, + (unsigned long long)found_key.objectid, + (unsigned long long)fs_root->objectid); ret = iterate(parent, iref, eb, ctx); if (ret) { free_extent_buffer(eb); @@ -1466,10 +1304,14 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref, return PTR_ERR(fspath); if (fspath > fspath_min) { + pr_debug("path resolved: %s\n", fspath); ipath->fspath->val[i] = (u64)(unsigned long)fspath; ++ipath->fspath->elem_cnt; ipath->fspath->bytes_left = fspath - fspath_min; } else { + pr_debug("missed path, not enough space. missing bytes: %lu, " + "constructed so far: %s\n", + (unsigned long)(fspath_min - fspath), fspath_min); ++ipath->fspath->elem_missed; ipath->fspath->bytes_missing += fspath_min - fspath; ipath->fspath->bytes_left = 0; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c48f2e9..9b05268 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2976,7 +2976,7 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root, { int ret = 0; int size; - u64 extent_offset; + u64 extent_item_pos; struct btrfs_ioctl_logical_ino_args *loi; struct btrfs_data_container *inodes = NULL; struct btrfs_path *path = NULL; @@ -3007,15 +3007,17 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root, } ret = extent_from_logical(root->fs_info, loi->logical, path, &key); + btrfs_release_path(path); if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) ret = -ENOENT; if (ret < 0) goto out; - extent_offset = loi->logical - key.objectid; + extent_item_pos = loi->logical - key.objectid; ret = iterate_extent_inodes(root->fs_info, path, key.objectid, - extent_offset, build_ino_list, inodes); + extent_item_pos, build_ino_list, + inodes); if (ret < 0) goto out; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index c27bcb6..b5edff2 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -309,7 +309,7 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio, u8 ref_level; unsigned long ptr = 0; const int bufsize = 4096; - u64 extent_offset; + u64 extent_item_pos; path = btrfs_alloc_path(); @@ -329,12 +329,13 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio, if (ret < 0) goto out; - extent_offset = swarn.logical - found_key.objectid; + extent_item_pos = swarn.logical - found_key.objectid; swarn.extent_item_size = found_key.offset; eb = path->nodes[0]; ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); item_size = btrfs_item_size_nr(eb, path->slots[0]); + btrfs_release_path(path); if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) { do { @@ -351,7 +352,7 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio, } else { swarn.path = path; iterate_extent_inodes(fs_info, path, found_key.objectid, - extent_offset, + extent_item_pos, scrub_print_warning_inode, &swarn); } -- cgit v0.10.2 From 6bf7e080d5bcb0d399ee38ce3dabbfad64448192 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 1 Dec 2011 14:35:19 +0100 Subject: Btrfs: make sure we're not using obsolete code in btrfs_get_extent There's code in btrfs_get_extent that should never be used. This patch turns a WARN_ON(1) into a BUG(), hoping we can remove the transaction code from btrfs_get_extent soon. Signed-off-by: Jan Schmidt diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ea81938..603d740 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5022,7 +5022,7 @@ again: } flush_dcache_page(page); } else if (create && PageUptodate(page)) { - WARN_ON(1); + BUG(); if (!trans) { kunmap(page); free_extent_map(em); -- cgit v0.10.2 From f69f2e264f6388df6d3cae45dd67ddfd52aaa14b Mon Sep 17 00:00:00 2001 From: Haitao Zhang Date: Sun, 1 Jan 2012 11:30:06 +0800 Subject: dma/imx-sdma: save irq flags when use spin_lock in sdma_tx_submit mx53_loco: fix deadlock report from sdma_tx_submit() during boot BugLink: http://bugs.launchpad.net/bugs/878701 Adjust to use spin_lock_irqsave()/spin_unlock_irqresotre(), so to make it safe when called from interrupt context. Signed-off-by: Haitao Zhang Signed-off-by: Eric Miao Signed-off-by: Richard Zhao Acked-by: Sascha Hauer Signed-off-by: Vinod Koul diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f59fd8f..a8af379 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -836,17 +836,18 @@ static struct sdma_channel *to_sdma_chan(struct dma_chan *chan) static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx) { + unsigned long flags; struct sdma_channel *sdmac = to_sdma_chan(tx->chan); struct sdma_engine *sdma = sdmac->sdma; dma_cookie_t cookie; - spin_lock_irq(&sdmac->lock); + spin_lock_irqsave(&sdmac->lock, flags); cookie = sdma_assign_cookie(sdmac); sdma_enable_channel(sdma, sdmac->channel); - spin_unlock_irq(&sdmac->lock); + spin_unlock_irqrestore(&sdmac->lock, flags); return cookie; } -- cgit v0.10.2 From c11b46c32c8a9bf05fdb76d70d8dc74fcbfd02d1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 4 Jan 2012 15:34:17 +0100 Subject: dma: shdma: fix runtime PM: clear channel buffers on reset On platforms, supporting power domains, if the domain, containing a DMAC instance is powered down, the driver fails to resume correctly. On those platforms DMAC channels have an additional CHCLR register for clearing channel buffers. Using this register during runtime resume fixes the problem. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 592304f..54043cd 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -56,6 +56,15 @@ static LIST_HEAD(sh_dmae_devices); static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)]; static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all); +static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan); + +static void chclr_write(struct sh_dmae_chan *sh_dc, u32 data) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + __raw_writel(data, shdev->chan_reg + + shdev->pdata->channel[sh_dc->id].chclr_offset); +} static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg) { @@ -128,6 +137,15 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev) dmaor = dmaor_read(shdev) & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME); + if (shdev->pdata->chclr_present) { + int i; + for (i = 0; i < shdev->pdata->channel_num; i++) { + struct sh_dmae_chan *sh_chan = shdev->chan[i]; + if (sh_chan) + chclr_write(sh_chan, 0); + } + } + dmaor_write(shdev, dmaor | shdev->pdata->dmaor_init); dmaor = dmaor_read(shdev); @@ -138,6 +156,10 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev) dev_warn(shdev->common.dev, "Can't initialize DMAOR.\n"); return -EIO; } + if (shdev->pdata->dmaor_init & ~dmaor) + dev_warn(shdev->common.dev, + "DMAOR=0x%x hasn't latched the initial value 0x%x.\n", + dmaor, shdev->pdata->dmaor_init); return 0; } @@ -258,8 +280,6 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) return 0; } -static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan); - static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) { struct sh_desc *desc = tx_to_sh_desc(tx), *chunk, *last = desc, *c; @@ -339,6 +359,8 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) sh_chan_xfer_ld_queue(sh_chan); sh_chan->pm_state = DMAE_PM_ESTABLISHED; } + } else { + sh_chan->pm_state = DMAE_PM_PENDING; } spin_unlock_irq(&sh_chan->desc_lock); @@ -1224,6 +1246,8 @@ static int __init sh_dmae_probe(struct platform_device *pdev) platform_set_drvdata(pdev, shdev); + shdev->common.dev = &pdev->dev; + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); @@ -1253,7 +1277,6 @@ static int __init sh_dmae_probe(struct platform_device *pdev) shdev->common.device_prep_slave_sg = sh_dmae_prep_slave_sg; shdev->common.device_control = sh_dmae_control; - shdev->common.dev = &pdev->dev; /* Default transfer size of 32 bytes requires 32-byte alignment */ shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE; @@ -1434,22 +1457,17 @@ static int sh_dmae_runtime_resume(struct device *dev) #ifdef CONFIG_PM static int sh_dmae_suspend(struct device *dev) { - struct sh_dmae_device *shdev = dev_get_drvdata(dev); - int i; - - for (i = 0; i < shdev->pdata->channel_num; i++) { - struct sh_dmae_chan *sh_chan = shdev->chan[i]; - if (sh_chan->descs_allocated) - sh_chan->pm_error = pm_runtime_put_sync(dev); - } - return 0; } static int sh_dmae_resume(struct device *dev) { struct sh_dmae_device *shdev = dev_get_drvdata(dev); - int i; + int i, ret; + + ret = sh_dmae_rst(shdev); + if (ret < 0) + dev_err(dev, "Failed to reset!\n"); for (i = 0; i < shdev->pdata->channel_num; i++) { struct sh_dmae_chan *sh_chan = shdev->chan[i]; @@ -1458,9 +1476,6 @@ static int sh_dmae_resume(struct device *dev) if (!sh_chan->descs_allocated) continue; - if (!sh_chan->pm_error) - pm_runtime_get_sync(dev); - if (param) { const struct sh_dmae_slave_config *cfg = param->config; dmae_set_dmars(sh_chan, cfg->mid_rid); diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 62ef693..8cd7fe5 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -48,6 +48,7 @@ struct sh_dmae_channel { unsigned int offset; unsigned int dmars; unsigned int dmars_bit; + unsigned int chclr_offset; }; struct sh_dmae_pdata { @@ -68,6 +69,7 @@ struct sh_dmae_pdata { unsigned int dmaor_is_32bit:1; unsigned int needs_tend_set:1; unsigned int no_dmars:1; + unsigned int chclr_present:1; }; /* DMA register */ -- cgit v0.10.2 From e08b881a69d638175bfa99b5af4d72b731633ea7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 4 Jan 2012 15:34:21 +0100 Subject: ARM: mach-shmobile: specify CHCLR registers on SH7372 Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c index 2380389..83dc940 100644 --- a/arch/arm/mach-shmobile/setup-sh7372.c +++ b/arch/arm/mach-shmobile/setup-sh7372.c @@ -445,31 +445,39 @@ static const struct sh_dmae_slave_config sh7372_dmae_slaves[] = { }, }; +#define SH7372_CHCLR 0x220 + static const struct sh_dmae_channel sh7372_dmae_channels[] = { { .offset = 0, .dmars = 0, .dmars_bit = 0, + .chclr_offset = SH7372_CHCLR + 0, }, { .offset = 0x10, .dmars = 0, .dmars_bit = 8, + .chclr_offset = SH7372_CHCLR + 0x10, }, { .offset = 0x20, .dmars = 4, .dmars_bit = 0, + .chclr_offset = SH7372_CHCLR + 0x20, }, { .offset = 0x30, .dmars = 4, .dmars_bit = 8, + .chclr_offset = SH7372_CHCLR + 0x30, }, { .offset = 0x50, .dmars = 8, .dmars_bit = 0, + .chclr_offset = SH7372_CHCLR + 0x50, }, { .offset = 0x60, .dmars = 8, .dmars_bit = 8, + .chclr_offset = SH7372_CHCLR + 0x60, } }; @@ -487,6 +495,7 @@ static struct sh_dmae_pdata dma_platform_data = { .ts_shift = ts_shift, .ts_shift_num = ARRAY_SIZE(ts_shift), .dmaor_init = DMAOR_DME, + .chclr_present = 1, }; /* Resource order important! */ @@ -494,7 +503,7 @@ static struct resource sh7372_dmae0_resources[] = { { /* Channel registers and DMAOR */ .start = 0xfe008020, - .end = 0xfe00808f, + .end = 0xfe00828f, .flags = IORESOURCE_MEM, }, { @@ -522,7 +531,7 @@ static struct resource sh7372_dmae1_resources[] = { { /* Channel registers and DMAOR */ .start = 0xfe018020, - .end = 0xfe01808f, + .end = 0xfe01828f, .flags = IORESOURCE_MEM, }, { @@ -550,7 +559,7 @@ static struct resource sh7372_dmae2_resources[] = { { /* Channel registers and DMAOR */ .start = 0xfe028020, - .end = 0xfe02808f, + .end = 0xfe02828f, .flags = IORESOURCE_MEM, }, { -- cgit v0.10.2 From 5e1a0f9f8d604c21fd72a09b632e3cd9ae66d3a6 Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 5 Jan 2012 15:55:46 -0800 Subject: ARM: msm: vreg is a module and so needs module.h The MSM7201 vreg appears to have gotten missed in the module.h cleanup. Signed-off-by: David Brown diff --git a/arch/arm/mach-msm/vreg.c b/arch/arm/mach-msm/vreg.c index a9103bc..bd66ed0 100644 --- a/arch/arm/mach-msm/vreg.c +++ b/arch/arm/mach-msm/vreg.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From 203bf287cb01a5dc26c20bd3737cecf3aeba1d48 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 6 Jan 2012 15:23:57 -0500 Subject: Btrfs: run chunk allocations while we do delayed refs Btrfs tries to batch extent allocation tree changes to improve performance and reduce metadata trashing. But it doesn't allocate new metadata chunks while it is doing allocations for the extent allocation tree. This commit changes the delayed refence code to do chunk allocations if we're getting low on room. It prevents crashes and improves performance. Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f5fbe57..71549d1 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2267,9 +2267,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, BUG_ON(ret); kfree(extent_op); - cond_resched(); - spin_lock(&delayed_refs->lock); - continue; + goto next; } list_del_init(&locked_ref->cluster); @@ -2289,7 +2287,11 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, btrfs_put_delayed_ref(ref); kfree(extent_op); count++; - +next: + do_chunk_alloc(trans, root->fs_info->extent_root, + 2 * 1024 * 1024, + btrfs_get_alloc_profile(root, 0), + CHUNK_ALLOC_NO_FORCE); cond_resched(); spin_lock(&delayed_refs->lock); } @@ -2317,6 +2319,10 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, if (root == root->fs_info->extent_root) root = root->fs_info->tree_root; + do_chunk_alloc(trans, root->fs_info->extent_root, + 2 * 1024 * 1024, btrfs_get_alloc_profile(root, 0), + CHUNK_ALLOC_NO_FORCE); + delayed_refs = &trans->transaction->delayed_refs; INIT_LIST_HEAD(&cluster); again: diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 81376d9..360c2df 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -467,19 +467,12 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, btrfs_trans_release_metadata(trans, root); trans->block_rsv = NULL; - while (count < 4) { + while (count < 2) { unsigned long cur = trans->delayed_ref_updates; trans->delayed_ref_updates = 0; if (cur && trans->transaction->delayed_refs.num_heads_ready > 64) { trans->delayed_ref_updates = 0; - - /* - * do a full flush if the transaction is trying - * to close - */ - if (trans->transaction->delayed_refs.flushing) - cur = 0; btrfs_run_delayed_refs(trans, root, cur); } else { break; -- cgit v0.10.2 From cf1d72c9ceec391d34c48724da57282e97f01122 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 6 Jan 2012 15:41:34 -0500 Subject: Btrfs: lower the bar for chunk allocation The chunk allocation code has tried to keep a pretty tight lid on creating new metadata chunks. This is partially because in the past the reservation code didn't give us an accurate idea of how much space was being used. The new code is much more accurate, so we're able to get rid of some of these checks. Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 71549d1..247d2c9 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3263,27 +3263,12 @@ static int should_alloc_chunk(struct btrfs_root *root, if (num_bytes - num_allocated < thresh) return 1; } - - /* - * we have two similar checks here, one based on percentage - * and once based on a hard number of 256MB. The idea - * is that if we have a good amount of free - * room, don't allocate a chunk. A good mount is - * less than 80% utilized of the chunks we have allocated, - * or more than 256MB free - */ - if (num_allocated + alloc_bytes + 256 * 1024 * 1024 < num_bytes) - return 0; - - if (num_allocated + alloc_bytes < div_factor(num_bytes, 8)) - return 0; - thresh = btrfs_super_total_bytes(root->fs_info->super_copy); - /* 256MB or 5% of the FS */ - thresh = max_t(u64, 256 * 1024 * 1024, div_factor_fine(thresh, 5)); + /* 256MB or 2% of the FS */ + thresh = max_t(u64, 256 * 1024 * 1024, div_factor_fine(thresh, 2)); - if (num_bytes > thresh && sinfo->bytes_used < div_factor(num_bytes, 3)) + if (num_bytes > thresh && sinfo->bytes_used < div_factor(num_bytes, 8)) return 0; return 1; } -- cgit v0.10.2 From 1100373f8aa69e377386499350496e3d8565605f Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 6 Jan 2012 15:47:38 -0500 Subject: Btrfs: use bigger metadata chunks on bigger filesystems The 256MB chunk is a little small on a huge FS. This scales up the chunk size. Signed-off-by: Chris Mason diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f4b839f..ac00e3a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2441,7 +2441,11 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, max_stripe_size = 1024 * 1024 * 1024; max_chunk_size = 10 * max_stripe_size; } else if (type & BTRFS_BLOCK_GROUP_METADATA) { - max_stripe_size = 256 * 1024 * 1024; + /* for larger filesystems, use larger metadata chunks */ + if (fs_devices->total_rw_bytes > 50ULL * 1024 * 1024 * 1024) + max_stripe_size = 1024 * 1024 * 1024; + else + max_stripe_size = 256 * 1024 * 1024; max_chunk_size = max_stripe_size; } else if (type & BTRFS_BLOCK_GROUP_SYSTEM) { max_stripe_size = 8 * 1024 * 1024; -- cgit v0.10.2 From a5f6f719a5cd7caeee8ed8137cf3f94c3bbebc65 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Mon, 12 Dec 2011 04:48:19 -0200 Subject: Btrfs: test free space only for unclustered allocation Since the clustered allocation may be taking extents from a different block group, there's no point in spin-locking and testing the current block group free space before attempting to allocate space from a cluster, even more so when we might refrain from even trying the cluster in the current block group because, after the cluster was set up, not enough free space remained. Furthermore, cluster creation attempts fail fast when the block group doesn't have enough free space, so the test was completely superfluous. I've move the free space test past the cluster allocation attempt, where it is more useful, and arranged for a cluster in the current block group to be released before trying an unclustered allocation, when we reach the LOOP_NO_EMPTY_SIZE stage, so that the free space in the cluster stands a chance of being combined with additional free space in the block group so as to succeed in the allocation attempt. Signed-off-by: Alexandre Oliva Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 247d2c9..5ea3acc 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5286,15 +5286,6 @@ alloc: if (unlikely(block_group->ro)) goto loop; - spin_lock(&block_group->free_space_ctl->tree_lock); - if (cached && - block_group->free_space_ctl->free_space < - num_bytes + empty_cluster + empty_size) { - spin_unlock(&block_group->free_space_ctl->tree_lock); - goto loop; - } - spin_unlock(&block_group->free_space_ctl->tree_lock); - /* * Ok we want to try and use the cluster allocator, so * lets look there @@ -5340,8 +5331,15 @@ refill_cluster: * plenty of times and not have found * anything, so we are likely way too * fragmented for the clustering stuff to find - * anything. */ - if (loop >= LOOP_NO_EMPTY_SIZE) { + * anything. + * + * However, if the cluster is taken from the + * current block group, release the cluster + * first, so that we stand a better chance of + * succeeding in the unclustered + * allocation. */ + if (loop >= LOOP_NO_EMPTY_SIZE && + last_ptr->block_group != block_group) { spin_unlock(&last_ptr->refill_lock); goto unclustered_alloc; } @@ -5352,6 +5350,11 @@ refill_cluster: */ btrfs_return_cluster_to_free_space(NULL, last_ptr); + if (loop >= LOOP_NO_EMPTY_SIZE) { + spin_unlock(&last_ptr->refill_lock); + goto unclustered_alloc; + } + /* allocate a cluster in this block group */ ret = btrfs_find_space_cluster(trans, root, block_group, last_ptr, @@ -5392,6 +5395,15 @@ refill_cluster: } unclustered_alloc: + spin_lock(&block_group->free_space_ctl->tree_lock); + if (cached && + block_group->free_space_ctl->free_space < + num_bytes + empty_cluster + empty_size) { + spin_unlock(&block_group->free_space_ctl->tree_lock); + goto loop; + } + spin_unlock(&block_group->free_space_ctl->tree_lock); + offset = btrfs_find_space_for_alloc(block_group, search_start, num_bytes, empty_size); /* -- cgit v0.10.2 From 23bd15ec662344dc10e9918fdd0dbc58bc71526d Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 14 Dec 2011 21:10:06 -0200 Subject: drm/i915: Fix TV Out refresh rate. TV Out refresh rate was half of the specification for almost all modes. Due to this reason pixel clock was so low for some modes causing flickering screen. Signed-off-by: Rodrigo Vivi Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index f3c6a9a..2b1fcad 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -417,7 +417,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-M", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */ @@ -460,7 +460,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-443", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */ @@ -502,7 +502,7 @@ static const struct tv_mode tv_modes[] = { { .name = "NTSC-J", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -545,7 +545,7 @@ static const struct tv_mode tv_modes[] = { { .name = "PAL-M", .clock = 108000, - .refresh = 29970, + .refresh = 59940, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -589,7 +589,7 @@ static const struct tv_mode tv_modes[] = { /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ .name = "PAL-N", .clock = 108000, - .refresh = 25000, + .refresh = 50000, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -634,7 +634,7 @@ static const struct tv_mode tv_modes[] = { /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */ .name = "PAL", .clock = 108000, - .refresh = 25000, + .refresh = 50000, .oversample = TV_OVERSAMPLE_8X, .component_only = 0, @@ -821,7 +821,7 @@ static const struct tv_mode tv_modes[] = { { .name = "1080i@50Hz", .clock = 148800, - .refresh = 25000, + .refresh = 50000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, @@ -847,7 +847,7 @@ static const struct tv_mode tv_modes[] = { { .name = "1080i@60Hz", .clock = 148800, - .refresh = 30000, + .refresh = 60000, .oversample = TV_OVERSAMPLE_2X, .component_only = 1, -- cgit v0.10.2 From 55a6713b3f30a5024056027e9dbf03ac8f13bfc9 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 15 Dec 2011 14:47:33 -0200 Subject: drm/i915: Removing TV Out modes. These modes are no longer needed or are not according to TV timing standards. Intel PRM Vol 3 - Display Registers Updated - Section 5 TV-Out Programming / 5.2.1 Television Standards / 5.2.1.1 Timing tables Signed-off-by: Rodrigo Vivi Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 2b1fcad..1571be3 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -674,78 +674,6 @@ static const struct tv_mode tv_modes[] = { .filter_table = filter_table, }, { - .name = "480p@59.94Hz", - .clock = 107520, - .refresh = 59940, - .oversample = TV_OVERSAMPLE_4X, - .component_only = 1, - - .hsync_end = 64, .hblank_end = 122, - .hblank_start = 842, .htotal = 857, - - .progressive = true, .trilevel_sync = false, - - .vsync_start_f1 = 12, .vsync_start_f2 = 12, - .vsync_len = 12, - - .veq_ena = false, - - .vi_end_f1 = 44, .vi_end_f2 = 44, - .nbr_end = 479, - - .burst_ena = false, - - .filter_table = filter_table, - }, - { - .name = "480p@60Hz", - .clock = 107520, - .refresh = 60000, - .oversample = TV_OVERSAMPLE_4X, - .component_only = 1, - - .hsync_end = 64, .hblank_end = 122, - .hblank_start = 842, .htotal = 856, - - .progressive = true, .trilevel_sync = false, - - .vsync_start_f1 = 12, .vsync_start_f2 = 12, - .vsync_len = 12, - - .veq_ena = false, - - .vi_end_f1 = 44, .vi_end_f2 = 44, - .nbr_end = 479, - - .burst_ena = false, - - .filter_table = filter_table, - }, - { - .name = "576p", - .clock = 107520, - .refresh = 50000, - .oversample = TV_OVERSAMPLE_4X, - .component_only = 1, - - .hsync_end = 64, .hblank_end = 139, - .hblank_start = 859, .htotal = 863, - - .progressive = true, .trilevel_sync = false, - - .vsync_start_f1 = 10, .vsync_start_f2 = 10, - .vsync_len = 10, - - .veq_ena = false, - - .vi_end_f1 = 48, .vi_end_f2 = 48, - .nbr_end = 575, - - .burst_ena = false, - - .filter_table = filter_table, - }, - { .name = "720p@60Hz", .clock = 148800, .refresh = 60000, @@ -770,30 +698,6 @@ static const struct tv_mode tv_modes[] = { .filter_table = filter_table, }, { - .name = "720p@59.94Hz", - .clock = 148800, - .refresh = 59940, - .oversample = TV_OVERSAMPLE_2X, - .component_only = 1, - - .hsync_end = 80, .hblank_end = 300, - .hblank_start = 1580, .htotal = 1651, - - .progressive = true, .trilevel_sync = true, - - .vsync_start_f1 = 10, .vsync_start_f2 = 10, - .vsync_len = 10, - - .veq_ena = false, - - .vi_end_f1 = 29, .vi_end_f2 = 29, - .nbr_end = 719, - - .burst_ena = false, - - .filter_table = filter_table, - }, - { .name = "720p@50Hz", .clock = 148800, .refresh = 50000, @@ -870,32 +774,6 @@ static const struct tv_mode tv_modes[] = { .filter_table = filter_table, }, - { - .name = "1080i@59.94Hz", - .clock = 148800, - .refresh = 29970, - .oversample = TV_OVERSAMPLE_2X, - .component_only = 1, - - .hsync_end = 88, .hblank_end = 235, - .hblank_start = 2155, .htotal = 2201, - - .progressive = false, .trilevel_sync = true, - - .vsync_start_f1 = 4, .vsync_start_f2 = 5, - .vsync_len = 10, - - .veq_ena = true, .veq_start_f1 = 4, - .veq_start_f2 = 4, .veq_len = 10, - - - .vi_end_f1 = 21, .vi_end_f2 = 22, - .nbr_end = 539, - - .burst_ena = false, - - .filter_table = filter_table, - }, }; static struct intel_tv *enc_to_intel_tv(struct drm_encoder *encoder) -- cgit v0.10.2 From ba68e086223a5f149f37bf8692c8cdbf1b0ba3ef Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 6 Jan 2012 19:45:34 -0200 Subject: drm/i915/sdvo: always set positive sync polarity This is a revert of 81a14b46846fea0741902e8d8dfcc6c6c78154c8. We already set the mode polarity using the SDVO commands with struct intel_sdvo_dtd. We have at least 3 bugs that get fixed with this patch. The documentation, despite not clear, can also be interpreted in a way that suggests this patch is needed. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=15766 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=42174 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=43333 Cc: stable@kernel.org Reviewed-by: Eric Anholt Reviewed-by: Jesse Barnes Signed-off-by: Paulo Zanoni Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f7b9268..e334ec3 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1066,15 +1066,13 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* Set the SDVO control regs. */ if (INTEL_INFO(dev)->gen >= 4) { - sdvox = 0; + /* The real mode polarity is set by the SDVO commands, using + * struct intel_sdvo_dtd. */ + sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; if (intel_sdvo->is_hdmi) sdvox |= intel_sdvo->color_range; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; - if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) - sdvox |= SDVO_VSYNC_ACTIVE_HIGH; - if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) - sdvox |= SDVO_HSYNC_ACTIVE_HIGH; } else { sdvox = I915_READ(intel_sdvo->sdvo_reg); switch (intel_sdvo->sdvo_reg) { -- cgit v0.10.2 From fc7c1077ceb99c35e5f9d0ce03dc7740565bb2bf Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Mon, 28 Nov 2011 12:36:17 -0200 Subject: Btrfs: don't set up allocation result twice We store the allocation start and length twice in ins, once right after the other, but with intervening calls that may prevent the duplicate from being optimized out by the compiler. Remove one of the assignments. Signed-off-by: Alexandre Oliva Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 5ea3acc..37594e4 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5441,9 +5441,6 @@ checks: goto loop; } - ins->objectid = search_start; - ins->offset = num_bytes; - if (offset < search_start) btrfs_add_free_space(used_block_group, offset, search_start - offset); -- cgit v0.10.2 From 1bb91902dc90e25449893e693ad45605cb08fbe5 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Fri, 14 Oct 2011 12:10:36 -0300 Subject: Btrfs: revamp clustered allocation logic Parameterize clusters on minimum total size, minimum chunk size and minimum contiguous size for at least one chunk, without limits on cluster, window or gap sizes. Don't tolerate any fragmentation for SSD_SPREAD; accept it for metadata, but try to keep data dense. Signed-off-by: Alexandre Oliva Signed-off-by: Chris Mason diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index ec23d43..ce40db5 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2283,23 +2283,23 @@ out: static int btrfs_bitmap_cluster(struct btrfs_block_group_cache *block_group, struct btrfs_free_space *entry, struct btrfs_free_cluster *cluster, - u64 offset, u64 bytes, u64 min_bytes) + u64 offset, u64 bytes, + u64 cont1_bytes, u64 min_bytes) { struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; unsigned long next_zero; unsigned long i; - unsigned long search_bits; - unsigned long total_bits; + unsigned long want_bits; + unsigned long min_bits; unsigned long found_bits; unsigned long start = 0; unsigned long total_found = 0; int ret; - bool found = false; i = offset_to_bit(entry->offset, block_group->sectorsize, max_t(u64, offset, entry->offset)); - search_bits = bytes_to_bits(bytes, block_group->sectorsize); - total_bits = bytes_to_bits(min_bytes, block_group->sectorsize); + want_bits = bytes_to_bits(bytes, block_group->sectorsize); + min_bits = bytes_to_bits(min_bytes, block_group->sectorsize); again: found_bits = 0; @@ -2308,7 +2308,7 @@ again: i = find_next_bit(entry->bitmap, BITS_PER_BITMAP, i + 1)) { next_zero = find_next_zero_bit(entry->bitmap, BITS_PER_BITMAP, i); - if (next_zero - i >= search_bits) { + if (next_zero - i >= min_bits) { found_bits = next_zero - i; break; } @@ -2318,10 +2318,9 @@ again: if (!found_bits) return -ENOSPC; - if (!found) { + if (!total_found) { start = i; cluster->max_size = 0; - found = true; } total_found += found_bits; @@ -2329,13 +2328,8 @@ again: if (cluster->max_size < found_bits * block_group->sectorsize) cluster->max_size = found_bits * block_group->sectorsize; - if (total_found < total_bits) { - i = find_next_bit(entry->bitmap, BITS_PER_BITMAP, next_zero); - if (i - start > total_bits * 2) { - total_found = 0; - cluster->max_size = 0; - found = false; - } + if (total_found < want_bits || cluster->max_size < cont1_bytes) { + i = next_zero + 1; goto again; } @@ -2351,23 +2345,23 @@ again: /* * This searches the block group for just extents to fill the cluster with. + * Try to find a cluster with at least bytes total bytes, at least one + * extent of cont1_bytes, and other clusters of at least min_bytes. */ static noinline int setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, struct btrfs_free_cluster *cluster, struct list_head *bitmaps, u64 offset, u64 bytes, - u64 min_bytes) + u64 cont1_bytes, u64 min_bytes) { struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; struct btrfs_free_space *first = NULL; struct btrfs_free_space *entry = NULL; - struct btrfs_free_space *prev = NULL; struct btrfs_free_space *last; struct rb_node *node; u64 window_start; u64 window_free; u64 max_extent; - u64 max_gap = 128 * 1024; entry = tree_search_offset(ctl, offset, 0, 1); if (!entry) @@ -2377,8 +2371,8 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, * We don't want bitmaps, so just move along until we find a normal * extent entry. */ - while (entry->bitmap) { - if (list_empty(&entry->list)) + while (entry->bitmap || entry->bytes < min_bytes) { + if (entry->bitmap && list_empty(&entry->list)) list_add_tail(&entry->list, bitmaps); node = rb_next(&entry->offset_index); if (!node) @@ -2391,12 +2385,9 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, max_extent = entry->bytes; first = entry; last = entry; - prev = entry; - while (window_free <= min_bytes) { - node = rb_next(&entry->offset_index); - if (!node) - return -ENOSPC; + for (node = rb_next(&entry->offset_index); node; + node = rb_next(&entry->offset_index)) { entry = rb_entry(node, struct btrfs_free_space, offset_index); if (entry->bitmap) { @@ -2405,26 +2396,18 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, continue; } - /* - * we haven't filled the empty size and the window is - * very large. reset and try again - */ - if (entry->offset - (prev->offset + prev->bytes) > max_gap || - entry->offset - window_start > (min_bytes * 2)) { - first = entry; - window_start = entry->offset; - window_free = entry->bytes; - last = entry; + if (entry->bytes < min_bytes) + continue; + + last = entry; + window_free += entry->bytes; + if (entry->bytes > max_extent) max_extent = entry->bytes; - } else { - last = entry; - window_free += entry->bytes; - if (entry->bytes > max_extent) - max_extent = entry->bytes; - } - prev = entry; } + if (window_free < bytes || max_extent < cont1_bytes) + return -ENOSPC; + cluster->window_start = first->offset; node = &first->offset_index; @@ -2438,7 +2421,7 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, entry = rb_entry(node, struct btrfs_free_space, offset_index); node = rb_next(&entry->offset_index); - if (entry->bitmap) + if (entry->bitmap || entry->bytes < min_bytes) continue; rb_erase(&entry->offset_index, &ctl->free_space_offset); @@ -2460,7 +2443,7 @@ static noinline int setup_cluster_bitmap(struct btrfs_block_group_cache *block_group, struct btrfs_free_cluster *cluster, struct list_head *bitmaps, u64 offset, u64 bytes, - u64 min_bytes) + u64 cont1_bytes, u64 min_bytes) { struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; struct btrfs_free_space *entry; @@ -2485,7 +2468,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group, if (entry->bytes < min_bytes) continue; ret = btrfs_bitmap_cluster(block_group, entry, cluster, offset, - bytes, min_bytes); + bytes, cont1_bytes, min_bytes); if (!ret) return 0; } @@ -2499,7 +2482,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group, /* * here we try to find a cluster of blocks in a block group. The goal - * is to find at least bytes free and up to empty_size + bytes free. + * is to find at least bytes+empty_size. * We might not find them all in one contiguous area. * * returns zero and sets up cluster if things worked out, otherwise @@ -2515,23 +2498,24 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, struct btrfs_free_space *entry, *tmp; LIST_HEAD(bitmaps); u64 min_bytes; + u64 cont1_bytes; int ret; - /* for metadata, allow allocates with more holes */ + /* + * Choose the minimum extent size we'll require for this + * cluster. For SSD_SPREAD, don't allow any fragmentation. + * For metadata, allow allocates with smaller extents. For + * data, keep it dense. + */ if (btrfs_test_opt(root, SSD_SPREAD)) { - min_bytes = bytes + empty_size; + cont1_bytes = min_bytes = bytes + empty_size; } else if (block_group->flags & BTRFS_BLOCK_GROUP_METADATA) { - /* - * we want to do larger allocations when we are - * flushing out the delayed refs, it helps prevent - * making more work as we go along. - */ - if (trans->transaction->delayed_refs.flushing) - min_bytes = max(bytes, (bytes + empty_size) >> 1); - else - min_bytes = max(bytes, (bytes + empty_size) >> 4); - } else - min_bytes = max(bytes, (bytes + empty_size) >> 2); + cont1_bytes = bytes; + min_bytes = block_group->sectorsize; + } else { + cont1_bytes = max(bytes, (bytes + empty_size) >> 2); + min_bytes = block_group->sectorsize; + } spin_lock(&ctl->tree_lock); @@ -2539,7 +2523,7 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, * If we know we don't have enough space to make a cluster don't even * bother doing all the work to try and find one. */ - if (ctl->free_space < min_bytes) { + if (ctl->free_space < bytes) { spin_unlock(&ctl->tree_lock); return -ENOSPC; } @@ -2553,10 +2537,12 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, } ret = setup_cluster_no_bitmap(block_group, cluster, &bitmaps, offset, - bytes, min_bytes); + bytes + empty_size, + cont1_bytes, min_bytes); if (ret) ret = setup_cluster_bitmap(block_group, cluster, &bitmaps, - offset, bytes, min_bytes); + offset, bytes + empty_size, + cont1_bytes, min_bytes); /* Clear our temporary list */ list_for_each_entry_safe(entry, tmp, &bitmaps, list) -- cgit v0.10.2 From 98c7089c769048f941bd5c5285287f8fc301f8b1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:00:31 -0500 Subject: btrfs: preparation to fixing mount/umount race We need fs_info and root to live until the moment when the victim superblock leaves the list, so we need to postpone free_fs_info() until after ->put_super(). The call is buried in close_ctree(), though, so we need to lift it into the callers (including btrfs_put_super()) first. Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index f99a099..dcb5d94 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2424,6 +2424,7 @@ retry_root_backup: up_read(&fs_info->cleanup_work_sem); if (err) { close_ctree(tree_root); + free_fs_info(fs_info); return ERR_PTR(err); } } @@ -3059,8 +3060,6 @@ int close_ctree(struct btrfs_root *root) bdi_destroy(&fs_info->bdi); cleanup_srcu_struct(&fs_info->subvol_srcu); - free_fs_info(fs_info); - return 0; } diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index ae488aa..a3f435e 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -151,6 +151,7 @@ static void btrfs_put_super(struct super_block *sb) int ret; ret = close_ctree(root); + free_fs_info(root->fs_info); sb->s_fs_info = NULL; (void)ret; /* FIXME: need to fix VFS to return error? */ @@ -589,6 +590,7 @@ static int btrfs_fill_super(struct super_block *sb, struct inode *inode; struct dentry *root_dentry; struct btrfs_root *tree_root; + struct btrfs_fs_info *fs_info; struct btrfs_key key; int err; @@ -609,12 +611,13 @@ static int btrfs_fill_super(struct super_block *sb, printk("btrfs: open_ctree failed\n"); return PTR_ERR(tree_root); } + fs_info = tree_root->fs_info; sb->s_fs_info = tree_root; key.objectid = BTRFS_FIRST_FREE_OBJECTID; key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; - inode = btrfs_iget(sb, &key, tree_root->fs_info->fs_root, NULL); + inode = btrfs_iget(sb, &key, fs_info->fs_root, NULL); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto fail_close; @@ -635,6 +638,7 @@ static int btrfs_fill_super(struct super_block *sb, fail_close: close_ctree(tree_root); + free_fs_info(fs_info); return err; } -- cgit v0.10.2 From aea52e19dd2085617dd7d247afb76a90cf2ea40c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:22:46 -0500 Subject: btrfs: get ->kill_sb() of its own ... and move free_fs_info() to that, out of ->put_super(). Do NOT set ->s_fs_info to NULL in the latter; we need it for sget() to be able to see and wait for fs in the middle of umount if we get a mount/umount race. Signed-off-by: Al Viro diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index a3f435e..eca4862 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -147,14 +147,13 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, static void btrfs_put_super(struct super_block *sb) { - struct btrfs_root *root = btrfs_sb(sb); - int ret; - - ret = close_ctree(root); - free_fs_info(root->fs_info); - sb->s_fs_info = NULL; - - (void)ret; /* FIXME: need to fix VFS to return error? */ + (void)close_ctree(btrfs_sb(sb)); + /* FIXME: need to fix VFS to return error? */ + /* AV: return it _where_? ->put_super() can be triggered by any number + * of async events, up to and including delivery of SIGKILL to the + * last process that kept it busy. Or segfault in the aforementioned + * process... Whom would you report that to? + */ } enum { @@ -1223,11 +1222,21 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } +static void btrfs_kill_super(struct super_block *sb) +{ + struct btrfs_fs_info *fs_info = NULL; + if (sb->s_root) + fs_info = btrfs_sb(sb)->fs_info; + kill_anon_super(sb); + if (fs_info) + free_fs_info(fs_info); +} + static struct file_system_type btrfs_fs_type = { .owner = THIS_MODULE, .name = "btrfs", .mount = btrfs_mount, - .kill_sb = kill_anon_super, + .kill_sb = btrfs_kill_super, .fs_flags = FS_REQUIRES_DEV, }; -- cgit v0.10.2 From 6de1d09d9677dce7a04ef485d426821159ab7de9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:29:09 -0500 Subject: btrfs: fix mount/umount race Do *NOT* skip doomed superblocks in btrfs_test_super(); we want sget() to wait for their shutdown to complete. Since we don't mutilate ->s_fs_info in ->put_super() anymore (or free what it used to point to until the superblock is past being findable by sget()), we can just DTRT there and report a match. Signed-off-by: Al Viro diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index eca4862..b9fd62a 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -733,20 +733,15 @@ static int btrfs_test_super(struct super_block *s, void *data) struct btrfs_root *test_root = data; struct btrfs_root *root = btrfs_sb(s); - /* - * If this super block is going away, return false as it - * can't match as an existing super block. - */ - if (!atomic_read(&s->s_active)) - return 0; return root->fs_info->fs_devices == test_root->fs_info->fs_devices; } static int btrfs_set_super(struct super_block *s, void *data) { - s->s_fs_info = data; - - return set_anon_super(s, data); + int err = set_anon_super(s, data); + if (!err) + s->s_fs_info = data; + return err; } /* -- cgit v0.10.2 From 10f6327b5d26660e06120ba17cff993cb616b1d2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 15:05:22 -0500 Subject: btrfs: fix a deadlock in btrfs_scan_one_device() pathname resolution under a global mutex, taken on some paths in ->mount() is a Bad Idea(tm) - think what happens if said pathname resolution triggers automount of some btrfs instance and walks into attempt to grab the same mutex. Deadlock - we are waiting for daemon to finish walking the path, daemon is waiting for us to release the mutex... Signed-off-by: Al Viro diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f4b839f..96bb3c0 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -706,8 +706,6 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, u64 devid; u64 transid; - mutex_lock(&uuid_mutex); - flags |= FMODE_EXCL; bdev = blkdev_get_by_path(path, flags, holder); @@ -716,6 +714,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, goto error; } + mutex_lock(&uuid_mutex); ret = set_blocksize(bdev, 4096); if (ret) goto error_close; @@ -737,9 +736,9 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, brelse(bh); error_close: + mutex_unlock(&uuid_mutex); blkdev_put(bdev, flags); error: - mutex_unlock(&uuid_mutex); return ret; } -- cgit v0.10.2 From 2eb34cd368b3e5cef960ae9d9971a037cd6fa9d3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 00:32:06 -0500 Subject: btrfs: sanitizing ->fs_info, part 1 take assignment of ->fs_info to callers of __setup_root() Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index dcb5d94..04eba10 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1142,7 +1142,6 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, root->orphan_item_inserted = 0; root->orphan_cleanup_state = 0; - root->fs_info = fs_info; root->objectid = objectid; root->last_trans = 0; root->highest_objectid = 0; @@ -1193,6 +1192,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root, u32 blocksize; u64 generation; + root->fs_info = fs_info; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, objectid); @@ -1227,6 +1227,7 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans, if (!root) return ERR_PTR(-ENOMEM); + root->fs_info = fs_info; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, BTRFS_TREE_LOG_OBJECTID); @@ -1330,6 +1331,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, goto out; } + root->fs_info = fs_info; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, location->objectid); @@ -2055,6 +2057,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, init_waitqueue_head(&fs_info->transaction_blocked_wait); init_waitqueue_head(&fs_info->async_submit_wait); + tree_root->fs_info = fs_info; __setup_root(4096, 4096, 4096, 4096, tree_root, fs_info, BTRFS_ROOT_TREE_OBJECTID); @@ -2247,6 +2250,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, btrfs_super_chunk_root_level(disk_super)); generation = btrfs_super_chunk_root_generation(disk_super); + chunk_root->fs_info = fs_info; __setup_root(nodesize, leafsize, sectorsize, stripesize, chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); @@ -2373,6 +2377,7 @@ retry_root_backup: goto fail_trans_kthread; } + log_tree_root->fs_info = fs_info; __setup_root(nodesize, leafsize, sectorsize, stripesize, log_tree_root, fs_info, BTRFS_TREE_LOG_OBJECTID); -- cgit v0.10.2 From 1233f546ec29ac32424327baf6ae5df81e3d9eae Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 00:34:00 -0500 Subject: btrfs: sanitizing ->fs_info, part 2 lift assignment to callers of find_and_setup_root() Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 04eba10..b8dae517 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1192,7 +1192,6 @@ static int find_and_setup_root(struct btrfs_root *tree_root, u32 blocksize; u64 generation; - root->fs_info = fs_info; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, objectid); @@ -1322,6 +1321,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, if (!root) return ERR_PTR(-ENOMEM); if (location->offset == (u64)-1) { + root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, location->objectid, root); if (ret) { @@ -2300,18 +2300,21 @@ retry_root_backup: btrfs_set_root_node(&tree_root->root_item, tree_root->node); tree_root->commit_root = btrfs_root_node(tree_root); + extent_root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, BTRFS_EXTENT_TREE_OBJECTID, extent_root); if (ret) goto recovery_tree_root; extent_root->track_dirty = 1; + dev_root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, BTRFS_DEV_TREE_OBJECTID, dev_root); if (ret) goto recovery_tree_root; dev_root->track_dirty = 1; + csum_root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, BTRFS_CSUM_TREE_OBJECTID, csum_root); if (ret) -- cgit v0.10.2 From 38a77db49ad8f78369dcdfb693b8e5a818a60104 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 00:38:54 -0500 Subject: btrfs: sanitizing ->fs_info, part 3 move assignments to ->fs_info in open_ctree() up, to the place just after the original allocations. Assignment for tree_root becomes a no-op - we'd obtained fs_info from tree_root->fs_info in the first place. Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b8dae517..06480fc 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1320,8 +1320,8 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, root = kzalloc(sizeof(*root), GFP_NOFS); if (!root) return ERR_PTR(-ENOMEM); + root->fs_info = fs_info; if (location->offset == (u64)-1) { - root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, location->objectid, root); if (ret) { @@ -1331,7 +1331,6 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, goto out; } - root->fs_info = fs_info; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, location->objectid); @@ -1914,6 +1913,10 @@ struct btrfs_root *open_ctree(struct super_block *sb, err = -ENOMEM; goto fail; } + chunk_root->fs_info = fs_info; + extent_root->fs_info = fs_info; + dev_root->fs_info = fs_info; + csum_root->fs_info = fs_info; ret = init_srcu_struct(&fs_info->subvol_srcu); if (ret) { @@ -2057,7 +2060,6 @@ struct btrfs_root *open_ctree(struct super_block *sb, init_waitqueue_head(&fs_info->transaction_blocked_wait); init_waitqueue_head(&fs_info->async_submit_wait); - tree_root->fs_info = fs_info; __setup_root(4096, 4096, 4096, 4096, tree_root, fs_info, BTRFS_ROOT_TREE_OBJECTID); @@ -2250,7 +2252,6 @@ struct btrfs_root *open_ctree(struct super_block *sb, btrfs_super_chunk_root_level(disk_super)); generation = btrfs_super_chunk_root_generation(disk_super); - chunk_root->fs_info = fs_info; __setup_root(nodesize, leafsize, sectorsize, stripesize, chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID); @@ -2300,21 +2301,18 @@ retry_root_backup: btrfs_set_root_node(&tree_root->root_item, tree_root->node); tree_root->commit_root = btrfs_root_node(tree_root); - extent_root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, BTRFS_EXTENT_TREE_OBJECTID, extent_root); if (ret) goto recovery_tree_root; extent_root->track_dirty = 1; - dev_root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, BTRFS_DEV_TREE_OBJECTID, dev_root); if (ret) goto recovery_tree_root; dev_root->track_dirty = 1; - csum_root->fs_info = fs_info; ret = find_and_setup_root(tree_root, fs_info, BTRFS_CSUM_TREE_OBJECTID, csum_root); if (ret) -- cgit v0.10.2 From 6f07e42ee6fcc252a210781d7262f4051e9fd8f6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 00:46:16 -0500 Subject: btrfs: sanitizing ->fs_info, part 4 A new helper: btrfs_alloc_root(fs_info); allocates btrfs_root and sets ->fs_info. All places allocating the suckers converted to it. At that point we *never* reassign ->fs_info of btrfs_root; it's set before anyone sees the address of newly allocated struct btrfs_root and never assigned anywhere else. Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 06480fc..ee846ce 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1215,6 +1215,14 @@ static int find_and_setup_root(struct btrfs_root *tree_root, return 0; } +struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info) +{ + struct btrfs_root *root = kzalloc(sizeof(*root), GFP_NOFS); + if (root) + root->fs_info = fs_info; + return root; +} + static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { @@ -1222,11 +1230,10 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans, struct btrfs_root *tree_root = fs_info->tree_root; struct extent_buffer *leaf; - root = kzalloc(sizeof(*root), GFP_NOFS); + root = btrfs_alloc_root(fs_info); if (!root) return ERR_PTR(-ENOMEM); - root->fs_info = fs_info; __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, root, fs_info, BTRFS_TREE_LOG_OBJECTID); @@ -1317,10 +1324,9 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, u32 blocksize; int ret = 0; - root = kzalloc(sizeof(*root), GFP_NOFS); + root = btrfs_alloc_root(fs_info); if (!root) return ERR_PTR(-ENOMEM); - root->fs_info = fs_info; if (location->offset == (u64)-1) { ret = find_and_setup_root(tree_root, fs_info, location->objectid, root); @@ -1900,23 +1906,15 @@ struct btrfs_root *open_ctree(struct super_block *sb, int num_backups_tried = 0; int backup_index = 0; - extent_root = fs_info->extent_root = - kzalloc(sizeof(struct btrfs_root), GFP_NOFS); - csum_root = fs_info->csum_root = - kzalloc(sizeof(struct btrfs_root), GFP_NOFS); - chunk_root = fs_info->chunk_root = - kzalloc(sizeof(struct btrfs_root), GFP_NOFS); - dev_root = fs_info->dev_root = - kzalloc(sizeof(struct btrfs_root), GFP_NOFS); + extent_root = fs_info->extent_root = btrfs_alloc_root(fs_info); + csum_root = fs_info->csum_root = btrfs_alloc_root(fs_info); + chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info); + dev_root = fs_info->dev_root = btrfs_alloc_root(fs_info); if (!extent_root || !csum_root || !chunk_root || !dev_root) { err = -ENOMEM; goto fail; } - chunk_root->fs_info = fs_info; - extent_root->fs_info = fs_info; - dev_root->fs_info = fs_info; - csum_root->fs_info = fs_info; ret = init_srcu_struct(&fs_info->subvol_srcu); if (ret) { @@ -2372,13 +2370,12 @@ retry_root_backup: btrfs_level_size(tree_root, btrfs_super_log_root_level(disk_super)); - log_tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS); + log_tree_root = btrfs_alloc_root(fs_info); if (!log_tree_root) { err = -ENOMEM; goto fail_trans_kthread; } - log_tree_root->fs_info = fs_info; __setup_root(nodesize, leafsize, sectorsize, stripesize, log_tree_root, fs_info, BTRFS_TREE_LOG_OBJECTID); diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index c99d0a8f..2bb5f59 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -86,6 +86,8 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans, int btrfs_add_log_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root); +struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info); + #ifdef CONFIG_DEBUG_LOCK_ALLOC void btrfs_init_lockdep(void); void btrfs_set_buffer_lockdep_class(u64 objectid, diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index b9fd62a..e9f876a 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -901,12 +901,11 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, if (!fs_info) return ERR_PTR(-ENOMEM); - fs_info->tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS); + fs_info->tree_root = btrfs_alloc_root(fs_info); if (!fs_info->tree_root) { error = -ENOMEM; goto error_fs_info; } - fs_info->tree_root->fs_info = fs_info; fs_info->fs_devices = fs_devices; fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS); -- cgit v0.10.2 From e3029d9fd426c8f582210ba35551ae5506218345 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 00:56:18 -0500 Subject: btrfs: sanitizing ->fs_info, part 5 close_ctree() uses a weird mix of accesses to root->fs_info and its value at the beginning of function stored in local variable. Since ->fs_info *never* changes, let's just use the local variable to avoid confusion. Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index ee846ce..9308c7f 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2982,7 +2982,7 @@ int close_ctree(struct btrfs_root *root) (atomic_read(&fs_info->defrag_running) == 0)); /* clear out the rbtree of defraggable inodes */ - btrfs_run_defrag_inodes(root->fs_info); + btrfs_run_defrag_inodes(fs_info); /* * Here come 2 situations when btrfs is broken to flip readonly: @@ -3011,8 +3011,8 @@ int close_ctree(struct btrfs_root *root) btrfs_put_block_group_cache(fs_info); - kthread_stop(root->fs_info->transaction_kthread); - kthread_stop(root->fs_info->cleaner_kthread); + kthread_stop(fs_info->transaction_kthread); + kthread_stop(fs_info->cleaner_kthread); fs_info->closing = 2; smp_mb(); @@ -3030,14 +3030,14 @@ int close_ctree(struct btrfs_root *root) free_extent_buffer(fs_info->extent_root->commit_root); free_extent_buffer(fs_info->tree_root->node); free_extent_buffer(fs_info->tree_root->commit_root); - free_extent_buffer(root->fs_info->chunk_root->node); - free_extent_buffer(root->fs_info->chunk_root->commit_root); - free_extent_buffer(root->fs_info->dev_root->node); - free_extent_buffer(root->fs_info->dev_root->commit_root); - free_extent_buffer(root->fs_info->csum_root->node); - free_extent_buffer(root->fs_info->csum_root->commit_root); - - btrfs_free_block_groups(root->fs_info); + free_extent_buffer(fs_info->chunk_root->node); + free_extent_buffer(fs_info->chunk_root->commit_root); + free_extent_buffer(fs_info->dev_root->node); + free_extent_buffer(fs_info->dev_root->commit_root); + free_extent_buffer(fs_info->csum_root->node); + free_extent_buffer(fs_info->csum_root->commit_root); + + btrfs_free_block_groups(fs_info); del_fs_roots(fs_info); -- cgit v0.10.2 From ad2b2c802be2d3e8ed8364fef5ffaddabe448219 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:10:02 -0500 Subject: btrfs: make open_ctree() return int It returns either ERR_PTR(-ve) or sb->s_fs_info. The latter can be found by caller just as well, TYVM, no need to return it. Just return -ve or 0... Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 9308c7f..7824752 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1880,9 +1880,9 @@ static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root) } -struct btrfs_root *open_ctree(struct super_block *sb, - struct btrfs_fs_devices *fs_devices, - char *options) +int open_ctree(struct super_block *sb, + struct btrfs_fs_devices *fs_devices, + char *options) { u32 sectorsize; u32 nodesize; @@ -2428,11 +2428,11 @@ retry_root_backup: if (err) { close_ctree(tree_root); free_fs_info(fs_info); - return ERR_PTR(err); + return err; } } - return tree_root; + return 0; fail_trans_kthread: kthread_stop(fs_info->transaction_kthread); @@ -2479,7 +2479,7 @@ fail_srcu: fail: btrfs_close_devices(fs_info->fs_devices); free_fs_info(fs_info); - return ERR_PTR(err); + return err; recovery_tree_root: if (!btrfs_test_opt(tree_root, RECOVERY)) diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index 2bb5f59..6f5f487 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -46,9 +46,9 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize); int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf); -struct btrfs_root *open_ctree(struct super_block *sb, - struct btrfs_fs_devices *fs_devices, - char *options); +int open_ctree(struct super_block *sb, + struct btrfs_fs_devices *fs_devices, + char *options); int close_ctree(struct btrfs_root *root); int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root *root, int max_mirrors); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index e9f876a..56e007f 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -604,12 +604,12 @@ static int btrfs_fill_super(struct super_block *sb, sb->s_flags |= MS_POSIXACL; #endif - tree_root = open_ctree(sb, fs_devices, (char *)data); - - if (IS_ERR(tree_root)) { + err = open_ctree(sb, fs_devices, (char *)data); + if (err) { printk("btrfs: open_ctree failed\n"); - return PTR_ERR(tree_root); + return err; } + tree_root = sb->s_fs_info; fs_info = tree_root->fs_info; sb->s_fs_info = tree_root; -- cgit v0.10.2 From 29db78aa0ac82319b764b87a1c5030d74523e296 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:12:38 -0500 Subject: btrfs: kill pointless reassignment of ->s_fs_info in btrfs_fill_super() We do not (fortunately) modify ->s_fs_info of superblock on the fly in btrfs_fill_super(); apparent assignment is a no-op. Signed-off-by: Al Viro diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 56e007f..a381f9f 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -588,8 +588,8 @@ static int btrfs_fill_super(struct super_block *sb, { struct inode *inode; struct dentry *root_dentry; - struct btrfs_root *tree_root; - struct btrfs_fs_info *fs_info; + struct btrfs_root *tree_root = sb->s_fs_info; + struct btrfs_fs_info *fs_info = tree_root->fs_info; struct btrfs_key key; int err; @@ -609,9 +609,6 @@ static int btrfs_fill_super(struct super_block *sb, printk("btrfs: open_ctree failed\n"); return err; } - tree_root = sb->s_fs_info; - fs_info = tree_root->fs_info; - sb->s_fs_info = tree_root; key.objectid = BTRFS_FIRST_FREE_OBJECTID; key.type = BTRFS_INODE_ITEM_KEY; -- cgit v0.10.2 From be7e0950def403e90b5295ff2192c39967bf2aec Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:40:05 -0500 Subject: btrfs: merge free_fs_info() calls on fill_super failures ... all the way up into btrfs_mount(). Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 7824752..b6d11eb 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2427,7 +2427,6 @@ retry_root_backup: up_read(&fs_info->cleanup_work_sem); if (err) { close_ctree(tree_root); - free_fs_info(fs_info); return err; } } @@ -2478,7 +2477,6 @@ fail_srcu: cleanup_srcu_struct(&fs_info->subvol_srcu); fail: btrfs_close_devices(fs_info->fs_devices); - free_fs_info(fs_info); return err; recovery_tree_root: diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index a381f9f..8901b6c 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -634,7 +634,6 @@ static int btrfs_fill_super(struct super_block *sb, fail_close: close_ctree(tree_root); - free_fs_info(fs_info); return err; } @@ -947,6 +946,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, error = btrfs_fill_super(s, fs_devices, data, flags & MS_SILENT ? 1 : 0); if (error) { + free_fs_info(fs_info); deactivate_locked_super(s); return ERR_PTR(error); } -- cgit v0.10.2 From d22ca7de770e2a683eac000ba4db5a95e2f4c969 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:46:50 -0500 Subject: btrfs: make free_fs_info() call ->kill_sb() unconditional ... and don't bother with it after btrfs_fill_super() failure - ->kill_sb() (unlike ->put_super()) will be called even if we have not got non-NULL ->s_root. Signed-off-by: Al Viro diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 8901b6c..f628a6a 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -946,7 +946,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, error = btrfs_fill_super(s, fs_devices, data, flags & MS_SILENT ? 1 : 0); if (error) { - free_fs_info(fs_info); deactivate_locked_super(s); return ERR_PTR(error); } @@ -1215,12 +1214,9 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) static void btrfs_kill_super(struct super_block *sb) { - struct btrfs_fs_info *fs_info = NULL; - if (sb->s_root) - fs_info = btrfs_sb(sb)->fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info; kill_anon_super(sb); - if (fs_info) - free_fs_info(fs_info); + free_fs_info(fs_info); } static struct file_system_type btrfs_fs_type = { -- cgit v0.10.2 From 59553edf110e5576d91be9dd5bd53d110e0d0290 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 01:56:28 -0500 Subject: btrfs: consolidate failure exits in btrfs_mount() a bit Signed-off-by: Al Viro diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index f628a6a..15c8ae5 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -630,6 +630,7 @@ static int btrfs_fill_super(struct super_block *sb, save_mount_options(sb, data); cleancache_init_fs(sb); + sb->s_flags |= MS_ACTIVE; return 0; fail_close: @@ -929,14 +930,10 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, } if (s->s_root) { - if ((flags ^ s->s_flags) & MS_RDONLY) { - deactivate_locked_super(s); - error = -EBUSY; - goto error_close_devices; - } - btrfs_close_devices(fs_devices); free_fs_info(fs_info); + if ((flags ^ s->s_flags) & MS_RDONLY) + error = -EBUSY; } else { char b[BDEVNAME_SIZE]; @@ -945,19 +942,11 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, btrfs_sb(s)->fs_info->bdev_holder = fs_type; error = btrfs_fill_super(s, fs_devices, data, flags & MS_SILENT ? 1 : 0); - if (error) { - deactivate_locked_super(s); - return ERR_PTR(error); - } - - s->s_flags |= MS_ACTIVE; } - root = get_default_root(s, subvol_objectid); - if (IS_ERR(root)) { + root = !error ? get_default_root(s, subvol_objectid) : ERR_PTR(error); + if (IS_ERR(root)) deactivate_locked_super(s); - return root; - } return root; -- cgit v0.10.2 From 815745cf3e46681241ad8025602ffbf2a452d514 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 15:40:49 -0500 Subject: btrfs: let ->s_fs_info point to fs_info, not root... the latter can be obtained from the former (by looking as ->tree_root) just as cheaply as we currently are doing the other way round. Signed-off-by: Al Viro diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6738503..dbb999a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2196,7 +2196,7 @@ static inline u32 btrfs_file_extent_inline_item_len(struct extent_buffer *eb, return btrfs_item_size(eb, e) - offset; } -static inline struct btrfs_root *btrfs_sb(struct super_block *sb) +static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb) { return sb->s_fs_info; } diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index b6d11eb..f7d8b9b 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1894,8 +1894,8 @@ int open_ctree(struct super_block *sb, struct btrfs_key location; struct buffer_head *bh; struct btrfs_super_block *disk_super; - struct btrfs_root *tree_root = btrfs_sb(sb); - struct btrfs_fs_info *fs_info = tree_root->fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *extent_root; struct btrfs_root *csum_root; struct btrfs_root *chunk_root; diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c index 1b8dc33..5f77166 100644 --- a/fs/btrfs/export.c +++ b/fs/btrfs/export.c @@ -67,7 +67,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, u64 root_objectid, u32 generation, int check_generation) { - struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(sb); struct btrfs_root *root; struct inode *inode; struct btrfs_key key; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5441ff1..0c55f8f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -276,14 +276,13 @@ static int btrfs_ioctl_getversion(struct file *file, int __user *arg) static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) { - struct btrfs_root *root = fdentry(file)->d_sb->s_fs_info; - struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(fdentry(file)->d_sb); struct btrfs_device *device; struct request_queue *q; struct fstrim_range range; u64 minlen = ULLONG_MAX; u64 num_devices = 0; - u64 total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy); + u64 total_bytes = btrfs_super_total_bytes(fs_info->super_copy); int ret; if (!capable(CAP_SYS_ADMIN)) @@ -312,7 +311,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) range.len = min(range.len, total_bytes - range.start); range.minlen = max(range.minlen, minlen); - ret = btrfs_trim_fs(root, &range); + ret = btrfs_trim_fs(fs_info->tree_root, &range); if (ret < 0) return ret; diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 15c8ae5..305adf6 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -147,7 +147,7 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, static void btrfs_put_super(struct super_block *sb) { - (void)close_ctree(btrfs_sb(sb)); + (void)close_ctree(btrfs_sb(sb)->tree_root); /* FIXME: need to fix VFS to return error? */ /* AV: return it _where_? ->put_super() can be triggered by any number * of async events, up to and including delivery of SIGKILL to the @@ -500,7 +500,8 @@ out: static struct dentry *get_default_root(struct super_block *sb, u64 subvol_objectid) { - struct btrfs_root *root = sb->s_fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_root *root = fs_info->tree_root; struct btrfs_root *new_root; struct btrfs_dir_item *di; struct btrfs_path *path; @@ -530,7 +531,7 @@ static struct dentry *get_default_root(struct super_block *sb, * will mount by default if we haven't been given a specific subvolume * to mount. */ - dir_id = btrfs_super_root_dir(root->fs_info->super_copy); + dir_id = btrfs_super_root_dir(fs_info->super_copy); di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0); if (IS_ERR(di)) { btrfs_free_path(path); @@ -544,7 +545,7 @@ static struct dentry *get_default_root(struct super_block *sb, */ btrfs_free_path(path); dir_id = BTRFS_FIRST_FREE_OBJECTID; - new_root = root->fs_info->fs_root; + new_root = fs_info->fs_root; goto setup_root; } @@ -552,7 +553,7 @@ static struct dentry *get_default_root(struct super_block *sb, btrfs_free_path(path); find_root: - new_root = btrfs_read_fs_root_no_name(root->fs_info, &location); + new_root = btrfs_read_fs_root_no_name(fs_info, &location); if (IS_ERR(new_root)) return ERR_CAST(new_root); @@ -588,8 +589,7 @@ static int btrfs_fill_super(struct super_block *sb, { struct inode *inode; struct dentry *root_dentry; - struct btrfs_root *tree_root = sb->s_fs_info; - struct btrfs_fs_info *fs_info = tree_root->fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(sb); struct btrfs_key key; int err; @@ -634,20 +634,21 @@ static int btrfs_fill_super(struct super_block *sb, return 0; fail_close: - close_ctree(tree_root); + close_ctree(fs_info->tree_root); return err; } int btrfs_sync_fs(struct super_block *sb, int wait) { struct btrfs_trans_handle *trans; - struct btrfs_root *root = btrfs_sb(sb); + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_root *root = fs_info->tree_root; int ret; trace_btrfs_sync_fs(wait); if (!wait) { - filemap_flush(root->fs_info->btree_inode->i_mapping); + filemap_flush(fs_info->btree_inode->i_mapping); return 0; } @@ -663,8 +664,8 @@ int btrfs_sync_fs(struct super_block *sb, int wait) static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) { - struct btrfs_root *root = btrfs_sb(dentry->d_sb); - struct btrfs_fs_info *info = root->fs_info; + struct btrfs_fs_info *info = btrfs_sb(dentry->d_sb); + struct btrfs_root *root = info->tree_root; char *compress_type; if (btrfs_test_opt(root, DEGRADED)) @@ -727,10 +728,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) static int btrfs_test_super(struct super_block *s, void *data) { - struct btrfs_root *test_root = data; - struct btrfs_root *root = btrfs_sb(s); + struct btrfs_fs_info *p = data; + struct btrfs_fs_info *fs_info = btrfs_sb(s); - return root->fs_info->fs_devices == test_root->fs_info->fs_devices; + return fs_info->fs_devices == p->fs_devices; } static int btrfs_set_super(struct super_block *s, void *data) @@ -922,8 +923,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, } bdev = fs_devices->latest_bdev; - s = sget(fs_type, btrfs_test_super, btrfs_set_super, - fs_info->tree_root); + s = sget(fs_type, btrfs_test_super, btrfs_set_super, fs_info); if (IS_ERR(s)) { error = PTR_ERR(s); goto error_close_devices; @@ -939,7 +939,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, s->s_flags = flags | MS_NOSEC; strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id)); - btrfs_sb(s)->fs_info->bdev_holder = fs_type; + btrfs_sb(s)->bdev_holder = fs_type; error = btrfs_fill_super(s, fs_devices, data, flags & MS_SILENT ? 1 : 0); } @@ -959,7 +959,8 @@ error_fs_info: static int btrfs_remount(struct super_block *sb, int *flags, char *data) { - struct btrfs_root *root = btrfs_sb(sb); + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_root *root = fs_info->tree_root; int ret; ret = btrfs_parse_options(root, data); @@ -975,13 +976,13 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) ret = btrfs_commit_super(root); WARN_ON(ret); } else { - if (root->fs_info->fs_devices->rw_devices == 0) + if (fs_info->fs_devices->rw_devices == 0) return -EACCES; - if (btrfs_super_log_root(root->fs_info->super_copy) != 0) + if (btrfs_super_log_root(fs_info->super_copy) != 0) return -EINVAL; - ret = btrfs_cleanup_fs_roots(root->fs_info); + ret = btrfs_cleanup_fs_roots(fs_info); WARN_ON(ret); /* recover relocation */ @@ -1150,18 +1151,18 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes) static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct btrfs_root *root = btrfs_sb(dentry->d_sb); - struct btrfs_super_block *disk_super = root->fs_info->super_copy; - struct list_head *head = &root->fs_info->space_info; + struct btrfs_fs_info *fs_info = btrfs_sb(dentry->d_sb); + struct btrfs_super_block *disk_super = fs_info->super_copy; + struct list_head *head = &fs_info->space_info; struct btrfs_space_info *found; u64 total_used = 0; u64 total_free_data = 0; int bits = dentry->d_sb->s_blocksize_bits; - __be32 *fsid = (__be32 *)root->fs_info->fsid; + __be32 *fsid = (__be32 *)fs_info->fsid; int ret; /* holding chunk_muext to avoid allocating new chunks */ - mutex_lock(&root->fs_info->chunk_mutex); + mutex_lock(&fs_info->chunk_mutex); rcu_read_lock(); list_for_each_entry_rcu(found, head, list) { if (found->flags & BTRFS_BLOCK_GROUP_DATA) { @@ -1180,14 +1181,14 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bsize = dentry->d_sb->s_blocksize; buf->f_type = BTRFS_SUPER_MAGIC; buf->f_bavail = total_free_data; - ret = btrfs_calc_avail_data_space(root, &total_free_data); + ret = btrfs_calc_avail_data_space(fs_info->tree_root, &total_free_data); if (ret) { - mutex_unlock(&root->fs_info->chunk_mutex); + mutex_unlock(&fs_info->chunk_mutex); return ret; } buf->f_bavail += total_free_data; buf->f_bavail = buf->f_bavail >> bits; - mutex_unlock(&root->fs_info->chunk_mutex); + mutex_unlock(&fs_info->chunk_mutex); /* We treat it as constant endianness (it doesn't matter _which_) because we want the fsid to come out the same whether mounted @@ -1203,7 +1204,7 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) static void btrfs_kill_super(struct super_block *sb) { - struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info; + struct btrfs_fs_info *fs_info = btrfs_sb(sb); kill_anon_super(sb); free_fs_info(fs_info); } @@ -1246,17 +1247,17 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd, static int btrfs_freeze(struct super_block *sb) { - struct btrfs_root *root = btrfs_sb(sb); - mutex_lock(&root->fs_info->transaction_kthread_mutex); - mutex_lock(&root->fs_info->cleaner_mutex); + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + mutex_lock(&fs_info->transaction_kthread_mutex); + mutex_lock(&fs_info->cleaner_mutex); return 0; } static int btrfs_unfreeze(struct super_block *sb) { - struct btrfs_root *root = btrfs_sb(sb); - mutex_unlock(&root->fs_info->cleaner_mutex); - mutex_unlock(&root->fs_info->transaction_kthread_mutex); + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + mutex_unlock(&fs_info->cleaner_mutex); + mutex_unlock(&fs_info->transaction_kthread_mutex); return 0; } -- cgit v0.10.2 From f84a8bd60e3ee49eacc9ba824babf149ba3dad7e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Nov 2011 15:57:57 -0500 Subject: btrfs: take allocation of ->tree_root into open_ctree() now that we don't need it for sget() anymore... Signed-off-by: Al Viro diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index f7d8b9b..dd71be8 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1215,7 +1215,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root, return 0; } -struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info) +static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info) { struct btrfs_root *root = kzalloc(sizeof(*root), GFP_NOFS); if (root) @@ -1895,7 +1895,7 @@ int open_ctree(struct super_block *sb, struct buffer_head *bh; struct btrfs_super_block *disk_super; struct btrfs_fs_info *fs_info = btrfs_sb(sb); - struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *tree_root; struct btrfs_root *extent_root; struct btrfs_root *csum_root; struct btrfs_root *chunk_root; @@ -1906,12 +1906,14 @@ int open_ctree(struct super_block *sb, int num_backups_tried = 0; int backup_index = 0; + tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info); extent_root = fs_info->extent_root = btrfs_alloc_root(fs_info); csum_root = fs_info->csum_root = btrfs_alloc_root(fs_info); chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info); dev_root = fs_info->dev_root = btrfs_alloc_root(fs_info); - if (!extent_root || !csum_root || !chunk_root || !dev_root) { + if (!tree_root || !extent_root || !csum_root || + !chunk_root || !dev_root) { err = -ENOMEM; goto fail; } diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index 6f5f487..e4bc474 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -86,8 +86,6 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans, int btrfs_add_log_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root); -struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info); - #ifdef CONFIG_DEBUG_LOCK_ALLOC void btrfs_init_lockdep(void); void btrfs_set_buffer_lockdep_class(u64 objectid, diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 305adf6..044652e 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -899,11 +899,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, if (!fs_info) return ERR_PTR(-ENOMEM); - fs_info->tree_root = btrfs_alloc_root(fs_info); - if (!fs_info->tree_root) { - error = -ENOMEM; - goto error_fs_info; - } fs_info->fs_devices = fs_devices; fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_NOFS); -- cgit v0.10.2 From 6b2a05584c5939eba24d22037dcb4cc71e5345e5 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 9 Jan 2012 18:10:56 +0000 Subject: ARM: picoxcell: fix sched_clock() cleanup fallout Commit 2f0778afa (ARM: 7205/2: sched_clock: allow sched_clock to be selected at runtime) replaced the picoxcell specific sched_clock() with a generic runtime selectable version but replaced "unsigned long long notrace sched_clock(void)" with "unsigned u32 notrace picoxcell_read_sched_clock(void)" fix this up to return a u32 as expected. Cc: Marc Zyngier Signed-off-by: Jamie Iles diff --git a/arch/arm/mach-picoxcell/time.c b/arch/arm/mach-picoxcell/time.c index 6c89cf8..2ecba67 100644 --- a/arch/arm/mach-picoxcell/time.c +++ b/arch/arm/mach-picoxcell/time.c @@ -67,7 +67,7 @@ static void picoxcell_add_clocksource(struct device_node *source_timer) static void __iomem *sched_io_base; -unsigned u32 notrace picoxcell_read_sched_clock(void) +static u32 picoxcell_read_sched_clock(void) { return __raw_readl(sched_io_base); } -- cgit v0.10.2 From a05a586239c66a256ea1fbae859e742e4c91c8d9 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 20 Dec 2011 08:54:15 -0800 Subject: drm/i915: Print debugfs object list sizes in KiB instead of bytes. They're all in increments of pages, so this just makes it easier on the eyes. Signed-off-by: Eric Anholt Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 1180798..f8b8ed2 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -121,11 +121,11 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zd %04x %04x %d %d%s%s%s", + seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), - obj->base.size, + obj->base.size / 1024, obj->base.read_domains, obj->base.write_domain, obj->last_rendering_seqno, -- cgit v0.10.2 From 5e5b7fa2ad84f7806d0c7f5af8e1440bc91b4ec7 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Sat, 7 Jan 2012 23:40:34 -0200 Subject: drm/i915: simplify pipe checking This is also handled by i915_reg.h, so just reuse this trick to reduce universe entropy. Signed-off-by: Eugeni Dodonov Reviewed-by: Jesse Barnes Reviewed-by: Cyril Brulebois Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 7886e4f..c0b945c 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -28,6 +28,7 @@ #include "drm.h" #include "i915_drm.h" #include "intel_drv.h" +#include "i915_reg.h" static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) { @@ -35,7 +36,7 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) u32 dpll_reg; if (HAS_PCH_SPLIT(dev)) - dpll_reg = (pipe == PIPE_A) ? _PCH_DPLL_A : _PCH_DPLL_B; + dpll_reg = PCH_DPLL(pipe); else dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; -- cgit v0.10.2 From 07c1e8c1462fa7324de4c36ae9e55da2abd79cee Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Sat, 7 Jan 2012 23:40:35 -0200 Subject: drm/i915: handle 3rd pipe We don't need to check 3rd pipe specifically, as it shares PLL with some other one. Signed-off-by: Eugeni Dodonov Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index c0b945c..30d924f 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -35,6 +35,10 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) struct drm_i915_private *dev_priv = dev->dev_private; u32 dpll_reg; + /* On IVB, 3rd pipe shares PLL with another one */ + if (pipe > 1) + return false; + if (HAS_PCH_SPLIT(dev)) dpll_reg = PCH_DPLL(pipe); else -- cgit v0.10.2 From d0cd5d482b8a6dc92c6c69a5387baf72ea84f23a Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 14 Nov 2011 17:51:39 -0800 Subject: xhci: Fix USB 3.0 device restart on resume. The xHCI hub port code gets passed a zero-based port number by the USB core. It then adds one to in order to find a device slot by port number and device speed by calling xhci_find_slot_id_by_port. That function clearly states it requires a one-based port number. The xHCI port status change event handler was using a zero-based port number that it got from find_faked_portnum_from_hw_portnum, not a one-based port number. This lead to the doorbells never being rung for a device after a resume, or worse, a different device with the same speed having its doorbell rung (which could lead to bad power management in the xHCI host controller). This patch should be backported to kernels as old as 2.6.39. Signed-off-by: Sarah Sharp Acked-by: Andiry Xu Cc: stable@vger.kernel.org diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b90e138..5a818cb 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1204,6 +1204,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci, * * Returns a zero-based port number, which is suitable for indexing into each of * the split roothubs' port arrays and bus state arrays. + * Add one to it in order to call xhci_find_slot_id_by_port. */ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, struct xhci_hcd *xhci, u32 port_id) @@ -1324,7 +1325,7 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_set_link_state(xhci, port_array, faked_port_index, XDEV_U0); slot_id = xhci_find_slot_id_by_port(hcd, xhci, - faked_port_index); + faked_port_index + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto cleanup; -- cgit v0.10.2 From c2f5b65020869215814df03c3941dac9436f99fb Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sat, 15 Oct 2011 07:33:46 -0400 Subject: NVMe: Simplify completion handling Instead of encoding the handler type in the bottom two bits of the per-completion context pointer, store the handler function as well as the context pointer. This gives us more flexibility and the code is clearer. It comes at the cost of an extra 8k of memory per queue, but this feels like a reasonable price to pay. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index a17f80f..4724655 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -135,8 +135,12 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64); } +typedef void (*nvme_completion_fn)(struct nvme_queue *, void *, + struct nvme_completion *); + struct nvme_cmd_info { - unsigned long ctx; + nvme_completion_fn fn; + void *ctx; unsigned long timeout; }; @@ -149,7 +153,7 @@ static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq) * alloc_cmdid() - Allocate a Command ID * @nvmeq: The queue that will be used for this command * @ctx: A pointer that will be passed to the handler - * @handler: The ID of the handler to call + * @handler: The function to call on completion * * Allocate a Command ID for a queue. The data passed in will * be passed to the completion handler. This is implemented by using @@ -160,28 +164,27 @@ static struct nvme_cmd_info *nvme_cmd_info(struct nvme_queue *nvmeq) * May be called with local interrupts disabled and the q_lock held, * or with interrupts enabled and no locks held. */ -static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler, - unsigned timeout) +static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, + nvme_completion_fn handler, unsigned timeout) { int depth = nvmeq->q_depth - 1; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); int cmdid; - BUG_ON((unsigned long)ctx & 3); - do { cmdid = find_first_zero_bit(nvmeq->cmdid_data, depth); if (cmdid >= depth) return -EBUSY; } while (test_and_set_bit(cmdid, nvmeq->cmdid_data)); - info[cmdid].ctx = (unsigned long)ctx | handler; + info[cmdid].fn = handler; + info[cmdid].ctx = ctx; info[cmdid].timeout = jiffies + timeout; return cmdid; } static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, - int handler, unsigned timeout) + nvme_completion_fn handler, unsigned timeout) { int cmdid; wait_event_killable(nvmeq->sq_full, @@ -189,47 +192,69 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, return (cmdid < 0) ? -EINTR : cmdid; } -/* - * If you need more than four handlers, you'll need to change how - * alloc_cmdid and nvme_process_cq work. Consider using a special - * CMD_CTX value instead, if that works for your situation. - */ -enum { - sync_completion_id = 0, - bio_completion_id, -}; - -/* Special values must be a multiple of 4, and less than 0x1000 */ -#define CMD_CTX_BASE (POISON_POINTER_DELTA + sync_completion_id) +/* Special values must be less than 0x1000 */ +#define CMD_CTX_BASE ((void *)POISON_POINTER_DELTA) #define CMD_CTX_CANCELLED (0x30C + CMD_CTX_BASE) #define CMD_CTX_COMPLETED (0x310 + CMD_CTX_BASE) #define CMD_CTX_INVALID (0x314 + CMD_CTX_BASE) #define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE) +static void special_completion(struct nvme_queue *nvmeq, void *ctx, + struct nvme_completion *cqe) +{ + if (ctx == CMD_CTX_CANCELLED) + return; + if (ctx == CMD_CTX_FLUSH) + return; + if (ctx == CMD_CTX_COMPLETED) { + dev_warn(nvmeq->q_dmadev, + "completed id %d twice on queue %d\n", + cqe->command_id, le16_to_cpup(&cqe->sq_id)); + return; + } + if (ctx == CMD_CTX_INVALID) { + dev_warn(nvmeq->q_dmadev, + "invalid id %d completed on queue %d\n", + cqe->command_id, le16_to_cpup(&cqe->sq_id)); + return; + } + + dev_warn(nvmeq->q_dmadev, "Unknown special completion %p\n", ctx); +} + /* * Called with local interrupts disabled and the q_lock held. May not sleep. */ -static unsigned long free_cmdid(struct nvme_queue *nvmeq, int cmdid) +static void *free_cmdid(struct nvme_queue *nvmeq, int cmdid, + nvme_completion_fn *fn) { - unsigned long data; + void *ctx; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); - if (cmdid >= nvmeq->q_depth) + if (cmdid >= nvmeq->q_depth) { + *fn = special_completion; return CMD_CTX_INVALID; - data = info[cmdid].ctx; + } + *fn = info[cmdid].fn; + ctx = info[cmdid].ctx; + info[cmdid].fn = special_completion; info[cmdid].ctx = CMD_CTX_COMPLETED; clear_bit(cmdid, nvmeq->cmdid_data); wake_up(&nvmeq->sq_full); - return data; + return ctx; } -static unsigned long cancel_cmdid(struct nvme_queue *nvmeq, int cmdid) +static void *cancel_cmdid(struct nvme_queue *nvmeq, int cmdid, + nvme_completion_fn *fn) { - unsigned long data; + void *ctx; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); - data = info[cmdid].ctx; + if (fn) + *fn = info[cmdid].fn; + ctx = info[cmdid].ctx; + info[cmdid].fn = special_completion; info[cmdid].ctx = CMD_CTX_CANCELLED; - return data; + return ctx; } static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) @@ -485,7 +510,7 @@ static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns, static int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns) { int cmdid = alloc_cmdid(nvmeq, (void *)CMD_CTX_FLUSH, - sync_completion_id, IO_TIMEOUT); + special_completion, IO_TIMEOUT); if (unlikely(cmdid < 0)) return cmdid; @@ -518,7 +543,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, nbio->bio = bio; result = -EBUSY; - cmdid = alloc_cmdid(nvmeq, nbio, bio_completion_id, IO_TIMEOUT); + cmdid = alloc_cmdid(nvmeq, nbio, bio_completion, IO_TIMEOUT); if (unlikely(cmdid < 0)) goto free_nbio; @@ -599,45 +624,6 @@ static int nvme_make_request(struct request_queue *q, struct bio *bio) return 0; } -struct sync_cmd_info { - struct task_struct *task; - u32 result; - int status; -}; - -static void sync_completion(struct nvme_queue *nvmeq, void *ctx, - struct nvme_completion *cqe) -{ - struct sync_cmd_info *cmdinfo = ctx; - if (unlikely((unsigned long)cmdinfo == CMD_CTX_CANCELLED)) - return; - if ((unsigned long)cmdinfo == CMD_CTX_FLUSH) - return; - if (unlikely((unsigned long)cmdinfo == CMD_CTX_COMPLETED)) { - dev_warn(nvmeq->q_dmadev, - "completed id %d twice on queue %d\n", - cqe->command_id, le16_to_cpup(&cqe->sq_id)); - return; - } - if (unlikely((unsigned long)cmdinfo == CMD_CTX_INVALID)) { - dev_warn(nvmeq->q_dmadev, - "invalid id %d completed on queue %d\n", - cqe->command_id, le16_to_cpup(&cqe->sq_id)); - return; - } - cmdinfo->result = le32_to_cpup(&cqe->result); - cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; - wake_up_process(cmdinfo->task); -} - -typedef void (*completion_fn)(struct nvme_queue *, void *, - struct nvme_completion *); - -static const completion_fn nvme_completions[4] = { - [sync_completion_id] = sync_completion, - [bio_completion_id] = bio_completion, -}; - static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) { u16 head, phase; @@ -646,9 +632,8 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) phase = nvmeq->cq_phase; for (;;) { - unsigned long data; - void *ptr; - unsigned char handler; + void *ctx; + nvme_completion_fn fn; struct nvme_completion cqe = nvmeq->cqes[head]; if ((le16_to_cpu(cqe.status) & 1) != phase) break; @@ -658,10 +643,8 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) phase = !phase; } - data = free_cmdid(nvmeq, cqe.command_id); - handler = data & 3; - ptr = (void *)(data & ~3UL); - nvme_completions[handler](nvmeq, ptr, &cqe); + ctx = free_cmdid(nvmeq, cqe.command_id, &fn); + fn(nvmeq, ctx, &cqe); } /* If the controller ignores the cq head doorbell and continuously @@ -702,10 +685,25 @@ static irqreturn_t nvme_irq_check(int irq, void *data) static void nvme_abort_command(struct nvme_queue *nvmeq, int cmdid) { spin_lock_irq(&nvmeq->q_lock); - cancel_cmdid(nvmeq, cmdid); + cancel_cmdid(nvmeq, cmdid, NULL); spin_unlock_irq(&nvmeq->q_lock); } +struct sync_cmd_info { + struct task_struct *task; + u32 result; + int status; +}; + +static void sync_completion(struct nvme_queue *nvmeq, void *ctx, + struct nvme_completion *cqe) +{ + struct sync_cmd_info *cmdinfo = ctx; + cmdinfo->result = le32_to_cpup(&cqe->result); + cmdinfo->status = le16_to_cpup(&cqe->status) >> 1; + wake_up_process(cmdinfo->task); +} + /* * Returns 0 on success. If the result is negative, it's a Linux error code; * if the result is positive, it's an NVM Express status code @@ -719,7 +717,7 @@ static int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, cmdinfo.task = current; cmdinfo.status = -EINTR; - cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion_id, + cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion, timeout); if (cmdid < 0) return cmdid; @@ -1201,18 +1199,15 @@ static void nvme_timeout_ios(struct nvme_queue *nvmeq) int cmdid; for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) { - unsigned long data; - void *ptr; - unsigned char handler; + void *ctx; + nvme_completion_fn fn; static struct nvme_completion cqe = { .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1, }; if (!time_after(now, info[cmdid].timeout)) continue; dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid); - data = cancel_cmdid(nvmeq, cmdid); - handler = data & 3; - ptr = (void *)(data & ~3UL); - nvme_completions[handler](nvmeq, ptr, &cqe); + ctx = cancel_cmdid(nvmeq, cmdid, &fn); + fn(nvmeq, ctx, &cqe); } } -- cgit v0.10.2 From 040a93b52a9eee8177ebaf2ba0ee0f9f518d1bf8 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 20 Dec 2011 11:04:12 -0500 Subject: NVMe: Change get_nvmeq to take a dev instead of a namespace Upcoming patches require calling get_nvmeq when we don't have a namespace. Some callers already have the device in a local variable anyway. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 4724655..aa2fd66 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -257,9 +257,9 @@ static void *cancel_cmdid(struct nvme_queue *nvmeq, int cmdid, return ctx; } -static struct nvme_queue *get_nvmeq(struct nvme_ns *ns) +static struct nvme_queue *get_nvmeq(struct nvme_dev *dev) { - return ns->dev->queues[get_cpu() + 1]; + return dev->queues[get_cpu() + 1]; } static void put_nvmeq(struct nvme_queue *nvmeq) @@ -606,7 +606,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, static int nvme_make_request(struct request_queue *q, struct bio *bio) { struct nvme_ns *ns = q->queuedata; - struct nvme_queue *nvmeq = get_nvmeq(ns); + struct nvme_queue *nvmeq = get_nvmeq(ns->dev); int result = -EBUSY; spin_lock_irq(&nvmeq->q_lock); @@ -1103,7 +1103,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) /* XXX: metadata */ prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); - nvmeq = get_nvmeq(ns); + nvmeq = get_nvmeq(dev); /* * Since nvme_submit_sync_cmd sleeps, we can't keep preemption * disabled. We may be preempted at any point, and be rescheduled -- cgit v0.10.2 From 5c1281a3bf5655ec1b90db495da3a2b77826ba88 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 20 Dec 2011 11:54:53 -0500 Subject: NVMe: Change nvme_completion_fn to take a dev The queue is only needed for some rare occasions, and it's more consistent to pass the device around. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index aa2fd66..b0e8a6d 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -135,7 +135,7 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_lba_range_type) != 64); } -typedef void (*nvme_completion_fn)(struct nvme_queue *, void *, +typedef void (*nvme_completion_fn)(struct nvme_dev *, void *, struct nvme_completion *); struct nvme_cmd_info { @@ -199,7 +199,7 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, #define CMD_CTX_INVALID (0x314 + CMD_CTX_BASE) #define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE) -static void special_completion(struct nvme_queue *nvmeq, void *ctx, +static void special_completion(struct nvme_dev *dev, void *ctx, struct nvme_completion *cqe) { if (ctx == CMD_CTX_CANCELLED) @@ -207,19 +207,19 @@ static void special_completion(struct nvme_queue *nvmeq, void *ctx, if (ctx == CMD_CTX_FLUSH) return; if (ctx == CMD_CTX_COMPLETED) { - dev_warn(nvmeq->q_dmadev, + dev_warn(&dev->pci_dev->dev, "completed id %d twice on queue %d\n", cqe->command_id, le16_to_cpup(&cqe->sq_id)); return; } if (ctx == CMD_CTX_INVALID) { - dev_warn(nvmeq->q_dmadev, + dev_warn(&dev->pci_dev->dev, "invalid id %d completed on queue %d\n", cqe->command_id, le16_to_cpup(&cqe->sq_id)); return; } - dev_warn(nvmeq->q_dmadev, "Unknown special completion %p\n", ctx); + dev_warn(&dev->pci_dev->dev, "Unknown special completion %p\n", ctx); } /* @@ -332,29 +332,36 @@ static struct nvme_bio *alloc_nbio(unsigned nseg, gfp_t gfp) sizeof(struct scatterlist) * nseg, gfp); } -static void free_nbio(struct nvme_queue *nvmeq, struct nvme_bio *nbio) +static void free_nbio(struct nvme_dev *dev, struct nvme_bio *nbio) { - nvme_free_prps(nvmeq->dev, nbio->prps); + nvme_free_prps(dev, nbio->prps); kfree(nbio); } -static void bio_completion(struct nvme_queue *nvmeq, void *ctx, +static void requeue_bio(struct nvme_dev *dev, struct bio *bio) +{ + struct nvme_queue *nvmeq = get_nvmeq(dev); + if (bio_list_empty(&nvmeq->sq_cong)) + add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); + bio_list_add(&nvmeq->sq_cong, bio); + put_nvmeq(nvmeq); + wake_up_process(nvme_thread); +} + +static void bio_completion(struct nvme_dev *dev, void *ctx, struct nvme_completion *cqe) { struct nvme_bio *nbio = ctx; struct bio *bio = nbio->bio; u16 status = le16_to_cpup(&cqe->status) >> 1; - dma_unmap_sg(nvmeq->q_dmadev, nbio->sg, nbio->nents, + dma_unmap_sg(&dev->pci_dev->dev, nbio->sg, nbio->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - free_nbio(nvmeq, nbio); + free_nbio(dev, nbio); if (status) { bio_endio(bio, -EIO); } else if (bio->bi_vcnt > bio->bi_idx) { - if (bio_list_empty(&nvmeq->sq_cong)) - add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); - bio_list_add(&nvmeq->sq_cong, bio); - wake_up_process(nvme_thread); + requeue_bio(dev, bio); } else { bio_endio(bio, 0); } @@ -594,7 +601,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return 0; free_nbio: - free_nbio(nvmeq, nbio); + free_nbio(nvmeq->dev, nbio); nomem: return result; } @@ -644,7 +651,7 @@ static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) } ctx = free_cmdid(nvmeq, cqe.command_id, &fn); - fn(nvmeq, ctx, &cqe); + fn(nvmeq->dev, ctx, &cqe); } /* If the controller ignores the cq head doorbell and continuously @@ -695,7 +702,7 @@ struct sync_cmd_info { int status; }; -static void sync_completion(struct nvme_queue *nvmeq, void *ctx, +static void sync_completion(struct nvme_dev *dev, void *ctx, struct nvme_completion *cqe) { struct sync_cmd_info *cmdinfo = ctx; @@ -1207,7 +1214,7 @@ static void nvme_timeout_ios(struct nvme_queue *nvmeq) continue; dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid); ctx = cancel_cmdid(nvmeq, cmdid, &fn); - fn(nvmeq, ctx, &cqe); + fn(nvmeq->dev, ctx, &cqe); } } -- cgit v0.10.2 From eca18b2394a9387feeaf14cd884ddddd7a809d19 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 20 Dec 2011 13:34:52 -0500 Subject: NVMe: Merge the nvme_bio and nvme_prp data structures The new merged data structure is called nvme_iod. This improves performance for mid-sized I/Os (in the 16k range) since we save a memory allocation. It is also a slightly simpler interface to use. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b0e8a6d..4517608 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -290,52 +290,70 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) return 0; } -struct nvme_prps { - int npages; /* 0 means small pool in use */ +/* + * The nvme_iod describes the data in an I/O, including the list of PRP + * entries. You can't see it in this data structure because C doesn't let + * me express that. Use nvme_alloc_iod to ensure there's enough space + * allocated to store the PRP list. + */ +struct nvme_iod { + void *private; /* For the use of the submitter of the I/O */ + int npages; /* In the PRP list. 0 means small pool in use */ + int offset; /* Of PRP list */ + int nents; /* Used in scatterlist */ + int length; /* Of data, in bytes */ dma_addr_t first_dma; - __le64 *list[0]; + struct scatterlist sg[0]; }; -static void nvme_free_prps(struct nvme_dev *dev, struct nvme_prps *prps) +static __le64 **iod_list(struct nvme_iod *iod) { - const int last_prp = PAGE_SIZE / 8 - 1; - int i; - dma_addr_t prp_dma; + return ((void *)iod) + iod->offset; +} - if (!prps) - return; +/* + * Will slightly overestimate the number of pages needed. This is OK + * as it only leads to a small amount of wasted memory for the lifetime of + * the I/O. + */ +static int nvme_npages(unsigned size) +{ + unsigned nprps = DIV_ROUND_UP(size + PAGE_SIZE, PAGE_SIZE); + return DIV_ROUND_UP(8 * nprps, PAGE_SIZE - 8); +} - prp_dma = prps->first_dma; +static struct nvme_iod * +nvme_alloc_iod(unsigned nseg, unsigned nbytes, gfp_t gfp) +{ + struct nvme_iod *iod = kmalloc(sizeof(struct nvme_iod) + + sizeof(__le64 *) * nvme_npages(nbytes) + + sizeof(struct scatterlist) * nseg, gfp); - if (prps->npages == 0) - dma_pool_free(dev->prp_small_pool, prps->list[0], prp_dma); - for (i = 0; i < prps->npages; i++) { - __le64 *prp_list = prps->list[i]; - dma_addr_t next_prp_dma = le64_to_cpu(prp_list[last_prp]); - dma_pool_free(dev->prp_page_pool, prp_list, prp_dma); - prp_dma = next_prp_dma; + if (iod) { + iod->offset = offsetof(struct nvme_iod, sg[nseg]); + iod->npages = -1; + iod->length = nbytes; } - kfree(prps); -} -struct nvme_bio { - struct bio *bio; - int nents; - struct nvme_prps *prps; - struct scatterlist sg[0]; -}; - -/* XXX: use a mempool */ -static struct nvme_bio *alloc_nbio(unsigned nseg, gfp_t gfp) -{ - return kzalloc(sizeof(struct nvme_bio) + - sizeof(struct scatterlist) * nseg, gfp); + return iod; } -static void free_nbio(struct nvme_dev *dev, struct nvme_bio *nbio) +static void nvme_free_iod(struct nvme_dev *dev, struct nvme_iod *iod) { - nvme_free_prps(dev, nbio->prps); - kfree(nbio); + const int last_prp = PAGE_SIZE / 8 - 1; + int i; + __le64 **list = iod_list(iod); + dma_addr_t prp_dma = iod->first_dma; + + if (iod->npages == 0) + dma_pool_free(dev->prp_small_pool, list[0], prp_dma); + for (i = 0; i < iod->npages; i++) { + __le64 *prp_list = list[i]; + dma_addr_t next_prp_dma = le64_to_cpu(prp_list[last_prp]); + dma_pool_free(dev->prp_page_pool, prp_list, prp_dma); + prp_dma = next_prp_dma; + } + kfree(iod); } static void requeue_bio(struct nvme_dev *dev, struct bio *bio) @@ -351,13 +369,13 @@ static void requeue_bio(struct nvme_dev *dev, struct bio *bio) static void bio_completion(struct nvme_dev *dev, void *ctx, struct nvme_completion *cqe) { - struct nvme_bio *nbio = ctx; - struct bio *bio = nbio->bio; + struct nvme_iod *iod = ctx; + struct bio *bio = iod->private; u16 status = le16_to_cpup(&cqe->status) >> 1; - dma_unmap_sg(&dev->pci_dev->dev, nbio->sg, nbio->nents, + dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - free_nbio(dev, nbio); + nvme_free_iod(dev, iod); if (status) { bio_endio(bio, -EIO); } else if (bio->bi_vcnt > bio->bi_idx) { @@ -368,25 +386,25 @@ static void bio_completion(struct nvme_dev *dev, void *ctx, } /* length is in bytes. gfp flags indicates whether we may sleep. */ -static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, - struct nvme_common_command *cmd, - struct scatterlist *sg, int *len, - gfp_t gfp) +static int nvme_setup_prps(struct nvme_dev *dev, + struct nvme_common_command *cmd, struct nvme_iod *iod, + int total_len, gfp_t gfp) { struct dma_pool *pool; - int length = *len; + int length = total_len; + struct scatterlist *sg = iod->sg; int dma_len = sg_dma_len(sg); u64 dma_addr = sg_dma_address(sg); int offset = offset_in_page(dma_addr); __le64 *prp_list; + __le64 **list = iod_list(iod); dma_addr_t prp_dma; - int nprps, npages, i; - struct nvme_prps *prps = NULL; + int nprps, i; cmd->prp1 = cpu_to_le64(dma_addr); length -= (PAGE_SIZE - offset); if (length <= 0) - return prps; + return total_len; dma_len -= (PAGE_SIZE - offset); if (dma_len) { @@ -399,46 +417,35 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, if (length <= PAGE_SIZE) { cmd->prp2 = cpu_to_le64(dma_addr); - return prps; + return total_len; } nprps = DIV_ROUND_UP(length, PAGE_SIZE); - npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE - 8); - prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, gfp); - if (!prps) { - cmd->prp2 = cpu_to_le64(dma_addr); - *len = (*len - length) + PAGE_SIZE; - return prps; - } - if (nprps <= (256 / 8)) { pool = dev->prp_small_pool; - prps->npages = 0; + iod->npages = 0; } else { pool = dev->prp_page_pool; - prps->npages = 1; + iod->npages = 1; } prp_list = dma_pool_alloc(pool, gfp, &prp_dma); if (!prp_list) { cmd->prp2 = cpu_to_le64(dma_addr); - *len = (*len - length) + PAGE_SIZE; - kfree(prps); - return NULL; + iod->npages = -1; + return (total_len - length) + PAGE_SIZE; } - prps->list[0] = prp_list; - prps->first_dma = prp_dma; + list[0] = prp_list; + iod->first_dma = prp_dma; cmd->prp2 = cpu_to_le64(prp_dma); i = 0; for (;;) { if (i == PAGE_SIZE / 8) { __le64 *old_prp_list = prp_list; prp_list = dma_pool_alloc(pool, gfp, &prp_dma); - if (!prp_list) { - *len = (*len - length); - return prps; - } - prps->list[prps->npages++] = prp_list; + if (!prp_list) + return total_len - length; + list[iod->npages++] = prp_list; prp_list[0] = old_prp_list[i - 1]; old_prp_list[i - 1] = cpu_to_le64(prp_dma); i = 1; @@ -457,21 +464,21 @@ static struct nvme_prps *nvme_setup_prps(struct nvme_dev *dev, dma_len = sg_dma_len(sg); } - return prps; + return total_len; } /* NVMe scatterlists require no holes in the virtual address */ #define BIOVEC_NOT_VIRT_MERGEABLE(vec1, vec2) ((vec2)->bv_offset || \ (((vec1)->bv_offset + (vec1)->bv_len) % PAGE_SIZE)) -static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, +static int nvme_map_bio(struct device *dev, struct nvme_iod *iod, struct bio *bio, enum dma_data_direction dma_dir, int psegs) { struct bio_vec *bvec, *bvprv = NULL; struct scatterlist *sg = NULL; int i, old_idx, length = 0, nsegs = 0; - sg_init_table(nbio->sg, psegs); + sg_init_table(iod->sg, psegs); old_idx = bio->bi_idx; bio_for_each_segment(bvec, bio, i) { if (bvprv && BIOVEC_PHYS_MERGEABLE(bvprv, bvec)) { @@ -479,7 +486,7 @@ static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, } else { if (bvprv && BIOVEC_NOT_VIRT_MERGEABLE(bvprv, bvec)) break; - sg = sg ? sg + 1 : nbio->sg; + sg = sg ? sg + 1 : iod->sg; sg_set_page(sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset); nsegs++; @@ -488,9 +495,9 @@ static int nvme_map_bio(struct device *dev, struct nvme_bio *nbio, bvprv = bvec; } bio->bi_idx = i; - nbio->nents = nsegs; + iod->nents = nsegs; sg_mark_end(sg); - if (dma_map_sg(dev, nbio->sg, nbio->nents, dma_dir) == 0) { + if (dma_map_sg(dev, iod->sg, iod->nents, dma_dir) == 0) { bio->bi_idx = old_idx; return -ENOMEM; } @@ -531,7 +538,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct bio *bio) { struct nvme_command *cmnd; - struct nvme_bio *nbio; + struct nvme_iod *iod; enum dma_data_direction dma_dir; int cmdid, length, result = -ENOMEM; u16 control; @@ -544,15 +551,15 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return result; } - nbio = alloc_nbio(psegs, GFP_ATOMIC); - if (!nbio) + iod = nvme_alloc_iod(psegs, bio->bi_size, GFP_ATOMIC); + if (!iod) goto nomem; - nbio->bio = bio; + iod->private = bio; result = -EBUSY; - cmdid = alloc_cmdid(nvmeq, nbio, bio_completion, IO_TIMEOUT); + cmdid = alloc_cmdid(nvmeq, iod, bio_completion, IO_TIMEOUT); if (unlikely(cmdid < 0)) - goto free_nbio; + goto free_iod; if ((bio->bi_rw & REQ_FLUSH) && !psegs) return nvme_submit_flush(nvmeq, ns, cmdid); @@ -578,15 +585,15 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dma_dir = DMA_FROM_DEVICE; } - result = nvme_map_bio(nvmeq->q_dmadev, nbio, bio, dma_dir, psegs); + result = nvme_map_bio(nvmeq->q_dmadev, iod, bio, dma_dir, psegs); if (result < 0) - goto free_nbio; + goto free_iod; length = result; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); - nbio->prps = nvme_setup_prps(nvmeq->dev, &cmnd->common, nbio->sg, - &length, GFP_ATOMIC); + length = nvme_setup_prps(nvmeq->dev, &cmnd->common, iod, length, + GFP_ATOMIC); cmnd->rw.slba = cpu_to_le64(bio->bi_sector >> (ns->lba_shift - 9)); cmnd->rw.length = cpu_to_le16((length >> ns->lba_shift) - 1); cmnd->rw.control = cpu_to_le16(control); @@ -600,8 +607,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return 0; - free_nbio: - free_nbio(nvmeq->dev, nbio); + free_iod: + nvme_free_iod(nvmeq->dev, iod); nomem: return result; } @@ -1005,18 +1012,18 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) return result; } -static int nvme_map_user_pages(struct nvme_dev *dev, int write, - unsigned long addr, unsigned length, - struct scatterlist **sgp) +static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write, + unsigned long addr, unsigned length) { int i, err, count, nents, offset; struct scatterlist *sg; struct page **pages; + struct nvme_iod *iod; if (addr & 3) - return -EINVAL; + return ERR_PTR(-EINVAL); if (!length) - return -EINVAL; + return ERR_PTR(-EINVAL); offset = offset_in_page(addr); count = DIV_ROUND_UP(offset + length, PAGE_SIZE); @@ -1029,7 +1036,8 @@ static int nvme_map_user_pages(struct nvme_dev *dev, int write, goto put_pages; } - sg = kcalloc(count, sizeof(*sg), GFP_KERNEL); + iod = nvme_alloc_iod(count, length, GFP_KERNEL); + sg = iod->sg; sg_init_table(sg, count); for (i = 0; i < count; i++) { sg_set_page(&sg[i], pages[i], @@ -1042,22 +1050,24 @@ static int nvme_map_user_pages(struct nvme_dev *dev, int write, nents = dma_map_sg(&dev->pci_dev->dev, sg, count, write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); if (!nents) - goto put_pages; + goto free_iod; kfree(pages); - *sgp = sg; - return nents; + return iod; + free_iod: + kfree(iod); put_pages: for (i = 0; i < count; i++) put_page(pages[i]); kfree(pages); - return err; + return ERR_PTR(err); } static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, - unsigned long addr, int length, struct scatterlist *sg) + unsigned long addr, int length, struct nvme_iod *iod) { + struct scatterlist *sg = iod->sg; int i, count; count = DIV_ROUND_UP(offset_in_page(addr) + length, PAGE_SIZE); @@ -1074,9 +1084,8 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) struct nvme_user_io io; struct nvme_command c; unsigned length; - int nents, status; - struct scatterlist *sg; - struct nvme_prps *prps; + int status; + struct nvme_iod *iod; if (copy_from_user(&io, uio, sizeof(io))) return -EFAULT; @@ -1086,15 +1095,14 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) case nvme_cmd_write: case nvme_cmd_read: case nvme_cmd_compare: - nents = nvme_map_user_pages(dev, io.opcode & 1, io.addr, - length, &sg); + iod = nvme_map_user_pages(dev, io.opcode & 1, io.addr, length); break; default: return -EINVAL; } - if (nents < 0) - return nents; + if (IS_ERR(iod)) + return PTR_ERR(iod); memset(&c, 0, sizeof(c)); c.rw.opcode = io.opcode; @@ -1108,7 +1116,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.apptag = io.apptag; c.rw.appmask = io.appmask; /* XXX: metadata */ - prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); + length = nvme_setup_prps(dev, &c.common, iod, length, GFP_KERNEL); nvmeq = get_nvmeq(dev); /* @@ -1123,8 +1131,8 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) else status = nvme_submit_sync_cmd(nvmeq, &c, NULL, IO_TIMEOUT); - nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, sg); - nvme_free_prps(dev, prps); + nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, iod); + nvme_free_iod(dev, iod); return status; } @@ -1134,9 +1142,8 @@ static int nvme_user_admin_cmd(struct nvme_ns *ns, struct nvme_dev *dev = ns->dev; struct nvme_admin_cmd cmd; struct nvme_command c; - int status, length, nents = 0; - struct scatterlist *sg; - struct nvme_prps *prps = NULL; + int status, length; + struct nvme_iod *iod; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -1158,19 +1165,21 @@ static int nvme_user_admin_cmd(struct nvme_ns *ns, length = cmd.data_len; if (cmd.data_len) { - nents = nvme_map_user_pages(dev, 1, cmd.addr, length, &sg); - if (nents < 0) - return nents; - prps = nvme_setup_prps(dev, &c.common, sg, &length, GFP_KERNEL); + iod = nvme_map_user_pages(dev, 1, cmd.addr, length); + if (IS_ERR(iod)) + return PTR_ERR(iod); + length = nvme_setup_prps(dev, &c.common, iod, length, + GFP_KERNEL); } if (length != cmd.data_len) status = -ENOMEM; else status = nvme_submit_admin_cmd(dev, &c, NULL); + if (cmd.data_len) { - nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, sg); - nvme_free_prps(dev, prps); + nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, iod); + nvme_free_iod(dev, iod); } return status; } -- cgit v0.10.2 From ff976d724a74e4522e9ca2de1fb37ac4520f454f Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 20 Dec 2011 13:53:01 -0500 Subject: NVMe: Rename IO_TIMEOUT to NVME_IO_TIMEOUT IO_TIMEOUT is a little too generic and might be used by other parts of the kernel in the future. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 4517608..1cc0187 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -45,7 +45,7 @@ #define SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) #define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) #define NVME_MINORS 64 -#define IO_TIMEOUT (5 * HZ) +#define NVME_IO_TIMEOUT (5 * HZ) #define ADMIN_TIMEOUT (60 * HZ) static int nvme_major; @@ -524,7 +524,7 @@ static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns, static int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns) { int cmdid = alloc_cmdid(nvmeq, (void *)CMD_CTX_FLUSH, - special_completion, IO_TIMEOUT); + special_completion, NVME_IO_TIMEOUT); if (unlikely(cmdid < 0)) return cmdid; @@ -557,7 +557,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, iod->private = bio; result = -EBUSY; - cmdid = alloc_cmdid(nvmeq, iod, bio_completion, IO_TIMEOUT); + cmdid = alloc_cmdid(nvmeq, iod, bio_completion, NVME_IO_TIMEOUT); if (unlikely(cmdid < 0)) goto free_iod; @@ -1129,7 +1129,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) if (length != (io.nblocks + 1) << ns->lba_shift) status = -ENOMEM; else - status = nvme_submit_sync_cmd(nvmeq, &c, NULL, IO_TIMEOUT); + status = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT); nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, iod); nvme_free_iod(dev, iod); -- cgit v0.10.2 From 497421880acecd0281d3182d534f3d28c927caec Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 6 Jan 2012 13:42:45 -0700 Subject: NVMe: Fix DMA mapping for admin commands We were always mapping as DMA_FROM_DEVICE then unmapping with DMA_TO_DEVICE which was clearly not correct. Follow the same pattern as nvme_submit_io() and key off the bottom bit of the opcode to determine whether this is a read or a write. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 1cc0187..3f8cae9 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1165,7 +1165,8 @@ static int nvme_user_admin_cmd(struct nvme_ns *ns, length = cmd.data_len; if (cmd.data_len) { - iod = nvme_map_user_pages(dev, 1, cmd.addr, length); + iod = nvme_map_user_pages(dev, cmd.opcode & 1, cmd.addr, + length); if (IS_ERR(iod)) return PTR_ERR(iod); length = nvme_setup_prps(dev, &c.common, iod, length, @@ -1178,7 +1179,8 @@ static int nvme_user_admin_cmd(struct nvme_ns *ns, status = nvme_submit_admin_cmd(dev, &c, NULL); if (cmd.data_len) { - nvme_unmap_user_pages(dev, 0, cmd.addr, cmd.data_len, iod); + nvme_unmap_user_pages(dev, cmd.opcode & 1, cmd.addr, + cmd.data_len, iod); nvme_free_iod(dev, iod); } return status; -- cgit v0.10.2 From fe304c43c6d63e29ed4fc46a874d7a74313788c5 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 6 Jan 2012 13:49:25 -0700 Subject: NVMe: Mark the end of the sg list For user I/O and admin commands, we were forgetting to mark the end of the SG list. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 3f8cae9..71fc903 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1045,6 +1045,7 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write, length -= (PAGE_SIZE - offset); offset = 0; } + sg_mark_end(&sg[i - 1]); err = -ENOMEM; nents = dma_map_sg(&dev->pci_dev->dev, sg, count, -- cgit v0.10.2 From 1c2ad9faaf662b4a525348775deca3ac8e6c35a0 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 6 Jan 2012 13:52:56 -0700 Subject: NVMe: Simplify nvme_unmap_user_pages By using the iod->nents field (the same way other I/O paths do), we can avoid recalculating the number of sg entries at unmap time, and make nvme_unmap_user_pages() easier to call. Also, use the 'write' parameter instead of assuming DMA_FROM_DEVICE. Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 71fc903..3cf82c2 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1046,6 +1046,7 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write, offset = 0; } sg_mark_end(&sg[i - 1]); + iod->nents = count; err = -ENOMEM; nents = dma_map_sg(&dev->pci_dev->dev, sg, count, @@ -1066,16 +1067,15 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write, } static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, - unsigned long addr, int length, struct nvme_iod *iod) + struct nvme_iod *iod) { - struct scatterlist *sg = iod->sg; - int i, count; + int i; - count = DIV_ROUND_UP(offset_in_page(addr) + length, PAGE_SIZE); - dma_unmap_sg(&dev->pci_dev->dev, sg, count, DMA_FROM_DEVICE); + dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - for (i = 0; i < count; i++) - put_page(sg_page(&sg[i])); + for (i = 0; i < iod->nents; i++) + put_page(sg_page(&iod->sg[i])); } static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) @@ -1132,7 +1132,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) else status = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT); - nvme_unmap_user_pages(dev, io.opcode & 1, io.addr, length, iod); + nvme_unmap_user_pages(dev, io.opcode & 1, iod); nvme_free_iod(dev, iod); return status; } @@ -1180,8 +1180,7 @@ static int nvme_user_admin_cmd(struct nvme_ns *ns, status = nvme_submit_admin_cmd(dev, &c, NULL); if (cmd.data_len) { - nvme_unmap_user_pages(dev, cmd.opcode & 1, cmd.addr, - cmd.data_len, iod); + nvme_unmap_user_pages(dev, cmd.opcode & 1, iod); nvme_free_iod(dev, iod); } return status; -- cgit v0.10.2 From 4eeb9215a0d5c9494ca8b20158cc8ee82618840c Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 10 Jan 2012 14:35:08 -0700 Subject: NVMe: Set queue flags correctly QUEUE_FLAG_* are flags (other than QUEUE_FLAG_DEFAULT), so they cannot be ORed together. Set the queue flags using queue_flag_set_unlocked(). Reported-by: Donald Wood Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 3cf82c2..b583603 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1315,8 +1315,10 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int nsid, ns->queue = blk_alloc_queue(GFP_KERNEL); if (!ns->queue) goto out_free_ns; - ns->queue->queue_flags = QUEUE_FLAG_DEFAULT | QUEUE_FLAG_NOMERGES | - QUEUE_FLAG_NONROT | QUEUE_FLAG_DISCARD; + ns->queue->queue_flags = QUEUE_FLAG_DEFAULT; + queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, ns->queue); + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, ns->queue); +/* queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, ns->queue); */ blk_queue_make_request(ns->queue, nvme_make_request); ns->dev = dev; ns->queue->queuedata = ns; -- cgit v0.10.2 From 366e8217e5ec6ce9f73aec19c46d983110fb4a98 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 10 Jan 2012 16:30:15 -0500 Subject: NVMe: Version 0.8 Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b583603..28c84b1 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -1726,6 +1726,6 @@ static void __exit nvme_exit(void) MODULE_AUTHOR("Matthew Wilcox "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.7"); +MODULE_VERSION("0.8"); module_init(nvme_init); module_exit(nvme_exit); -- cgit v0.10.2 From 9fc397fc0878c9540af20cbffc4d546541fe8b23 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Fri, 18 Nov 2011 01:44:27 +0100 Subject: [SCSI] scsi_dh_rdac: Fix error path If create_singlethread_workqueue() failes, rdac_init should fail too. Signed-off-by: Richard Weinberger Acked-by: "Moger, Babu" Signed-off-by: James Bottomley diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 841ebf4..53a31c7 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -953,6 +953,8 @@ static int __init rdac_init(void) if (!kmpath_rdacd) { scsi_unregister_device_handler(&rdac_dh); printk(KERN_ERR "kmpath_rdacd creation failed.\n"); + + r = -EINVAL; } done: return r; -- cgit v0.10.2 From cced5041ed5a2d1352186510944b0ddfbdbe4c0b Mon Sep 17 00:00:00 2001 From: Stratos Psomadakis Date: Sun, 4 Dec 2011 02:23:54 +0200 Subject: [SCSI] sym53c8xx: Fix NULL pointer dereference in slave_destroy sym53c8xx_slave_destroy unconditionally assumes that sym53c8xx_slave_alloc has succesesfully allocated a sym_lcb. This can lead to a NULL pointer dereference (exposed by commit 4e6c82b). Signed-off-by: Stratos Psomadakis Cc: stable@vger.kernel.org Signed-off-by: James Bottomley diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index b4543f5..36d1ed7 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -839,6 +839,10 @@ static void sym53c8xx_slave_destroy(struct scsi_device *sdev) struct sym_lcb *lp = sym_lp(tp, sdev->lun); unsigned long flags; + /* if slave_alloc returned before allocating a sym_lcb, return */ + if (!lp) + return; + spin_lock_irqsave(np->s.host->host_lock, flags); if (lp->busy_itlq || lp->busy_itl) { -- cgit v0.10.2 From 00c4a09bb0840457f5f8f5753a562e5e19a91baf Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 14 Dec 2011 13:46:23 -0200 Subject: [SCSI] libcxgbi: do not print a message when memory allocation fails In alloc_pdu, libcxgbi tries to allocate a skb with GFP_ATOMIC, which may potentially fail. When it happens, the current code prints a warning message. When the system is under IO stress, this failure may happen lots of times and it usually scares users. Instead of printing the warning message, the code now increases the tx_dropped statistics for the ethernet interface wich is doing the iscsi task. Signed-off-by: Thadeu Lima de Souza Cascardo Acked-by: Karen Xie Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index c5360ff..d3ff9cd 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -1868,8 +1868,9 @@ int cxgbi_conn_alloc_pdu(struct iscsi_task *task, u8 opcode) tdata->skb = alloc_skb(cdev->skb_tx_rsvd + headroom, GFP_ATOMIC); if (!tdata->skb) { - pr_warn("alloc skb %u+%u, opcode 0x%x failed.\n", - cdev->skb_tx_rsvd, headroom, opcode); + struct cxgbi_sock *csk = cconn->cep->csk; + struct net_device *ndev = cdev->ports[csk->port_id]; + ndev->stats.tx_dropped++; return -ENOMEM; } -- cgit v0.10.2 From 4a4bc2e90c1c689bf929914256310361d4012df1 Mon Sep 17 00:00:00 2001 From: Lalit Chandivade Date: Fri, 16 Dec 2011 01:58:55 -0800 Subject: [SCSI] qla4xxx: cleanup, make qla4xxx_build_ddb_list short Make qla4xxx_build_ddb_list shorter by adding more helper functions. Signed-off-by: Lalit Chandivade Signed-off-by: Vikas Chaudhary Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index ec393a0..87e3699 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -2078,7 +2078,7 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha) } } -void qla4xxx_check_relogin_flash_ddb(struct iscsi_cls_session *cls_sess) +static void qla4xxx_check_relogin_flash_ddb(struct iscsi_cls_session *cls_sess) { struct iscsi_session *sess; struct ddb_entry *ddb_entry; @@ -3826,16 +3826,14 @@ exit_check: return ret; } -static void qla4xxx_free_nt_list(struct list_head *list_nt) +static void qla4xxx_free_ddb_list(struct list_head *list_ddb) { - struct qla_ddb_index *nt_ddb_idx, *nt_ddb_idx_tmp; + struct qla_ddb_index *ddb_idx, *ddb_idx_tmp; - /* Free up the normaltargets list */ - list_for_each_entry_safe(nt_ddb_idx, nt_ddb_idx_tmp, list_nt, list) { - list_del_init(&nt_ddb_idx->list); - vfree(nt_ddb_idx); + list_for_each_entry_safe(ddb_idx, ddb_idx_tmp, list_ddb, list) { + list_del_init(&ddb_idx->list); + vfree(ddb_idx); } - } static struct iscsi_endpoint *qla4xxx_get_ep_fwdb(struct scsi_qla_host *ha, @@ -3934,7 +3932,6 @@ static void qla4xxx_wait_for_ip_configuration(struct scsi_qla_host *ha) ip_state == IP_ADDRSTATE_DEPRICATED || ip_state == IP_ADDRSTATE_DISABLING) ip_idx[idx] = -1; - } /* Break if all IP states checked */ @@ -3947,52 +3944,34 @@ static void qla4xxx_wait_for_ip_configuration(struct scsi_qla_host *ha) } while (time_after(wtime, jiffies)); } -void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset) +static void qla4xxx_build_st_list(struct scsi_qla_host *ha, + struct list_head *list_st) { + struct qla_ddb_index *st_ddb_idx; int max_ddbs; + int fw_idx_size; + struct dev_db_entry *fw_ddb_entry; + dma_addr_t fw_ddb_dma; int ret; uint32_t idx = 0, next_idx = 0; uint32_t state = 0, conn_err = 0; - uint16_t conn_id; - struct dev_db_entry *fw_ddb_entry; - struct ddb_entry *ddb_entry = NULL; - dma_addr_t fw_ddb_dma; - struct iscsi_cls_session *cls_sess; - struct iscsi_session *sess; - struct iscsi_cls_conn *cls_conn; - struct iscsi_endpoint *ep; - uint16_t cmds_max = 32, tmo = 0; - uint32_t initial_cmdsn = 0; - struct list_head list_st, list_nt; /* List of sendtargets */ - struct qla_ddb_index *st_ddb_idx, *st_ddb_idx_tmp; - int fw_idx_size; - unsigned long wtime; - struct qla_ddb_index *nt_ddb_idx; - - if (!test_bit(AF_LINK_UP, &ha->flags)) { - set_bit(AF_BUILD_DDB_LIST, &ha->flags); - ha->is_reset = is_reset; - return; - } - max_ddbs = is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : - MAX_DEV_DB_ENTRIES; + uint16_t conn_id = 0; fw_ddb_entry = dma_pool_alloc(ha->fw_ddb_dma_pool, GFP_KERNEL, &fw_ddb_dma); if (fw_ddb_entry == NULL) { DEBUG2(ql4_printk(KERN_ERR, ha, "Out of memory\n")); - goto exit_ddb_list; + goto exit_st_list; } - INIT_LIST_HEAD(&list_st); - INIT_LIST_HEAD(&list_nt); + max_ddbs = is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : + MAX_DEV_DB_ENTRIES; fw_idx_size = sizeof(struct qla_ddb_index); for (idx = 0; idx < max_ddbs; idx = next_idx) { - ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, - fw_ddb_dma, NULL, - &next_idx, &state, &conn_err, - NULL, &conn_id); + ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, fw_ddb_dma, + NULL, &next_idx, &state, + &conn_err, NULL, &conn_id); if (ret == QLA_ERROR) break; @@ -4009,59 +3988,155 @@ void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset) st_ddb_idx->fw_ddb_idx = idx; - list_add_tail(&st_ddb_idx->list, &list_st); + list_add_tail(&st_ddb_idx->list, list_st); continue_next_st: if (next_idx == 0) break; } - /* Before issuing conn open mbox, ensure all IPs states are configured - * Note, conn open fails if IPs are not configured +exit_st_list: + if (fw_ddb_entry) + dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma); +} + +/** + * qla4xxx_remove_failed_ddb - Remove inactive or failed ddb from list + * @ha: pointer to adapter structure + * @list_ddb: List from which failed ddb to be removed + * + * Iterate over the list of DDBs and find and remove DDBs that are either in + * no connection active state or failed state + **/ +static void qla4xxx_remove_failed_ddb(struct scsi_qla_host *ha, + struct list_head *list_ddb) +{ + struct qla_ddb_index *ddb_idx, *ddb_idx_tmp; + uint32_t next_idx = 0; + uint32_t state = 0, conn_err = 0; + int ret; + + list_for_each_entry_safe(ddb_idx, ddb_idx_tmp, list_ddb, list) { + ret = qla4xxx_get_fwddb_entry(ha, ddb_idx->fw_ddb_idx, + NULL, 0, NULL, &next_idx, &state, + &conn_err, NULL, NULL); + if (ret == QLA_ERROR) + continue; + + if (state == DDB_DS_NO_CONNECTION_ACTIVE || + state == DDB_DS_SESSION_FAILED) { + list_del_init(&ddb_idx->list); + vfree(ddb_idx); + } + } +} + +static int qla4xxx_sess_conn_setup(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry, + int is_reset) +{ + struct iscsi_cls_session *cls_sess; + struct iscsi_session *sess; + struct iscsi_cls_conn *cls_conn; + struct iscsi_endpoint *ep; + uint16_t cmds_max = 32; + uint16_t conn_id = 0; + uint32_t initial_cmdsn = 0; + int ret = QLA_SUCCESS; + + struct ddb_entry *ddb_entry = NULL; + + /* Create session object, with INVALID_ENTRY, + * the targer_id would get set when we issue the login */ - qla4xxx_wait_for_ip_configuration(ha); + cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, ha->host, + cmds_max, sizeof(struct ddb_entry), + sizeof(struct ql4_task_data), + initial_cmdsn, INVALID_ENTRY); + if (!cls_sess) { + ret = QLA_ERROR; + goto exit_setup; + } - /* Go thru the STs and fire the sendtargets by issuing conn open mbx */ - list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, list) { - qla4xxx_conn_open(ha, st_ddb_idx->fw_ddb_idx); + /* + * so calling module_put function to decrement the + * reference count. + **/ + module_put(qla4xxx_iscsi_transport.owner); + sess = cls_sess->dd_data; + ddb_entry = sess->dd_data; + ddb_entry->sess = cls_sess; + + cls_sess->recovery_tmo = ql4xsess_recovery_tmo; + memcpy(&ddb_entry->fw_ddb_entry, fw_ddb_entry, + sizeof(struct dev_db_entry)); + + qla4xxx_setup_flash_ddb_entry(ha, ddb_entry); + + cls_conn = iscsi_conn_setup(cls_sess, sizeof(struct qla_conn), conn_id); + + if (!cls_conn) { + ret = QLA_ERROR; + goto exit_setup; } - /* Wait to ensure all sendtargets are done for min 12 sec wait */ - tmo = ((ha->def_timeout < LOGIN_TOV) ? LOGIN_TOV : ha->def_timeout); - DEBUG2(ql4_printk(KERN_INFO, ha, - "Default time to wait for build ddb %d\n", tmo)); + ddb_entry->conn = cls_conn; - wtime = jiffies + (HZ * tmo); - do { - list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, - list) { - ret = qla4xxx_get_fwddb_entry(ha, - st_ddb_idx->fw_ddb_idx, - NULL, 0, NULL, &next_idx, - &state, &conn_err, NULL, - NULL); - if (ret == QLA_ERROR) - continue; + /* Setup ep, for displaying attributes in sysfs */ + ep = qla4xxx_get_ep_fwdb(ha, fw_ddb_entry); + if (ep) { + ep->conn = cls_conn; + cls_conn->ep = ep; + } else { + DEBUG2(ql4_printk(KERN_ERR, ha, "Unable to get ep\n")); + ret = QLA_ERROR; + goto exit_setup; + } - if (state == DDB_DS_NO_CONNECTION_ACTIVE || - state == DDB_DS_SESSION_FAILED) { - list_del_init(&st_ddb_idx->list); - vfree(st_ddb_idx); - } - } - schedule_timeout_uninterruptible(HZ / 10); - } while (time_after(wtime, jiffies)); + /* Update sess/conn params */ + qla4xxx_copy_fwddb_param(ha, fw_ddb_entry, cls_sess, cls_conn); - /* Free up the sendtargets list */ - list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, list) { - list_del_init(&st_ddb_idx->list); - vfree(st_ddb_idx); + if (is_reset == RESET_ADAPTER) { + iscsi_block_session(cls_sess); + /* Use the relogin path to discover new devices + * by short-circuting the logic of setting + * timer to relogin - instead set the flags + * to initiate login right away. + */ + set_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags); + set_bit(DF_RELOGIN, &ddb_entry->flags); } +exit_setup: + return ret; +} + +static void qla4xxx_build_nt_list(struct scsi_qla_host *ha, + struct list_head *list_nt, int is_reset) +{ + struct dev_db_entry *fw_ddb_entry; + dma_addr_t fw_ddb_dma; + int max_ddbs; + int fw_idx_size; + int ret; + uint32_t idx = 0, next_idx = 0; + uint32_t state = 0, conn_err = 0; + uint16_t conn_id = 0; + struct qla_ddb_index *nt_ddb_idx; + + fw_ddb_entry = dma_pool_alloc(ha->fw_ddb_dma_pool, GFP_KERNEL, + &fw_ddb_dma); + if (fw_ddb_entry == NULL) { + DEBUG2(ql4_printk(KERN_ERR, ha, "Out of memory\n")); + goto exit_nt_list; + } + max_ddbs = is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : + MAX_DEV_DB_ENTRIES; + fw_idx_size = sizeof(struct qla_ddb_index); + for (idx = 0; idx < max_ddbs; idx = next_idx) { - ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, - fw_ddb_dma, NULL, - &next_idx, &state, &conn_err, - NULL, &conn_id); + ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, fw_ddb_dma, + NULL, &next_idx, &state, + &conn_err, NULL, &conn_id); if (ret == QLA_ERROR) break; @@ -4072,107 +4147,107 @@ continue_next_st: if (strlen((char *) fw_ddb_entry->iscsi_name) == 0) goto continue_next_nt; - if (state == DDB_DS_NO_CONNECTION_ACTIVE || - state == DDB_DS_SESSION_FAILED) { - DEBUG2(ql4_printk(KERN_INFO, ha, - "Adding DDB to session = 0x%x\n", - idx)); - if (is_reset == INIT_ADAPTER) { - nt_ddb_idx = vmalloc(fw_idx_size); - if (!nt_ddb_idx) - break; - - nt_ddb_idx->fw_ddb_idx = idx; - - memcpy(&nt_ddb_idx->fw_ddb, fw_ddb_entry, - sizeof(struct dev_db_entry)); - - if (qla4xxx_is_flash_ddb_exists(ha, &list_nt, - fw_ddb_entry) == QLA_SUCCESS) { - vfree(nt_ddb_idx); - goto continue_next_nt; - } - list_add_tail(&nt_ddb_idx->list, &list_nt); - } else if (is_reset == RESET_ADAPTER) { - if (qla4xxx_is_session_exists(ha, - fw_ddb_entry) == QLA_SUCCESS) - goto continue_next_nt; - } + if (!(state == DDB_DS_NO_CONNECTION_ACTIVE || + state == DDB_DS_SESSION_FAILED)) + goto continue_next_nt; - /* Create session object, with INVALID_ENTRY, - * the targer_id would get set when we issue the login - */ - cls_sess = iscsi_session_setup(&qla4xxx_iscsi_transport, - ha->host, cmds_max, - sizeof(struct ddb_entry), - sizeof(struct ql4_task_data), - initial_cmdsn, INVALID_ENTRY); - if (!cls_sess) - goto exit_ddb_list; + DEBUG2(ql4_printk(KERN_INFO, ha, + "Adding DDB to session = 0x%x\n", idx)); + if (is_reset == INIT_ADAPTER) { + nt_ddb_idx = vmalloc(fw_idx_size); + if (!nt_ddb_idx) + break; - /* - * iscsi_session_setup increments the driver reference - * count which wouldn't let the driver to be unloaded. - * so calling module_put function to decrement the - * reference count. - **/ - module_put(qla4xxx_iscsi_transport.owner); - sess = cls_sess->dd_data; - ddb_entry = sess->dd_data; - ddb_entry->sess = cls_sess; + nt_ddb_idx->fw_ddb_idx = idx; - cls_sess->recovery_tmo = ql4xsess_recovery_tmo; - memcpy(&ddb_entry->fw_ddb_entry, fw_ddb_entry, + memcpy(&nt_ddb_idx->fw_ddb, fw_ddb_entry, sizeof(struct dev_db_entry)); - qla4xxx_setup_flash_ddb_entry(ha, ddb_entry); - - cls_conn = iscsi_conn_setup(cls_sess, - sizeof(struct qla_conn), - conn_id); - if (!cls_conn) - goto exit_ddb_list; - - ddb_entry->conn = cls_conn; - - /* Setup ep, for displaying attributes in sysfs */ - ep = qla4xxx_get_ep_fwdb(ha, fw_ddb_entry); - if (ep) { - ep->conn = cls_conn; - cls_conn->ep = ep; - } else { - DEBUG2(ql4_printk(KERN_ERR, ha, - "Unable to get ep\n")); - } - - /* Update sess/conn params */ - qla4xxx_copy_fwddb_param(ha, fw_ddb_entry, cls_sess, - cls_conn); - - if (is_reset == RESET_ADAPTER) { - iscsi_block_session(cls_sess); - /* Use the relogin path to discover new devices - * by short-circuting the logic of setting - * timer to relogin - instead set the flags - * to initiate login right away. - */ - set_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags); - set_bit(DF_RELOGIN, &ddb_entry->flags); + if (qla4xxx_is_flash_ddb_exists(ha, list_nt, + fw_ddb_entry) == QLA_SUCCESS) { + vfree(nt_ddb_idx); + goto continue_next_nt; } + list_add_tail(&nt_ddb_idx->list, list_nt); + } else if (is_reset == RESET_ADAPTER) { + if (qla4xxx_is_session_exists(ha, fw_ddb_entry) == + QLA_SUCCESS) + goto continue_next_nt; } + + ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, is_reset); + if (ret == QLA_ERROR) + goto exit_nt_list; + continue_next_nt: if (next_idx == 0) break; } -exit_ddb_list: - qla4xxx_free_nt_list(&list_nt); + +exit_nt_list: if (fw_ddb_entry) dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma); +} + +/** + * qla4xxx_build_ddb_list - Build ddb list and setup sessions + * @ha: pointer to adapter structure + * @is_reset: Is this init path or reset path + * + * Create a list of sendtargets (st) from firmware DDBs, issue send targets + * using connection open, then create the list of normal targets (nt) + * from firmware DDBs. Based on the list of nt setup session and connection + * objects. + **/ +void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset) +{ + uint16_t tmo = 0; + struct list_head list_st, list_nt; + struct qla_ddb_index *st_ddb_idx, *st_ddb_idx_tmp; + unsigned long wtime; + + if (!test_bit(AF_LINK_UP, &ha->flags)) { + set_bit(AF_BUILD_DDB_LIST, &ha->flags); + ha->is_reset = is_reset; + return; + } + + INIT_LIST_HEAD(&list_st); + INIT_LIST_HEAD(&list_nt); + + qla4xxx_build_st_list(ha, &list_st); + + /* Before issuing conn open mbox, ensure all IPs states are configured + * Note, conn open fails if IPs are not configured + */ + qla4xxx_wait_for_ip_configuration(ha); + + /* Go thru the STs and fire the sendtargets by issuing conn open mbx */ + list_for_each_entry_safe(st_ddb_idx, st_ddb_idx_tmp, &list_st, list) { + qla4xxx_conn_open(ha, st_ddb_idx->fw_ddb_idx); + } + + /* Wait to ensure all sendtargets are done for min 12 sec wait */ + tmo = ((ha->def_timeout < LOGIN_TOV) ? LOGIN_TOV : ha->def_timeout); + DEBUG2(ql4_printk(KERN_INFO, ha, + "Default time to wait for build ddb %d\n", tmo)); + + wtime = jiffies + (HZ * tmo); + do { + qla4xxx_remove_failed_ddb(ha, &list_st); + schedule_timeout_uninterruptible(HZ / 10); + } while (time_after(wtime, jiffies)); + + /* Free up the sendtargets list */ + qla4xxx_free_ddb_list(&list_st); + + qla4xxx_build_nt_list(ha, &list_nt, is_reset); + + qla4xxx_free_ddb_list(&list_nt); qla4xxx_free_ddb_index(ha); } - /** * qla4xxx_probe_adapter - callback function to probe HBA * @pdev: pointer to pci_dev structure -- cgit v0.10.2 From c28eaaca7acea214937fb9b3ae45e001d49947bf Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Sun, 18 Dec 2011 21:40:44 -0800 Subject: [SCSI] qla4xxx: Limit the ACB Default Timeout value to 12s A wrong default timeout value programmed in the adapter causes driver to wait for that much time while waiting for target discoveries to complete. This could add huge delays during the driver load time. To avoid this, limit the default timeout value to 12 seconds if the default timeout value set in adapter is less than 12 seconds and greater than 120 seconds. Signed-off-by: Nilesh Javali Signed-off-by: Vikas Chaudhary Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 87e3699..09b28f8 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1630,7 +1630,9 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha, /* Update timers after login */ ddb_entry->default_relogin_timeout = - le16_to_cpu(fw_ddb_entry->def_timeout); + (le16_to_cpu(fw_ddb_entry->def_timeout) > LOGIN_TOV) && + (le16_to_cpu(fw_ddb_entry->def_timeout) < LOGIN_TOV * 10) ? + le16_to_cpu(fw_ddb_entry->def_timeout) : LOGIN_TOV; ddb_entry->default_time2wait = le16_to_cpu(fw_ddb_entry->iscsi_def_time2wait); @@ -3882,6 +3884,8 @@ static int qla4xxx_verify_boot_idx(struct scsi_qla_host *ha, uint16_t idx) static void qla4xxx_setup_flash_ddb_entry(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry) { + uint16_t def_timeout; + ddb_entry->ddb_type = FLASH_DDB; ddb_entry->fw_ddb_index = INVALID_ENTRY; ddb_entry->fw_ddb_device_state = DDB_DS_NO_CONNECTION_ACTIVE; @@ -3892,9 +3896,10 @@ static void qla4xxx_setup_flash_ddb_entry(struct scsi_qla_host *ha, atomic_set(&ddb_entry->retry_relogin_timer, INVALID_ENTRY); atomic_set(&ddb_entry->relogin_timer, 0); atomic_set(&ddb_entry->relogin_retry_count, 0); - + def_timeout = le16_to_cpu(ddb_entry->fw_ddb_entry.def_timeout); ddb_entry->default_relogin_timeout = - le16_to_cpu(ddb_entry->fw_ddb_entry.def_timeout); + (def_timeout > LOGIN_TOV) && (def_timeout < LOGIN_TOV * 10) ? + def_timeout : LOGIN_TOV; ddb_entry->default_time2wait = le16_to_cpu(ddb_entry->fw_ddb_entry.iscsi_def_time2wait); } @@ -4228,7 +4233,10 @@ void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset) } /* Wait to ensure all sendtargets are done for min 12 sec wait */ - tmo = ((ha->def_timeout < LOGIN_TOV) ? LOGIN_TOV : ha->def_timeout); + tmo = ((ha->def_timeout > LOGIN_TOV) && + (ha->def_timeout < LOGIN_TOV * 10) ? + ha->def_timeout : LOGIN_TOV); + DEBUG2(ql4_printk(KERN_INFO, ha, "Default time to wait for build ddb %d\n", tmo)); -- cgit v0.10.2 From f1f2e60e1a2320d0ce11102b72b15d5dedb29fe2 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Fri, 16 Dec 2011 01:58:57 -0800 Subject: [SCSI] qla4xxx: Break the loop if the sendtargets list was empty Signed-off-by: Nilesh Javali Signed-off-by: Vikas Chaudhary Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 09b28f8..ae5d512 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -4242,6 +4242,9 @@ void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset) wtime = jiffies + (HZ * tmo); do { + if (list_empty(&list_st)) + break; + qla4xxx_remove_failed_ddb(ha, &list_st); schedule_timeout_uninterruptible(HZ / 10); } while (time_after(wtime, jiffies)); -- cgit v0.10.2 From 368d45cc61c062a1291d0b29257a0697d6aeabd9 Mon Sep 17 00:00:00 2001 From: Manish Rangankar Date: Fri, 16 Dec 2011 01:58:58 -0800 Subject: [SCSI] qla4xxx: Fixed BFS with sendtargets as boot index. If ql4xdisablesysfsboot = 0 and sendtargets entry as boot index then driver does export sendtarget entries in sysfs but iscsistart does not do discovery. So in this case let driver do the discovery and login to the targets. Signed-off-by: Manish Rangankar Signed-off-by: Vikas Chaudhary Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index ae5d512..0048a3f 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -3980,9 +3980,6 @@ static void qla4xxx_build_st_list(struct scsi_qla_host *ha, if (ret == QLA_ERROR) break; - if (qla4xxx_verify_boot_idx(ha, idx) != QLA_SUCCESS) - goto continue_next_st; - /* Check if ST, add to the list_st */ if (strlen((char *) fw_ddb_entry->iscsi_name) != 0) goto continue_next_st; -- cgit v0.10.2 From 3a19cbf530648a63fb3f73630046c561f70c6b71 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Fri, 16 Dec 2011 01:58:59 -0800 Subject: [SCSI] qla4xxx: Update driver version to 5.02.00-k11 Signed-off-by: Vikas Chaudhary Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index 26a3fa3..7d04eb0 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,4 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.02.00-k10" +#define QLA4XXX_DRIVER_VERSION "5.02.00-k11" -- cgit v0.10.2 From f1633011e4fcb65bf65aebeaf4024efdc4c49d6e Mon Sep 17 00:00:00 2001 From: Robert Love Date: Fri, 16 Dec 2011 14:24:49 -0800 Subject: [SCSI] fcoe: remove double check if skb is nonlinear skb_linearize already has a check for skb_is_nonlinear, there is no need to duplicate the check in fcoe.c. This patch simply removes the unnecessary check and calls skb_linearize unconditionally. Reported-by: patrick kelle Signed-off-by: Robert Love Acked-by: patrick kelle Signed-off-by: James Bottomley diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 8d67467..e535f95 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1671,8 +1671,7 @@ static void fcoe_recv_frame(struct sk_buff *skb) skb->dev ? skb->dev->name : ""); port = lport_priv(lport); - if (skb_is_nonlinear(skb)) - skb_linearize(skb); /* not ideal */ + skb_linearize(skb); /* check for skb_is_nonlinear is within skb_linearize */ /* * Frame length checks and setting up the header pointers -- cgit v0.10.2 From 6bc6204e3b7f4ac235e98e362714213369d6a012 Mon Sep 17 00:00:00 2001 From: Krishna Gudipati Date: Tue, 20 Dec 2011 18:54:30 -0800 Subject: [SCSI] bfa: Fix possible NULL pointer dereference in lunmask add/delete. Patch fixes the possible NULL pointer dereference when we try to add or delete a rpwwn to the lunmask config which is not zoned to this port. Check if the FCS rport is not NULL before de-referencing it. Signed-off-by: Krishna Gudipati Signed-off-by: James Bottomley diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c index e07bd47..846f15b 100644 --- a/drivers/scsi/bfa/bfa_fcpim.c +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -2647,7 +2647,8 @@ bfa_fcpim_lunmask_add(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn, if (port) { *pwwn = port->port_cfg.pwwn; rp_fcs = bfa_fcs_lport_get_rport_by_pwwn(port, rpwwn); - rp = rp_fcs->bfa_rport; + if (rp_fcs) + rp = rp_fcs->bfa_rport; } lunm_list = bfa_get_lun_mask_list(bfa); @@ -2715,7 +2716,8 @@ bfa_fcpim_lunmask_delete(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn, if (port) { *pwwn = port->port_cfg.pwwn; rp_fcs = bfa_fcs_lport_get_rport_by_pwwn(port, rpwwn); - rp = rp_fcs->bfa_rport; + if (rp_fcs) + rp = rp_fcs->bfa_rport; } } -- cgit v0.10.2 From 8ca2dd87e7f29df99389e9dc41f7a45a35223672 Mon Sep 17 00:00:00 2001 From: Krishna Gudipati Date: Tue, 20 Dec 2011 18:55:07 -0800 Subject: [SCSI] bfa: Revert back the current LUN Masking Implementation. This patch reverts the current LUN Masking Implementation. We re-implemented this feature using the SCSI Slave Callout's as per the review comments. Signed-off-by: Krishna Gudipati Signed-off-by: James Bottomley diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h index 78963be..cb07c62 100644 --- a/drivers/scsi/bfa/bfa_defs_svc.h +++ b/drivers/scsi/bfa/bfa_defs_svc.h @@ -673,12 +673,7 @@ struct bfa_itnim_iostats_s { u32 tm_iocdowns; /* TM cleaned-up due to IOC down */ u32 tm_cleanups; /* TM cleanup requests */ u32 tm_cleanup_comps; /* TM cleanup completions */ - u32 lm_lun_across_sg; /* LM lun is across sg data buf */ - u32 lm_lun_not_sup; /* LM lun not supported */ - u32 lm_rpl_data_changed; /* LM report-lun data changed */ - u32 lm_wire_residue_changed; /* LM report-lun rsp residue changed */ - u32 lm_small_buf_addresidue; /* LM buf smaller than reported cnt */ - u32 lm_lun_not_rdy; /* LM lun not ready */ + u32 rsvd[6]; }; /* Modify char* port_stt[] in bfal_port.c if a new state was added */ diff --git a/drivers/scsi/bfa/bfa_fc.h b/drivers/scsi/bfa/bfa_fc.h index 50b6a1c..8d0b88f 100644 --- a/drivers/scsi/bfa/bfa_fc.h +++ b/drivers/scsi/bfa/bfa_fc.h @@ -56,161 +56,6 @@ struct scsi_cdb_s { #define SCSI_MAX_ALLOC_LEN 0xFF /* maximum allocarion length */ -#define SCSI_SENSE_CUR_ERR 0x70 -#define SCSI_SENSE_DEF_ERR 0x71 - -/* - * SCSI additional sense codes - */ -#define SCSI_ASC_LUN_NOT_READY 0x04 -#define SCSI_ASC_LUN_NOT_SUPPORTED 0x25 -#define SCSI_ASC_TOCC 0x3F - -/* - * SCSI additional sense code qualifiers - */ -#define SCSI_ASCQ_MAN_INTR_REQ 0x03 /* manual intervention req */ -#define SCSI_ASCQ_RL_DATA_CHANGED 0x0E /* report luns data changed */ - -/* - * Methods of reporting informational exceptions - */ -#define SCSI_MP_IEC_UNIT_ATTN 0x2 /* generate unit attention */ - -struct scsi_report_luns_data_s { - u32 lun_list_length; /* length of LUN list length */ - u32 reserved; - struct scsi_lun lun[1]; /* first LUN in lun list */ -}; - -struct scsi_inquiry_vendor_s { - u8 vendor_id[8]; -}; - -struct scsi_inquiry_prodid_s { - u8 product_id[16]; -}; - -struct scsi_inquiry_prodrev_s { - u8 product_rev[4]; -}; - -struct scsi_inquiry_data_s { -#ifdef __BIG_ENDIAN - u8 peripheral_qual:3; /* peripheral qualifier */ - u8 device_type:5; /* peripheral device type */ - u8 rmb:1; /* removable medium bit */ - u8 device_type_mod:7; /* device type modifier */ - u8 version; - u8 aenc:1; /* async evt notification capability */ - u8 trm_iop:1; /* terminate I/O process */ - u8 norm_aca:1; /* normal ACA supported */ - u8 hi_support:1; /* SCSI-3: supports REPORT LUNS */ - u8 rsp_data_format:4; - u8 additional_len; - u8 sccs:1; - u8 reserved1:7; - u8 reserved2:1; - u8 enc_serv:1; /* enclosure service component */ - u8 reserved3:1; - u8 multi_port:1; /* multi-port device */ - u8 m_chngr:1; /* device in medium transport element */ - u8 ack_req_q:1; /* SIP specific bit */ - u8 addr32:1; /* SIP specific bit */ - u8 addr16:1; /* SIP specific bit */ - u8 rel_adr:1; /* relative address */ - u8 w_bus32:1; - u8 w_bus16:1; - u8 synchronous:1; - u8 linked_commands:1; - u8 trans_dis:1; - u8 cmd_queue:1; /* command queueing supported */ - u8 soft_reset:1; /* soft reset alternative (VS) */ -#else - u8 device_type:5; /* peripheral device type */ - u8 peripheral_qual:3; /* peripheral qualifier */ - u8 device_type_mod:7; /* device type modifier */ - u8 rmb:1; /* removable medium bit */ - u8 version; - u8 rsp_data_format:4; - u8 hi_support:1; /* SCSI-3: supports REPORT LUNS */ - u8 norm_aca:1; /* normal ACA supported */ - u8 terminate_iop:1;/* terminate I/O process */ - u8 aenc:1; /* async evt notification capability */ - u8 additional_len; - u8 reserved1:7; - u8 sccs:1; - u8 addr16:1; /* SIP specific bit */ - u8 addr32:1; /* SIP specific bit */ - u8 ack_req_q:1; /* SIP specific bit */ - u8 m_chngr:1; /* device in medium transport element */ - u8 multi_port:1; /* multi-port device */ - u8 reserved3:1; /* TBD - Vendor Specific */ - u8 enc_serv:1; /* enclosure service component */ - u8 reserved2:1; - u8 soft_seset:1; /* soft reset alternative (VS) */ - u8 cmd_queue:1; /* command queueing supported */ - u8 trans_dis:1; - u8 linked_commands:1; - u8 synchronous:1; - u8 w_bus16:1; - u8 w_bus32:1; - u8 rel_adr:1; /* relative address */ -#endif - struct scsi_inquiry_vendor_s vendor_id; - struct scsi_inquiry_prodid_s product_id; - struct scsi_inquiry_prodrev_s product_rev; - u8 vendor_specific[20]; - u8 reserved4[40]; -}; - -/* - * SCSI sense data format - */ -struct scsi_sense_s { -#ifdef __BIG_ENDIAN - u8 valid:1; - u8 rsp_code:7; -#else - u8 rsp_code:7; - u8 valid:1; -#endif - u8 seg_num; -#ifdef __BIG_ENDIAN - u8 file_mark:1; - u8 eom:1; /* end of media */ - u8 ili:1; /* incorrect length indicator */ - u8 reserved:1; - u8 sense_key:4; -#else - u8 sense_key:4; - u8 reserved:1; - u8 ili:1; /* incorrect length indicator */ - u8 eom:1; /* end of media */ - u8 file_mark:1; -#endif - u8 information[4]; /* device-type or cmd specific info */ - u8 add_sense_length; /* additional sense length */ - u8 command_info[4];/* command specific information */ - u8 asc; /* additional sense code */ - u8 ascq; /* additional sense code qualifier */ - u8 fru_code; /* field replaceable unit code */ -#ifdef __BIG_ENDIAN - u8 sksv:1; /* sense key specific valid */ - u8 c_d:1; /* command/data bit */ - u8 res1:2; - u8 bpv:1; /* bit pointer valid */ - u8 bpointer:3; /* bit pointer */ -#else - u8 bpointer:3; /* bit pointer */ - u8 bpv:1; /* bit pointer valid */ - u8 res1:2; - u8 c_d:1; /* command/data bit */ - u8 sksv:1; /* sense key specific valid */ -#endif - u8 fpointer[2]; /* field pointer */ -}; - /* * Fibre Channel Header Structure (FCHS) definition */ diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c index 846f15b..f0f80e2 100644 --- a/drivers/scsi/bfa/bfa_fcpim.c +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -24,8 +24,6 @@ BFA_TRC_FILE(HAL, FCPIM); * BFA ITNIM Related definitions */ static void bfa_itnim_update_del_itn_stats(struct bfa_itnim_s *itnim); -static bfa_boolean_t bfa_ioim_lm_proc_rpl_data(struct bfa_ioim_s *ioim); -static bfa_boolean_t bfa_ioim_lm_proc_inq_data(struct bfa_ioim_s *ioim); static void bfa_ioim_lm_init(struct bfa_s *bfa); #define BFA_ITNIM_FROM_TAG(_fcpim, _tag) \ @@ -60,14 +58,6 @@ static void bfa_ioim_lm_init(struct bfa_s *bfa); } \ } while (0) -#define bfa_ioim_rp_wwn(__ioim) \ - (((struct bfa_fcs_rport_s *) \ - (__ioim)->itnim->rport->rport_drv)->pwwn) - -#define bfa_ioim_lp_wwn(__ioim) \ - ((BFA_LPS_FROM_TAG(BFA_LPS_MOD((__ioim)->bfa), \ - (__ioim)->itnim->rport->rport_info.lp_tag))->pwwn) \ - #define bfa_itnim_sler_cb(__itnim) do { \ if ((__itnim)->bfa->fcs) \ bfa_cb_itnim_sler((__itnim)->ditn); \ @@ -77,13 +67,6 @@ static void bfa_ioim_lm_init(struct bfa_s *bfa); } \ } while (0) -enum bfa_ioim_lm_status { - BFA_IOIM_LM_PRESENT = 1, - BFA_IOIM_LM_LUN_NOT_SUP = 2, - BFA_IOIM_LM_RPL_DATA_CHANGED = 3, - BFA_IOIM_LM_LUN_NOT_RDY = 4, -}; - enum bfa_ioim_lm_ua_status { BFA_IOIM_LM_UA_RESET = 0, BFA_IOIM_LM_UA_SET = 1, @@ -145,9 +128,6 @@ enum bfa_ioim_event { BFA_IOIM_SM_TMDONE = 16, /* IO cleanup from tskim */ BFA_IOIM_SM_HWFAIL = 17, /* IOC h/w failure event */ BFA_IOIM_SM_IOTOV = 18, /* ITN offline TOV */ - BFA_IOIM_SM_LM_LUN_NOT_SUP = 19,/* lunmask lun not supported */ - BFA_IOIM_SM_LM_RPL_DC = 20, /* lunmask report-lun data changed */ - BFA_IOIM_SM_LM_LUN_NOT_RDY = 21,/* lunmask lun not ready */ }; @@ -245,9 +225,6 @@ static void __bfa_cb_ioim_abort(void *cbarg, bfa_boolean_t complete); static void __bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete); static void __bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete); static bfa_boolean_t bfa_ioim_is_abortable(struct bfa_ioim_s *ioim); -static void __bfa_cb_ioim_lm_lun_not_sup(void *cbarg, bfa_boolean_t complete); -static void __bfa_cb_ioim_lm_rpl_dc(void *cbarg, bfa_boolean_t complete); -static void __bfa_cb_ioim_lm_lun_not_rdy(void *cbarg, bfa_boolean_t complete); /* * forward declaration of BFA IO state machine @@ -445,12 +422,6 @@ bfa_fcpim_add_stats(struct bfa_itnim_iostats_s *lstats, bfa_fcpim_add_iostats(lstats, rstats, output_reqs); bfa_fcpim_add_iostats(lstats, rstats, rd_throughput); bfa_fcpim_add_iostats(lstats, rstats, wr_throughput); - bfa_fcpim_add_iostats(lstats, rstats, lm_lun_across_sg); - bfa_fcpim_add_iostats(lstats, rstats, lm_lun_not_sup); - bfa_fcpim_add_iostats(lstats, rstats, lm_rpl_data_changed); - bfa_fcpim_add_iostats(lstats, rstats, lm_wire_residue_changed); - bfa_fcpim_add_iostats(lstats, rstats, lm_small_buf_addresidue); - bfa_fcpim_add_iostats(lstats, rstats, lm_lun_not_rdy); } bfa_status_t @@ -1580,27 +1551,6 @@ bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) __bfa_cb_ioim_abort, ioim); break; - case BFA_IOIM_SM_LM_LUN_NOT_SUP: - bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); - bfa_ioim_move_to_comp_q(ioim); - bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, - __bfa_cb_ioim_lm_lun_not_sup, ioim); - break; - - case BFA_IOIM_SM_LM_RPL_DC: - bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); - bfa_ioim_move_to_comp_q(ioim); - bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, - __bfa_cb_ioim_lm_rpl_dc, ioim); - break; - - case BFA_IOIM_SM_LM_LUN_NOT_RDY: - bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); - bfa_ioim_move_to_comp_q(ioim); - bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, - __bfa_cb_ioim_lm_lun_not_rdy, ioim); - break; - default: bfa_sm_fault(ioim->bfa, event); } @@ -2160,243 +2110,6 @@ bfa_ioim_lm_init(struct bfa_s *bfa) } } -/* - * Validate LUN for LUN masking - */ -static enum bfa_ioim_lm_status -bfa_ioim_lm_check(struct bfa_ioim_s *ioim, struct bfa_lps_s *lps, - struct bfa_rport_s *rp, struct scsi_lun lun) -{ - u8 i; - struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(ioim->bfa); - struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio; - struct scsi_cdb_s *cdb = (struct scsi_cdb_s *)cmnd->cmnd; - - if ((cdb->scsi_cdb[0] == REPORT_LUNS) && - (scsilun_to_int((struct scsi_lun *)&lun) == 0)) { - ioim->proc_rsp_data = bfa_ioim_lm_proc_rpl_data; - return BFA_IOIM_LM_PRESENT; - } - - for (i = 0; i < MAX_LUN_MASK_CFG; i++) { - - if (lun_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE) - continue; - - if ((scsilun_to_int((struct scsi_lun *)&lun_list[i].lun) == - scsilun_to_int((struct scsi_lun *)&lun)) - && (rp->rport_tag == lun_list[i].rp_tag) - && ((u8)ioim->itnim->rport->rport_info.lp_tag == - lun_list[i].lp_tag)) { - bfa_trc(ioim->bfa, lun_list[i].rp_tag); - bfa_trc(ioim->bfa, lun_list[i].lp_tag); - bfa_trc(ioim->bfa, scsilun_to_int( - (struct scsi_lun *)&lun_list[i].lun)); - - if ((lun_list[i].ua == BFA_IOIM_LM_UA_SET) && - ((cdb->scsi_cdb[0] != INQUIRY) || - (cdb->scsi_cdb[0] != REPORT_LUNS))) { - lun_list[i].ua = BFA_IOIM_LM_UA_RESET; - return BFA_IOIM_LM_RPL_DATA_CHANGED; - } - - if (cdb->scsi_cdb[0] == REPORT_LUNS) - ioim->proc_rsp_data = bfa_ioim_lm_proc_rpl_data; - - return BFA_IOIM_LM_PRESENT; - } - } - - if ((cdb->scsi_cdb[0] == INQUIRY) && - (scsilun_to_int((struct scsi_lun *)&lun) == 0)) { - ioim->proc_rsp_data = bfa_ioim_lm_proc_inq_data; - return BFA_IOIM_LM_PRESENT; - } - - if (cdb->scsi_cdb[0] == TEST_UNIT_READY) - return BFA_IOIM_LM_LUN_NOT_RDY; - - return BFA_IOIM_LM_LUN_NOT_SUP; -} - -static bfa_boolean_t -bfa_ioim_lm_proc_rsp_data_dummy(struct bfa_ioim_s *ioim) -{ - return BFA_TRUE; -} - -static void -bfa_ioim_lm_fetch_lun(struct bfa_ioim_s *ioim, u8 *rl_data, int offset, - int buf_lun_cnt) -{ - struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(ioim->bfa); - struct scsi_lun *lun_data = (struct scsi_lun *)(rl_data + offset); - struct scsi_lun lun; - int i, j; - - bfa_trc(ioim->bfa, buf_lun_cnt); - for (j = 0; j < buf_lun_cnt; j++) { - lun = *((struct scsi_lun *)(lun_data + j)); - for (i = 0; i < MAX_LUN_MASK_CFG; i++) { - if (lun_list[i].state != BFA_IOIM_LUN_MASK_ACTIVE) - continue; - if ((lun_list[i].rp_wwn == bfa_ioim_rp_wwn(ioim)) && - (lun_list[i].lp_wwn == bfa_ioim_lp_wwn(ioim)) && - (scsilun_to_int((struct scsi_lun *)&lun_list[i].lun) - == scsilun_to_int((struct scsi_lun *)&lun))) { - lun_list[i].state = BFA_IOIM_LUN_MASK_FETCHED; - break; - } - } /* next lun in mask DB */ - } /* next lun in buf */ -} - -static int -bfa_ioim_lm_update_lun_sg(struct bfa_ioim_s *ioim, u32 *pgdlen, - struct scsi_report_luns_data_s *rl) -{ - struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio; - struct scatterlist *sg = scsi_sglist(cmnd); - struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(ioim->bfa); - struct scsi_lun *prev_rl_data = NULL, *base_rl_data; - int i, j, sgeid, lun_fetched_cnt = 0, prev_sg_len = 0, base_count; - int lun_across_sg_bytes, bytes_from_next_buf; - u64 last_lun, temp_last_lun; - - /* fetch luns from the first sg element */ - bfa_ioim_lm_fetch_lun(ioim, (u8 *)(rl->lun), 0, - (sg_dma_len(sg) / sizeof(struct scsi_lun)) - 1); - - /* fetch luns from multiple sg elements */ - scsi_for_each_sg(cmnd, sg, scsi_sg_count(cmnd), sgeid) { - if (sgeid == 0) { - prev_sg_len = sg_dma_len(sg); - prev_rl_data = (struct scsi_lun *) - phys_to_virt(sg_dma_address(sg)); - continue; - } - - /* if the buf is having more data */ - lun_across_sg_bytes = prev_sg_len % sizeof(struct scsi_lun); - if (lun_across_sg_bytes) { - bfa_trc(ioim->bfa, lun_across_sg_bytes); - bfa_stats(ioim->itnim, lm_lun_across_sg); - bytes_from_next_buf = sizeof(struct scsi_lun) - - lun_across_sg_bytes; - - /* from next buf take higher bytes */ - temp_last_lun = *((u64 *) - phys_to_virt(sg_dma_address(sg))); - last_lun |= temp_last_lun >> - (lun_across_sg_bytes * BITS_PER_BYTE); - - /* from prev buf take higher bytes */ - temp_last_lun = *((u64 *)(prev_rl_data + - (prev_sg_len - lun_across_sg_bytes))); - temp_last_lun >>= bytes_from_next_buf * BITS_PER_BYTE; - last_lun = last_lun | (temp_last_lun << - (bytes_from_next_buf * BITS_PER_BYTE)); - - bfa_ioim_lm_fetch_lun(ioim, (u8 *)&last_lun, 0, 1); - } else - bytes_from_next_buf = 0; - - *pgdlen += sg_dma_len(sg); - prev_sg_len = sg_dma_len(sg); - prev_rl_data = (struct scsi_lun *) - phys_to_virt(sg_dma_address(sg)); - bfa_ioim_lm_fetch_lun(ioim, (u8 *)prev_rl_data, - bytes_from_next_buf, - sg_dma_len(sg) / sizeof(struct scsi_lun)); - } - - /* update the report luns data - based on fetched luns */ - sg = scsi_sglist(cmnd); - base_rl_data = (struct scsi_lun *)rl->lun; - base_count = (sg_dma_len(sg) / sizeof(struct scsi_lun)) - 1; - for (i = 0, j = 0; i < MAX_LUN_MASK_CFG; i++) { - if (lun_list[i].state == BFA_IOIM_LUN_MASK_FETCHED) { - base_rl_data[j] = lun_list[i].lun; - lun_list[i].state = BFA_IOIM_LUN_MASK_ACTIVE; - j++; - lun_fetched_cnt++; - } - - if (j > base_count) { - j = 0; - sg = sg_next(sg); - base_rl_data = (struct scsi_lun *) - phys_to_virt(sg_dma_address(sg)); - base_count = sg_dma_len(sg) / sizeof(struct scsi_lun); - } - } - - bfa_trc(ioim->bfa, lun_fetched_cnt); - return lun_fetched_cnt; -} - -static bfa_boolean_t -bfa_ioim_lm_proc_inq_data(struct bfa_ioim_s *ioim) -{ - struct scsi_inquiry_data_s *inq; - struct scatterlist *sg = scsi_sglist((struct scsi_cmnd *)ioim->dio); - - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; - inq = (struct scsi_inquiry_data_s *)phys_to_virt(sg_dma_address(sg)); - - bfa_trc(ioim->bfa, inq->device_type); - inq->peripheral_qual = SCSI_INQ_PQ_NOT_CON; - return 0; -} - -static bfa_boolean_t -bfa_ioim_lm_proc_rpl_data(struct bfa_ioim_s *ioim) -{ - struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio; - struct scatterlist *sg = scsi_sglist(cmnd); - struct bfi_ioim_rsp_s *m; - struct scsi_report_luns_data_s *rl = NULL; - int lun_count = 0, lun_fetched_cnt = 0; - u32 residue, pgdlen = 0; - - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; - if (bfa_get_lun_mask_status(ioim->bfa) != BFA_LUNMASK_ENABLED) - return BFA_TRUE; - - m = (struct bfi_ioim_rsp_s *) &ioim->iosp->comp_rspmsg; - if (m->scsi_status == SCSI_STATUS_CHECK_CONDITION) - return BFA_TRUE; - - pgdlen = sg_dma_len(sg); - bfa_trc(ioim->bfa, pgdlen); - rl = (struct scsi_report_luns_data_s *)phys_to_virt(sg_dma_address(sg)); - lun_count = cpu_to_be32(rl->lun_list_length) / sizeof(struct scsi_lun); - lun_fetched_cnt = bfa_ioim_lm_update_lun_sg(ioim, &pgdlen, rl); - - if (lun_count == lun_fetched_cnt) - return BFA_TRUE; - - bfa_trc(ioim->bfa, lun_count); - bfa_trc(ioim->bfa, lun_fetched_cnt); - bfa_trc(ioim->bfa, be32_to_cpu(rl->lun_list_length)); - - if (be32_to_cpu(rl->lun_list_length) <= pgdlen) - rl->lun_list_length = be32_to_cpu(lun_fetched_cnt) * - sizeof(struct scsi_lun); - else - bfa_stats(ioim->itnim, lm_small_buf_addresidue); - - bfa_trc(ioim->bfa, be32_to_cpu(rl->lun_list_length)); - bfa_trc(ioim->bfa, be32_to_cpu(m->residue)); - - residue = be32_to_cpu(m->residue); - residue += (lun_count - lun_fetched_cnt) * sizeof(struct scsi_lun); - bfa_stats(ioim->itnim, lm_wire_residue_changed); - m->residue = be32_to_cpu(residue); - bfa_trc(ioim->bfa, ioim->nsges); - return BFA_FALSE; -} - static void __bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete) { @@ -2454,83 +2167,6 @@ __bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete) m->scsi_status, sns_len, snsinfo, residue); } -static void -__bfa_cb_ioim_lm_lun_not_sup(void *cbarg, bfa_boolean_t complete) -{ - struct bfa_ioim_s *ioim = cbarg; - int sns_len = 0xD; - u32 residue = scsi_bufflen((struct scsi_cmnd *)ioim->dio); - struct scsi_sense_s *snsinfo; - - if (!complete) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); - return; - } - - snsinfo = (struct scsi_sense_s *)BFA_SNSINFO_FROM_TAG( - ioim->fcpim->fcp, ioim->iotag); - snsinfo->rsp_code = SCSI_SENSE_CUR_ERR; - snsinfo->add_sense_length = 0xa; - snsinfo->asc = SCSI_ASC_LUN_NOT_SUPPORTED; - snsinfo->sense_key = ILLEGAL_REQUEST; - bfa_trc(ioim->bfa, residue); - bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_OK, - SCSI_STATUS_CHECK_CONDITION, sns_len, - (u8 *)snsinfo, residue); -} - -static void -__bfa_cb_ioim_lm_rpl_dc(void *cbarg, bfa_boolean_t complete) -{ - struct bfa_ioim_s *ioim = cbarg; - int sns_len = 0xD; - u32 residue = scsi_bufflen((struct scsi_cmnd *)ioim->dio); - struct scsi_sense_s *snsinfo; - - if (!complete) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); - return; - } - - snsinfo = (struct scsi_sense_s *)BFA_SNSINFO_FROM_TAG(ioim->fcpim->fcp, - ioim->iotag); - snsinfo->rsp_code = SCSI_SENSE_CUR_ERR; - snsinfo->sense_key = SCSI_MP_IEC_UNIT_ATTN; - snsinfo->asc = SCSI_ASC_TOCC; - snsinfo->add_sense_length = 0x6; - snsinfo->ascq = SCSI_ASCQ_RL_DATA_CHANGED; - bfa_trc(ioim->bfa, residue); - bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_OK, - SCSI_STATUS_CHECK_CONDITION, sns_len, - (u8 *)snsinfo, residue); -} - -static void -__bfa_cb_ioim_lm_lun_not_rdy(void *cbarg, bfa_boolean_t complete) -{ - struct bfa_ioim_s *ioim = cbarg; - int sns_len = 0xD; - u32 residue = scsi_bufflen((struct scsi_cmnd *)ioim->dio); - struct scsi_sense_s *snsinfo; - - if (!complete) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); - return; - } - - snsinfo = (struct scsi_sense_s *)BFA_SNSINFO_FROM_TAG( - ioim->fcpim->fcp, ioim->iotag); - snsinfo->rsp_code = SCSI_SENSE_CUR_ERR; - snsinfo->add_sense_length = 0xa; - snsinfo->sense_key = NOT_READY; - snsinfo->asc = SCSI_ASC_LUN_NOT_READY; - snsinfo->ascq = SCSI_ASCQ_MAN_INTR_REQ; - bfa_trc(ioim->bfa, residue); - bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_OK, - SCSI_STATUS_CHECK_CONDITION, sns_len, - (u8 *)snsinfo, residue); -} - void bfa_fcpim_lunmask_rp_update(struct bfa_s *bfa, wwn_t lp_wwn, wwn_t rp_wwn, u16 rp_tag, u8 lp_tag) @@ -2759,7 +2395,6 @@ __bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete) return; } - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_ABORTED, 0, 0, NULL, 0); } @@ -2775,7 +2410,6 @@ __bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete) return; } - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_PATHTOV, 0, 0, NULL, 0); } @@ -2790,7 +2424,6 @@ __bfa_cb_ioim_abort(void *cbarg, bfa_boolean_t complete) return; } - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; bfa_cb_ioim_abort(ioim->bfa->bfad, ioim->dio); } @@ -3134,7 +2767,6 @@ bfa_ioim_attach(struct bfa_fcpim_s *fcpim) ioim->bfa = fcpim->bfa; ioim->fcpim = fcpim; ioim->iosp = iosp; - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; INIT_LIST_HEAD(&ioim->sgpg_q); bfa_reqq_winit(&ioim->iosp->reqq_wait, bfa_ioim_qresume, ioim); @@ -3172,7 +2804,6 @@ bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) evt = BFA_IOIM_SM_DONE; else evt = BFA_IOIM_SM_COMP; - ioim->proc_rsp_data(ioim); break; case BFI_IOIM_STS_TIMEDOUT: @@ -3208,7 +2839,6 @@ bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) if (rsp->abort_tag != ioim->abort_tag) { bfa_trc(ioim->bfa, rsp->abort_tag); bfa_trc(ioim->bfa, ioim->abort_tag); - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; return; } @@ -3227,7 +2857,6 @@ bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) WARN_ON(1); } - ioim->proc_rsp_data = bfa_ioim_lm_proc_rsp_data_dummy; bfa_sm_send_event(ioim, evt); } @@ -3246,15 +2875,7 @@ bfa_ioim_good_comp_isr(struct bfa_s *bfa, struct bfi_msg_s *m) bfa_ioim_cb_profile_comp(fcpim, ioim); - if (bfa_get_lun_mask_status(bfa) != BFA_LUNMASK_ENABLED) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD); - return; - } - - if (ioim->proc_rsp_data(ioim) == BFA_TRUE) - bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD); - else - bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP); + bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD); } /* @@ -3366,35 +2987,6 @@ bfa_ioim_free(struct bfa_ioim_s *ioim) void bfa_ioim_start(struct bfa_ioim_s *ioim) { - struct scsi_cmnd *cmnd = (struct scsi_cmnd *)ioim->dio; - struct bfa_lps_s *lps; - enum bfa_ioim_lm_status status; - struct scsi_lun scsilun; - - if (bfa_get_lun_mask_status(ioim->bfa) == BFA_LUNMASK_ENABLED) { - lps = BFA_IOIM_TO_LPS(ioim); - int_to_scsilun(cmnd->device->lun, &scsilun); - status = bfa_ioim_lm_check(ioim, lps, - ioim->itnim->rport, scsilun); - if (status == BFA_IOIM_LM_LUN_NOT_RDY) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_LM_LUN_NOT_RDY); - bfa_stats(ioim->itnim, lm_lun_not_rdy); - return; - } - - if (status == BFA_IOIM_LM_LUN_NOT_SUP) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_LM_LUN_NOT_SUP); - bfa_stats(ioim->itnim, lm_lun_not_sup); - return; - } - - if (status == BFA_IOIM_LM_RPL_DATA_CHANGED) { - bfa_sm_send_event(ioim, BFA_IOIM_SM_LM_RPL_DC); - bfa_stats(ioim->itnim, lm_rpl_data_changed); - return; - } - } - bfa_ioim_cb_profile_start(ioim->fcpim, ioim); /* diff --git a/drivers/scsi/bfa/bfa_fcpim.h b/drivers/scsi/bfa/bfa_fcpim.h index 1080bcb..36f26da 100644 --- a/drivers/scsi/bfa/bfa_fcpim.h +++ b/drivers/scsi/bfa/bfa_fcpim.h @@ -110,7 +110,6 @@ struct bfad_ioim_s; struct bfad_tskim_s; typedef void (*bfa_fcpim_profile_t) (struct bfa_ioim_s *ioim); -typedef bfa_boolean_t (*bfa_ioim_lm_proc_rsp_data_t) (struct bfa_ioim_s *ioim); struct bfa_fcpim_s { struct bfa_s *bfa; @@ -124,7 +123,6 @@ struct bfa_fcpim_s { u32 path_tov; u16 q_depth; u8 reqq; /* Request queue to be used */ - u8 lun_masking_pending; struct list_head itnim_q; /* queue of active itnim */ struct list_head ioim_resfree_q; /* IOs waiting for f/w */ struct list_head ioim_comp_q; /* IO global comp Q */ @@ -181,7 +179,6 @@ struct bfa_ioim_s { u8 reqq; /* Request queue for I/O */ u8 mode; /* IO is passthrough or not */ u64 start_time; /* IO's Profile start val */ - bfa_ioim_lm_proc_rsp_data_t proc_rsp_data; /* RSP data adjust */ }; struct bfa_ioim_sp_s { @@ -261,10 +258,6 @@ struct bfa_itnim_s { (__ioim)->iotag |= k << BFA_IOIM_RETRY_TAG_OFFSET; \ } while (0) -#define BFA_IOIM_TO_LPS(__ioim) \ - BFA_LPS_FROM_TAG(BFA_LPS_MOD(__ioim->bfa), \ - __ioim->itnim->rport->rport_info.lp_tag) - static inline bfa_boolean_t bfa_ioim_maxretry_reached(struct bfa_ioim_s *ioim) { diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h index 95adb86..b52cbb6 100644 --- a/drivers/scsi/bfa/bfa_svc.h +++ b/drivers/scsi/bfa/bfa_svc.h @@ -582,11 +582,6 @@ void bfa_cb_rport_qos_scn_prio(void *rport, #define BFA_LP_TAG_INVALID 0xff void bfa_rport_set_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp); void bfa_rport_unset_lunmask(struct bfa_s *bfa, struct bfa_rport_s *rp); -bfa_boolean_t bfa_rport_lunmask_active(struct bfa_rport_s *rp); -wwn_t bfa_rport_get_pwwn(struct bfa_s *bfa, struct bfa_rport_s *rp); -struct bfa_rport_s *bfa_rport_get_by_wwn(struct bfa_s *bfa, u16 vf_id, - wwn_t *lpwwn, wwn_t rpwwn); -void *bfa_cb_get_rp_by_wwn(void *arg, u16 vf_id, wwn_t *lpwwn, wwn_t rpwwn); /* * bfa fcxp API functions -- cgit v0.10.2 From 5b7db7af522d9f281ff8bf540d2b5cbea2206b27 Mon Sep 17 00:00:00 2001 From: Krishna Gudipati Date: Tue, 20 Dec 2011 18:58:32 -0800 Subject: [SCSI] bfa: Implement LUN Masking feature using the SCSI Slave Callouts. This patch re-implements LUN Masking feature using SCSI Slave Callouts. With the new design in the slave_alloc entry point; for each new LUN discovered we check with our internal LUN Masking config whether to expose or to mask this particular LUN. We return -ENXIO (No such device or address) from slave_alloc for the LUNs we don't want to be exposed. We also notify the SCSI mid-layer to do a sequential LUN scan rather than REPORT_LUNS based scan if LUN masking is enabled on our HBA port, since a -ENXIO from any LUN in REPORT_LUNS based scan translates to a scan abort. This patch also handles the dynamic lun masking config change from enable to disable or vice-versa by resetting sdev_bflags of LUN 0 appropriately. Signed-off-by: Krishna Gudipati Signed-off-by: James Bottomley diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index 66fb725..404fd10 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -674,6 +674,7 @@ bfad_vport_create(struct bfad_s *bfad, u16 vf_id, spin_lock_irqsave(&bfad->bfad_lock, flags); bfa_fcs_vport_start(&vport->fcs_vport); + list_add_tail(&vport->list_entry, &bfad->vport_list); spin_unlock_irqrestore(&bfad->bfad_lock, flags); return BFA_STATUS_OK; @@ -1404,6 +1405,7 @@ bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) bfad->ref_count = 0; bfad->pport.bfad = bfad; INIT_LIST_HEAD(&bfad->pbc_vport_list); + INIT_LIST_HEAD(&bfad->vport_list); /* Setup the debugfs node for this bfad */ if (bfa_debugfs_enable) diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c index 9d95844..1938fe0 100644 --- a/drivers/scsi/bfa/bfad_attr.c +++ b/drivers/scsi/bfa/bfad_attr.c @@ -491,7 +491,7 @@ bfad_im_vport_delete(struct fc_vport *fc_vport) free_scsi_host: bfad_scsi_host_free(bfad, im_port); - + list_del(&vport->list_entry); kfree(vport); return 0; diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c index 06fc00c..530de2b 100644 --- a/drivers/scsi/bfa/bfad_bsg.c +++ b/drivers/scsi/bfa/bfad_bsg.c @@ -2394,6 +2394,21 @@ out: return 0; } +/* Function to reset the LUN SCAN mode */ +static void +bfad_iocmd_lunmask_reset_lunscan_mode(struct bfad_s *bfad, int lunmask_cfg) +{ + struct bfad_im_port_s *pport_im = bfad->pport.im_port; + struct bfad_vport_s *vport = NULL; + + /* Set the scsi device LUN SCAN flags for base port */ + bfad_reset_sdev_bflags(pport_im, lunmask_cfg); + + /* Set the scsi device LUN SCAN flags for the vports */ + list_for_each_entry(vport, &bfad->vport_list, list_entry) + bfad_reset_sdev_bflags(vport->drv_port.im_port, lunmask_cfg); +} + int bfad_iocmd_lunmask(struct bfad_s *bfad, void *pcmd, unsigned int v_cmd) { @@ -2401,11 +2416,17 @@ bfad_iocmd_lunmask(struct bfad_s *bfad, void *pcmd, unsigned int v_cmd) unsigned long flags; spin_lock_irqsave(&bfad->bfad_lock, flags); - if (v_cmd == IOCMD_FCPIM_LUNMASK_ENABLE) + if (v_cmd == IOCMD_FCPIM_LUNMASK_ENABLE) { iocmd->status = bfa_fcpim_lunmask_update(&bfad->bfa, BFA_TRUE); - else if (v_cmd == IOCMD_FCPIM_LUNMASK_DISABLE) + /* Set the LUN Scanning mode to be Sequential scan */ + if (iocmd->status == BFA_STATUS_OK) + bfad_iocmd_lunmask_reset_lunscan_mode(bfad, BFA_TRUE); + } else if (v_cmd == IOCMD_FCPIM_LUNMASK_DISABLE) { iocmd->status = bfa_fcpim_lunmask_update(&bfad->bfa, BFA_FALSE); - else if (v_cmd == IOCMD_FCPIM_LUNMASK_CLEAR) + /* Set the LUN Scanning mode to default REPORT_LUNS scan */ + if (iocmd->status == BFA_STATUS_OK) + bfad_iocmd_lunmask_reset_lunscan_mode(bfad, BFA_FALSE); + } else if (v_cmd == IOCMD_FCPIM_LUNMASK_CLEAR) iocmd->status = bfa_fcpim_lunmask_clear(&bfad->bfa); spin_unlock_irqrestore(&bfad->bfad_lock, flags); return 0; diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h index 5e19a5f..dc5b9d9 100644 --- a/drivers/scsi/bfa/bfad_drv.h +++ b/drivers/scsi/bfa/bfad_drv.h @@ -43,6 +43,7 @@ #include #include #include +#include #include "bfa_modules.h" #include "bfa_fcs.h" @@ -227,6 +228,7 @@ struct bfad_s { struct list_head active_aen_q; struct bfa_aen_entry_s aen_list[BFA_AEN_MAX_ENTRY]; spinlock_t bfad_aen_spinlock; + struct list_head vport_list; }; /* BFAD state machine events */ diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index e5db649..3153923 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -918,16 +918,70 @@ bfad_get_itnim(struct bfad_im_port_s *im_port, int id) } /* + * Function is invoked from the SCSI Host Template slave_alloc() entry point. + * Has the logic to query the LUN Mask database to check if this LUN needs to + * be made visible to the SCSI mid-layer or not. + * + * Returns BFA_STATUS_OK if this LUN needs to be added to the OS stack. + * Returns -ENXIO to notify SCSI mid-layer to not add this LUN to the OS stack. + */ +static int +bfad_im_check_if_make_lun_visible(struct scsi_device *sdev, + struct fc_rport *rport) +{ + struct bfad_itnim_data_s *itnim_data = + (struct bfad_itnim_data_s *) rport->dd_data; + struct bfa_s *bfa = itnim_data->itnim->bfa_itnim->bfa; + struct bfa_rport_s *bfa_rport = itnim_data->itnim->bfa_itnim->rport; + struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(bfa); + int i = 0, ret = -ENXIO; + + for (i = 0; i < MAX_LUN_MASK_CFG; i++) { + if (lun_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE && + scsilun_to_int(&lun_list[i].lun) == sdev->lun && + lun_list[i].rp_tag == bfa_rport->rport_tag && + lun_list[i].lp_tag == (u8)bfa_rport->rport_info.lp_tag) { + ret = BFA_STATUS_OK; + break; + } + } + return ret; +} + +/* * Scsi_Host template entry slave_alloc */ static int bfad_im_slave_alloc(struct scsi_device *sdev) { struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + struct bfad_itnim_data_s *itnim_data = + (struct bfad_itnim_data_s *) rport->dd_data; + struct bfa_s *bfa = itnim_data->itnim->bfa_itnim->bfa; if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; + if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_ENABLED) { + /* + * We should not mask LUN 0 - since this will translate + * to no LUN / TARGET for SCSI ml resulting no scan. + */ + if (sdev->lun == 0) { + sdev->sdev_bflags |= BLIST_NOREPORTLUN | + BLIST_SPARSELUN; + goto done; + } + + /* + * Query LUN Mask configuration - to expose this LUN + * to the SCSI mid-layer or to mask it. + */ + if (bfad_im_check_if_make_lun_visible(sdev, rport) != + BFA_STATUS_OK) + return -ENXIO; + } +done: sdev->hostdata = rport->dd_data; return 0; @@ -1037,6 +1091,8 @@ bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim) && (fc_rport->scsi_target_id < MAX_FCP_TARGET)) itnim->scsi_tgt_id = fc_rport->scsi_target_id; + itnim->channel = fc_rport->channel; + return; } diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h index 004b6cf..0814367 100644 --- a/drivers/scsi/bfa/bfad_im.h +++ b/drivers/scsi/bfa/bfad_im.h @@ -91,6 +91,7 @@ struct bfad_itnim_s { struct fc_rport *fc_rport; struct bfa_itnim_s *bfa_itnim; u16 scsi_tgt_id; + u16 channel; u16 queue_work; unsigned long last_ramp_up_time; unsigned long last_queue_full_time; @@ -166,4 +167,30 @@ irqreturn_t bfad_intx(int irq, void *dev_id); int bfad_im_bsg_request(struct fc_bsg_job *job); int bfad_im_bsg_timeout(struct fc_bsg_job *job); +/* + * Macro to set the SCSI device sdev_bflags - sdev_bflags are used by the + * SCSI mid-layer to choose LUN Scanning mode REPORT_LUNS vs. Sequential Scan + * + * Internally iterate's over all the ITNIM's part of the im_port & set's the + * sdev_bflags for the scsi_device associated with LUN #0. + */ +#define bfad_reset_sdev_bflags(__im_port, __lunmask_cfg) do { \ + struct scsi_device *__sdev = NULL; \ + struct bfad_itnim_s *__itnim = NULL; \ + u32 scan_flags = BLIST_NOREPORTLUN | BLIST_SPARSELUN; \ + list_for_each_entry(__itnim, &((__im_port)->itnim_mapped_list), \ + list_entry) { \ + __sdev = scsi_device_lookup((__im_port)->shost, \ + __itnim->channel, \ + __itnim->scsi_tgt_id, 0); \ + if (__sdev) { \ + if ((__lunmask_cfg) == BFA_TRUE) \ + __sdev->sdev_bflags |= scan_flags; \ + else \ + __sdev->sdev_bflags &= ~scan_flags; \ + scsi_device_put(__sdev); \ + } \ + } \ +} while (0) + #endif -- cgit v0.10.2 From 410f02d813212eef1dedfcfd43460dd11a0ff707 Mon Sep 17 00:00:00 2001 From: "Moger, Babu" Date: Wed, 21 Dec 2011 18:01:37 -0500 Subject: [SCSI] scsi_dh_alua: Retry the check-condition in case Mode Parameters Changed This patch adds a check-condition in scsi_dh_alua handler for a retry. Sometimes, I have seen attach failing due to this check-condition with following error messages on NetApp E series storage. Dec 7 15:31:01 nilgiris kernel: [102979.696673] scsi 3:0:2:9: alua: port group 00 rel port 01 Dec 7 15:31:01 nilgiris kernel: [102979.697082] scsi 3:0:2:9: alua: rtpg failed with 8000002 Dec 7 15:31:01 nilgiris kernel: [102979.697086] scsi 3:0:2:9: alua: rtpg sense code 06/2a/01 Dec 7 15:31:01 nilgiris kernel: [102979.697088] scsi 3:0:2:9: alua: not attached Signed-off-by: Babu Moger Signed-off-by: James Bottomley diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 4ef0212..04c5cea 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -466,6 +466,11 @@ static int alua_check_sense(struct scsi_device *sdev, * Power On, Reset, or Bus Device Reset, just retry. */ return ADD_TO_MLQUEUE; + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x01) + /* + * Mode Parameters Changed + */ + return ADD_TO_MLQUEUE; if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) /* * ALUA state changed -- cgit v0.10.2 From e51e07e0ac7e3ff847d640f41b7527db04d4a4e7 Mon Sep 17 00:00:00 2001 From: Tkhai Kirill Date: Tue, 10 Jan 2012 13:17:03 +0000 Subject: sparc32: forced setting of mode of sun4m per-cpu timers SUN4M per-cpu timers have two modes of work. These are timer mode and counter mode. Kernel doesn't write anything to the register, which is connected with mode choice. So, the mode is chosen by bootloader. This patch forces to use timer mode from the kernel and to be independent of bootloader. I had this problem with OpenBIOS. Timers don't tick and kernel fails on QEMU, when it's compiled with SMP support. The patch fixes problem. Signed-off-by: Tkhai Kirill Acked-by: Sam Ravnborg Signed-off-by: David S. Miller diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 422c16d..e611651 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -399,6 +399,9 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) timers_global = (void __iomem *) (unsigned long) addr[num_cpu_timers]; + /* Every per-cpu timer works in timer mode */ + sbus_writel(0x00000000, &timers_global->timer_config); + sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); master_l10_counter = &timers_global->l10_count; -- cgit v0.10.2 From db804f23a72bada58f083dfad6a65d019ddb3bd4 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 10 Jan 2012 16:41:01 +0800 Subject: Btrfs: add pinned extents to on-disk free space cache correctly I got this while running xfstests: [24256.836098] block group 317849600 has an wrong amount of free space [24256.836100] btrfs: failed to load free space cache for block group 317849600 We should clamp the extent returned by find_first_extent_bit(), so the start of the extent won't smaller than the start of the block group. Signed-off-by: Li Zefan diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index ec23d43..01840ef 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -838,7 +838,7 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, struct io_ctl io_ctl; struct list_head bitmap_list; struct btrfs_key key; - u64 start, end, len; + u64 start, extent_start, extent_end, len; int entries = 0; int bitmaps = 0; int ret; @@ -857,25 +857,12 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, struct btrfs_free_cluster, block_group_list); - /* - * We shouldn't have switched the pinned extents yet so this is the - * right one - */ - unpin = root->fs_info->pinned_extents; - /* Lock all pages first so we can lock the extent safely. */ io_ctl_prepare_pages(&io_ctl, inode, 0); lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1, 0, &cached_state, GFP_NOFS); - /* - * When searching for pinned extents, we need to start at our start - * offset. - */ - if (block_group) - start = block_group->key.objectid; - node = rb_first(&ctl->free_space_offset); if (!node && cluster) { node = rb_first(&cluster->root); @@ -918,9 +905,20 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, * We want to add any pinned extents to our free space cache * so we don't leak the space */ + + /* + * We shouldn't have switched the pinned extents yet so this is the + * right one + */ + unpin = root->fs_info->pinned_extents; + + if (block_group) + start = block_group->key.objectid; + while (block_group && (start < block_group->key.objectid + block_group->key.offset)) { - ret = find_first_extent_bit(unpin, start, &start, &end, + ret = find_first_extent_bit(unpin, start, + &extent_start, &extent_end, EXTENT_DIRTY); if (ret) { ret = 0; @@ -928,20 +926,21 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, } /* This pinned extent is out of our range */ - if (start >= block_group->key.objectid + + if (extent_start >= block_group->key.objectid + block_group->key.offset) break; - len = block_group->key.objectid + - block_group->key.offset - start; - len = min(len, end + 1 - start); + extent_start = max(extent_start, start); + extent_end = min(block_group->key.objectid + + block_group->key.offset, extent_end + 1); + len = extent_end - extent_start; entries++; - ret = io_ctl_add_entry(&io_ctl, start, len, NULL); + ret = io_ctl_add_entry(&io_ctl, extent_start, len, NULL); if (ret) goto out_nospc; - start = end + 1; + start = extent_end; } /* Write out the bitmaps */ -- cgit v0.10.2 From a1ee5a45818acc7f9c13e560827cf3e8735ac919 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 9 Jan 2012 14:27:42 +0800 Subject: Btrfs: avoid possible NULL deref in io_ctl_drop_pages() If we run into some failure path in io_ctl_prepare_pages(), io_ctl->pages[] array may have some NULL pointers. Signed-off-by: Li Zefan diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 01840ef..4e55af3 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -319,9 +319,11 @@ static void io_ctl_drop_pages(struct io_ctl *io_ctl) io_ctl_unmap_page(io_ctl); for (i = 0; i < io_ctl->num_pages; i++) { - ClearPageChecked(io_ctl->pages[i]); - unlock_page(io_ctl->pages[i]); - page_cache_release(io_ctl->pages[i]); + if (io_ctl->pages[i]) { + ClearPageChecked(io_ctl->pages[i]); + unlock_page(io_ctl->pages[i]); + page_cache_release(io_ctl->pages[i]); + } } } -- cgit v0.10.2 From 706efc6630c2722602541a6a2fc5900a4e38456a Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 9 Jan 2012 14:36:28 +0800 Subject: Btrfs: check the return value of io_ctl_init() It can return -ENOMEM. Signed-off-by: Li Zefan diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 4e55af3..e4eb222 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -637,7 +637,10 @@ int __load_free_space_cache(struct btrfs_root *root, struct inode *inode, if (!num_entries) return 0; - io_ctl_init(&io_ctl, inode, root); + ret = io_ctl_init(&io_ctl, inode, root); + if (ret) + return ret; + ret = readahead_cache(inode); if (ret) goto out; @@ -851,7 +854,9 @@ int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, if (!i_size_read(inode)) return -1; - io_ctl_init(&io_ctl, inode, root); + ret = io_ctl_init(&io_ctl, inode, root); + if (ret) + return -1; /* Get the cluster for this block_group if it exists */ if (block_group && !list_empty(&block_group->cluster_list)) -- cgit v0.10.2 From f062abf089ff705e09bbaa6fa1e2fd7688a0f2ea Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 29 Dec 2011 13:36:45 +0800 Subject: Btrfs: remove BUG_ON()s in btrfs_ioctl_setflags() We can recover from errors and return -errno to user space. Signed-off-by: Li Zefan diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c04f02c..9619fb0 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -176,6 +176,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) struct btrfs_trans_handle *trans; unsigned int flags, oldflags; int ret; + u64 ip_oldflags; + unsigned int i_oldflags; if (btrfs_root_readonly(root)) return -EROFS; @@ -192,6 +194,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) mutex_lock(&inode->i_mutex); + ip_oldflags = ip->flags; + i_oldflags = inode->i_flags; + flags = btrfs_mask_flags(inode->i_mode, flags); oldflags = btrfs_flags_to_ioctl(ip->flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { @@ -250,18 +255,23 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) } trans = btrfs_join_transaction(root); - BUG_ON(IS_ERR(trans)); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out_drop; + } btrfs_update_iflags(inode); inode->i_ctime = CURRENT_TIME; ret = btrfs_update_inode(trans, root, inode); - BUG_ON(ret); btrfs_end_transaction(trans, root); + out_drop: + if (ret) { + ip->flags = ip_oldflags; + inode->i_flags = i_oldflags; + } mnt_drop_write(file->f_path.mnt); - - ret = 0; out_unlock: mutex_unlock(&inode->i_mutex); return ret; -- cgit v0.10.2 From 4da6f1a332f6c16b6594c7892f13c31459b9b1c8 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 29 Dec 2011 13:39:50 +0800 Subject: Btrfs: reserve metadata space in btrfs_ioctl_setflags() Check and reserve space for btrfs_update_inode(). Signed-off-by: Li Zefan diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9619fb0..fe8a60c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -254,7 +254,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); } - trans = btrfs_join_transaction(root); + trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out_drop; -- cgit v0.10.2 From 125ccb0ae6806dbec31abf4a85448971df3b4e39 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 8 Dec 2011 15:07:24 +0800 Subject: Btrfs: don't pass a trans handle unnecessarily in volumes.c Some functions never use the transaction handle passed to them. Signed-off-by: Li Zefan diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 8603ee4..5b53479 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7084,7 +7084,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) * space to fit our block group in. */ if (device->total_bytes > device->bytes_used + min_free) { - ret = find_free_dev_extent(NULL, device, min_free, + ret = find_free_dev_extent(device, min_free, &dev_offset, NULL); if (!ret) dev_nr++; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f4b839f..73f673c 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -829,7 +829,6 @@ out: /* * find_free_dev_extent - find free space in the specified device - * @trans: transaction handler * @device: the device which we search the free space in * @num_bytes: the size of the free space that we need * @start: store the start of the free space. @@ -848,8 +847,7 @@ out: * But if we don't find suitable free space, it is used to store the size of * the max free space. */ -int find_free_dev_extent(struct btrfs_trans_handle *trans, - struct btrfs_device *device, u64 num_bytes, +int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, u64 *start, u64 *len) { struct btrfs_key key; @@ -893,7 +891,7 @@ int find_free_dev_extent(struct btrfs_trans_handle *trans, key.offset = search_start; key.type = BTRFS_DEV_EXTENT_KEY; - ret = btrfs_search_slot(trans, root, &key, path, 0, 0); + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto out; if (ret > 0) { @@ -1469,8 +1467,7 @@ error_undo: /* * does all the dirty work required for changing file system's UUID. */ -static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans, - struct btrfs_root *root) +static int btrfs_prepare_sprout(struct btrfs_root *root) { struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices; struct btrfs_fs_devices *old_devices; @@ -1695,7 +1692,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) if (seeding_dev) { sb->s_flags &= ~MS_RDONLY; - ret = btrfs_prepare_sprout(trans, root); + ret = btrfs_prepare_sprout(root); BUG_ON(ret); } @@ -2323,8 +2320,7 @@ done: return ret; } -static int btrfs_add_system_chunk(struct btrfs_trans_handle *trans, - struct btrfs_root *root, +static int btrfs_add_system_chunk(struct btrfs_root *root, struct btrfs_key *key, struct btrfs_chunk *chunk, int item_size) { @@ -2496,7 +2492,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, if (total_avail == 0) continue; - ret = find_free_dev_extent(trans, device, + ret = find_free_dev_extent(device, max_stripe_size * dev_stripes, &dev_offset, &max_avail); if (ret && ret != -ENOSPC) @@ -2687,7 +2683,7 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans, BUG_ON(ret); if (map->type & BTRFS_BLOCK_GROUP_SYSTEM) { - ret = btrfs_add_system_chunk(trans, chunk_root, &key, chunk, + ret = btrfs_add_system_chunk(chunk_root, &key, chunk, item_size); BUG_ON(ret); } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 78f2d4d..c1701ec 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -230,7 +230,6 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size); int btrfs_init_new_device(struct btrfs_root *root, char *path); int btrfs_balance(struct btrfs_root *dev_root); int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); -int find_free_dev_extent(struct btrfs_trans_handle *trans, - struct btrfs_device *device, u64 num_bytes, +int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, u64 *start, u64 *max_avail); #endif -- cgit v0.10.2 From de11cc12df17337979e0929d2831887432f236ca Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 1 Dec 2011 12:55:47 +0800 Subject: Btrfs: don't pre-allocate btrfs bio We pre-allocate a btrfs bio with fixed size, and then may re-allocate memory if we find stripes are bigger than the fixed size. But this pre-allocation is not necessary. Also we don't have to calcuate the stripe number twice. Signed-off-by: Li Zefan diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 73f673c..540fdd2 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2897,26 +2897,13 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, u64 stripe_nr; u64 stripe_nr_orig; u64 stripe_nr_end; - int stripes_allocated = 8; - int stripes_required = 1; int stripe_index; int i; + int ret = 0; int num_stripes; int max_errors = 0; struct btrfs_bio *bbio = NULL; - if (bbio_ret && !(rw & (REQ_WRITE | REQ_DISCARD))) - stripes_allocated = 1; -again: - if (bbio_ret) { - bbio = kzalloc(btrfs_bio_size(stripes_allocated), - GFP_NOFS); - if (!bbio) - return -ENOMEM; - - atomic_set(&bbio->error, 0); - } - read_lock(&em_tree->lock); em = lookup_extent_mapping(em_tree, logical, *length); read_unlock(&em_tree->lock); @@ -2935,32 +2922,6 @@ again: if (mirror_num > map->num_stripes) mirror_num = 0; - /* if our btrfs_bio struct is too small, back off and try again */ - if (rw & REQ_WRITE) { - if (map->type & (BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_DUP)) { - stripes_required = map->num_stripes; - max_errors = 1; - } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) { - stripes_required = map->sub_stripes; - max_errors = 1; - } - } - if (rw & REQ_DISCARD) { - if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | - BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_DUP | - BTRFS_BLOCK_GROUP_RAID10)) { - stripes_required = map->num_stripes; - } - } - if (bbio_ret && (rw & (REQ_WRITE | REQ_DISCARD)) && - stripes_allocated < stripes_required) { - stripes_allocated = map->num_stripes; - free_extent_map(em); - kfree(bbio); - goto again; - } stripe_nr = offset; /* * stripe_nr counts the total number of stripes we have to stride @@ -3055,6 +3016,13 @@ again: } BUG_ON(stripe_index >= map->num_stripes); + bbio = kzalloc(btrfs_bio_size(num_stripes), GFP_NOFS); + if (!bbio) { + ret = -ENOMEM; + goto out; + } + atomic_set(&bbio->error, 0); + if (rw & REQ_DISCARD) { for (i = 0; i < num_stripes; i++) { bbio->stripes[i].physical = @@ -3151,15 +3119,22 @@ again: stripe_index++; } } - if (bbio_ret) { - *bbio_ret = bbio; - bbio->num_stripes = num_stripes; - bbio->max_errors = max_errors; - bbio->mirror_num = mirror_num; + + if (rw & REQ_WRITE) { + if (map->type & (BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_RAID10 | + BTRFS_BLOCK_GROUP_DUP)) { + max_errors = 1; + } } + + *bbio_ret = bbio; + bbio->num_stripes = num_stripes; + bbio->max_errors = max_errors; + bbio->mirror_num = mirror_num; out: free_extent_map(em); - return 0; + return ret; } int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, -- cgit v0.10.2 From ec9ef7a13be4dcce964c8503e8999087945e5b9e Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 1 Dec 2011 14:06:42 +0800 Subject: Btrfs: simplfy calculation of stripe length for discard operation For btrfs raid, while discarding a range of space, we'll need to know the start offset and length to discard for each device, and it's done in btrfs_map_block(). However the calculation is a bit complex for raid0 and raid10, so I reimplement it based on a fact that: dev1 dev2 dev3 (raid0) ----------------------------------- s0 s3 s6 s1 s4 s7 s2 s5 Each device has (total_stripes / nr_dev) stripes, or plus one. Signed-off-by: Li Zefan diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 540fdd2..563ef65 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3024,80 +3024,47 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, atomic_set(&bbio->error, 0); if (rw & REQ_DISCARD) { + int factor = 0; + int sub_stripes = 0; + u64 stripes_per_dev = 0; + u32 remaining_stripes = 0; + + if (map->type & + (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID10)) { + if (map->type & BTRFS_BLOCK_GROUP_RAID0) + sub_stripes = 1; + else + sub_stripes = map->sub_stripes; + + factor = map->num_stripes / sub_stripes; + stripes_per_dev = div_u64_rem(stripe_nr_end - + stripe_nr_orig, + factor, + &remaining_stripes); + } + for (i = 0; i < num_stripes; i++) { bbio->stripes[i].physical = map->stripes[stripe_index].physical + stripe_offset + stripe_nr * map->stripe_len; bbio->stripes[i].dev = map->stripes[stripe_index].dev; - if (map->type & BTRFS_BLOCK_GROUP_RAID0) { - u64 stripes; - u32 last_stripe = 0; - int j; - - div_u64_rem(stripe_nr_end - 1, - map->num_stripes, - &last_stripe); - - for (j = 0; j < map->num_stripes; j++) { - u32 test; - - div_u64_rem(stripe_nr_end - 1 - j, - map->num_stripes, &test); - if (test == stripe_index) - break; - } - stripes = stripe_nr_end - 1 - j; - do_div(stripes, map->num_stripes); - bbio->stripes[i].length = map->stripe_len * - (stripes - stripe_nr + 1); - - if (i == 0) { + if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | + BTRFS_BLOCK_GROUP_RAID10)) { + bbio->stripes[i].length = stripes_per_dev * + map->stripe_len; + if (i / sub_stripes < remaining_stripes) + bbio->stripes[i].length += + map->stripe_len; + if (i < sub_stripes) bbio->stripes[i].length -= stripe_offset; - stripe_offset = 0; - } - if (stripe_index == last_stripe) - bbio->stripes[i].length -= - stripe_end_offset; - } else if (map->type & BTRFS_BLOCK_GROUP_RAID10) { - u64 stripes; - int j; - int factor = map->num_stripes / - map->sub_stripes; - u32 last_stripe = 0; - - div_u64_rem(stripe_nr_end - 1, - factor, &last_stripe); - last_stripe *= map->sub_stripes; - - for (j = 0; j < factor; j++) { - u32 test; - - div_u64_rem(stripe_nr_end - 1 - j, - factor, &test); - - if (test == - stripe_index / map->sub_stripes) - break; - } - stripes = stripe_nr_end - 1 - j; - do_div(stripes, factor); - bbio->stripes[i].length = map->stripe_len * - (stripes - stripe_nr + 1); - - if (i < map->sub_stripes) { - bbio->stripes[i].length -= - stripe_offset; - if (i == map->sub_stripes - 1) - stripe_offset = 0; - } - if (stripe_index >= last_stripe && - stripe_index <= (last_stripe + - map->sub_stripes - 1)) { + if ((i / sub_stripes + 1) % + sub_stripes == remaining_stripes) bbio->stripes[i].length -= stripe_end_offset; - } + if (i == sub_stripes - 1) + stripe_offset = 0; } else bbio->stripes[i].length = *length; -- cgit v0.10.2 From 7fe1e641502616220437079258506196bc4d8cbf Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 29 Dec 2011 14:47:27 +0800 Subject: Btrfs: rewrite btrfs_trim_block_group() There are various bugs in block group trimming: - It may trim from offset smaller than user-specified offset. - It may trim beyond user-specified range. - It may leak free space for extents smaller than specified minlen. - It may truncate the last trimmed extent thus leak free space. - With mixed extents+bitmaps, some extents may not be trimmed. - With mixed extents+bitmaps, some bitmaps may not be trimmed (even none will be trimmed). Even for those trimmed, not all the free space in the bitmaps will be trimmed. I rewrite btrfs_trim_block_group() and break it into two functions. One is to trim extents only, and the other is to trim bitmaps only. Before patching: # fstrim -v /mnt/ /mnt/: 1496465408 bytes were trimmed After patching: # fstrim -v /mnt/ /mnt/: 2193768448 bytes were trimmed And this matches the total free space: # btrfs fi df /mnt Data: total=3.58GB, used=1.79GB System, DUP: total=8.00MB, used=4.00KB System: total=4.00MB, used=0.00 Metadata, DUP: total=205.12MB, used=97.14MB Metadata: total=8.00MB, used=0.00 Signed-off-by: Li Zefan diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e4eb222..b3cbb89 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2594,17 +2594,57 @@ void btrfs_init_free_cluster(struct btrfs_free_cluster *cluster) cluster->block_group = NULL; } -int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, - u64 *trimmed, u64 start, u64 end, u64 minlen) +static int do_trimming(struct btrfs_block_group_cache *block_group, + u64 *total_trimmed, u64 start, u64 bytes, + u64 reserved_start, u64 reserved_bytes) { - struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; - struct btrfs_free_space *entry = NULL; + struct btrfs_space_info *space_info = block_group->space_info; struct btrfs_fs_info *fs_info = block_group->fs_info; - u64 bytes = 0; - u64 actually_trimmed; - int ret = 0; + int ret; + int update = 0; + u64 trimmed = 0; - *trimmed = 0; + spin_lock(&space_info->lock); + spin_lock(&block_group->lock); + if (!block_group->ro) { + block_group->reserved += reserved_bytes; + space_info->bytes_reserved += reserved_bytes; + update = 1; + } + spin_unlock(&block_group->lock); + spin_unlock(&space_info->lock); + + ret = btrfs_error_discard_extent(fs_info->extent_root, + start, bytes, &trimmed); + if (!ret) + *total_trimmed += trimmed; + + btrfs_add_free_space(block_group, reserved_start, reserved_bytes); + + if (update) { + spin_lock(&space_info->lock); + spin_lock(&block_group->lock); + if (block_group->ro) + space_info->bytes_readonly += reserved_bytes; + block_group->reserved -= reserved_bytes; + space_info->bytes_reserved -= reserved_bytes; + spin_unlock(&space_info->lock); + spin_unlock(&block_group->lock); + } + + return ret; +} + +static int trim_no_bitmap(struct btrfs_block_group_cache *block_group, + u64 *total_trimmed, u64 start, u64 end, u64 minlen) +{ + struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; + struct btrfs_free_space *entry; + struct rb_node *node; + int ret = 0; + u64 extent_start; + u64 extent_bytes; + u64 bytes; while (start < end) { spin_lock(&ctl->tree_lock); @@ -2615,81 +2655,118 @@ int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, } entry = tree_search_offset(ctl, start, 0, 1); - if (!entry) - entry = tree_search_offset(ctl, - offset_to_bitmap(ctl, start), - 1, 1); - - if (!entry || entry->offset >= end) { + if (!entry) { spin_unlock(&ctl->tree_lock); break; } - if (entry->bitmap) { - ret = search_bitmap(ctl, entry, &start, &bytes); - if (!ret) { - if (start >= end) { - spin_unlock(&ctl->tree_lock); - break; - } - bytes = min(bytes, end - start); - bitmap_clear_bits(ctl, entry, start, bytes); - if (entry->bytes == 0) - free_bitmap(ctl, entry); - } else { - start = entry->offset + BITS_PER_BITMAP * - block_group->sectorsize; + /* skip bitmaps */ + while (entry->bitmap) { + node = rb_next(&entry->offset_index); + if (!node) { spin_unlock(&ctl->tree_lock); - ret = 0; - continue; + goto out; } - } else { - start = entry->offset; - bytes = min(entry->bytes, end - start); - unlink_free_space(ctl, entry); - kmem_cache_free(btrfs_free_space_cachep, entry); + entry = rb_entry(node, struct btrfs_free_space, + offset_index); + } + + if (entry->offset >= end) { + spin_unlock(&ctl->tree_lock); + break; + } + + extent_start = entry->offset; + extent_bytes = entry->bytes; + start = max(start, extent_start); + bytes = min(extent_start + extent_bytes, end) - start; + if (bytes < minlen) { + spin_unlock(&ctl->tree_lock); + goto next; } + unlink_free_space(ctl, entry); + kmem_cache_free(btrfs_free_space_cachep, entry); + spin_unlock(&ctl->tree_lock); - if (bytes >= minlen) { - struct btrfs_space_info *space_info; - int update = 0; - - space_info = block_group->space_info; - spin_lock(&space_info->lock); - spin_lock(&block_group->lock); - if (!block_group->ro) { - block_group->reserved += bytes; - space_info->bytes_reserved += bytes; - update = 1; - } - spin_unlock(&block_group->lock); - spin_unlock(&space_info->lock); - - ret = btrfs_error_discard_extent(fs_info->extent_root, - start, - bytes, - &actually_trimmed); - - btrfs_add_free_space(block_group, start, bytes); - if (update) { - spin_lock(&space_info->lock); - spin_lock(&block_group->lock); - if (block_group->ro) - space_info->bytes_readonly += bytes; - block_group->reserved -= bytes; - space_info->bytes_reserved -= bytes; - spin_unlock(&space_info->lock); - spin_unlock(&block_group->lock); - } + ret = do_trimming(block_group, total_trimmed, start, bytes, + extent_start, extent_bytes); + if (ret) + break; +next: + start += bytes; - if (ret) - break; - *trimmed += actually_trimmed; + if (fatal_signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cond_resched(); + } +out: + return ret; +} + +static int trim_bitmaps(struct btrfs_block_group_cache *block_group, + u64 *total_trimmed, u64 start, u64 end, u64 minlen) +{ + struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; + struct btrfs_free_space *entry; + int ret = 0; + int ret2; + u64 bytes; + u64 offset = offset_to_bitmap(ctl, start); + + while (offset < end) { + bool next_bitmap = false; + + spin_lock(&ctl->tree_lock); + + if (ctl->free_space < minlen) { + spin_unlock(&ctl->tree_lock); + break; + } + + entry = tree_search_offset(ctl, offset, 1, 0); + if (!entry) { + spin_unlock(&ctl->tree_lock); + next_bitmap = true; + goto next; + } + + bytes = minlen; + ret2 = search_bitmap(ctl, entry, &start, &bytes); + if (ret2 || start >= end) { + spin_unlock(&ctl->tree_lock); + next_bitmap = true; + goto next; + } + + bytes = min(bytes, end - start); + if (bytes < minlen) { + spin_unlock(&ctl->tree_lock); + goto next; + } + + bitmap_clear_bits(ctl, entry, start, bytes); + if (entry->bytes == 0) + free_bitmap(ctl, entry); + + spin_unlock(&ctl->tree_lock); + + ret = do_trimming(block_group, total_trimmed, start, bytes, + start, bytes); + if (ret) + break; +next: + if (next_bitmap) { + offset += BITS_PER_BITMAP * ctl->unit; + } else { + start += bytes; + if (start >= offset + BITS_PER_BITMAP * ctl->unit) + offset += BITS_PER_BITMAP * ctl->unit; } - start += bytes; - bytes = 0; if (fatal_signal_pending(current)) { ret = -ERESTARTSYS; @@ -2702,6 +2779,22 @@ int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, return ret; } +int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, + u64 *trimmed, u64 start, u64 end, u64 minlen) +{ + int ret; + + *trimmed = 0; + + ret = trim_no_bitmap(block_group, trimmed, start, end, minlen); + if (ret) + return ret; + + ret = trim_bitmaps(block_group, trimmed, start, end, minlen); + + return ret; +} + /* * Find the left-most item in the cache tree, and then return the * smallest inode number in the item. -- cgit v0.10.2 From c7c144db531fda414e532adac56e965ce332e2a5 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 7 Dec 2011 10:39:22 +0800 Subject: Btrfs: update global block_rsv when creating a new block group A bug was triggered while using seed device: # mkfs.btrfs /dev/loop1 # btrfstune -S 1 /dev/loop1 # mount -o /dev/loop1 /mnt # btrfs dev add /dev/loop2 /mnt btrfs: block rsv returned -28 ------------[ cut here ]------------ WARNING: at fs/btrfs/extent-tree.c:5969 btrfs_alloc_free_block+0x166/0x396 [btrfs]() ... Call Trace: ... [] btrfs_cow_block+0x101/0x147 [btrfs] [] btrfs_search_slot+0x1b8/0x55f [btrfs] [] btrfs_insert_empty_items+0x42/0x7f [btrfs] [] btrfs_insert_item+0x40/0x7e [btrfs] [] btrfs_make_block_group+0x243/0x2aa [btrfs] [] __btrfs_alloc_chunk+0x672/0x70e [btrfs] [] init_first_rw_device+0x77/0x13c [btrfs] [] btrfs_init_new_device+0x664/0x9fd [btrfs] [] btrfs_ioctl+0x694/0xdbe [btrfs] [] do_vfs_ioctl+0x496/0x4cc [] sys_ioctl+0x33/0x4f [] sysenter_do_call+0x12/0x38 ---[ end trace 906adac595facc7d ]--- Since seed device is readonly, there's no usable space in the filesystem. Afterwards we add a sprout device to it, and the kernel creates a METADATA block group and a SYSTEM block group where comes free space we can reserve, but we still get revervation failure because the global block_rsv hasn't been updated accordingly. Signed-off-by: Li Zefan diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 5b53479..bf30f67 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7446,6 +7446,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, ret = update_space_info(root->fs_info, cache->flags, size, bytes_used, &cache->space_info); BUG_ON(ret); + update_global_block_rsv(root->fs_info); spin_lock(&cache->space_info->lock); cache->space_info->bytes_readonly += cache->bytes_super; -- cgit v0.10.2 From b367e47fb3a70f5d24ebd6faf7d42436d485fb2d Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 7 Dec 2011 11:38:24 +0800 Subject: Btrfs: fix possible deadlock when opening a seed device The correct lock order is uuid_mutex -> volume_mutex -> chunk_mutex, but when we mount a filesystem which has backing seed devices, we have this lock chain: open_ctree() lock(chunk_mutex); read_chunk_tree(); read_one_dev(); open_seed_devices(); lock(uuid_mutex); and then we hit a lockdep splat. Signed-off-by: Li Zefan diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 3f9d555..858ab34 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2270,9 +2270,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, (unsigned long)btrfs_header_chunk_tree_uuid(chunk_root->node), BTRFS_UUID_SIZE); - mutex_lock(&fs_info->chunk_mutex); ret = btrfs_read_chunk_tree(chunk_root); - mutex_unlock(&fs_info->chunk_mutex); if (ret) { printk(KERN_WARNING "btrfs: failed to read chunk tree on %s\n", sb->s_id); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 563ef65..fbb493b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3506,7 +3506,7 @@ static int open_seed_devices(struct btrfs_root *root, u8 *fsid) struct btrfs_fs_devices *fs_devices; int ret; - mutex_lock(&uuid_mutex); + BUG_ON(!mutex_is_locked(&uuid_mutex)); fs_devices = root->fs_info->fs_devices->seed; while (fs_devices) { @@ -3544,7 +3544,6 @@ static int open_seed_devices(struct btrfs_root *root, u8 *fsid) fs_devices->seed = root->fs_info->fs_devices->seed; root->fs_info->fs_devices->seed = fs_devices; out: - mutex_unlock(&uuid_mutex); return ret; } @@ -3687,6 +3686,9 @@ int btrfs_read_chunk_tree(struct btrfs_root *root) if (!path) return -ENOMEM; + mutex_lock(&uuid_mutex); + lock_chunks(root); + /* first we search for all of the device items, and then we * read in all of the chunk items. This way we can create chunk * mappings that reference all of the devices that are afound @@ -3737,6 +3739,9 @@ again: } ret = 0; error: + unlock_chunks(root); + mutex_unlock(&uuid_mutex); + btrfs_free_path(path); return ret; } -- cgit v0.10.2 From d761f0c521868e59cd0bc59159cbdb4686fe210d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 10 Jan 2012 12:15:58 +0100 Subject: microblaze: Add topology init Init topology subsystem by cpu registration. Microblaze Linux kernel is fauling by "Oops: kernel access of bad area, sig: 11" because cpu is not initialized. Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 604cd9d..d4fc1a9 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -226,5 +227,23 @@ static int __init setup_bus_notifier(void) return 0; } - arch_initcall(setup_bus_notifier); + +static DEFINE_PER_CPU(struct cpu, cpu_devices); + +static int __init topology_init(void) +{ + int i, ret; + + for_each_present_cpu(i) { + struct cpu *c = &per_cpu(cpu_devices, i); + + ret = register_cpu(c, i); + if (ret) + printk(KERN_WARNING "topology_init: register_cpu %d " + "failed (%d)\n", i, ret); + } + + return 0; +} +subsys_initcall(topology_init); -- cgit v0.10.2 From 12183a20a8baf009bf570ab3db45a27fd6b1fd03 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Dec 2011 23:01:20 +0100 Subject: mtd: nand: fix typo in comment Funny one :) "Heck" fits somehow, too, but I am sure it was meant to be "Check". Signed-off-by: Wolfram Sang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 35b4565..8a393f9 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2588,7 +2588,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, instr->state = MTD_ERASING; while (len) { - /* Heck if we have a bad block, we do not erase bad blocks! */ + /* Check if we have a bad block, we do not erase bad blocks! */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", -- cgit v0.10.2 From 9398d1ce09b9009996f7d2468e1d3c785fa6feda Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 4 Jan 2012 11:18:46 +0800 Subject: mtd: gpmi-nand bugfix: reset the BCH module when it is not MX23 In MX28, if we do not reset the BCH module. The BCH module may becomes unstable when the board reboots for several thousands times. This bug has been catched in customer's production. The patch adds some comments (some from Wolfram Sang), and fixes it now. Also change gpmi_reset_block() to static. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse Cc: stable@kernel.org [3.1+] diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 2a56fc6..c56f8e0 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -69,17 +69,19 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) * [1] enable the module. * [2] reset the module. * - * In most of the cases, it's ok. But there is a hardware bug in the BCH block. + * In most of the cases, it's ok. + * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). * If you try to soft reset the BCH block, it becomes unusable until * the next hard reset. This case occurs in the NAND boot mode. When the board * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. * So If the driver tries to reset the BCH again, the BCH will not work anymore. - * You will see a DMA timeout in this case. + * You will see a DMA timeout in this case. The bug has been fixed + * in the following chips, such as MX28. * * To avoid this bug, just add a new parameter `just_enable` for * the mxs_reset_block(), and rewrite it here. */ -int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) +static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) { int ret; int timeout = 0x400; @@ -206,7 +208,15 @@ int bch_set_geometry(struct gpmi_nand_data *this) if (ret) goto err_out; - ret = gpmi_reset_block(r->bch_regs, true); + /* + * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this + * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. + * On the other hand, the MX28 needs the reset, because one case has been + * seen where the BCH produced ECC errors constantly after 10000 + * consecutive reboots. The latter case has not been seen on the MX23 yet, + * still we don't know if it could happen there as well. + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this)); if (ret) goto err_out; -- cgit v0.10.2 From bce41d601e58af12cee1398fe836e6b9a8fb5396 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 10 Jan 2012 15:32:29 +0200 Subject: jffs2: do not initialize variable unnecessarily Remove unnecessary initializer for a local variable. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index a01cdad..eafb8d3 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -335,7 +335,7 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl void *ebuf; uint32_t ofs; size_t retlen; - int ret = -EIO; + int ret; unsigned long *wordebuf; ret = mtd_point(c->mtd, jeb->offset, c->sector_size, &retlen, -- cgit v0.10.2 From 353b67d8ced4dc53281c88150ad295e24bc4b4c5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sat, 26 Nov 2011 00:35:39 +0100 Subject: jbd: Issue cache flush after checkpointing When we reach cleanup_journal_tail(), there is no guarantee that checkpointed buffers are on a stable storage - especially if buffers were written out by log_do_checkpoint(), they are likely to be only in disk's caches. Thus when we update journal superblock, effectively removing old transaction from journal, this write of superblock can get to stable storage before those checkpointed buffers which can result in filesystem corruption after a crash. A similar problem can happen if we replay the journal and wipe it before flushing disk's caches. Thus we must unconditionally issue a cache flush before we update journal superblock in these cases. The fix is slightly complicated by the fact that we have to get log tail before we issue cache flush but we can store it in the journal superblock only after the cache flush. Otherwise we risk races where new tail is written before appropriate cache flush is finished. I managed to reproduce the corruption using somewhat tweaked Chris Mason's barrier-test scheduler. Also this should fix occasional reports of 'Bit already freed' filesystem errors which are totally unreproducible but inspection of several fs images I've gathered over time points to a problem like this. CC: stable@kernel.org Signed-off-by: Jan Kara diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c index 5d1a00a..05f0754 100644 --- a/fs/jbd/checkpoint.c +++ b/fs/jbd/checkpoint.c @@ -453,8 +453,6 @@ out: * * Return <0 on error, 0 on success, 1 if there was nothing to clean up. * - * Called with the journal lock held. - * * This is the only part of the journaling code which really needs to be * aware of transaction aborts. Checkpointing involves writing to the * main filesystem area rather than to the journal, so it can proceed @@ -472,13 +470,14 @@ int cleanup_journal_tail(journal_t *journal) if (is_journal_aborted(journal)) return 1; - /* OK, work out the oldest transaction remaining in the log, and + /* + * OK, work out the oldest transaction remaining in the log, and * the log block it starts at. * * If the log is now empty, we need to work out which is the * next transaction ID we will write, and where it will - * start. */ - + * start. + */ spin_lock(&journal->j_state_lock); spin_lock(&journal->j_list_lock); transaction = journal->j_checkpoint_transactions; @@ -504,7 +503,25 @@ int cleanup_journal_tail(journal_t *journal) spin_unlock(&journal->j_state_lock); return 1; } + spin_unlock(&journal->j_state_lock); + + /* + * We need to make sure that any blocks that were recently written out + * --- perhaps by log_do_checkpoint() --- are flushed out before we + * drop the transactions from the journal. It's unlikely this will be + * necessary, especially with an appropriately sized journal, but we + * need this to guarantee correctness. Fortunately + * cleanup_journal_tail() doesn't get called all that often. + */ + if (journal->j_flags & JFS_BARRIER) + blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); + spin_lock(&journal->j_state_lock); + if (!tid_gt(first_tid, journal->j_tail_sequence)) { + spin_unlock(&journal->j_state_lock); + /* Someone else cleaned up journal so return 0 */ + return 0; + } /* OK, update the superblock to recover the freed space. * Physical blocks come first: have we wrapped beyond the end of * the log? */ diff --git a/fs/jbd/recovery.c b/fs/jbd/recovery.c index 5b43e96..008bf06 100644 --- a/fs/jbd/recovery.c +++ b/fs/jbd/recovery.c @@ -20,6 +20,7 @@ #include #include #include +#include #endif /* @@ -263,6 +264,9 @@ int journal_recover(journal_t *journal) err2 = sync_blockdev(journal->j_fs_dev); if (!err) err = err2; + /* Flush disk caches to get replayed data on the permanent storage */ + if (journal->j_flags & JFS_BARRIER) + blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); return err; } -- cgit v0.10.2 From 34b07840565004cfa444e165e88bf77a5cbcdb25 Mon Sep 17 00:00:00 2001 From: Djalal Harouni Date: Mon, 9 Jan 2012 15:58:37 +0100 Subject: ext2: protect inode changes in the SETVERSION and SETFLAGS ioctls Unlock mutex after i_flags and i_ctime updates in the EXT2_IOC_SETFLAGS ioctl. Use i_mutex in the EXT2_IOC_SETVERSION ioctl to protect i_ctime and i_generation updates and make the ioctl consistent since i_mutex is also used in other places to protect timestamps and inode changes. Cc: Andreas Dilger Cc: Jan Kara Signed-off-by: Djalal Harouni Signed-off-by: Jan Kara diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 1089f76..2de655f 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -77,10 +77,11 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) flags = flags & EXT2_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; ei->i_flags = flags; - mutex_unlock(&inode->i_mutex); ext2_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME_SEC; + mutex_unlock(&inode->i_mutex); + mark_inode_dirty(inode); setflags_out: mnt_drop_write_file(filp); @@ -88,20 +89,29 @@ setflags_out: } case EXT2_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *) arg); - case EXT2_IOC_SETVERSION: + case EXT2_IOC_SETVERSION: { + __u32 generation; + if (!inode_owner_or_capable(inode)) return -EPERM; ret = mnt_want_write_file(filp); if (ret) return ret; - if (get_user(inode->i_generation, (int __user *) arg)) { + if (get_user(generation, (int __user *) arg)) { ret = -EFAULT; - } else { - inode->i_ctime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); + goto setversion_out; } + + mutex_lock(&inode->i_mutex); + inode->i_ctime = CURRENT_TIME_SEC; + inode->i_generation = generation; + mutex_unlock(&inode->i_mutex); + + mark_inode_dirty(inode); +setversion_out: mnt_drop_write_file(filp); return ret; + } case EXT2_IOC_GETRSVSZ: if (test_opt(inode->i_sb, RESERVATION) && S_ISREG(inode->i_mode) -- cgit v0.10.2 From df3481399042200792822b6243e36a95a557b57e Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 11 Jan 2012 07:29:56 -0700 Subject: NVMe: Set number of queues correctly The number of submission & completion queues should be set by calling Set Features, not Get Features. Reported-by: Kwok Kong Signed-off-by: Matthew Wilcox diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index 28c84b1..f4996b0 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -840,7 +840,7 @@ static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns, } static int nvme_get_features(struct nvme_dev *dev, unsigned fid, - unsigned dword11, dma_addr_t dma_addr, u32 *result) + unsigned dword11, dma_addr_t dma_addr) { struct nvme_command c; @@ -850,6 +850,20 @@ static int nvme_get_features(struct nvme_dev *dev, unsigned fid, c.features.fid = cpu_to_le32(fid); c.features.dword11 = cpu_to_le32(dword11); + return nvme_submit_admin_cmd(dev, &c, NULL); +} + +static int nvme_set_features(struct nvme_dev *dev, unsigned fid, + unsigned dword11, dma_addr_t dma_addr, u32 *result) +{ + struct nvme_command c; + + memset(&c, 0, sizeof(c)); + c.features.opcode = nvme_admin_set_features; + c.features.prp1 = cpu_to_le64(dma_addr); + c.features.fid = cpu_to_le32(fid); + c.features.dword11 = cpu_to_le32(dword11); + return nvme_submit_admin_cmd(dev, &c, result); } @@ -1365,7 +1379,7 @@ static int set_queue_count(struct nvme_dev *dev, int count) u32 result; u32 q_count = (count - 1) | ((count - 1) << 16); - status = nvme_get_features(dev, NVME_FEAT_NUM_QUEUES, q_count, 0, + status = nvme_set_features(dev, NVME_FEAT_NUM_QUEUES, q_count, 0, &result); if (status) return -EIO; @@ -1482,7 +1496,7 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev) continue; res = nvme_get_features(dev, NVME_FEAT_LBA_RANGE, i, - dma_addr + 4096, NULL); + dma_addr + 4096); if (res) continue; -- cgit v0.10.2 From 8ca0686aabbf145014fa22bc7fd06189b564f119 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 26 Dec 2011 20:28:23 +0900 Subject: ARM: SAMSUNG: Guard against multiple inclusion of plat/dma.h Otherwise it'll generate errors if included twice. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/plat-samsung/include/plat/dma.h b/arch/arm/plat-samsung/include/plat/dma.h index b906112..7b02143 100644 --- a/arch/arm/plat-samsung/include/plat/dma.h +++ b/arch/arm/plat-samsung/include/plat/dma.h @@ -10,6 +10,9 @@ * published by the Free Software Foundation. */ +#ifndef __PLAT_DMA_H +#define __PLAT_DMA_H + #include enum s3c2410_dma_buffresult { @@ -122,5 +125,6 @@ extern int s3c2410_dma_getposition(enum dma_ch channel, extern int s3c2410_dma_set_opfn(enum dma_ch, s3c2410_dma_opfn_t rtn); extern int s3c2410_dma_set_buffdone_fn(enum dma_ch, s3c2410_dma_cbfn_t rtn); - #include + +#endif -- cgit v0.10.2 From 6fd9dd38a1124db66d2438bf9b225aef09597478 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 26 Dec 2011 20:28:57 +0900 Subject: ARM: SAMSUNG: dma-ops.h needs mach/dma.h dma-ops.h uses samsung_dma_is_dmadev so it needs to have mach/dma.h included. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h index 22eafc3..7c6c2a8 100644 --- a/arch/arm/plat-samsung/include/plat/dma-ops.h +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h @@ -14,6 +14,7 @@ #define __SAMSUNG_DMA_OPS_H_ __FILE__ #include +#include struct samsung_dma_prep_info { enum dma_transaction_type cap; -- cgit v0.10.2 From 5b0b34ea755810462346b24e24643eb93ebc919c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 29 Dec 2011 18:01:08 +0900 Subject: ARM: SAMSUNG: Declare struct platform_device in plat/s3c64xx-spi.h The function declarations in plat/s3c64xx-spi.h rely on struct platform_device but it's not declared by the header causing compiler warnings if it is included prior to another header which does that. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h index aea68b6..fa95e9a 100644 --- a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h +++ b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h @@ -11,6 +11,8 @@ #ifndef __S3C64XX_PLAT_SPI_H #define __S3C64XX_PLAT_SPI_H +struct platform_device; + /** * struct s3c64xx_spi_csinfo - ChipSelect description * @fb_delay: Slave specific feedback delay. -- cgit v0.10.2 From d5b9cdbcf74c192907e8290b85c3dcb3117984fb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 30 Dec 2011 09:38:13 +0900 Subject: ARM: S3C64XX: Remove unconditional power domain disables Now that we have software runtime power management support this code is redundant. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c index 055dac9..7d3e81b 100644 --- a/arch/arm/mach-s3c64xx/pm.c +++ b/arch/arm/mach-s3c64xx/pm.c @@ -346,23 +346,10 @@ int __init s3c64xx_pm_init(void) static __init int s3c64xx_pm_initcall(void) { - u32 val; - pm_cpu_prep = s3c64xx_pm_prepare; pm_cpu_sleep = s3c64xx_cpu_suspend; pm_uart_udivslot = 1; - /* - * Unconditionally disable power domains that contain only - * blocks which have no mainline driver support. - */ - val = __raw_readl(S3C64XX_NORMAL_CFG); - val &= ~(S3C64XX_NORMALCFG_DOMAIN_G_ON | - S3C64XX_NORMALCFG_DOMAIN_V_ON | - S3C64XX_NORMALCFG_DOMAIN_I_ON | - S3C64XX_NORMALCFG_DOMAIN_P_ON); - __raw_writel(val, S3C64XX_NORMAL_CFG); - #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK gpio_request(S3C64XX_GPN(12), "DEBUG_LED0"); gpio_request(S3C64XX_GPN(13), "DEBUG_LED1"); -- cgit v0.10.2 From d7d3077a2843faa6877280a4fac8cc732f5e7dce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 29 Dec 2011 17:09:23 +0900 Subject: ARM: S3C64XX: Remove hsmmc1 from Cragganmore The second MMC slot was only present on revision 1 of Cragganmore so remove it. Only MMC0 and MMC2 are there on production systems. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 680fd75..2e34fe8 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -328,7 +328,6 @@ static struct platform_device wallvdd_device = { static struct platform_device *crag6410_devices[] __initdata = { &s3c_device_hsmmc0, - &s3c_device_hsmmc1, &s3c_device_hsmmc2, &s3c_device_i2c0, &s3c_device_i2c1, @@ -683,12 +682,6 @@ static struct s3c_sdhci_platdata crag6410_hsmmc2_pdata = { .cd_type = S3C_SDHCI_CD_PERMANENT, }; -static struct s3c_sdhci_platdata crag6410_hsmmc1_pdata = { - .max_width = 4, - .cd_type = S3C_SDHCI_CD_GPIO, - .ext_cd_gpio = S3C64XX_GPF(11), -}; - static void crag6410_cfg_sdhci0(struct platform_device *dev, int width) { /* Set all the necessary GPG pins to special-function 2 */ @@ -723,7 +716,6 @@ static void __init crag6410_machine_init(void) gpio_direction_output(S3C64XX_GPF(10), 1); s3c_sdhci0_set_platdata(&crag6410_hsmmc0_pdata); - s3c_sdhci1_set_platdata(&crag6410_hsmmc1_pdata); s3c_sdhci2_set_platdata(&crag6410_hsmmc2_pdata); s3c_i2c0_set_platdata(&i2c0_pdata); -- cgit v0.10.2 From 91b60b1d5a56e5971237f9c88de2faf168a5ceaf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 29 Dec 2011 18:02:39 +0900 Subject: ARM: S3C64XX: Fix the memory mapped GPIOs on Cragganmore Rather than letting them get allocated dynamically where we don't know where they are, and also name the data line resource as gpio-generic requires that. Without these changes the GPIOs are useless. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c64xx/include/mach/crag6410.h b/arch/arm/mach-s3c64xx/include/mach/crag6410.h index 5d55ab0..4cb2f951 100644 --- a/arch/arm/mach-s3c64xx/include/mach/crag6410.h +++ b/arch/arm/mach-s3c64xx/include/mach/crag6410.h @@ -21,5 +21,6 @@ #define CODEC_GPIO_BASE (GPIO_BOARD_START + 8) #define GLENFARCLAS_PMIC_GPIO_BASE (GPIO_BOARD_START + 32) #define BANFF_PMIC_GPIO_BASE (GPIO_BOARD_START + 64) +#define MMGPIO_GPIO_BASE (GPIO_BOARD_START + 96) #endif diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 2e34fe8..6998b5b 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -260,6 +260,7 @@ static struct platform_device crag6410_dm9k_device = { static struct resource crag6410_mmgpio_resource[] = { [0] = { + .name = "dat", .start = S3C64XX_PA_XM0CSN4 + 1, .end = S3C64XX_PA_XM0CSN4 + 1, .flags = IORESOURCE_MEM, @@ -272,7 +273,7 @@ static struct platform_device crag6410_mmgpio = { .resource = crag6410_mmgpio_resource, .num_resources = ARRAY_SIZE(crag6410_mmgpio_resource), .dev.platform_data = &(struct bgpio_pdata) { - .base = -1, + .base = MMGPIO_GPIO_BASE, }, }; -- cgit v0.10.2 From 6e11e0bd82ecacd13b839558a49f65ebc958e1bd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 30 Dec 2011 09:46:40 +0900 Subject: ARM: S3C64XX: Fix interrupt configuration for PCA935x on Cragganmore 0 is a valid IRQ but the interrupt line for the pca935x certainly isn't connected there which causes it to fail to probe. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 6998b5b..3d8b20b 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -355,7 +355,7 @@ static struct platform_device *crag6410_devices[] __initdata = { static struct pca953x_platform_data crag6410_pca_data = { .gpio_base = PCA935X_GPIO_BASE, - .irq_base = 0, + .irq_base = -1, }; /* VDDARM is controlled by DVS1 connected to GPK(0) */ -- cgit v0.10.2 From 815ed6fc0a4d82bb39ed43d230c4e516214987e7 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Thu, 29 Dec 2011 16:48:08 +0900 Subject: ARM: EXYNOS: Invert VCLK polarity for framebuffer on ORIGEN Framebuffer driver needs to fetch the video data during the rising edge of the VCLK. Otherwise, there are some glitches in the LCD display. Signed-off-by: Tushar Behera Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 2b11e04..0679b8a 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -597,7 +597,8 @@ static struct s3c_fb_pd_win origen_fb_win0 = { static struct s3c_fb_platdata origen_lcd_pdata __initdata = { .win[0] = &origen_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, - .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | + VIDCON1_INV_VCLK, .setup_gpio = exynos4_fimd0_gpio_setup_24bpp, }; -- cgit v0.10.2 From 46fe44ce8777f087aa8ad4a2605fdcfb9c2d63af Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 16 Nov 2011 15:03:59 +0100 Subject: quota: Pass information that quota is stored in system file to userspace Quota tools need to know whether quota is stored in a system file or in classical aquota.{user|group} files. So pass this information as a flag in GETINFO quotactl. Signed-off-by: Jan Kara diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 5ec59b2..4674197 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2125,6 +2125,8 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, mutex_unlock(&dqopt->dqio_mutex); goto out_file_init; } + if (dqopt->flags & DQUOT_QUOTA_SYS_FILE) + dqopt->info[type].dqi_flags |= DQF_SYS_FILE; mutex_unlock(&dqopt->dqio_mutex); spin_lock(&dq_state_lock); dqopt->flags |= dquot_state_flag(flags, type); @@ -2464,7 +2466,7 @@ int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) spin_lock(&dq_data_lock); ii->dqi_bgrace = mi->dqi_bgrace; ii->dqi_igrace = mi->dqi_igrace; - ii->dqi_flags = mi->dqi_flags & DQF_MASK; + ii->dqi_flags = mi->dqi_flags & DQF_GETINFO_MASK; ii->dqi_valid = IIF_ALL; spin_unlock(&dq_data_lock); mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); @@ -2490,8 +2492,8 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) if (ii->dqi_valid & IIF_IGRACE) mi->dqi_igrace = ii->dqi_igrace; if (ii->dqi_valid & IIF_FLAGS) - mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | - (ii->dqi_flags & DQF_MASK); + mi->dqi_flags = (mi->dqi_flags & ~DQF_SETINFO_MASK) | + (ii->dqi_flags & DQF_SETINFO_MASK); spin_unlock(&dq_data_lock); mark_info_dirty(sb, type); /* Force write to disk */ diff --git a/include/linux/quota.h b/include/linux/quota.h index cb78556..c09fa04 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -230,7 +230,11 @@ struct mem_dqinfo { struct super_block; #define DQF_MASK 0xffff /* Mask for format specific flags */ -#define DQF_INFO_DIRTY_B 16 +#define DQF_GETINFO_MASK 0x1ffff /* Mask for flags passed to userspace */ +#define DQF_SETINFO_MASK 0xffff /* Mask for flags modifiable from userspace */ +#define DQF_SYS_FILE_B 16 +#define DQF_SYS_FILE (1 << DQF_SYS_FILE_B) /* Quota file stored as system file */ +#define DQF_INFO_DIRTY_B 31 #define DQF_INFO_DIRTY (1 << DQF_INFO_DIRTY_B) /* Is info dirty? */ extern void mark_info_dirty(struct super_block *sb, int type); -- cgit v0.10.2 From 8ea11f7f11c811d1f502c2dabc5259d447c2f2a0 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 12 Jan 2012 11:35:50 -0500 Subject: xen/balloon: Move the registration from device to subsystem. With git commit 070680218379e15c1901f4bf21b98e3cbf12b527 "xen-balloon: convert sysdev_class to a regular subsystem" we would end up with the attributes being put in: /sys/devices/xen_memory0/target_kb instead of /sys/devices/system/xen_memory/xen_memory0/target_kb Making the tools inable to deflate the kernel to make more space for launching another guest and printing: Error: Failed to query current memory allocation of dom0 Reported-by: Sander Eikelenboom Suggested-by: Kay Sievers Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index 3832e30..596e6a7 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -221,7 +221,7 @@ static int register_balloon(struct device *dev) { int i, error; - error = bus_register(&balloon_subsys); + error = subsys_system_register(&balloon_subsys, NULL); if (error) return error; -- cgit v0.10.2 From 7250170c9ed00f3b74b11b98afefab45020672dd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 11 Jan 2012 10:46:27 +0300 Subject: cifs: integer overflow in parse_dacl() On 32 bit systems num_aces * sizeof(struct cifs_ace *) could overflow leading to a smaller ppace buffer than we expected. Signed-off-by: Dan Carpenter Acked-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 72ddf23..c1b2544 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -909,6 +909,8 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, umode_t group_mask = S_IRWXG; umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO; + if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) + return; ppace = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL); if (!ppace) { -- cgit v0.10.2 From 220d9f259536e22afef30347b7b42148640282a7 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Thu, 12 Jan 2012 12:30:38 +0100 Subject: ASoC: Fix DMA channel leak in imx-pcm-dma-mx2 driver. "snd_imx_pcm_hw_params" callback can be called several times by the user (i.e. OSS emulation) leading to a DMA channel leak. Signed-off-by: Javier Martin Signed-off-by: Mark Brown diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 1cf2fe8..7d7fbb5 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -88,11 +88,13 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, iprtd->dma_data.dma_request = dma_params->dma; /* Try to grab a DMA channel */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - iprtd->dma_chan = dma_request_channel(mask, filter, iprtd); - if (!iprtd->dma_chan) - return -EINVAL; + if (!iprtd->dma_chan) { + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + iprtd->dma_chan = dma_request_channel(mask, filter, iprtd); + if (!iprtd->dma_chan) + return -EINVAL; + } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: -- cgit v0.10.2 From 8ca4013d702dae4913fbb625aabf4c2966cdf1f0 Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Tue, 25 Oct 2011 15:42:21 -0700 Subject: CHROMIUM: i915: Add DMI override to skip CRT initialization on ZGB This is the method used to override LVDS in intel_lvds and appears to be an effective way to ensure that the driver does not enable VGA hotplug. This is the same patch from 2.6.32 kernel in R12 but ported to 2.6.38, will send upstream next. Signed-off-by: Duncan Laurie BUG=chrome-os-partner:117 TEST=Check PORT_HOTPLUG_EN to see if hotplug interrupt is disabled. Run the following command as root, specifically looking at bit 9: mmio_read32 $[$(pci_read32 0 2 0 0x10) + 0x61110] = 0x00000000 Change-Id: Id8240f9fb31d058d8d79ee72f7b4615c43893f5a Reviewed-on: http://gerrit.chromium.org/gerrit/1390 Reviewed-by: Olof Johansson Tested-by: Duncan Laurie Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index fee0ad0..dd729d4 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -24,6 +24,7 @@ * Eric Anholt */ +#include #include #include #include "drmP.h" @@ -540,6 +541,24 @@ static const struct drm_encoder_funcs intel_crt_enc_funcs = { .destroy = intel_encoder_destroy, }; +static int __init intel_no_crt_dmi_callback(const struct dmi_system_id *id) +{ + DRM_DEBUG_KMS("Skipping CRT initialization for %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id intel_no_crt[] = { + { + .callback = intel_no_crt_dmi_callback, + .ident = "ACER ZGB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ACER"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), + }, + }, + { } +}; + void intel_crt_init(struct drm_device *dev) { struct drm_connector *connector; @@ -547,6 +566,10 @@ void intel_crt_init(struct drm_device *dev) struct intel_connector *intel_connector; struct drm_i915_private *dev_priv = dev->dev_private; + /* Skip machines without VGA that falsely report hotplug events */ + if (dmi_check_system(intel_no_crt)) + return; + crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL); if (!crt) return; -- cgit v0.10.2 From 44306ab302687b519a31aa498b954c1e26f95a6b Mon Sep 17 00:00:00 2001 From: Joel Sass Date: Tue, 10 Jan 2012 13:03:55 -0500 Subject: drm/i915: Add Clientron E830 to the ignore LVDS list Signed-off-by: Joel Sass Reviewed-by: Adam Jackson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e441911..798f6e1 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -708,6 +708,14 @@ static const struct dmi_system_id intel_no_lvds[] = { }, }, { + .callback = intel_no_lvds_dmi_callback, + .ident = "Clientron E830", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), + DMI_MATCH(DMI_PRODUCT_NAME, "E830"), + }, + }, + { .callback = intel_no_lvds_dmi_callback, .ident = "Asus EeeBox PC EB1007", .matches = { -- cgit v0.10.2 From 7885d2052bd94395e337709cfba093a41f273ff1 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 12 Jan 2012 14:51:17 -0800 Subject: drm/i915: mask transcoder select bits before setting them on LVDS The transcoder port may changed from mode set to mode set, so make sure to mask out the selection bits before setting the right ones or we'll get black screens when going from transcoder B to A. Tested-by: Vincent Vanackere Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2a3f707..96cea08 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5808,12 +5808,15 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds) { temp = I915_READ(PCH_LVDS); temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) + if (HAS_PCH_CPT(dev)) { + temp &= ~PORT_TRANS_SEL_MASK; temp |= PORT_TRANS_SEL_CPT(pipe); - else if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; + } else { + if (pipe == 1) + temp |= LVDS_PIPEB_SELECT; + else + temp &= ~LVDS_PIPEB_SELECT; + } /* set the corresponsding LVDS_BORDER bit */ temp |= dev_priv->lvds_border_bits; -- cgit v0.10.2 From b01de4fb40137fbda7530550ff0cd37171dafb0c Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 12 Jan 2012 16:31:14 +0100 Subject: ALSA: HDA: Use LPIB position fix for Macbook Pro 7,1 Several users have reported "choppy" audio under the 3.2 kernel, and that changing position_fix to 1 has resolved their problem. The chip is an nVidia Corporation MCP89 High Definition Audio, [10de:0d94] (rev a2). Cc: stable@kernel.org (v3.2+) BugLink: https://bugs.launchpad.net/bugs/909419 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 0852e20..fb35474 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2498,6 +2498,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), + SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB), SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB), SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), -- cgit v0.10.2 From 5c845beb4220ba7cddb099bd47c72be7e6919712 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Jan 2012 09:53:53 +0100 Subject: ALSA: Don't prompt for CONFIG_SND_COMPRESS_OFFLOAD CONFIG_SND_COMPRESS_OFFLOAD is an item to be selected by the dirver just like CONFIG_SND_PCM, and no need to prompt for explicit selection. Signed-off-by: Takashi Iwai diff --git a/sound/core/Kconfig b/sound/core/Kconfig index ca6cdf6..3497569 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -13,6 +13,9 @@ config SND_HWDEP config SND_RAWMIDI tristate +config SND_COMPRESS_OFFLOAD + tristate + # To be effective this also requires INPUT - users should say: # select SND_JACK if INPUT=y || INPUT=SND # to avoid having to force INPUT on. @@ -155,16 +158,6 @@ config SND_DYNAMIC_MINORS If you are unsure about this, say N here. -config SND_COMPRESS_OFFLOAD - tristate "ALSA Compressed audio offload support" - default n - help - If you want support for offloading compressed audio and have such - a hardware, then you should say Y here and also to the DSP driver - of your platform. - - If you are unsure about this, say N here. - config SND_SUPPORT_OLD_API bool "Support old ALSA API" default y -- cgit v0.10.2 From 96c0a2f52c45d8ec0a2b70810f4693530feaba5d Mon Sep 17 00:00:00 2001 From: Rohit Jain Date: Thu, 12 Jan 2012 12:19:44 +0530 Subject: drm/i915: VBT Parser cleanup for eDP block Support for parsing parameters for S3D support and T3 optimization support is implemented. The order for the bdb_edp struct was also made similar to that indicated in spec. Signed-off-by: Rohit Jain Reviewed-by: Shobhit Kumar Reviewed-by: Vijay A. Purushothaman Reviewed-by: Eugeni Dodonov Acked-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 8af3735..dbda6e3 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -467,8 +467,12 @@ struct edp_link_params { struct bdb_edp { struct edp_power_seq power_seqs[16]; u32 color_depth; - u32 sdrrs_msa_timing_delay; struct edp_link_params link_params[16]; + u32 sdrrs_msa_timing_delay; + + /* ith bit indicates enabled/disabled for (i+1)th panel */ + u16 edp_s3d_feature; + u16 edp_t3_optimization; } __attribute__ ((packed)); void intel_setup_bios(struct drm_device *dev); -- cgit v0.10.2 From 252c3d84ed398b090ac2dace46fc6faa6cfaea99 Mon Sep 17 00:00:00 2001 From: "RongQing.Li" Date: Thu, 12 Jan 2012 22:33:46 +0000 Subject: ipv6: release idev when ip6_neigh_lookup failed in icmp6_dst_alloc release idev when ip6_neigh_lookup failed in icmp6_dst_alloc Signed-off-by: RongQing.Li Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 07361df..8c2e3ab 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1091,6 +1091,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, else { neigh = ip6_neigh_lookup(&rt->dst, &fl6->daddr); if (IS_ERR(neigh)) { + in6_dev_put(idev); dst_free(&rt->dst); return ERR_CAST(neigh); } -- cgit v0.10.2 From 673e8e597c06eb81954bf21a10f5cce74a1de8f1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:04 +0000 Subject: xfs: remove xfs_itruncate_data This wrapper isn't overly useful, not to say rather confusing. Around the call to xfs_itruncate_extents it does: - add tracing - add a few asserts in debug builds - conditionally update the inode size in two places - log the inode Both the tracing and the inode logging can be moved to xfs_itruncate_extents as they are useful for the attribute fork as well - in fact the attr code already does an equivalent xfs_trans_log_inode call just after calling xfs_itruncate_extents. The conditional size updates are a mess, and there was no reason to do them in two places anyway, as the first one was conditional on the inode having extents - but without extents we xfs_itruncate_extents would be a no-op and the placement wouldn't matter anyway. Instead move the size assignments and the asserts that make sense to the callers that want it. As a side effect of this clean up xfs_setattr_size by introducing variables for the old and new inode size, and moving the size updates into a common place. Reviewed-by: Dave Chinner Signed-off-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index 1e5d97f..08b9ac6 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -827,10 +827,6 @@ xfs_attr_inactive(xfs_inode_t *dp) if (error) goto out; - /* - * Commit the last in the sequence of transactions. - */ - xfs_trans_log_inode(trans, dp, XFS_ILOG_CORE); error = xfs_trans_commit(trans, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(dp, XFS_ILOCK_EXCL); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 9dda7cc..ccd619a 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1166,52 +1166,6 @@ xfs_ialloc( } /* - * Check to make sure that there are no blocks allocated to the - * file beyond the size of the file. We don't check this for - * files with fixed size extents or real time extents, but we - * at least do it for regular files. - */ -#ifdef DEBUG -STATIC void -xfs_isize_check( - struct xfs_inode *ip, - xfs_fsize_t isize) -{ - struct xfs_mount *mp = ip->i_mount; - xfs_fileoff_t map_first; - int nimaps; - xfs_bmbt_irec_t imaps[2]; - int error; - - if (!S_ISREG(ip->i_d.di_mode)) - return; - - if (XFS_IS_REALTIME_INODE(ip)) - return; - - if (ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE) - return; - - nimaps = 2; - map_first = XFS_B_TO_FSB(mp, (xfs_ufsize_t)isize); - /* - * The filesystem could be shutting down, so bmapi may return - * an error. - */ - error = xfs_bmapi_read(ip, map_first, - (XFS_B_TO_FSB(mp, - (xfs_ufsize_t)XFS_MAXIOFFSET(mp)) - map_first), - imaps, &nimaps, XFS_BMAPI_ENTIRE); - if (error) - return; - ASSERT(nimaps == 1); - ASSERT(imaps[0].br_startblock == HOLESTARTBLOCK); -} -#else /* DEBUG */ -#define xfs_isize_check(ip, isize) -#endif /* DEBUG */ - -/* * Free up the underlying blocks past new_size. The new size must be smaller * than the current size. This routine can be used both for the attribute and * data fork, and does not modify the inode size, which is left to the caller. @@ -1258,6 +1212,8 @@ xfs_itruncate_extents( ASSERT(ip->i_itemp->ili_lock_flags == 0); ASSERT(!XFS_NOT_DQATTACHED(mp, ip)); + trace_xfs_itruncate_extents_start(ip, new_size); + /* * Since it is possible for space to become allocated beyond * the end of the file (in a crash where the space is allocated @@ -1325,6 +1281,14 @@ xfs_itruncate_extents( goto out; } + /* + * Always re-log the inode so that our permanent transaction can keep + * on rolling it forward in the log. + */ + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + + trace_xfs_itruncate_extents_end(ip, new_size); + out: *tpp = tp; return error; @@ -1338,74 +1302,6 @@ out_bmap_cancel: goto out; } -int -xfs_itruncate_data( - struct xfs_trans **tpp, - struct xfs_inode *ip, - xfs_fsize_t new_size) -{ - int error; - - trace_xfs_itruncate_data_start(ip, new_size); - - /* - * The first thing we do is set the size to new_size permanently on - * disk. This way we don't have to worry about anyone ever being able - * to look at the data being freed even in the face of a crash. - * What we're getting around here is the case where we free a block, it - * is allocated to another file, it is written to, and then we crash. - * If the new data gets written to the file but the log buffers - * containing the free and reallocation don't, then we'd end up with - * garbage in the blocks being freed. As long as we make the new_size - * permanent before actually freeing any blocks it doesn't matter if - * they get written to. - */ - if (ip->i_d.di_nextents > 0) { - /* - * If we are not changing the file size then do not update - * the on-disk file size - we may be called from - * xfs_inactive_free_eofblocks(). If we update the on-disk - * file size and then the system crashes before the contents - * of the file are flushed to disk then the files may be - * full of holes (ie NULL files bug). - */ - if (ip->i_size != new_size) { - ip->i_d.di_size = new_size; - ip->i_size = new_size; - xfs_trans_log_inode(*tpp, ip, XFS_ILOG_CORE); - } - } - - error = xfs_itruncate_extents(tpp, ip, XFS_DATA_FORK, new_size); - if (error) - return error; - - /* - * If we are not changing the file size then do not update the on-disk - * file size - we may be called from xfs_inactive_free_eofblocks(). - * If we update the on-disk file size and then the system crashes - * before the contents of the file are flushed to disk then the files - * may be full of holes (ie NULL files bug). - */ - xfs_isize_check(ip, new_size); - if (ip->i_size != new_size) { - ip->i_d.di_size = new_size; - ip->i_size = new_size; - } - - ASSERT(new_size != 0 || ip->i_delayed_blks == 0); - ASSERT(new_size != 0 || ip->i_d.di_nextents == 0); - - /* - * Always re-log the inode so that our permanent transaction can keep - * on rolling it forward in the log. - */ - xfs_trans_log_inode(*tpp, ip, XFS_ILOG_CORE); - - trace_xfs_itruncate_data_end(ip, new_size); - return 0; -} - /* * This is called when the inode's link count goes to 0. * We place the on-disk inode on a list in the AGI. It diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index f0e6b15..440f2ac 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -491,8 +491,6 @@ int xfs_ifree(struct xfs_trans *, xfs_inode_t *, struct xfs_bmap_free *); int xfs_itruncate_extents(struct xfs_trans **, struct xfs_inode *, int, xfs_fsize_t); -int xfs_itruncate_data(struct xfs_trans **, struct xfs_inode *, - xfs_fsize_t); int xfs_iunlink(struct xfs_trans *, xfs_inode_t *); void xfs_iext_realloc(xfs_inode_t *, int, int); diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f9babd1..f02eaa2 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -750,6 +750,7 @@ xfs_setattr_size( struct xfs_mount *mp = ip->i_mount; struct inode *inode = VFS_I(ip); int mask = iattr->ia_valid; + xfs_off_t oldsize, newsize; struct xfs_trans *tp; int error; uint lock_flags; @@ -777,11 +778,13 @@ xfs_setattr_size( lock_flags |= XFS_IOLOCK_EXCL; xfs_ilock(ip, lock_flags); + oldsize = ip->i_size; + newsize = iattr->ia_size; + /* * Short circuit the truncate case for zero length files. */ - if (iattr->ia_size == 0 && - ip->i_size == 0 && ip->i_d.di_nextents == 0) { + if (newsize == 0 && oldsize == 0 && ip->i_d.di_nextents == 0) { if (!(mask & (ATTR_CTIME|ATTR_MTIME))) goto out_unlock; @@ -807,14 +810,14 @@ xfs_setattr_size( * the inode to the transaction, because the inode cannot be unlocked * once it is a part of the transaction. */ - if (iattr->ia_size > ip->i_size) { + if (newsize > oldsize) { /* * Do the first part of growing a file: zero any data in the * last block that is beyond the old EOF. We need to do this * before the inode is joined to the transaction to modify * i_size. */ - error = xfs_zero_eof(ip, iattr->ia_size, ip->i_size); + error = xfs_zero_eof(ip, newsize, oldsize); if (error) goto out_unlock; } @@ -833,8 +836,8 @@ xfs_setattr_size( * here and prevents waiting for other data not within the range we * care about here. */ - if (ip->i_size != ip->i_d.di_size && iattr->ia_size > ip->i_d.di_size) { - error = xfs_flush_pages(ip, ip->i_d.di_size, iattr->ia_size, 0, + if (oldsize != ip->i_d.di_size && newsize > ip->i_d.di_size) { + error = xfs_flush_pages(ip, ip->i_d.di_size, newsize, 0, FI_NONE); if (error) goto out_unlock; @@ -845,8 +848,7 @@ xfs_setattr_size( */ inode_dio_wait(inode); - error = -block_truncate_page(inode->i_mapping, iattr->ia_size, - xfs_get_blocks); + error = -block_truncate_page(inode->i_mapping, newsize, xfs_get_blocks); if (error) goto out_unlock; @@ -857,7 +859,7 @@ xfs_setattr_size( if (error) goto out_trans_cancel; - truncate_setsize(inode, iattr->ia_size); + truncate_setsize(inode, newsize); commit_flags = XFS_TRANS_RELEASE_LOG_RES; lock_flags |= XFS_ILOCK_EXCL; @@ -876,19 +878,30 @@ xfs_setattr_size( * these flags set. For all other operations the VFS set these flags * explicitly if it wants a timestamp update. */ - if (iattr->ia_size != ip->i_size && - (!(mask & (ATTR_CTIME | ATTR_MTIME)))) { + if (newsize != oldsize && (!(mask & (ATTR_CTIME | ATTR_MTIME)))) { iattr->ia_ctime = iattr->ia_mtime = current_fs_time(inode->i_sb); mask |= ATTR_CTIME | ATTR_MTIME; } - if (iattr->ia_size > ip->i_size) { - ip->i_d.di_size = iattr->ia_size; - ip->i_size = iattr->ia_size; - } else if (iattr->ia_size <= ip->i_size || - (iattr->ia_size == 0 && ip->i_d.di_nextents)) { - error = xfs_itruncate_data(&tp, ip, iattr->ia_size); + /* + * The first thing we do is set the size to new_size permanently on + * disk. This way we don't have to worry about anyone ever being able + * to look at the data being freed even in the face of a crash. + * What we're getting around here is the case where we free a block, it + * is allocated to another file, it is written to, and then we crash. + * If the new data gets written to the file but the log buffers + * containing the free and reallocation don't, then we'd end up with + * garbage in the blocks being freed. As long as we make the new size + * permanent before actually freeing any blocks it doesn't matter if + * they get written to. + */ + ip->i_d.di_size = newsize; + ip->i_size = newsize; + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + + if (newsize <= oldsize) { + error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, newsize); if (error) goto out_trans_abort; diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 5cc3dde..2737865 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -31,6 +31,7 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_inode.h" +#include "xfs_inode_item.h" #include "xfs_itable.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" @@ -263,13 +264,19 @@ xfs_qm_scall_trunc_qfile( xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); - error = xfs_itruncate_data(&tp, ip, 0); + ip->i_d.di_size = 0; + ip->i_size = 0; + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + + error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0); if (error) { xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); goto out_unlock; } + ASSERT(ip->i_d.di_nextents == 0); + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index a9d5b1e..297f9fa 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1090,8 +1090,8 @@ DECLARE_EVENT_CLASS(xfs_itrunc_class, DEFINE_EVENT(xfs_itrunc_class, name, \ TP_PROTO(struct xfs_inode *ip, xfs_fsize_t new_size), \ TP_ARGS(ip, new_size)) -DEFINE_ITRUNC_EVENT(xfs_itruncate_data_start); -DEFINE_ITRUNC_EVENT(xfs_itruncate_data_end); +DEFINE_ITRUNC_EVENT(xfs_itruncate_extents_start); +DEFINE_ITRUNC_EVENT(xfs_itruncate_extents_end); TRACE_EVENT(xfs_pagecache_inval, TP_PROTO(struct xfs_inode *ip, xfs_off_t start, xfs_off_t finish), diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index f2fea86..96ff034 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -226,7 +226,14 @@ xfs_free_eofblocks( xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); - error = xfs_itruncate_data(&tp, ip, ip->i_size); + /* + * Do not update the on-disk file size. If we update the + * on-disk file size and then the system crashes before the + * contents of the file are flushed to disk then the files + * may be full of holes (ie NULL files bug). + */ + error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, + ip->i_size); if (error) { /* * If we get an error at this point we simply don't @@ -670,13 +677,19 @@ xfs_inactive( xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); - error = xfs_itruncate_data(&tp, ip, 0); + ip->i_d.di_size = 0; + ip->i_size = 0; + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + + error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0); if (error) { xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); return VN_INACTIVE_CACHE; } + + ASSERT(ip->i_d.di_nextents == 0); } else if (S_ISLNK(ip->i_d.di_mode)) { /* -- cgit v0.10.2 From bf322d983e540f66517db85b6870017613bb1e8d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:05 +0000 Subject: xfs: cleanup xfs_iomap_eof_align_last_fsb Replace the nasty if, else if, elseif condition with more natural C flow that expressed the logic we want here better. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 9afa282..a27a446 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -57,26 +57,26 @@ xfs_iomap_eof_align_last_fsb( xfs_fileoff_t *last_fsb) { xfs_fileoff_t new_last_fsb = 0; - xfs_extlen_t align; + xfs_extlen_t align = 0; int eof, error; - if (XFS_IS_REALTIME_INODE(ip)) - ; - /* - * If mounted with the "-o swalloc" option, roundup the allocation - * request to a stripe width boundary if the file size is >= - * stripe width and we are allocating past the allocation eof. - */ - else if (mp->m_swidth && (mp->m_flags & XFS_MOUNT_SWALLOC) && - (ip->i_size >= XFS_FSB_TO_B(mp, mp->m_swidth))) - new_last_fsb = roundup_64(*last_fsb, mp->m_swidth); - /* - * Roundup the allocation request to a stripe unit (m_dalign) boundary - * if the file size is >= stripe unit size, and we are allocating past - * the allocation eof. - */ - else if (mp->m_dalign && (ip->i_size >= XFS_FSB_TO_B(mp, mp->m_dalign))) - new_last_fsb = roundup_64(*last_fsb, mp->m_dalign); + if (!XFS_IS_REALTIME_INODE(ip)) { + /* + * Round up the allocation request to a stripe unit + * (m_dalign) boundary if the file size is >= stripe unit + * size, and we are allocating past the allocation eof. + * + * If mounted with the "-o swalloc" option the alignment is + * increased from the strip unit size to the stripe width. + */ + if (mp->m_swidth && (mp->m_flags & XFS_MOUNT_SWALLOC)) + align = mp->m_swidth; + else if (mp->m_dalign) + align = mp->m_dalign; + + if (align && ip->i_size >= XFS_FSB_TO_B(mp, align)) + new_last_fsb = roundup_64(*last_fsb, align); + } /* * Always round up the allocation request to an extent boundary -- cgit v0.10.2 From 3d2b3129c2c48cf0153e0f2058cf87e4b45ca3ac Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:06 +0000 Subject: xfs: remove the unused dm_attrs structure .. and the just as dead bhv_desc forward declaration while we're at it. Signed-off-by: Christoph Hellwig Reviewed-by: Alex Elder Reviewed-by: Dave Chinner Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 440f2ac..c157472 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -211,7 +211,6 @@ typedef struct xfs_icdinode { #ifdef __KERNEL__ -struct bhv_desc; struct xfs_buf; struct xfs_bmap_free; struct xfs_bmbt_irec; @@ -220,12 +219,6 @@ struct xfs_mount; struct xfs_trans; struct xfs_dquot; -typedef struct dm_attrs_s { - __uint32_t da_dmevmask; /* DMIG event mask */ - __uint16_t da_dmstate; /* DMIG state info */ - __uint16_t da_pad; /* DMIG extra padding */ -} dm_attrs_t; - typedef struct xfs_inode { /* Inode linking and identification information. */ struct xfs_mount *i_mount; /* fs mount struct ptr */ -- cgit v0.10.2 From 7c7c7f01cc5e3e423120a4848a73dd5e4586f2f9 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 11 Jan 2012 19:30:38 +0000 Subject: vhost-net: add module alias (v2.1) By adding some module aliases, programs (or users) won't have to explicitly call modprobe. Vhost-net will always be available if built into the kernel. It does require assigning a permanent minor number for depmod to work. Also: - use C99 style initialization. - add missing entry in documentation for loop-control Signed-off-by: Stephen Hemminger Acked-By: Kay Sievers Signed-off-by: David S. Miller diff --git a/Documentation/devices.txt b/Documentation/devices.txt index cec8864..0038318 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -447,6 +447,9 @@ Your cooperation is appreciated. 234 = /dev/btrfs-control Btrfs control device 235 = /dev/autofs Autofs control device 236 = /dev/mapper/control Device-Mapper control device + 237 = /dev/loop-control Loopback control device + 238 = /dev/vhost-net Host kernel accelerator for virtio net + 240-254 Reserved for local use 255 Reserved for MISC_DYNAMIC_MINOR diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 882a51f..9dab1f5 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -856,9 +856,9 @@ static const struct file_operations vhost_net_fops = { }; static struct miscdevice vhost_net_misc = { - MISC_DYNAMIC_MINOR, - "vhost-net", - &vhost_net_fops, + .minor = VHOST_NET_MINOR, + .name = "vhost-net", + .fops = &vhost_net_fops, }; static int vhost_net_init(void) @@ -879,3 +879,5 @@ MODULE_VERSION("0.0.1"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Michael S. Tsirkin"); MODULE_DESCRIPTION("Host kernel accelerator for virtio net"); +MODULE_ALIAS_MISCDEV(VHOST_NET_MINOR); +MODULE_ALIAS("devname:vhost-net"); diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 3208524..0549d211 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -42,6 +42,7 @@ #define AUTOFS_MINOR 235 #define MAPPER_CTRL_MINOR 236 #define LOOP_CTRL_MINOR 237 +#define VHOST_NET_MINOR 238 #define MISC_DYNAMIC_MINOR 255 struct device; -- cgit v0.10.2 From ccde8a45cbd3ea9a6e785e4393d3a1f6b4e6b495 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 6 Jan 2012 12:58:16 +0100 Subject: wireless: b43: fix Oops on card eject during transfer An Oops has once been observed, when the SDIO card had been ejected during IO. The PC value shows, that the dev pointer in b43_op_stop() was NULL. (I moved the NULL check before the lock, based upon a suggestion from Julian Calaby . -- JWL) Signed-off-by: Guennadi Liakhovetski Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 1c6f193..b91f28e 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4852,6 +4852,9 @@ static void b43_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&(wl->beacon_update_trigger)); + if (!dev) + goto out; + mutex_lock(&wl->mutex); if (b43_status(dev) >= B43_STAT_STARTED) { dev = b43_wireless_core_stop(dev); @@ -4863,7 +4866,7 @@ static void b43_op_stop(struct ieee80211_hw *hw) out_unlock: mutex_unlock(&wl->mutex); - +out: cancel_work_sync(&(wl->txpower_adjust_work)); } -- cgit v0.10.2 From 92c1ff1fa772ee76a18ae1edd2e4baecf381f17e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 12 Jan 2012 09:34:50 +0300 Subject: ipw2x00: signedness bug handling frame length This is basically just a cleanup. Large positive numbers get counted as negative but then get implicitly cast to positive again for the checks that matter. This does make a small difference in ipw_handle_promiscuous_rx() when we test "if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb)))" It should return there, but we don't return until a couple lines later when we test "if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {". The difference is that in the second test the sizeof() means that there is an implied cast to unsigned. Signed-off-by: Dan Carpenter Reviewed-by: Stanislaw Gruszka Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 018a8de..4fcdac6 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -7848,7 +7848,7 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, * more efficiently than we can parse it. ORDER MATTERS HERE */ struct ipw_rt_hdr *ipw_rt; - short len = le16_to_cpu(pkt->u.frame.length); + unsigned short len = le16_to_cpu(pkt->u.frame.length); /* We received data from the HW, so stop the watchdog */ dev->trans_start = jiffies; @@ -8023,7 +8023,7 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM; s8 noise = (s8) le16_to_cpu(frame->noise); u8 rate = frame->rate; - short len = le16_to_cpu(pkt->u.frame.length); + unsigned short len = le16_to_cpu(pkt->u.frame.length); struct sk_buff *skb; int hdr_only = 0; u16 filter = priv->prom_priv->filter; -- cgit v0.10.2 From dfd00c4c8f3dfa1fd7cec45f83d98b2a49743dcd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 13 Jan 2012 12:59:32 +0100 Subject: rt2800pci: fix spurious interrupts generation Same devices can generate interrupt without properly setting bit in INT_SOURCE_CSR register (spurious interrupt), what will cause IRQ line will be disabled by interrupts controller driver. We discovered that clearing INT_MASK_CSR stops such behaviour. We previously first read that register, and then clear all know interrupt sources bits and do not touch reserved bits. After this patch, we write to all register content (I believe writing to reserved bits on that register will not cause any problems, I tested that on my rt2800pci device). This fix very bad performance problem, practically making device unusable (since worked without interrupts), reported in: https://bugzilla.redhat.com/show_bug.cgi?id=658451 We previously tried to workaround that issue in commit 4ba7d9997869d25bd223dea7536fc1ce9fab3b3b "rt2800pci: handle spurious interrupts", but it was reverted in commit 82e5fc2a34fa9ffea38f00c4066b7e600a0ca5e6 as thing, that will prevent to detect real spurious interrupts. Reported-and-tested-by: Amir Hedayaty Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 4941a1a..dc88bae 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -422,7 +422,6 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - int mask = (state == STATE_RADIO_IRQ_ON); u32 reg; unsigned long flags; @@ -436,25 +435,14 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, } spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, INT_MASK_CSR_RXDELAYINT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_TXDELAYINT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, mask); - rt2x00_set_field32(®, INT_MASK_CSR_AC0_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_AC1_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_AC2_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_AC3_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_HCCA_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_MGMT_DMA_DONE, 0); - rt2x00_set_field32(®, INT_MASK_CSR_MCU_COMMAND, 0); - rt2x00_set_field32(®, INT_MASK_CSR_RXTX_COHERENT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_TBTT, mask); - rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, mask); - rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, mask); - rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, mask); - rt2x00_set_field32(®, INT_MASK_CSR_GPTIMER, 0); - rt2x00_set_field32(®, INT_MASK_CSR_RX_COHERENT, 0); - rt2x00_set_field32(®, INT_MASK_CSR_TX_COHERENT, 0); + reg = 0; + if (state == STATE_RADIO_IRQ_ON) { + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); + } rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); -- cgit v0.10.2 From 543d1b92d5500841e70f15e79d197bbffed36561 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Jan 2012 14:17:59 +0100 Subject: mac80211: fix no-op authorized transitions When userspace attempts to authorize a station that is already authorized, nothing happens as you'd expect. Similarly, when it unauthorizes a station that is associated, nothing happens. However, when it unauthorizes a station that isn't even associated yet, we erroneously try to move the station to associated. This seems to happen occasionally as a result of a race when wpa_supplicant attempts to unauthorize the port in managed mode. Particularly with my new patches to keep stations, it can then move a station into ASSOCIATED state before we have really associated, which is really confusing. I introduced this bug in "mac80211: refactor station state transitions" Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e60df48..296620d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -791,7 +791,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) ret = sta_info_move_state_checked(sta, IEEE80211_STA_AUTHORIZED); - else + else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) ret = sta_info_move_state_checked(sta, IEEE80211_STA_ASSOC); if (ret) -- cgit v0.10.2 From 00c2064b7766c4e24af42e21da1903aedc8ca4c0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 13 Jan 2012 15:48:39 -0800 Subject: drm/i915: sprite init failure on pre-SNB is not a failure We can call the plane init function unconditionally, but don't need to complain if it fails, since that will only happen if we're out of memory (and other things will fail) or if we're on the wrong platform (which is ok). And remove the DRM_ERRORs from the sprite code itself to avoid dmesg spam. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 96cea08..b3b51c4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9028,12 +9028,9 @@ void intel_modeset_init(struct drm_device *dev) for (i = 0; i < dev_priv->num_pipe; i++) { intel_crtc_init(dev, i); - if (HAS_PCH_SPLIT(dev)) { - ret = intel_plane_init(dev, i); - if (ret) - DRM_ERROR("plane %d init failed: %d\n", - i, ret); - } + ret = intel_plane_init(dev, i); + if (ret) + DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret); } /* Just disable it once at startup */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index d13989f..2288abf 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -466,10 +466,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); - if (ret) { - DRM_ERROR("failed to pin object\n"); + if (ret) goto out_unlock; - } intel_plane->obj = obj; @@ -632,10 +630,8 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) unsigned long possible_crtcs; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) { - DRM_ERROR("new plane code only for SNB+\n"); + if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; - } intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL); if (!intel_plane) -- cgit v0.10.2 From 8109021313c7a3d8947677391ce6ab9cd0bb1d28 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 13 Jan 2012 16:20:06 -0800 Subject: drm/i915: convert force_wake_get to func pointer in the gpu reset code This was forgotten in the original multi-threaded forcewake conversion: commit 8d715f0024f64ad1b1be85d8c081cf577944c847 Author: Keith Packard Date: Fri Nov 18 20:39:01 2011 -0800 drm/i915: add multi-threaded forcewake support Signed-Off-by: Daniel Vetter Reviewed-by: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 8f71879..46c36f5 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -645,7 +645,7 @@ int i915_reset(struct drm_device *dev, u8 flags) ret = gen6_do_reset(dev, flags); /* If reset with a user forcewake, try to restore */ if (atomic_read(&dev_priv->forcewake_count)) - __gen6_gt_force_wake_get(dev_priv); + dev_priv->display.force_wake_get(dev_priv); break; case 5: ret = ironlake_do_reset(dev, flags); -- cgit v0.10.2 From 84e31fdb7c797a7303e0cc295cb9bc8b73fb872d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 14 Jan 2012 21:27:37 +0300 Subject: crypto: sha512 - make it work, undo percpu message schedule commit f9e2bca6c22d75a289a349f869701214d63b5060 aka "crypto: sha512 - Move message schedule W[80] to static percpu area" created global message schedule area. If sha512_update will ever be entered twice, hash will be silently calculated incorrectly. Probably the easiest way to notice incorrect hashes being calculated is to run 2 ping floods over AH with hmac(sha512): #!/usr/sbin/setkey -f flush; spdflush; add IP1 IP2 ah 25 -A hmac-sha512 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025; add IP2 IP1 ah 52 -A hmac-sha512 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052; spdadd IP1 IP2 any -P out ipsec ah/transport//require; spdadd IP2 IP1 any -P in ipsec ah/transport//require; XfrmInStateProtoError will start ticking with -EBADMSG being returned from ah_input(). This never happens with, say, hmac(sha1). With patch applied (on BOTH sides), XfrmInStateProtoError does not tick with multiple bidirectional ping flood streams like it doesn't tick with SHA-1. After this patch sha512_transform() will start using ~750 bytes of stack on x86_64. This is OK for simple loads, for something more heavy, stack reduction will be done separatedly. Signed-off-by: Alexey Dobriyan Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 9ed9f60..8b9035b 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -21,8 +21,6 @@ #include #include -static DEFINE_PER_CPU(u64[80], msg_schedule); - static inline u64 Ch(u64 x, u64 y, u64 z) { return z ^ (x & (y ^ z)); @@ -89,7 +87,7 @@ sha512_transform(u64 *state, const u8 *input) u64 a, b, c, d, e, f, g, h, t1, t2; int i; - u64 *W = get_cpu_var(msg_schedule); + u64 W[80]; /* load the input */ for (i = 0; i < 16; i++) @@ -128,8 +126,6 @@ sha512_transform(u64 *state, const u8 *input) /* erase our data */ a = b = c = d = e = f = g = h = t1 = t2 = 0; - memset(W, 0, sizeof(__get_cpu_var(msg_schedule))); - put_cpu_var(msg_schedule); } static int -- cgit v0.10.2 From 51fc6dc8f948047364f7d42a4ed89b416c6cc0a3 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 14 Jan 2012 21:40:57 +0300 Subject: crypto: sha512 - reduce stack usage to safe number For rounds 16--79, W[i] only depends on W[i - 2], W[i - 7], W[i - 15] and W[i - 16]. Consequently, keeping all W[80] array on stack is unnecessary, only 16 values are really needed. Using W[16] instead of W[80] greatly reduces stack usage (~750 bytes to ~340 bytes on x86_64). Line by line explanation: * BLEND_OP array is "circular" now, all indexes have to be modulo 16. Round number is positive, so remainder operation should be without surprises. * initial full message scheduling is trimmed to first 16 values which come from data block, the rest is calculated before it's needed. * original loop body is unrolled version of new SHA512_0_15 and SHA512_16_79 macros, unrolling was done to not do explicit variable renaming. Otherwise it's the very same code after preprocessing. See sha1_transform() code which does the same trick. Patch survives in-tree crypto test and original bugreport test (ping flood with hmac(sha512). See FIPS 180-2 for SHA-512 definition http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf Signed-off-by: Alexey Dobriyan Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 8b9035b..88f160b 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -78,7 +78,7 @@ static inline void LOAD_OP(int I, u64 *W, const u8 *input) static inline void BLEND_OP(int I, u64 *W) { - W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; + W[I % 16] += s1(W[(I-2) % 16]) + W[(I-7) % 16] + s0(W[(I-15) % 16]); } static void @@ -87,38 +87,48 @@ sha512_transform(u64 *state, const u8 *input) u64 a, b, c, d, e, f, g, h, t1, t2; int i; - u64 W[80]; + u64 W[16]; /* load the input */ for (i = 0; i < 16; i++) LOAD_OP(i, W, input); - for (i = 16; i < 80; i++) { - BLEND_OP(i, W); - } - /* load the state into our registers */ a=state[0]; b=state[1]; c=state[2]; d=state[3]; e=state[4]; f=state[5]; g=state[6]; h=state[7]; - /* now iterate */ - for (i=0; i<80; i+=8) { - t1 = h + e1(e) + Ch(e,f,g) + sha512_K[i ] + W[i ]; - t2 = e0(a) + Maj(a,b,c); d+=t1; h=t1+t2; - t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[i+1]; - t2 = e0(h) + Maj(h,a,b); c+=t1; g=t1+t2; - t1 = f + e1(c) + Ch(c,d,e) + sha512_K[i+2] + W[i+2]; - t2 = e0(g) + Maj(g,h,a); b+=t1; f=t1+t2; - t1 = e + e1(b) + Ch(b,c,d) + sha512_K[i+3] + W[i+3]; - t2 = e0(f) + Maj(f,g,h); a+=t1; e=t1+t2; - t1 = d + e1(a) + Ch(a,b,c) + sha512_K[i+4] + W[i+4]; - t2 = e0(e) + Maj(e,f,g); h+=t1; d=t1+t2; - t1 = c + e1(h) + Ch(h,a,b) + sha512_K[i+5] + W[i+5]; - t2 = e0(d) + Maj(d,e,f); g+=t1; c=t1+t2; - t1 = b + e1(g) + Ch(g,h,a) + sha512_K[i+6] + W[i+6]; - t2 = e0(c) + Maj(c,d,e); f+=t1; b=t1+t2; - t1 = a + e1(f) + Ch(f,g,h) + sha512_K[i+7] + W[i+7]; - t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; +#define SHA512_0_15(i, a, b, c, d, e, f, g, h) \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[i]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2 + +#define SHA512_16_79(i, a, b, c, d, e, f, g, h) \ + BLEND_OP(i, W); \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)%16]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2 + + for (i = 0; i < 16; i += 8) { + SHA512_0_15(i, a, b, c, d, e, f, g, h); + SHA512_0_15(i + 1, h, a, b, c, d, e, f, g); + SHA512_0_15(i + 2, g, h, a, b, c, d, e, f); + SHA512_0_15(i + 3, f, g, h, a, b, c, d, e); + SHA512_0_15(i + 4, e, f, g, h, a, b, c, d); + SHA512_0_15(i + 5, d, e, f, g, h, a, b, c); + SHA512_0_15(i + 6, c, d, e, f, g, h, a, b); + SHA512_0_15(i + 7, b, c, d, e, f, g, h, a); + } + for (i = 16; i < 80; i += 8) { + SHA512_16_79(i, a, b, c, d, e, f, g, h); + SHA512_16_79(i + 1, h, a, b, c, d, e, f, g); + SHA512_16_79(i + 2, g, h, a, b, c, d, e, f); + SHA512_16_79(i + 3, f, g, h, a, b, c, d, e); + SHA512_16_79(i + 4, e, f, g, h, a, b, c, d); + SHA512_16_79(i + 5, d, e, f, g, h, a, b, c); + SHA512_16_79(i + 6, c, d, e, f, g, h, a, b); + SHA512_16_79(i + 7, b, c, d, e, f, g, h, a); } state[0] += a; state[1] += b; state[2] += c; state[3] += d; -- cgit v0.10.2 From f4b1952a453f4b4b4c4cd3436b9cd632bf4202aa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 13 Jan 2012 11:09:23 -0200 Subject: [media] dvb-core: fix a regression with MythTV As reported by Lawrence[1], MythTV 0.24.1 does the wrong thing with a DVBv5 call: it fills the delivery system with SYS_UNDEFINED, expecting that the DVB core would work with that. This used to work by accident, as the DVB core were missing the check for the supported delivery systems. Yet, fixing it is easy, so let's add a logic to handle this case, to provide backward compatibility. [1] http://patchwork.linuxtv.org/patch/8314/ Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index b15db4f..2ad7faf 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1413,6 +1413,15 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) struct dtv_frontend_properties *c = &fe->dtv_property_cache; enum dvbv3_emulation_type type; + /* + * It was reported that some old DVBv5 applications were + * filling delivery_system with SYS_UNDEFINED. If this happens, + * assume that the application wants to use the first supported + * delivery system. + */ + if (c->delivery_system == SYS_UNDEFINED) + c->delivery_system = fe->ops.delsys[0]; + if (desired_system == SYS_UNDEFINED) { /* * A DVBv3 call doesn't know what's the desired system. -- cgit v0.10.2 From 149709f5b8a4a8678401facb5c670119751f6087 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 13 Jan 2012 11:46:36 -0200 Subject: [media] dvb-core: preserve the delivery system at cache clear The changeset 240ab508aa is incomplete, as the first thing that happens at cache clear is to do a memset with 0 to the cache. So, the delivery system needs to be explicitly preserved there. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 2ad7faf..f5fa7aa 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -904,8 +904,11 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i; + u32 delsys; + delsys = c->delivery_system; memset(c, 0, sizeof(struct dtv_frontend_properties)); + c->delivery_system = delsys; c->state = DTV_CLEAR; -- cgit v0.10.2 From 0eecee413d3157bac4477362bfc4b907b4b2988b Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 3 Nov 2011 11:10:40 -0400 Subject: [SCSI] scsi_transport_fc: Clear Devloss Callback Done flag in fc_remote_port_rolechg This patch fixes a bug where devloss is not called on fc_host teardown. The issue is seen if the LLDD uses rport_rolechg to add the target role to an rport. When an rport goes away, the LLDD will call fc_remote_port_delete, which will start the devloss timer. If the timer expires, the transport will call the devloss callback and set the FC_RPORT_DEVLOSS_CALLBK_DONE flag. However, the rport structure is not deleted, it is retained to store the SCSI id mappings for the rport in case it comes back. In the scenario where it does come back, and the driver calls fc_remote_port_add, but does not indicate the "target" role for the rport - the create will clear the structure, but forgets to clear FC_RPORT_DEVLOSS_CALLBK_DONE flag (which is cleared if it's added with the target role). The secondary call, of fc_remote_port_rolechg to add the target role also does not clear the flag. Thus, the next time the rport goes away, the resulting devloss timer expiration will not call the driver callback as the flag is still set. This patch adds the FC_RPORT_DEVLOSS_CALLBK_DONE flags to the list of those that are cleared upon reuse of the rport structure. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 1b21491..f59d4a0 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3048,7 +3048,8 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) spin_lock_irqsave(shost->host_lock, flags); rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT | - FC_RPORT_DEVLOSS_PENDING); + FC_RPORT_DEVLOSS_PENDING | + FC_RPORT_DEVLOSS_CALLBK_DONE); spin_unlock_irqrestore(shost->host_lock, flags); /* ensure any stgt delete functions are done */ -- cgit v0.10.2 From f2d7e40e8069f0d6b4958dbda4383f412f53c7d1 Mon Sep 17 00:00:00 2001 From: Alok Kataria Date: Thu, 10 Nov 2011 20:04:03 -0800 Subject: [SCSI] MAINTAINERS: pvscsi maintainers update Welcome to Arvind Kumar, our new pvscsi maintainer. Signed-off-by: Alok N Kataria Signed-off-by: James Bottomley diff --git a/MAINTAINERS b/MAINTAINERS index a2883a2..9a2b8f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7171,7 +7171,7 @@ S: Maintained F: drivers/net/vmxnet3/ VMware PVSCSI driver -M: Alok Kataria +M: Arvind Kumar M: VMware PV-Drivers L: linux-scsi@vger.kernel.org S: Maintained -- cgit v0.10.2 From 775bf2773858c50d2acfcdf71889984be94e7037 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 5 Jan 2012 10:15:31 -0600 Subject: [SCSI] hpsa: do not sleep in atomic context in rmmod path. Don't call kthread_stop with a spin lock held and interrupts disabled because kthread_stop will sleep waiting for the thread to stop. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 5140f5d..b96962c 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4271,7 +4271,9 @@ static void stop_controller_lockup_detector(struct ctlr_info *h) remove_ctlr_from_lockup_detector_list(h); /* If the list of ctlr's to monitor is empty, stop the thread */ if (list_empty(&hpsa_ctlr_list)) { + spin_unlock_irqrestore(&lockup_detector_lock, flags); kthread_stop(hpsa_lockup_detector); + spin_lock_irqsave(&lockup_detector_lock, flags); hpsa_lockup_detector = NULL; } spin_unlock_irqrestore(&lockup_detector_lock, flags); -- cgit v0.10.2 From f7e6746ebae984ea67b0a1a1e23c7e6698240631 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 14 Jan 2012 00:01:01 +0000 Subject: sched/accounting, proc: Fix /proc/stat interrupts sum Commit 3292beb340c7688 ("sched/accounting: Change cpustat fields to an array") deleted the code which provides us with the sum of all interrupts in the system, causing vmstat to report zero interrupts occuring in the system. Fix this by restoring the code. Signed-off-by: Russell King Tested-by: Russell King # [on ARM] Tested-by: Tony Luck Tested-by: Steven Rostedt Cc: Glauber Costa Cc: KAMEZAWA Hiroyuki Cc: Linus Torvalds Cc: Andrew Morton Cc: Paul Tuner Cc: Peter Zijlstra Signed-off-by: Ingo Molnar diff --git a/fs/proc/stat.c b/fs/proc/stat.c index d76ca6a..121f77c 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -77,6 +77,8 @@ static int show_stat(struct seq_file *p, void *v) steal += kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; guest += kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; guest_nice += kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; + sum += kstat_cpu_irqs_sum(i); + sum += arch_irq_stat_cpu(i); for (j = 0; j < NR_SOFTIRQS; j++) { unsigned int softirq_stat = kstat_softirqs_cpu(j, i); -- cgit v0.10.2 From 8d973b624ece3b85cfae9474935795d034f72faf Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 15 Jan 2012 19:40:24 -0500 Subject: x86/kprobes: Fix typo transferred from Intel manual The arch/x86/lib/x86-opcode-map.txt file [used by the kprobes instruction decoder] contains the line: af: SCAS/W/D/Q rAX,Xv This is what the Intel manuals show, but it's not correct. The 'X' stands for: Memory addressed by the DS:rSI register pair (for example, MOVS, CMPS, OUTS, or LODS). On the other hand 'Y' means (also see the ae byte entry for SCASB): Memory addressed by the ES:rDI register pair (for example, MOVS, CMPS, INS, STOS, or SCAS). Signed-off-by: Ulrich Drepper Acked-by: Masami Hiramatsu Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/CAOPLpQfytPyDEBF1Hbkpo7ovUerEsstVGxBr%3DEpDL-BKEMaqLA@mail.gmail.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/lib/x86-opcode-map.txt b/arch/x86/lib/x86-opcode-map.txt index a793da5..8641bbb 100644 --- a/arch/x86/lib/x86-opcode-map.txt +++ b/arch/x86/lib/x86-opcode-map.txt @@ -210,7 +210,9 @@ ab: STOS/W/D/Q Yv,rAX ac: LODS/B AL,Xb ad: LODS/W/D/Q rAX,Xv ae: SCAS/B AL,Yb -af: SCAS/W/D/Q rAX,Xv +# Note: The May 2011 Intel manual shows Xv for the second parameter of the +# next instruction but Yv is correct +af: SCAS/W/D/Q rAX,Yv # 0xb0 - 0xbf b0: MOV AL/R8L,Ib b1: MOV CL/R9L,Ib -- cgit v0.10.2 From a1c611745c8c4e8996c1877d4e5d0fc95f227c38 Mon Sep 17 00:00:00 2001 From: "xiyou.wangcong@gmail.com" Date: Sun, 15 Jan 2012 20:02:17 +0800 Subject: x86/kprobes: Add arch/x86/tools/insn_sanity to .gitignore After compiling the kernel, I got: % git status # On branch master # Untracked files: # (use "git add ..." to include in what will be committed) # # arch/x86/tools/insn_sanity nothing added to commit but untracked files present (use "git add" to track) it should be added to .gitignore. Signed-off-by: WANG Cong Acked-by: Masami Hiramatsu Link: http://lkml.kernel.org/r/1326628937-27609-1-git-send-email-xiyou.wangcong@gmail.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/.gitignore b/arch/x86/.gitignore index 0280790..7cab8c0 100644 --- a/arch/x86/.gitignore +++ b/arch/x86/.gitignore @@ -1,3 +1,4 @@ boot/compressed/vmlinux tools/test_get_len +tools/insn_sanity -- cgit v0.10.2 From 7f788ec07d169123bb1bd14e6221a2b228079ce8 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 12 Jan 2012 09:18:03 +0100 Subject: USB: EHCI: Don't use NO_IRQ in xilinx ehci driver Drivers shouldn't use NO_IRQ. This driver is used by Microblaze and PPC. PPC defines NO_IRQ as 0 and Microblaze has removed it. Signed-off-by: Michal Simek Acked-by: Alan Stern CC: Greg Kroah-Hartman CC: Grant Likely CC: linux-usb@vger.kernel.org CC: devicetree-discuss@lists.ozlabs.org diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 32793ce..9c2cc46 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -183,7 +183,7 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) } irq = irq_of_parse_and_map(dn, 0); - if (irq == NO_IRQ) { + if (!irq) { printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); rv = -EBUSY; goto err_irq; -- cgit v0.10.2 From 0d4c24cc1bf62e75fdb31e73c68daa50e0877564 Mon Sep 17 00:00:00 2001 From: adam radford Date: Fri, 6 Jan 2012 17:02:26 -0800 Subject: [SCSI] megaraid_sas: Fix reglockFlags for degraded raid5/6 The following patch for megaraid_sas fixes the reglockFlags field for degraded raid5/6 for MR9360/9380, which will result in a performance improvement. Signed-off-by: Adam Radford Signed-off-by: James Bottomley diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 5255dd6..294abb0 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -282,7 +282,9 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow, else { *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */ if ((raid->level >= 5) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER)) + ((instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER && + raid->regTypeReqOnRead != REGION_TYPE_UNUSED))) pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE; else if (raid->level == 1) { /* Get alternate Pd. */ -- cgit v0.10.2 From 882be7c3be72f6d72ae7a81e707154287ea5d289 Mon Sep 17 00:00:00 2001 From: adam radford Date: Fri, 6 Jan 2012 17:02:34 -0800 Subject: [SCSI] megaraid_sas: mask off flags in ioctl path Mask off flags in the ioctl path to prevent memory scribble with older MegaCLI versions. Signed-off-by: Adam Radford Signed-off-by: James Bottomley diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 29a994f..2bdc707 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -4773,6 +4773,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); cmd->frame->hdr.context = cmd->index; cmd->frame->hdr.pad_0 = 0; + cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 | + MFI_FRAME_SENSE64); /* * The management interface between applications and the fw uses -- cgit v0.10.2 From 7895f9c957ff5dbce8e1b96ed11919bb2e20641f Mon Sep 17 00:00:00 2001 From: adam radford Date: Fri, 6 Jan 2012 17:02:40 -0800 Subject: [SCSI] megaraid_sas: remove poll_mode_io code This code has never worked correctly, doesn't disable interrupts when set as a module parameter, doesn't disable interrupts when set after driver load time in sysfs node, etc. Signed-off-by: Adam Radford Signed-off-by: James Bottomley diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index dd94c7d..7a03a9a 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -773,7 +773,6 @@ struct megasas_ctrl_info { #define MFI_OB_INTR_STATUS_MASK 0x00000002 #define MFI_POLL_TIMEOUT_SECS 60 -#define MEGASAS_COMPLETION_TIMER_INTERVAL (HZ/10) #define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000 #define MFI_REPLY_GEN2_MESSAGE_INTERRUPT 0x00000001 @@ -1353,7 +1352,6 @@ struct megasas_instance { u32 mfiStatus; u32 last_seq_num; - struct timer_list io_completion_timer; struct list_head internal_reset_pending_q; /* Ptr to hba specific information */ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 2bdc707..f42bd88 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -59,14 +59,6 @@ #include "megaraid_sas.h" /* - * poll_mode_io:1- schedule complete completion from q cmd - */ -static unsigned int poll_mode_io; -module_param_named(poll_mode_io, poll_mode_io, int, 0); -MODULE_PARM_DESC(poll_mode_io, - "Complete cmds from IO path, (default=0)"); - -/* * Number of sectors per IO command * Will be set in megasas_init_mfi if user does not provide */ @@ -1439,11 +1431,6 @@ megasas_build_and_issue_cmd(struct megasas_instance *instance, instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, cmd->frame_count-1, instance->reg_set); - /* - * Check if we have pend cmds to be completed - */ - if (poll_mode_io && atomic_read(&instance->fw_outstanding)) - tasklet_schedule(&instance->isr_tasklet); return 0; out_return_cmd: @@ -3370,47 +3357,6 @@ fail_fw_init: return -EINVAL; } -/** - * megasas_start_timer - Initializes a timer object - * @instance: Adapter soft state - * @timer: timer object to be initialized - * @fn: timer function - * @interval: time interval between timer function call - */ -static inline void -megasas_start_timer(struct megasas_instance *instance, - struct timer_list *timer, - void *fn, unsigned long interval) -{ - init_timer(timer); - timer->expires = jiffies + interval; - timer->data = (unsigned long)instance; - timer->function = fn; - add_timer(timer); -} - -/** - * megasas_io_completion_timer - Timer fn - * @instance_addr: Address of adapter soft state - * - * Schedules tasklet for cmd completion - * if poll_mode_io is set - */ -static void -megasas_io_completion_timer(unsigned long instance_addr) -{ - struct megasas_instance *instance = - (struct megasas_instance *)instance_addr; - - if (atomic_read(&instance->fw_outstanding)) - tasklet_schedule(&instance->isr_tasklet); - - /* Restart timer */ - if (poll_mode_io) - mod_timer(&instance->io_completion_timer, - jiffies + MEGASAS_COMPLETION_TIMER_INTERVAL); -} - static u32 megasas_init_adapter_mfi(struct megasas_instance *instance) { @@ -3638,11 +3584,6 @@ static int megasas_init_fw(struct megasas_instance *instance) tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, (unsigned long)instance); - /* Initialize the cmd completion timer */ - if (poll_mode_io) - megasas_start_timer(instance, &instance->io_completion_timer, - megasas_io_completion_timer, - MEGASAS_COMPLETION_TIMER_INTERVAL); return 0; fail_init_adapter: @@ -4369,9 +4310,6 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) host = instance->host; instance->unload = 1; - if (poll_mode_io) - del_timer_sync(&instance->io_completion_timer); - megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); @@ -4511,12 +4449,6 @@ megasas_resume(struct pci_dev *pdev) } instance->instancet->enable_intr(instance->reg_set); - - /* Initialize the cmd completion timer */ - if (poll_mode_io) - megasas_start_timer(instance, &instance->io_completion_timer, - megasas_io_completion_timer, - MEGASAS_COMPLETION_TIMER_INTERVAL); instance->unload = 0; /* @@ -4570,9 +4502,6 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) host = instance->host; fusion = instance->ctrl_context; - if (poll_mode_io) - del_timer_sync(&instance->io_completion_timer); - scsi_remove_host(instance->host); megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); @@ -5221,60 +5150,6 @@ megasas_sysfs_set_dbg_lvl(struct device_driver *dd, const char *buf, size_t coun static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUSR, megasas_sysfs_show_dbg_lvl, megasas_sysfs_set_dbg_lvl); -static ssize_t -megasas_sysfs_show_poll_mode_io(struct device_driver *dd, char *buf) -{ - return sprintf(buf, "%u\n", poll_mode_io); -} - -static ssize_t -megasas_sysfs_set_poll_mode_io(struct device_driver *dd, - const char *buf, size_t count) -{ - int retval = count; - int tmp = poll_mode_io; - int i; - struct megasas_instance *instance; - - if (sscanf(buf, "%u", &poll_mode_io) < 1) { - printk(KERN_ERR "megasas: could not set poll_mode_io\n"); - retval = -EINVAL; - } - - /* - * Check if poll_mode_io is already set or is same as previous value - */ - if ((tmp && poll_mode_io) || (tmp == poll_mode_io)) - goto out; - - if (poll_mode_io) { - /* - * Start timers for all adapters - */ - for (i = 0; i < megasas_mgmt_info.max_index; i++) { - instance = megasas_mgmt_info.instance[i]; - if (instance) { - megasas_start_timer(instance, - &instance->io_completion_timer, - megasas_io_completion_timer, - MEGASAS_COMPLETION_TIMER_INTERVAL); - } - } - } else { - /* - * Delete timers for all adapters - */ - for (i = 0; i < megasas_mgmt_info.max_index; i++) { - instance = megasas_mgmt_info.instance[i]; - if (instance) - del_timer_sync(&instance->io_completion_timer); - } - } - -out: - return retval; -} - static void megasas_aen_polling(struct work_struct *work) { @@ -5504,11 +5379,6 @@ megasas_aen_polling(struct work_struct *work) kfree(ev); } - -static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUSR, - megasas_sysfs_show_poll_mode_io, - megasas_sysfs_set_poll_mode_io); - /** * megasas_init - Driver load entry point */ @@ -5568,11 +5438,6 @@ static int __init megasas_init(void) if (rval) goto err_dcf_dbg_lvl; rval = driver_create_file(&megasas_pci_driver.driver, - &driver_attr_poll_mode_io); - if (rval) - goto err_dcf_poll_mode_io; - - rval = driver_create_file(&megasas_pci_driver.driver, &driver_attr_support_device_change); if (rval) goto err_dcf_support_device_change; @@ -5581,10 +5446,6 @@ static int __init megasas_init(void) err_dcf_support_device_change: driver_remove_file(&megasas_pci_driver.driver, - &driver_attr_poll_mode_io); - -err_dcf_poll_mode_io: - driver_remove_file(&megasas_pci_driver.driver, &driver_attr_dbg_lvl); err_dcf_dbg_lvl: driver_remove_file(&megasas_pci_driver.driver, @@ -5609,8 +5470,6 @@ err_pcidrv: static void __exit megasas_exit(void) { driver_remove_file(&megasas_pci_driver.driver, - &driver_attr_poll_mode_io); - driver_remove_file(&megasas_pci_driver.driver, &driver_attr_dbg_lvl); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_poll_for_event); -- cgit v0.10.2 From 798edaadba97612224c250d50f880405d75a3af1 Mon Sep 17 00:00:00 2001 From: adam radford Date: Fri, 6 Jan 2012 17:02:45 -0800 Subject: [SCSI] megaraid_sas: Version and Changelog update Signed-off-by: Adam Radford Signed-off-by: James Bottomley diff --git a/Documentation/scsi/ChangeLog.megaraid_sas b/Documentation/scsi/ChangeLog.megaraid_sas index 64adb98..57566ba 100644 --- a/Documentation/scsi/ChangeLog.megaraid_sas +++ b/Documentation/scsi/ChangeLog.megaraid_sas @@ -1,3 +1,13 @@ +Release Date : Fri. Jan 6, 2012 17:00:00 PST 2010 - + (emaild-id:megaraidlinux@lsi.com) + Adam Radford +Current Version : 00.00.06.14-rc1 +Old Version : 00.00.06.12-rc1 + 1. Fix reglockFlags for degraded raid5/6 for MR 9360/9380. + 2. Mask off flags in ioctl path to prevent memory scribble with older + MegaCLI versions. + 3. Remove poll_mode_io module paramater, sysfs node, and associated code. +------------------------------------------------------------------------------- Release Date : Wed. Oct 5, 2011 17:00:00 PST 2010 - (emaild-id:megaraidlinux@lsi.com) Adam Radford diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 7a03a9a..e5f416f 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -33,9 +33,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.06.12-rc1" -#define MEGASAS_RELDATE "Oct. 5, 2011" -#define MEGASAS_EXT_VERSION "Wed. Oct. 5 17:00:00 PDT 2011" +#define MEGASAS_VERSION "00.00.06.14-rc1" +#define MEGASAS_RELDATE "Jan. 6, 2012" +#define MEGASAS_EXT_VERSION "Fri. Jan. 6 17:00:00 PDT 2012" /* * Device IDs diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index f42bd88..8b300be 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FILE: megaraid_sas_base.c - * Version : v00.00.06.12-rc1 + * Version : v00.00.06.14-rc1 * * Authors: LSI Corporation * Sreenivas Bagalkote -- cgit v0.10.2 From 7d99b3abaf8412932269a2a2e561138326d61f8b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 4 Jan 2012 01:32:28 -0800 Subject: [SCSI] isci, firmware: Remove isci fallback parameter blob and generator This parameter blob and generator program have been moved to the linux-firmware.git repository. Signed-off-by: Ben Hutchings Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/firmware/Makefile b/drivers/scsi/isci/firmware/Makefile deleted file mode 100644 index 5f54461..0000000 --- a/drivers/scsi/isci/firmware/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Makefile for create_fw -# -CC=gcc -CFLAGS=-c -Wall -O2 -g -LDFLAGS= -SOURCES=create_fw.c -OBJECTS=$(SOURCES:.cpp=.o) -EXECUTABLE=create_fw - -all: $(SOURCES) $(EXECUTABLE) - -$(EXECUTABLE): $(OBJECTS) - $(CC) $(LDFLAGS) $(OBJECTS) -o $@ - -.c.o: - $(CC) $(CFLAGS) $< -O $@ - -clean: - rm -f *.o $(EXECUTABLE) diff --git a/drivers/scsi/isci/firmware/README b/drivers/scsi/isci/firmware/README deleted file mode 100644 index 8056d2b..0000000 --- a/drivers/scsi/isci/firmware/README +++ /dev/null @@ -1,36 +0,0 @@ -This defines the temporary binary blow we are to pass to the SCU -driver to emulate the binary firmware that we will eventually be -able to access via NVRAM on the SCU controller. - -The current size of the binary blob is expected to be 149 bytes or larger - -Header Types: -0x1: Phy Masks -0x2: Phy Gens -0x3: SAS Addrs -0xff: End of Data - -ID string - u8[12]: "#SCU MAGIC#\0" -Version - u8: 1 -SubVersion - u8: 0 - -Header Type - u8: 0x1 -Size - u8: 8 -Phy Mask - u32[8] - -Header Type - u8: 0x2 -Size - u8: 8 -Phy Gen - u32[8] - -Header Type - u8: 0x3 -Size - u8: 8 -Sas Addr - u64[8] - -Header Type - u8: 0xf - - -============================================================================== - -Place isci_firmware.bin in /lib/firmware -Be sure to recreate the initramfs image to include the firmware. - diff --git a/drivers/scsi/isci/firmware/create_fw.c b/drivers/scsi/isci/firmware/create_fw.c deleted file mode 100644 index c7a2887..0000000 --- a/drivers/scsi/isci/firmware/create_fw.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "create_fw.h" -#include "../probe_roms.h" - -int write_blob(struct isci_orom *isci_orom) -{ - FILE *fd; - int err; - size_t count; - - fd = fopen(blob_name, "w+"); - if (!fd) { - perror("Open file for write failed"); - fclose(fd); - return -EIO; - } - - count = fwrite(isci_orom, sizeof(struct isci_orom), 1, fd); - if (count != 1) { - perror("Write data failed"); - fclose(fd); - return -EIO; - } - - fclose(fd); - - return 0; -} - -void set_binary_values(struct isci_orom *isci_orom) -{ - int ctrl_idx, phy_idx, port_idx; - - /* setting OROM signature */ - strncpy(isci_orom->hdr.signature, sig, strlen(sig)); - isci_orom->hdr.version = version; - isci_orom->hdr.total_block_length = sizeof(struct isci_orom); - isci_orom->hdr.hdr_length = sizeof(struct sci_bios_oem_param_block_hdr); - isci_orom->hdr.num_elements = num_elements; - - for (ctrl_idx = 0; ctrl_idx < 2; ctrl_idx++) { - isci_orom->ctrl[ctrl_idx].controller.mode_type = mode_type; - isci_orom->ctrl[ctrl_idx].controller.max_concurrent_dev_spin_up = - max_num_concurrent_dev_spin_up; - isci_orom->ctrl[ctrl_idx].controller.do_enable_ssc = - enable_ssc; - - for (port_idx = 0; port_idx < 4; port_idx++) - isci_orom->ctrl[ctrl_idx].ports[port_idx].phy_mask = - phy_mask[ctrl_idx][port_idx]; - - for (phy_idx = 0; phy_idx < 4; phy_idx++) { - isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.high = - (__u32)(sas_addr[ctrl_idx][phy_idx] >> 32); - isci_orom->ctrl[ctrl_idx].phys[phy_idx].sas_address.low = - (__u32)(sas_addr[ctrl_idx][phy_idx]); - - isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control0 = - afe_tx_amp_control0; - isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control1 = - afe_tx_amp_control1; - isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control2 = - afe_tx_amp_control2; - isci_orom->ctrl[ctrl_idx].phys[phy_idx].afe_tx_amp_control3 = - afe_tx_amp_control3; - } - } -} - -int main(void) -{ - int err; - struct isci_orom *isci_orom; - - isci_orom = malloc(sizeof(struct isci_orom)); - memset(isci_orom, 0, sizeof(struct isci_orom)); - - set_binary_values(isci_orom); - - err = write_blob(isci_orom); - if (err < 0) { - free(isci_orom); - return err; - } - - free(isci_orom); - return 0; -} diff --git a/drivers/scsi/isci/firmware/create_fw.h b/drivers/scsi/isci/firmware/create_fw.h deleted file mode 100644 index 5f29882..0000000 --- a/drivers/scsi/isci/firmware/create_fw.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _CREATE_FW_H_ -#define _CREATE_FW_H_ -#include "../probe_roms.h" - - -/* we are configuring for 2 SCUs */ -static const int num_elements = 2; - -/* - * For all defined arrays: - * elements 0-3 are for SCU0, ports 0-3 - * elements 4-7 are for SCU1, ports 0-3 - * - * valid configurations for one SCU are: - * P0 P1 P2 P3 - * ---------------- - * 0xF,0x0,0x0,0x0 # 1 x4 port - * 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1 - * # ports - * 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2 - * # port - * 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port - * 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration) - * - * if there is a port/phy on which you do not wish to override the default - * values, use the value assigned to UNINIT_PARAM (255). - */ - -/* discovery mode type (port auto config mode by default ) */ - -/* - * if there is a port/phy on which you do not wish to override the default - * values, use the value "0000000000000000". SAS address of zero's is - * considered invalid and will not be used. - */ -#ifdef MPC -static const int mode_type = SCIC_PORT_MANUAL_CONFIGURATION_MODE; -static const __u8 phy_mask[2][4] = { {1, 2, 4, 8}, - {1, 2, 4, 8} }; -static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFFF0000001ULL, - 0x5FCFFFFFF0000002ULL, - 0x5FCFFFFFF0000003ULL, - 0x5FCFFFFFF0000004ULL }, - { 0x5FCFFFFFF0000005ULL, - 0x5FCFFFFFF0000006ULL, - 0x5FCFFFFFF0000007ULL, - 0x5FCFFFFFF0000008ULL } }; -#else /* APC (default) */ -static const int mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; -static const __u8 phy_mask[2][4]; -static const unsigned long long sas_addr[2][4] = { { 0x5FCFFFFF00000001ULL, - 0x5FCFFFFF00000001ULL, - 0x5FCFFFFF00000001ULL, - 0x5FCFFFFF00000001ULL }, - { 0x5FCFFFFF00000002ULL, - 0x5FCFFFFF00000002ULL, - 0x5FCFFFFF00000002ULL, - 0x5FCFFFFF00000002ULL } }; -#endif - -/* Maximum number of concurrent device spin up */ -static const int max_num_concurrent_dev_spin_up = 1; - -/* enable of ssc operation */ -static const int enable_ssc; - -/* AFE_TX_AMP_CONTROL */ -static const unsigned int afe_tx_amp_control0 = 0x000bdd08; -static const unsigned int afe_tx_amp_control1 = 0x000ffc00; -static const unsigned int afe_tx_amp_control2 = 0x000b7c09; -static const unsigned int afe_tx_amp_control3 = 0x000afc6e; - -static const char blob_name[] = "isci_firmware.bin"; -static const char sig[] = "ISCUOEMB"; -static const unsigned char version = 0x10; - -#endif diff --git a/firmware/Makefile b/firmware/Makefile index 5f43bfb..0d15a3d 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -82,7 +82,6 @@ fw-shipped-$(CONFIG_SERIAL_8250_CS) += cis/MT5634ZLX.cis cis/RS-COM-2P.cis \ fw-shipped-$(CONFIG_PCMCIA_SMC91C92) += ositech/Xilinx7OD.bin fw-shipped-$(CONFIG_SCSI_ADVANSYS) += advansys/mcode.bin advansys/38C1600.bin \ advansys/3550.bin advansys/38C0800.bin -fw-shipped-$(CONFIG_SCSI_ISCI) += isci/isci_firmware.bin fw-shipped-$(CONFIG_SCSI_QLOGIC_1280) += qlogic/1040.bin qlogic/1280.bin \ qlogic/12160.bin fw-shipped-$(CONFIG_SCSI_QLOGICPTI) += qlogic/isp1000.bin diff --git a/firmware/isci/isci_firmware.bin.ihex b/firmware/isci/isci_firmware.bin.ihex deleted file mode 100644 index 2e66195..0000000 --- a/firmware/isci/isci_firmware.bin.ihex +++ /dev/null @@ -1,16 +0,0 @@ -:10000000495343554F454D42E80018100002000087 -:1000100000000000000000000101000000000000DE -:10002000FFFFCF5F0100000008DD0B0000FC0F00A8 -:10003000097C0B006EFC0A00FFFFCF5F010000008F -:1000400008DD0B0000FC0F00097C0B006EFC0A00B1 -:10005000FFFFCF5F0100000008DD0B0000FC0F0078 -:10006000097C0B006EFC0A00FFFFCF5F010000005F -:1000700008DD0B0000FC0F00097C0B006EFC0A0081 -:100080000101000000000000FFFFCF5F0200000040 -:1000900008DD0B0000FC0F00097C0B006EFC0A0061 -:1000A000FFFFCF5F0200000008DD0B0000FC0F0027 -:1000B000097C0B006EFC0A00FFFFCF5F020000000E -:1000C00008DD0B0000FC0F00097C0B006EFC0A0031 -:1000D000FFFFCF5F0200000008DD0B0000FC0F00F7 -:0800E000097C0B006EFC0A0014 -:00000001FF -- cgit v0.10.2 From 5d53cb27d849c899136c048ec84c940ac449494b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 13 Jan 2012 10:14:12 -0800 Subject: memblock: Fix alloc failure due to dumb underflow protection in memblock_find_in_range_node() 7bd0b0f0da ("memblock: Reimplement memblock allocation using reverse free area iterator") implemented a simple top-down allocator using a reverse memblock iterator. To avoid underflow in the allocator loop, it simply raised the lower boundary to the requested size under the assumption that requested size would be far smaller than available memblocks. This causes early page table allocation failure under certain configurations in Xen. Fix it by checking for underflow directly instead of bumping up lower bound. Signed-off-by: Tejun Heo Reported-by: Konrad Rzeszutek Wilk Cc: rjw@sisk.pl Cc: xen-devel@lists.xensource.com Cc: Benjamin Herrenschmidt Cc: Andrew Morton Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20120113181412.GA11112@google.com Signed-off-by: Ingo Molnar diff --git a/mm/memblock.c b/mm/memblock.c index 2f55f19..77b5f22 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -106,14 +106,17 @@ phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t start, if (end == MEMBLOCK_ALLOC_ACCESSIBLE) end = memblock.current_limit; - /* adjust @start to avoid underflow and allocating the first page */ - start = max3(start, size, (phys_addr_t)PAGE_SIZE); + /* avoid allocating the first page */ + start = max_t(phys_addr_t, start, PAGE_SIZE); end = max(start, end); for_each_free_mem_range_reverse(i, nid, &this_start, &this_end, NULL) { this_start = clamp(this_start, start, end); this_end = clamp(this_end, start, end); + if (this_end < size) + continue; + cand = round_down(this_end - size, align); if (cand >= this_start) return cand; -- cgit v0.10.2 From 2e5da889d44a3a9629f895de3488306e7f5ddf16 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 4 Jan 2012 01:32:34 -0800 Subject: [SCSI] isci: cleanup oem parameter and recipe handling Before updating the code to support the latest platform updates and silicon revision cleanup some of the long deref chains. Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index e7fe9c4..8e7de19 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1910,160 +1910,147 @@ void sci_controller_power_control_queue_remove(struct isci_host *ihost, #define AFE_REGISTER_WRITE_DELAY 10 -/* Initialize the AFE for this phy index. We need to read the AFE setup from - * the OEM parameters - */ static void sci_controller_afe_initialization(struct isci_host *ihost) { + struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; const struct sci_oem_params *oem = &ihost->oem_parameters; struct pci_dev *pdev = ihost->pdev; u32 afe_status; u32 phy_id; /* Clear DFX Status registers */ - writel(0x0081000f, &ihost->scu_registers->afe.afe_dfx_master_control0); + writel(0x0081000f, &afe->afe_dfx_master_control0); udelay(AFE_REGISTER_WRITE_DELAY); if (is_b0(pdev)) { /* PM Rx Equalization Save, PM SPhy Rx Acknowledgement - * Timer, PM Stagger Timer */ - writel(0x0007BFFF, &ihost->scu_registers->afe.afe_pmsn_master_control2); + * Timer, PM Stagger Timer + */ + writel(0x0007BFFF, &afe->afe_pmsn_master_control2); udelay(AFE_REGISTER_WRITE_DELAY); } /* Configure bias currents to normal */ if (is_a2(pdev)) - writel(0x00005A00, &ihost->scu_registers->afe.afe_bias_control); + writel(0x00005A00, &afe->afe_bias_control); else if (is_b0(pdev) || is_c0(pdev)) - writel(0x00005F00, &ihost->scu_registers->afe.afe_bias_control); + writel(0x00005F00, &afe->afe_bias_control); udelay(AFE_REGISTER_WRITE_DELAY); /* Enable PLL */ if (is_b0(pdev) || is_c0(pdev)) - writel(0x80040A08, &ihost->scu_registers->afe.afe_pll_control0); + writel(0x80040A08, &afe->afe_pll_control0); else - writel(0x80040908, &ihost->scu_registers->afe.afe_pll_control0); + writel(0x80040908, &afe->afe_pll_control0); udelay(AFE_REGISTER_WRITE_DELAY); /* Wait for the PLL to lock */ do { - afe_status = readl(&ihost->scu_registers->afe.afe_common_block_status); + afe_status = readl(&afe->afe_common_block_status); udelay(AFE_REGISTER_WRITE_DELAY); } while ((afe_status & 0x00001000) == 0); if (is_a2(pdev)) { - /* Shorten SAS SNW lock time (RxLock timer value from 76 us to 50 us) */ - writel(0x7bcc96ad, &ihost->scu_registers->afe.afe_pmsn_master_control0); + /* Shorten SAS SNW lock time (RxLock timer value from 76 + * us to 50 us) + */ + writel(0x7bcc96ad, &afe->afe_pmsn_master_control0); udelay(AFE_REGISTER_WRITE_DELAY); } for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) { + struct scu_afe_transceiver *xcvr = &afe->scu_afe_xcvr[phy_id]; const struct sci_phy_oem_params *oem_phy = &oem->phys[phy_id]; if (is_b0(pdev)) { /* Configure transmitter SSC parameters */ - writel(0x00030000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_ssc_control); + writel(0x00030000, &xcvr->afe_tx_ssc_control); udelay(AFE_REGISTER_WRITE_DELAY); } else if (is_c0(pdev)) { /* Configure transmitter SSC parameters */ - writel(0x0003000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_ssc_control); + writel(0x0003000, &xcvr->afe_tx_ssc_control); udelay(AFE_REGISTER_WRITE_DELAY); - /* - * All defaults, except the Receive Word Alignament/Comma Detect - * Enable....(0xe800) */ - writel(0x00004500, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0); + /* All defaults, except the Receive Word + * Alignament/Comma Detect Enable....(0xe800) + */ + writel(0x00004500, &xcvr->afe_xcvr_control0); udelay(AFE_REGISTER_WRITE_DELAY); } else { - /* - * All defaults, except the Receive Word Alignament/Comma Detect - * Enable....(0xe800) */ - writel(0x00004512, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0); + /* All defaults, except the Receive Word + * Alignament/Comma Detect Enable....(0xe800) + */ + writel(0x00004512, &xcvr->afe_xcvr_control0); udelay(AFE_REGISTER_WRITE_DELAY); - writel(0x0050100F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control1); + writel(0x0050100F, &xcvr->afe_xcvr_control1); udelay(AFE_REGISTER_WRITE_DELAY); } - /* - * Power up TX and RX out from power down (PWRDNTX and PWRDNRX) - * & increase TX int & ext bias 20%....(0xe85c) */ + /* Power up TX and RX out from power down (PWRDNTX and PWRDNRX) + * & increase TX int & ext bias 20%....(0xe85c) + */ if (is_a2(pdev)) - writel(0x000003F0, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control); + writel(0x000003F0, &xcvr->afe_channel_control); else if (is_b0(pdev)) { /* Power down TX and RX (PWRDNTX and PWRDNRX) */ - writel(0x000003D7, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control); + writel(0x000003D7, &xcvr->afe_channel_control); udelay(AFE_REGISTER_WRITE_DELAY); - - /* - * Power up TX and RX out from power down (PWRDNTX and PWRDNRX) - * & increase TX int & ext bias 20%....(0xe85c) */ - writel(0x000003D4, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control); + writel(0x000003D4, &xcvr->afe_channel_control); } else { - writel(0x000001E7, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control); + writel(0x000001E7, &xcvr->afe_channel_control); udelay(AFE_REGISTER_WRITE_DELAY); - - /* - * Power up TX and RX out from power down (PWRDNTX and PWRDNRX) - * & increase TX int & ext bias 20%....(0xe85c) */ - writel(0x000001E4, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_channel_control); + writel(0x000001E4, &xcvr->afe_channel_control); } udelay(AFE_REGISTER_WRITE_DELAY); if (is_a2(pdev)) { /* Enable TX equalization (0xe824) */ - writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control); + writel(0x00040000, &xcvr->afe_tx_control); udelay(AFE_REGISTER_WRITE_DELAY); } - /* - * RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0, TPD=0x0(TX Power On), - * RDD=0x0(RX Detect Enabled) ....(0xe800) */ - writel(0x00004100, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_xcvr_control0); + writel(0x00004100, &xcvr->afe_xcvr_control0); udelay(AFE_REGISTER_WRITE_DELAY); /* Leave DFE/FFE on */ if (is_a2(pdev)) - writel(0x3F11103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0); + writel(0x3F11103F, &xcvr->afe_rx_ssc_control0); else if (is_b0(pdev)) { - writel(0x3F11103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0); + writel(0x3F11103F, &xcvr->afe_rx_ssc_control0); udelay(AFE_REGISTER_WRITE_DELAY); /* Enable TX equalization (0xe824) */ - writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control); + writel(0x00040000, &xcvr->afe_tx_control); } else { - writel(0x0140DF0F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control1); + writel(0x0140DF0F, &xcvr->afe_rx_ssc_control1); udelay(AFE_REGISTER_WRITE_DELAY); - writel(0x3F6F103F, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_rx_ssc_control0); + writel(0x3F6F103F, &xcvr->afe_rx_ssc_control0); udelay(AFE_REGISTER_WRITE_DELAY); /* Enable TX equalization (0xe824) */ - writel(0x00040000, &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_control); + writel(0x00040000, &xcvr->afe_tx_control); } udelay(AFE_REGISTER_WRITE_DELAY); - writel(oem_phy->afe_tx_amp_control0, - &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control0); + writel(oem_phy->afe_tx_amp_control0, &xcvr->afe_tx_amp_control0); udelay(AFE_REGISTER_WRITE_DELAY); - writel(oem_phy->afe_tx_amp_control1, - &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control1); + writel(oem_phy->afe_tx_amp_control1, &xcvr->afe_tx_amp_control1); udelay(AFE_REGISTER_WRITE_DELAY); - writel(oem_phy->afe_tx_amp_control2, - &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control2); + writel(oem_phy->afe_tx_amp_control2, &xcvr->afe_tx_amp_control2); udelay(AFE_REGISTER_WRITE_DELAY); - writel(oem_phy->afe_tx_amp_control3, - &ihost->scu_registers->afe.scu_afe_xcvr[phy_id].afe_tx_amp_control3); + writel(oem_phy->afe_tx_amp_control3, &xcvr->afe_tx_amp_control3); udelay(AFE_REGISTER_WRITE_DELAY); } /* Transfer control to the PEs */ - writel(0x00010f00, &ihost->scu_registers->afe.afe_dfx_master_control0); + writel(0x00010f00, &afe->afe_dfx_master_control0); udelay(AFE_REGISTER_WRITE_DELAY); } diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 35f50c2..0ae0990 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -91,22 +91,23 @@ sci_phy_transport_layer_initialization(struct isci_phy *iphy, static enum sci_status sci_phy_link_layer_initialization(struct isci_phy *iphy, - struct scu_link_layer_registers __iomem *reg) + struct scu_link_layer_registers __iomem *llr) { struct isci_host *ihost = iphy->owning_port->owning_controller; + struct sci_phy_user_params *phy_user; + struct sci_phy_oem_params *phy_oem; int phy_idx = iphy->phy_index; - struct sci_phy_user_params *phy_user = &ihost->user_parameters.phys[phy_idx]; - struct sci_phy_oem_params *phy_oem = - &ihost->oem_parameters.phys[phy_idx]; - u32 phy_configuration; struct sci_phy_cap phy_cap; + u32 phy_configuration; u32 parity_check = 0; u32 parity_count = 0; u32 llctl, link_rate; u32 clksm_value = 0; u32 sp_timeouts = 0; - iphy->link_layer_registers = reg; + phy_user = &ihost->user_parameters.phys[phy_idx]; + phy_oem = &ihost->oem_parameters.phys[phy_idx]; + iphy->link_layer_registers = llr; /* Set our IDENTIFY frame data */ #define SCI_END_DEVICE 0x01 @@ -116,32 +117,26 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, SCU_SAS_TIID_GEN_BIT(STP_INITIATOR) | SCU_SAS_TIID_GEN_BIT(DA_SATA_HOST) | SCU_SAS_TIID_GEN_VAL(DEVICE_TYPE, SCI_END_DEVICE), - &iphy->link_layer_registers->transmit_identification); + &llr->transmit_identification); /* Write the device SAS Address */ - writel(0xFEDCBA98, - &iphy->link_layer_registers->sas_device_name_high); - writel(phy_idx, &iphy->link_layer_registers->sas_device_name_low); + writel(0xFEDCBA98, &llr->sas_device_name_high); + writel(phy_idx, &llr->sas_device_name_low); /* Write the source SAS Address */ - writel(phy_oem->sas_address.high, - &iphy->link_layer_registers->source_sas_address_high); - writel(phy_oem->sas_address.low, - &iphy->link_layer_registers->source_sas_address_low); + writel(phy_oem->sas_address.high, &llr->source_sas_address_high); + writel(phy_oem->sas_address.low, &llr->source_sas_address_low); /* Clear and Set the PHY Identifier */ - writel(0, &iphy->link_layer_registers->identify_frame_phy_id); - writel(SCU_SAS_TIPID_GEN_VALUE(ID, phy_idx), - &iphy->link_layer_registers->identify_frame_phy_id); + writel(0, &llr->identify_frame_phy_id); + writel(SCU_SAS_TIPID_GEN_VALUE(ID, phy_idx), &llr->identify_frame_phy_id); /* Change the initial state of the phy configuration register */ - phy_configuration = - readl(&iphy->link_layer_registers->phy_configuration); + phy_configuration = readl(&llr->phy_configuration); /* Hold OOB state machine in reset */ phy_configuration |= SCU_SAS_PCFG_GEN_BIT(OOB_RESET); - writel(phy_configuration, - &iphy->link_layer_registers->phy_configuration); + writel(phy_configuration, &llr->phy_configuration); /* Configure the SNW capabilities */ phy_cap.all = 0; @@ -155,9 +150,9 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, phy_cap.gen1_ssc = 1; } - /* - * The SAS specification indicates that the phy_capabilities that - * are transmitted shall have an even parity. Calculate the parity. */ + /* The SAS specification indicates that the phy_capabilities that + * are transmitted shall have an even parity. Calculate the parity. + */ parity_check = phy_cap.all; while (parity_check != 0) { if (parity_check & 0x1) @@ -165,20 +160,20 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, parity_check >>= 1; } - /* - * If parity indicates there are an odd number of bits set, then - * set the parity bit to 1 in the phy capabilities. */ + /* If parity indicates there are an odd number of bits set, then + * set the parity bit to 1 in the phy capabilities. + */ if ((parity_count % 2) != 0) phy_cap.parity = 1; - writel(phy_cap.all, &iphy->link_layer_registers->phy_capabilities); + writel(phy_cap.all, &llr->phy_capabilities); /* Set the enable spinup period but disable the ability to send * notify enable spinup */ writel(SCU_ENSPINUP_GEN_VAL(COUNT, phy_user->notify_enable_spin_up_insertion_frequency), - &iphy->link_layer_registers->notify_enable_spinup_control); + &llr->notify_enable_spinup_control); /* Write the ALIGN Insertion Ferequency for connected phy and * inpendent of connected state @@ -189,11 +184,10 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, clksm_value |= SCU_ALIGN_INSERTION_FREQUENCY_GEN_VAL(GENERAL, phy_user->align_insertion_frequency); - writel(clksm_value, &iphy->link_layer_registers->clock_skew_management); + writel(clksm_value, &llr->clock_skew_management); /* @todo Provide a way to write this register correctly */ - writel(0x02108421, - &iphy->link_layer_registers->afe_lookup_table_control); + writel(0x02108421, &llr->afe_lookup_table_control); llctl = SCU_SAS_LLCTL_GEN_VAL(NO_OUTBOUND_TASK_TIMEOUT, (u8)ihost->user_parameters.no_outbound_task_timeout); @@ -210,9 +204,9 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, break; } llctl |= SCU_SAS_LLCTL_GEN_VAL(MAX_LINK_RATE, link_rate); - writel(llctl, &iphy->link_layer_registers->link_layer_control); + writel(llctl, &llr->link_layer_control); - sp_timeouts = readl(&iphy->link_layer_registers->sas_phy_timeouts); + sp_timeouts = readl(&llr->sas_phy_timeouts); /* Clear the default 0x36 (54us) RATE_CHANGE timeout value. */ sp_timeouts &= ~SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0xFF); @@ -222,20 +216,23 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, */ sp_timeouts |= SCU_SAS_PHYTOV_GEN_VAL(RATE_CHANGE, 0x3B); - writel(sp_timeouts, &iphy->link_layer_registers->sas_phy_timeouts); + writel(sp_timeouts, &llr->sas_phy_timeouts); if (is_a2(ihost->pdev)) { - /* Program the max ARB time for the PHY to 700us so we inter-operate with - * the PMC expander which shuts down PHYs if the expander PHY generates too - * many breaks. This time value will guarantee that the initiator PHY will - * generate the break. + /* Program the max ARB time for the PHY to 700us so we + * inter-operate with the PMC expander which shuts down + * PHYs if the expander PHY generates too many breaks. + * This time value will guarantee that the initiator PHY + * will generate the break. */ writel(SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME, - &iphy->link_layer_registers->maximum_arbitration_wait_timer_timeout); + &llr->maximum_arbitration_wait_timer_timeout); } - /* Disable link layer hang detection, rely on the OS timeout for I/O timeouts. */ - writel(0, &iphy->link_layer_registers->link_layer_hang_detection_timeout); + /* Disable link layer hang detection, rely on the OS timeout for + * I/O timeouts. + */ + writel(0, &llr->link_layer_hang_detection_timeout); /* We can exit the initial state to the stopped state */ sci_change_state(&iphy->sm, SCI_PHY_STOPPED); -- cgit v0.10.2 From afd13a1f2b05157c7621d87dfe89ea6ea9061bd8 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Wed, 4 Jan 2012 01:32:39 -0800 Subject: [SCSI] isci: update afe (analog-front-end) recipe for C1 C1 silicon requires updates to the phy tuning recipe and also support for user provided cable selects (per-phy) for short, medium, and long cables. Default to 'short' awaiting support for selecting the cable via oem parameters. Reviewed-by: Jiangbi Liu Signed-off-by: Marcin Tomczak Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 8e7de19..383bb69 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1908,12 +1908,23 @@ void sci_controller_power_control_queue_remove(struct isci_host *ihost, ihost->power_control.requesters[iphy->phy_index] = NULL; } +static int is_long_cable(int phy, unsigned char selection_byte) +{ + return 0; +} + +static int is_medium_cable(int phy, unsigned char selection_byte) +{ + return 0; +} + #define AFE_REGISTER_WRITE_DELAY 10 static void sci_controller_afe_initialization(struct isci_host *ihost) { struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; const struct sci_oem_params *oem = &ihost->oem_parameters; + unsigned char cable_selection_mask = 0; struct pci_dev *pdev = ihost->pdev; u32 afe_status; u32 phy_id; @@ -1922,11 +1933,11 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) writel(0x0081000f, &afe->afe_dfx_master_control0); udelay(AFE_REGISTER_WRITE_DELAY); - if (is_b0(pdev)) { + if (is_b0(pdev) || is_c0(pdev) || is_c1(pdev)) { /* PM Rx Equalization Save, PM SPhy Rx Acknowledgement * Timer, PM Stagger Timer */ - writel(0x0007BFFF, &afe->afe_pmsn_master_control2); + writel(0x0007FFFF, &afe->afe_pmsn_master_control2); udelay(AFE_REGISTER_WRITE_DELAY); } @@ -1935,14 +1946,23 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) writel(0x00005A00, &afe->afe_bias_control); else if (is_b0(pdev) || is_c0(pdev)) writel(0x00005F00, &afe->afe_bias_control); + else if (is_c1(pdev)) + writel(0x00005500, &afe->afe_bias_control); udelay(AFE_REGISTER_WRITE_DELAY); /* Enable PLL */ - if (is_b0(pdev) || is_c0(pdev)) - writel(0x80040A08, &afe->afe_pll_control0); - else + if (is_a2(pdev)) writel(0x80040908, &afe->afe_pll_control0); + else if (is_b0(pdev) || is_c0(pdev)) + writel(0x80040A08, &afe->afe_pll_control0); + else if (is_c1(pdev)) { + writel(0x80000B08, &afe->afe_pll_control0); + udelay(AFE_REGISTER_WRITE_DELAY); + writel(0x00000B08, &afe->afe_pll_control0); + udelay(AFE_REGISTER_WRITE_DELAY); + writel(0x80000B08, &afe->afe_pll_control0); + } udelay(AFE_REGISTER_WRITE_DELAY); @@ -1963,46 +1983,68 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) { struct scu_afe_transceiver *xcvr = &afe->scu_afe_xcvr[phy_id]; const struct sci_phy_oem_params *oem_phy = &oem->phys[phy_id]; + int cable_length_long = + is_long_cable(phy_id, cable_selection_mask); + int cable_length_medium = + is_medium_cable(phy_id, cable_selection_mask); + + if (is_a2(pdev)) { + /* All defaults, except the Receive Word + * Alignament/Comma Detect Enable....(0xe800) + */ + writel(0x00004512, &xcvr->afe_xcvr_control0); + udelay(AFE_REGISTER_WRITE_DELAY); - if (is_b0(pdev)) { - /* Configure transmitter SSC parameters */ + writel(0x0050100F, &xcvr->afe_xcvr_control1); + udelay(AFE_REGISTER_WRITE_DELAY); + } else if (is_b0(pdev)) { + /* Configure transmitter SSC parameters */ writel(0x00030000, &xcvr->afe_tx_ssc_control); udelay(AFE_REGISTER_WRITE_DELAY); } else if (is_c0(pdev)) { - /* Configure transmitter SSC parameters */ - writel(0x0003000, &xcvr->afe_tx_ssc_control); + /* Configure transmitter SSC parameters */ + writel(0x00010202, &xcvr->afe_tx_ssc_control); udelay(AFE_REGISTER_WRITE_DELAY); /* All defaults, except the Receive Word * Alignament/Comma Detect Enable....(0xe800) */ - writel(0x00004500, &xcvr->afe_xcvr_control0); + writel(0x00014500, &xcvr->afe_xcvr_control0); udelay(AFE_REGISTER_WRITE_DELAY); - } else { + } else if (is_c1(pdev)) { + /* Configure transmitter SSC parameters */ + writel(0x00010202, &xcvr->afe_tx_ssc_control); + udelay(AFE_REGISTER_WRITE_DELAY); + /* All defaults, except the Receive Word * Alignament/Comma Detect Enable....(0xe800) */ - writel(0x00004512, &xcvr->afe_xcvr_control0); - udelay(AFE_REGISTER_WRITE_DELAY); - - writel(0x0050100F, &xcvr->afe_xcvr_control1); + writel(0x0001C500, &xcvr->afe_xcvr_control0); udelay(AFE_REGISTER_WRITE_DELAY); } - /* Power up TX and RX out from power down (PWRDNTX and PWRDNRX) - * & increase TX int & ext bias 20%....(0xe85c) + /* Power up TX and RX out from power down (PWRDNTX and + * PWRDNRX) & increase TX int & ext bias 20%....(0xe85c) */ if (is_a2(pdev)) writel(0x000003F0, &xcvr->afe_channel_control); else if (is_b0(pdev)) { - /* Power down TX and RX (PWRDNTX and PWRDNRX) */ writel(0x000003D7, &xcvr->afe_channel_control); udelay(AFE_REGISTER_WRITE_DELAY); + writel(0x000003D4, &xcvr->afe_channel_control); - } else { + } else if (is_c0(pdev)) { writel(0x000001E7, &xcvr->afe_channel_control); udelay(AFE_REGISTER_WRITE_DELAY); + writel(0x000001E4, &xcvr->afe_channel_control); + } else if (is_c1(pdev)) { + writel(cable_length_long ? 0x000002F7 : 0x000001F7, + &xcvr->afe_channel_control); + udelay(AFE_REGISTER_WRITE_DELAY); + + writel(cable_length_long ? 0x000002F4 : 0x000001F4, + &xcvr->afe_channel_control); } udelay(AFE_REGISTER_WRITE_DELAY); @@ -2012,7 +2054,16 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) udelay(AFE_REGISTER_WRITE_DELAY); } - writel(0x00004100, &xcvr->afe_xcvr_control0); + if (is_a2(pdev) || is_b0(pdev)) + /* RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0, + * TPD=0x0(TX Power On), RDD=0x0(RX Detect + * Enabled) ....(0xe800) + */ + writel(0x00004100, &xcvr->afe_xcvr_control0); + else if (is_c0(pdev)) + writel(0x00014100, &xcvr->afe_xcvr_control0); + else if (is_c1(pdev)) + writel(0x0001C100, &xcvr->afe_xcvr_control0); udelay(AFE_REGISTER_WRITE_DELAY); /* Leave DFE/FFE on */ @@ -2023,8 +2074,8 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) udelay(AFE_REGISTER_WRITE_DELAY); /* Enable TX equalization (0xe824) */ writel(0x00040000, &xcvr->afe_tx_control); - } else { - writel(0x0140DF0F, &xcvr->afe_rx_ssc_control1); + } else if (is_c0(pdev)) { + writel(0x01400C0F, &xcvr->afe_rx_ssc_control1); udelay(AFE_REGISTER_WRITE_DELAY); writel(0x3F6F103F, &xcvr->afe_rx_ssc_control0); @@ -2032,6 +2083,22 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) /* Enable TX equalization (0xe824) */ writel(0x00040000, &xcvr->afe_tx_control); + } else if (is_c1(pdev)) { + writel(cable_length_long ? 0x01500C0C : + cable_length_medium ? 0x01400C0D : 0x02400C0D, + &xcvr->afe_xcvr_control1); + udelay(AFE_REGISTER_WRITE_DELAY); + + writel(0x000003E0, &xcvr->afe_dfx_rx_control1); + udelay(AFE_REGISTER_WRITE_DELAY); + + writel(cable_length_long ? 0x33091C1F : + cable_length_medium ? 0x3315181F : 0x2B17161F, + &xcvr->afe_rx_ssc_control0); + udelay(AFE_REGISTER_WRITE_DELAY); + + /* Enable TX equalization (0xe824) */ + writel(0x00040000, &xcvr->afe_tx_control); } udelay(AFE_REGISTER_WRITE_DELAY); diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 646051a..4573075 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -435,7 +435,14 @@ static inline bool is_b0(struct pci_dev *pdev) static inline bool is_c0(struct pci_dev *pdev) { - if (pdev->revision >= 5) + if (pdev->revision == 5) + return true; + return false; +} + +static inline bool is_c1(struct pci_dev *pdev) +{ + if (pdev->revision >= 6) return true; return false; } diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 0ae0990..c650d30 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -186,8 +186,11 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, writel(clksm_value, &llr->clock_skew_management); - /* @todo Provide a way to write this register correctly */ - writel(0x02108421, &llr->afe_lookup_table_control); + if (is_c0(ihost->pdev) || is_c1(ihost->pdev)) { + writel(0x04210400, &llr->afe_lookup_table_control); + writel(0x020A7C05, &llr->sas_primitive_timeout); + } else + writel(0x02108421, &llr->afe_lookup_table_control); llctl = SCU_SAS_LLCTL_GEN_VAL(NO_OUTBOUND_TASK_TIMEOUT, (u8)ihost->user_parameters.no_outbound_task_timeout); diff --git a/drivers/scsi/isci/probe_roms.c b/drivers/scsi/isci/probe_roms.c index b5f4341..9b8117b 100644 --- a/drivers/scsi/isci/probe_roms.c +++ b/drivers/scsi/isci/probe_roms.c @@ -147,7 +147,7 @@ struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmw memcpy(orom, fw->data, fw->size); - if (is_c0(pdev)) + if (is_c0(pdev) || is_c1(pdev)) goto out; /* -- cgit v0.10.2 From 594e566ae5985e0cc3185ac21509a86e90aad577 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Wed, 4 Jan 2012 01:32:44 -0800 Subject: [SCSI] isci: oem parameter format v1.1 (ssc select) v1.1 allows finer grained tuning of the SSC (spread-spectrum-clocking) settings for SAS and SATA. See notes in probe_roms.h Signed-off-by: Dave Jiang Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 383bb69..ed1441c 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1759,7 +1759,7 @@ static enum sci_status sci_controller_construct(struct isci_host *ihost, return sci_controller_reset(ihost); } -int sci_oem_parameters_validate(struct sci_oem_params *oem) +int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version) { int i; @@ -1791,18 +1791,61 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem) oem->controller.max_concurr_spin_up < 1) return -EINVAL; + if (oem->controller.do_enable_ssc) { + if (version < ISCI_ROM_VER_1_1 && oem->controller.do_enable_ssc != 1) + return -EINVAL; + + if (version >= ISCI_ROM_VER_1_1) { + u8 test = oem->controller.ssc_sata_tx_spread_level; + + switch (test) { + case 0: + case 2: + case 3: + case 6: + case 7: + break; + default: + return -EINVAL; + } + + test = oem->controller.ssc_sas_tx_spread_level; + if (oem->controller.ssc_sas_tx_type == 0) { + switch (test) { + case 0: + case 2: + case 3: + break; + default: + return -EINVAL; + } + } else if (oem->controller.ssc_sas_tx_type == 1) { + switch (test) { + case 0: + case 3: + case 6: + break; + default: + return -EINVAL; + } + } + } + } + return 0; } static enum sci_status sci_oem_parameters_set(struct isci_host *ihost) { u32 state = ihost->sm.current_state_id; + struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); if (state == SCIC_RESET || state == SCIC_INITIALIZING || state == SCIC_INITIALIZED) { - if (sci_oem_parameters_validate(&ihost->oem_parameters)) + if (sci_oem_parameters_validate(&ihost->oem_parameters, + pci_info->orom->hdr.version)) return SCI_FAILURE_INVALID_PARAMETER_VALUE; return SCI_SUCCESS; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index a97edab..8a34fd9 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -466,7 +466,8 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic orom = isci_request_oprom(pdev); for (i = 0; orom && i < ARRAY_SIZE(orom->ctrl); i++) { - if (sci_oem_parameters_validate(&orom->ctrl[i])) { + if (sci_oem_parameters_validate(&orom->ctrl[i], + orom->hdr.version)) { dev_warn(&pdev->dev, "[%d]: invalid oem parameters detected, falling back to firmware\n", i); devm_kfree(&pdev->dev, orom); diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index c650d30..61000cd 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -144,10 +144,59 @@ sci_phy_link_layer_initialization(struct isci_phy *iphy, phy_cap.gen3_no_ssc = 1; phy_cap.gen2_no_ssc = 1; phy_cap.gen1_no_ssc = 1; - if (ihost->oem_parameters.controller.do_enable_ssc == true) { + if (ihost->oem_parameters.controller.do_enable_ssc) { + struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; + struct scu_afe_transceiver *xcvr = &afe->scu_afe_xcvr[phy_idx]; + struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); + bool en_sas = false; + bool en_sata = false; + u32 sas_type = 0; + u32 sata_spread = 0x2; + u32 sas_spread = 0x2; + phy_cap.gen3_ssc = 1; phy_cap.gen2_ssc = 1; phy_cap.gen1_ssc = 1; + + if (pci_info->orom->hdr.version < ISCI_ROM_VER_1_1) + en_sas = en_sata = true; + else { + sata_spread = ihost->oem_parameters.controller.ssc_sata_tx_spread_level; + sas_spread = ihost->oem_parameters.controller.ssc_sas_tx_spread_level; + + if (sata_spread) + en_sata = true; + + if (sas_spread) { + en_sas = true; + sas_type = ihost->oem_parameters.controller.ssc_sas_tx_type; + } + + } + + if (en_sas) { + u32 reg; + + reg = readl(&xcvr->afe_xcvr_control0); + reg |= (0x00100000 | (sas_type << 19)); + writel(reg, &xcvr->afe_xcvr_control0); + + reg = readl(&xcvr->afe_tx_ssc_control); + reg |= sas_spread << 8; + writel(reg, &xcvr->afe_tx_ssc_control); + } + + if (en_sata) { + u32 reg; + + reg = readl(&xcvr->afe_tx_ssc_control); + reg |= sata_spread; + writel(reg, &xcvr->afe_tx_ssc_control); + + reg = readl(&llr->stp_control); + reg |= 1 << 12; + writel(reg, &llr->stp_control); + } } /* The SAS specification indicates that the phy_capabilities that diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index 2c75248..42dd054 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h @@ -152,7 +152,7 @@ struct sci_user_parameters { #define MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT 4 struct sci_oem_params; -int sci_oem_parameters_validate(struct sci_oem_params *oem); +int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version); struct isci_orom; struct isci_orom *isci_request_oprom(struct pci_dev *pdev); @@ -191,6 +191,10 @@ struct isci_oem_hdr { 0x1a, 0x04, 0xc6) #define ISCI_EFI_VAR_NAME "RstScuO" +#define ISCI_ROM_VER_1_0 0x10 +#define ISCI_ROM_VER_1_1 0x11 +#define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_1 + /* Allowed PORT configuration modes APC Automatic PORT configuration mode is * defined by the OEM configuration parameters providing no PHY_MASK parameters * for any PORT. i.e. There are no phys assigned to any of the ports at start. @@ -220,7 +224,52 @@ struct sci_oem_params { struct { uint8_t mode_type; uint8_t max_concurr_spin_up; - uint8_t do_enable_ssc; + /* + * This bitfield indicates the OEM's desired default Tx + * Spread Spectrum Clocking (SSC) settings for SATA and SAS. + * NOTE: Default SSC Modulation Frequency is 31.5KHz. + */ + union { + struct { + /* + * NOTE: Max spread for SATA is +0 / -5000 PPM. + * Down-spreading SSC (only method allowed for SATA): + * SATA SSC Tx Disabled = 0x0 + * SATA SSC Tx at +0 / -1419 PPM Spread = 0x2 + * SATA SSC Tx at +0 / -2129 PPM Spread = 0x3 + * SATA SSC Tx at +0 / -4257 PPM Spread = 0x6 + * SATA SSC Tx at +0 / -4967 PPM Spread = 0x7 + */ + uint8_t ssc_sata_tx_spread_level:4; + /* + * SAS SSC Tx Disabled = 0x0 + * + * NOTE: Max spread for SAS down-spreading +0 / + * -2300 PPM + * Down-spreading SSC: + * SAS SSC Tx at +0 / -1419 PPM Spread = 0x2 + * SAS SSC Tx at +0 / -2129 PPM Spread = 0x3 + * + * NOTE: Max spread for SAS center-spreading +2300 / + * -2300 PPM + * Center-spreading SSC: + * SAS SSC Tx at +1064 / -1064 PPM Spread = 0x3 + * SAS SSC Tx at +2129 / -2129 PPM Spread = 0x6 + */ + uint8_t ssc_sas_tx_spread_level:3; + /* + * NOTE: Refer to the SSC section of the SAS 2.x + * Specification for proper setting of this field. + * For standard SAS Initiator SAS PHY operation it + * should be 0 for Down-spreading. + * SAS SSC Tx spread type: + * Down-spreading SSC = 0 + * Center-spreading SSC = 1 + */ + uint8_t ssc_sas_tx_type:1; + }; + uint8_t do_enable_ssc; + }; uint8_t reserved; } controller; -- cgit v0.10.2 From 9fee607f0b29adabd72265a847b8e421dff10d66 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Wed, 4 Jan 2012 01:32:49 -0800 Subject: [SCSI] isci: oem parameter format v1.3 (cable select) v1.3 allows the attenuation of the attached cables to be specified to the driver in terms of 'short', 'medium', and 'long' (see probe_roms.h). These settings (per phy) are retrieved from the platform oem-parameters (BIOS rom) or via a module parameter override. Reviewed-by: Jiangbi Liu Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index ed1441c..9a52b98 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1666,6 +1666,9 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost /* Default to no SSC operation. */ ihost->oem_parameters.controller.do_enable_ssc = false; + /* Default to short cables on all phys. */ + ihost->oem_parameters.controller.cable_selection_mask = 0; + /* Initialize all of the port parameter information to narrow ports. */ for (index = 0; index < SCI_MAX_PORTS; index++) { ihost->oem_parameters.ports[index].phy_mask = 0; @@ -1953,12 +1956,46 @@ void sci_controller_power_control_queue_remove(struct isci_host *ihost, static int is_long_cable(int phy, unsigned char selection_byte) { - return 0; + return !!(selection_byte & (1 << phy)); } static int is_medium_cable(int phy, unsigned char selection_byte) { - return 0; + return !!(selection_byte & (1 << (phy + 4))); +} + +static enum cable_selections decode_selection_byte( + int phy, + unsigned char selection_byte) +{ + return ((selection_byte & (1 << phy)) ? 1 : 0) + + (selection_byte & (1 << (phy + 4)) ? 2 : 0); +} + +static unsigned char *to_cable_select(struct isci_host *ihost) +{ + if (is_cable_select_overridden()) + return ((unsigned char *)&cable_selection_override) + + ihost->id; + else + return &ihost->oem_parameters.controller.cable_selection_mask; +} + +enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy) +{ + return decode_selection_byte(phy, *to_cable_select(ihost)); +} + +char *lookup_cable_names(enum cable_selections selection) +{ + static char *cable_names[] = { + [short_cable] = "short", + [long_cable] = "long", + [medium_cable] = "medium", + [undefined_cable] = "" /* bit 0==1 */ + }; + return (selection <= undefined_cable) ? cable_names[selection] + : cable_names[undefined_cable]; } #define AFE_REGISTER_WRITE_DELAY 10 @@ -1967,10 +2004,10 @@ static void sci_controller_afe_initialization(struct isci_host *ihost) { struct scu_afe_registers __iomem *afe = &ihost->scu_registers->afe; const struct sci_oem_params *oem = &ihost->oem_parameters; - unsigned char cable_selection_mask = 0; struct pci_dev *pdev = ihost->pdev; u32 afe_status; u32 phy_id; + unsigned char cable_selection_mask = *to_cable_select(ihost); /* Clear DFX Status registers */ writel(0x0081000f, &afe->afe_dfx_master_control0); diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 4573075..5477f0f 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -447,6 +447,24 @@ static inline bool is_c1(struct pci_dev *pdev) return false; } +enum cable_selections { + short_cable = 0, + long_cable = 1, + medium_cable = 2, + undefined_cable = 3 +}; + +#define CABLE_OVERRIDE_DISABLED (0x10000) + +static inline int is_cable_select_overridden(void) +{ + return cable_selection_override < CABLE_OVERRIDE_DISABLED; +} + +enum cable_selections decode_cable_selection(struct isci_host *ihost, int phy); +void validate_cable_selections(struct isci_host *ihost); +char *lookup_cable_names(enum cable_selections); + /* set hw control for 'activity', even though active enclosures seem to drive * the activity led on their own. Skip setting FSENG control on 'status' due * to unexpected operation and 'error' due to not being a supported automatic diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 8a34fd9..1047108 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -122,6 +122,14 @@ unsigned char max_concurr_spinup; module_param(max_concurr_spinup, byte, 0); MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup"); +uint cable_selection_override = CABLE_OVERRIDE_DISABLED; +module_param(cable_selection_override, uint, 0); + +MODULE_PARM_DESC(cable_selection_override, + "This field indicates length of the SAS/SATA cable between " + "host and device. If any bits > 15 are set (default) " + "indicates \"use platform defaults\""); + static ssize_t isci_show_id(struct device *dev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = container_of(dev, typeof(*shost), shost_dev); @@ -412,6 +420,14 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) return NULL; isci_host->shost = shost; + dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: " + "{%s, %s, %s, %s}\n", + (is_cable_select_overridden() ? "* " : ""), isci_host->id, + lookup_cable_names(decode_cable_selection(isci_host, 3)), + lookup_cable_names(decode_cable_selection(isci_host, 2)), + lookup_cable_names(decode_cable_selection(isci_host, 1)), + lookup_cable_names(decode_cable_selection(isci_host, 0))); + err = isci_host_init(isci_host); if (err) goto err_shost; diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h index 8efeb6b..234ab46 100644 --- a/drivers/scsi/isci/isci.h +++ b/drivers/scsi/isci/isci.h @@ -480,6 +480,7 @@ extern u16 ssp_inactive_to; extern u16 stp_inactive_to; extern unsigned char phy_gen; extern unsigned char max_concurr_spinup; +extern uint cable_selection_override; irqreturn_t isci_msix_isr(int vec, void *data); irqreturn_t isci_intx_isr(int vec, void *data); diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index 42dd054..bb0e9d4 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h @@ -193,7 +193,8 @@ struct isci_oem_hdr { #define ISCI_ROM_VER_1_0 0x10 #define ISCI_ROM_VER_1_1 0x11 -#define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_1 +#define ISCI_ROM_VER_1_3 0x13 +#define ISCI_ROM_VER_LATEST ISCI_ROM_VER_1_3 /* Allowed PORT configuration modes APC Automatic PORT configuration mode is * defined by the OEM configuration parameters providing no PHY_MASK parameters @@ -270,7 +271,40 @@ struct sci_oem_params { }; uint8_t do_enable_ssc; }; - uint8_t reserved; + /* + * This field indicates length of the SAS/SATA cable between + * host and device. + * This field is used make relationship between analog + * parameters of the phy in the silicon and length of the cable. + * Supported cable attenuation levels: + * "short"- up to 3m, "medium"-3m to 6m, and "long"- more than + * 6m. + * + * This is bit mask field: + * + * BIT: (MSB) 7 6 5 4 + * ASSIGNMENT: - Medium cable + * length assignment + * BIT: 3 2 1 0 (LSB) + * ASSIGNMENT: - Long cable length + * assignment + * + * BITS 7-4 are set when the cable length is assigned to medium + * BITS 3-0 are set when the cable length is assigned to long + * + * The BIT positions are clear when the cable length is + * assigned to short. + * + * Setting the bits for both long and medium cable length is + * undefined. + * + * A value of 0x84 would assign + * phy3 - medium + * phy2 - long + * phy1 - short + * phy0 - short + */ + uint8_t cable_selection_mask; } controller; struct { -- cgit v0.10.2 From 6024d38b86ecc7943e398ef01b0dc2765870d444 Mon Sep 17 00:00:00 2001 From: Marcin Tomczak Date: Wed, 4 Jan 2012 01:32:54 -0800 Subject: [SCSI] isci: performance-fix, shorten default "no outbound task" timeout "No task timeout timer reduced from 20 to 2 This timer controls how long the SCU hardware will hold open the TX side of the connection before sending a DONE. The timer allows the hardware to attempt to optimize the DONE/CLOSE behavior to allow for new COMMAND IU to be posted. In practice closing the connection quicker is better." Signed-off-by: Marcin Tomczak Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 9a52b98..670ecb4 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1697,7 +1697,7 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost ihost->user_parameters.ssp_inactivity_timeout = 5; ihost->user_parameters.stp_max_occupancy_timeout = 5; ihost->user_parameters.ssp_max_occupancy_timeout = 20; - ihost->user_parameters.no_outbound_task_timeout = 20; + ihost->user_parameters.no_outbound_task_timeout = 2; } static void controller_timeout(unsigned long data) diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 1047108..3c4ddad 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -94,7 +94,7 @@ MODULE_DEVICE_TABLE(pci, isci_id_table); /* linux isci specific settings */ -unsigned char no_outbound_task_to = 20; +unsigned char no_outbound_task_to = 2; module_param(no_outbound_task_to, byte, 0); MODULE_PARM_DESC(no_outbound_task_to, "No Outbound Task Timeout (1us incr)"); -- cgit v0.10.2 From be168a3b89b43b8bb4ef71066e91439d400e20a6 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Wed, 4 Jan 2012 01:33:00 -0800 Subject: [SCSI] isci: link speeds default to gen 2 Gen-3 operation is marginal, default to gen-2 for now. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 670ecb4..7e4d709 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1676,8 +1676,9 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost /* Initialize all of the phy parameter information. */ for (index = 0; index < SCI_MAX_PHYS; index++) { - /* Default to 6G (i.e. Gen 3) for now. */ - ihost->user_parameters.phys[index].max_speed_generation = 3; + /* Default to 3G (i.e. Gen 2). */ + ihost->user_parameters.phys[index].max_speed_generation = + SCIC_SDS_PARM_GEN2_SPEED; /* the frequencies cannot be 0 */ ihost->user_parameters.phys[index].align_insertion_frequency = 0x7f; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 3c4ddad..2bcfb40 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -114,7 +114,7 @@ u16 stp_inactive_to = 5; module_param(stp_inactive_to, ushort, 0); MODULE_PARM_DESC(stp_inactive_to, "STP inactivity timeout (100us incr)"); -unsigned char phy_gen = 3; +unsigned char phy_gen = SCIC_SDS_PARM_GEN2_SPEED; module_param(phy_gen, byte, 0); MODULE_PARM_DESC(phy_gen, "PHY generation (1: 1.5Gbps 2: 3.0Gbps 3: 6.0Gbps)"); -- cgit v0.10.2 From 27234ab44f97d85bab062a9d18aaff99addd267d Mon Sep 17 00:00:00 2001 From: Maciej Trela Date: Wed, 4 Jan 2012 01:33:05 -0800 Subject: [SCSI] isci: remove unused 'isci_tmf->device' field As the field was never set, isci_print_tmf() using 'isci_tmf->device' sometimes causes a kernel crash if the dev_dbg() statement is enabled. Remove the unused field both from isci_tmf struct definition and from isci_print_tmf() Signed-off-by: Maciej Trela Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 66ad3dc..f5a3f7d 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -496,7 +496,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost, } } - isci_print_tmf(tmf); + isci_print_tmf(ihost, tmf); if (tmf->status == SCI_SUCCESS) ret = TMF_RESP_FUNC_COMPLETE; diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index bc78c0a..1b27b37 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -106,7 +106,6 @@ struct isci_tmf { } resp; unsigned char lun[8]; u16 io_tag; - struct isci_remote_device *device; enum isci_tmf_function_codes tmf_code; int status; @@ -120,10 +119,10 @@ struct isci_tmf { }; -static inline void isci_print_tmf(struct isci_tmf *tmf) +static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf) { if (SAS_PROTOCOL_SATA == tmf->proto) - dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev, + dev_dbg(&ihost->pdev->dev, "%s: status = %x\n" "tmf->resp.d2h_fis.status = %x\n" "tmf->resp.d2h_fis.error = %x\n", @@ -132,7 +131,7 @@ static inline void isci_print_tmf(struct isci_tmf *tmf) tmf->resp.d2h_fis.status, tmf->resp.d2h_fis.error); else - dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev, + dev_dbg(&ihost->pdev->dev, "%s: status = %x\n" "tmf->resp.resp_iu.data_present = %x\n" "tmf->resp.resp_iu.status = %x\n" -- cgit v0.10.2 From d3fd2e2bc29b1f0979352994f9035359003461c3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 4 Jan 2012 01:33:10 -0800 Subject: [SCSI] isci: update version to 1.1 Bump the version now that the driver has atapi support and the initial round of hotplug fixes. The EXPERIMENTAL tag should have been removed a while back. While we're here also kill the "select SCSI_SAS_HOST_SMP" as the build error was separately fixed by commit d962480e "[SCSI] libsas: fix try_test_sas_gpio_gp_bit() build error". Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 06ea3bc..16570aa 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -830,16 +830,11 @@ config SCSI_ISCI tristate "Intel(R) C600 Series Chipset SAS Controller" depends on PCI && SCSI depends on X86 - # (temporary): known alpha quality driver - depends on EXPERIMENTAL select SCSI_SAS_LIBSAS - select SCSI_SAS_HOST_SMP ---help--- This driver supports the 6Gb/s SAS capabilities of the storage control unit found in the Intel(R) C600 series chipset. - The experimental tag will be removed after the driver exits alpha - config SCSI_GENERIC_NCR5380 tristate "Generic NCR5380/53c400 SCSI PIO support" depends on ISA && SCSI diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 2bcfb40..17c4c2c 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -65,7 +65,7 @@ #include "probe_roms.h" #define MAJ 1 -#define MIN 0 +#define MIN 1 #define BUILD 0 #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \ __stringify(BUILD) -- cgit v0.10.2 From d4ec1cf61fb081a9dde0c0e0b2d0201f4005f937 Mon Sep 17 00:00:00 2001 From: Marcin Tomczak Date: Wed, 4 Jan 2012 01:33:15 -0800 Subject: [SCSI] isci: Fix IO fails when pull cable from phy in x4 wideport in MPC mode. Failure seen pulling a cable from a x4 port configured in manual port configuration (MPC) mode (MPC mode is set by the the OEM paramaters provided by the platform or isci_firmware.bin). While IO running to devices behind and expander, plugging out the cable from phy is causing IO failures and IO drops on disks and never recover. It happens because during link up/down the phy were being taken out of the port. Fix: during link down the phy is kept in the same logical port. Signed-off-by: Marcin Tomczak Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index ac7f277..4cb071d 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -674,9 +674,13 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy, iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN; - /* Re-assign the phy back to the LP as if it were a narrow port */ - writel(iphy->phy_index, - &iport->port_pe_configuration_register[iphy->phy_index]); + /* Re-assign the phy back to the LP as if it were a narrow port for APC + * mode. For MPC mode, the phy will remain in the port. + */ + if (iport->owning_controller->oem_parameters.controller.mode_type == + SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE) + writel(iphy->phy_index, + &iport->port_pe_configuration_register[iphy->phy_index]); if (do_notify_user == true) isci_port_link_down(ihost, iphy, iport); -- cgit v0.10.2 From 7e629841b8f8f7bc84d74de994d748b0a3282225 Mon Sep 17 00:00:00 2001 From: Bartek Nowakowski Date: Wed, 4 Jan 2012 01:33:20 -0800 Subject: [SCSI] isci: enable wide port targets Arrange for task_contexts prepared for the wide targets to account for all the attached phys in the port. Signed-off-by: Bartek Nowakowski Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 4cb071d..49e8a72 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -114,7 +114,7 @@ static u32 sci_port_get_phys(struct isci_port *iport) * value is returned if the specified port is not valid. When this value is * returned, no data is copied to the properties output parameter. */ -static enum sci_status sci_port_get_properties(struct isci_port *iport, +enum sci_status sci_port_get_properties(struct isci_port *iport, struct sci_port_properties *prop) { if (!iport || iport->logical_port_index == SCIC_SDS_DUMMY_PORT) diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index cb5ffbc..b3a7f12 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -250,6 +250,10 @@ bool sci_port_link_detected( struct isci_port *iport, struct isci_phy *iphy); +enum sci_status sci_port_get_properties( + struct isci_port *iport, + struct sci_port_properties *prop); + enum sci_status sci_port_link_up(struct isci_port *iport, struct isci_phy *iphy); enum sci_status sci_port_link_down(struct isci_port *iport, diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b207cd3..dd74b6c 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -53,6 +53,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include "isci.h" #include "port.h" #include "remote_device.h" @@ -1101,6 +1102,7 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport, struct isci_remote_device *idev) { enum sci_status status; + struct sci_port_properties properties; struct domain_device *dev = idev->domain_dev; sci_remote_device_construct(iport, idev); @@ -1110,6 +1112,11 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport, * entries will be needed to store the remote node. */ idev->is_direct_attached = true; + + sci_port_get_properties(iport, &properties); + /* Get accurate port width from port's phy mask for a DA device. */ + idev->device_port_width = hweight32(properties.phy_mask); + status = sci_controller_allocate_remote_node_context(iport->owning_controller, idev, &idev->rnc.remote_node_index); @@ -1125,9 +1132,6 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport, idev->connection_rate = sci_port_get_max_allowed_speed(iport); - /* / @todo Should I assign the port width by reading all of the phys on the port? */ - idev->device_port_width = 1; - return SCI_SUCCESS; } -- cgit v0.10.2 From 472d4d2cfbc169f3868a5f63ce727a482a2fd487 Mon Sep 17 00:00:00 2001 From: Marcin Tomczak Date: Wed, 4 Jan 2012 01:33:26 -0800 Subject: [SCSI] isci: allow more time for wide port targets When hot insert the wide port device through the mini-sas port, the first IOs (Report Luns or Inquiry) may fail due to the device trying to open to a SCU phy that is still in suspended state. This IO failure causes the wide port device stuck in UPDATING_PORT_WIDTH state. Signed-off-by: Marcin Tomczak Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 38a99d2..37d15d3 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -57,7 +57,7 @@ #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10) #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10) -#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (100) +#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (250) enum SCIC_SDS_APC_ACTIVITY { SCIC_SDS_APC_SKIP_PHY, -- cgit v0.10.2 From be778341812dc75b1c515fab6ebd39c0daf1e2bc Mon Sep 17 00:00:00 2001 From: Marcin Tomczak Date: Wed, 4 Jan 2012 01:33:31 -0800 Subject: [SCSI] isci: fix io failures while wide port links are coming up When the first phy of a wide port comes up, don't report the port ready yet, always wait for 250 miliseconds then config the port with all phys added to the port. So that we can avoid reporting wide port device too early to kernel, which caused the first IOs (report luns, inquirys) failed due to not all the phys are configured into its port. Changes also made that the phys in a wide port don't need to go through half second wait time for consuming power. Signed-off-by: Marcin Tomczak Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 7e4d709..1a65d65 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -899,7 +899,8 @@ static enum sci_status sci_controller_start_next_phy(struct isci_host *ihost) */ if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) || (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) || - (iphy->is_in_link_training == true && is_phy_starting(iphy))) { + (iphy->is_in_link_training == true && is_phy_starting(iphy)) || + (ihost->port_agent.phy_ready_mask != ihost->port_agent.phy_configured_mask)) { is_controller_start_complete = false; break; } @@ -1904,6 +1905,31 @@ static void power_control_timeout(unsigned long data) ihost->power_control.phys_waiting--; ihost->power_control.phys_granted_power++; sci_phy_consume_power_handler(iphy); + + if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + u8 j; + + for (j = 0; j < SCI_MAX_PHYS; j++) { + struct isci_phy *requester = ihost->power_control.requesters[j]; + + /* + * Search the power_control queue to see if there are other phys + * attached to the same remote device. If found, take all of + * them out of await_sas_power state. + */ + if (requester != NULL && requester != iphy) { + u8 other = memcmp(requester->frame_rcvd.iaf.sas_addr, + iphy->frame_rcvd.iaf.sas_addr, + sizeof(requester->frame_rcvd.iaf.sas_addr)); + + if (other == 0) { + ihost->power_control.requesters[j] = NULL; + ihost->power_control.phys_waiting--; + sci_phy_consume_power_handler(requester); + } + } + } + } } /* @@ -1938,9 +1964,34 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost, ihost->power_control.timer_started = true; } else { - /* Add the phy in the waiting list */ - ihost->power_control.requesters[iphy->phy_index] = iphy; - ihost->power_control.phys_waiting++; + /* + * There are phys, attached to the same sas address as this phy, are + * already in READY state, this phy don't need wait. + */ + u8 i; + struct isci_phy *current_phy; + + for (i = 0; i < SCI_MAX_PHYS; i++) { + u8 other; + current_phy = &ihost->phys[i]; + + other = memcmp(current_phy->frame_rcvd.iaf.sas_addr, + iphy->frame_rcvd.iaf.sas_addr, + sizeof(current_phy->frame_rcvd.iaf.sas_addr)); + + if (current_phy->sm.current_state_id == SCI_PHY_READY && + current_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS && + other == 0) { + sci_phy_consume_power_handler(iphy); + break; + } + } + + if (i == SCI_MAX_PHYS) { + /* Add the phy in the waiting list */ + ihost->power_control.requesters[iphy->phy_index] = iphy; + ihost->power_control.phys_waiting++; + } } } diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 37d15d3..6d1e954 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -466,6 +466,23 @@ sci_apc_agent_validate_phy_configuration(struct isci_host *ihost, return sci_port_configuration_agent_validate_ports(ihost, port_agent); } +/* + * This routine will restart the automatic port configuration timeout + * timer for the next time period. This could be caused by either a link + * down event or a link up event where we can not yet tell to which a phy + * belongs. + */ +static void sci_apc_agent_start_timer( + struct sci_port_configuration_agent *port_agent, + u32 timeout) +{ + if (port_agent->timer_pending) + sci_del_timer(&port_agent->timer); + + port_agent->timer_pending = true; + sci_mod_timer(&port_agent->timer, timeout); +} + static void sci_apc_agent_configure_ports(struct isci_host *ihost, struct sci_port_configuration_agent *port_agent, struct isci_phy *iphy, @@ -565,17 +582,8 @@ static void sci_apc_agent_configure_ports(struct isci_host *ihost, break; case SCIC_SDS_APC_START_TIMER: - /* - * This can occur for either a link down event, or a link - * up event where we cannot yet tell the port to which a - * phy belongs. - */ - if (port_agent->timer_pending) - sci_del_timer(&port_agent->timer); - - port_agent->timer_pending = true; - sci_mod_timer(&port_agent->timer, - SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION); + sci_apc_agent_start_timer(port_agent, + SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION); break; case SCIC_SDS_APC_SKIP_PHY: @@ -607,7 +615,8 @@ static void sci_apc_agent_link_up(struct isci_host *ihost, if (!iport) { /* the phy is not the part of this port */ port_agent->phy_ready_mask |= 1 << phy_index; - sci_apc_agent_configure_ports(ihost, port_agent, iphy, true); + sci_apc_agent_start_timer(port_agent, + SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION); } else { /* the phy is already the part of the port */ u32 port_state = iport->sm.current_state_id; -- cgit v0.10.2 From 0953dbea1d9a84c8443b5f5bb45229a6c9d7f9f3 Mon Sep 17 00:00:00 2001 From: Marcin Tomczak Date: Wed, 4 Jan 2012 01:33:36 -0800 Subject: [SCSI] isci: fix start OOB Split scu_link_layer_start_oob function to reset and enable and add flush after all steps. Signed-off-by: Marcin Tomczak Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 61000cd..fe18acf 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -1098,24 +1098,25 @@ static void scu_link_layer_stop_protocol_engine( writel(enable_spinup_value, &iphy->link_layer_registers->notify_enable_spinup_control); } -/** - * - * - * This method will start the OOB/SN state machine for this struct isci_phy object. - */ -static void scu_link_layer_start_oob( - struct isci_phy *iphy) +static void scu_link_layer_start_oob(struct isci_phy *iphy) { - u32 scu_sas_pcfg_value; - - scu_sas_pcfg_value = - readl(&iphy->link_layer_registers->phy_configuration); - scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); - scu_sas_pcfg_value &= - ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) | - SCU_SAS_PCFG_GEN_BIT(HARD_RESET)); - writel(scu_sas_pcfg_value, - &iphy->link_layer_registers->phy_configuration); + struct scu_link_layer_registers __iomem *ll = iphy->link_layer_registers; + u32 val; + + /** Reset OOB sequence - start */ + val = readl(&ll->phy_configuration); + val &= ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) | + SCU_SAS_PCFG_GEN_BIT(HARD_RESET)); + writel(val, &ll->phy_configuration); + readl(&ll->phy_configuration); /* flush */ + /** Reset OOB sequence - end */ + + /** Start OOB sequence - start */ + val = readl(&ll->phy_configuration); + val |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); + writel(val, &ll->phy_configuration); + readl(&ll->phy_configuration); /* flush */ + /** Start OOB sequence - end */ } /** -- cgit v0.10.2 From 05b080fc933bb068b32fa119db00e8efcc10e3bd Mon Sep 17 00:00:00 2001 From: Marcin Tomczak Date: Wed, 4 Jan 2012 01:33:41 -0800 Subject: [SCSI] isci: fix, prevent port from getting stuck in the 'configuring' state When expander connected in x2 or x4 mode and with IO runnning, if a cable from wideport is plugged out from the phy, IO's start failing on all the targets. Observed that when cable is pulled with IO running, cominit is happening on all the links and IO's start dropping to 0 and eventually the whole IO fails. Second observation, target is trying to open and SCU is responding with "Open reject no destination". A cause of the problem is when the port went from the "ready configuring substate" back to "ready configuring substate" as a result of phy being pulled off, scic suspended the port task scheduler register. As a result no IO was allowed and in the "substate configuring enter" routine the IO never goes back to 0. As a result the port never comes out of "ready substate configuring". The patch adds a mechanism of activate and deactivate phy when a port link up, which fixes the problem. Signed-off-by: Bartek Nowakowski Signed-off-by: Maciej Trela Signed-off-by: Marcin Tomczak Signed-off-by: Dan Williams Signed-off-by: James Bottomley diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 49e8a72..7c6ac58 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -647,19 +647,26 @@ void sci_port_setup_transports(struct isci_port *iport, u32 device_id) } } -static void sci_port_activate_phy(struct isci_port *iport, struct isci_phy *iphy, - bool do_notify_user) +static void sci_port_resume_phy(struct isci_port *iport, struct isci_phy *iphy) +{ + sci_phy_resume(iphy); + iport->enabled_phy_mask |= 1 << iphy->phy_index; +} + +static void sci_port_activate_phy(struct isci_port *iport, + struct isci_phy *iphy, + u8 flags) { struct isci_host *ihost = iport->owning_controller; - if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA) + if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA && (flags & PF_RESUME)) sci_phy_resume(iphy); iport->active_phy_mask |= 1 << iphy->phy_index; sci_controller_clear_invalid_phy(ihost, iphy); - if (do_notify_user == true) + if (flags & PF_NOTIFY) isci_port_link_up(ihost, iport, iphy); } @@ -669,6 +676,7 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy, struct isci_host *ihost = iport->owning_controller; iport->active_phy_mask &= ~(1 << iphy->phy_index); + iport->enabled_phy_mask &= ~(1 << iphy->phy_index); if (!iport->active_phy_mask) iport->last_active_phy = iphy->phy_index; @@ -705,18 +713,16 @@ static void sci_port_invalid_link_up(struct isci_port *iport, struct isci_phy *i * sci_port_general_link_up_handler - phy can be assigned to port? * @sci_port: sci_port object for which has a phy that has gone link up. * @sci_phy: This is the struct isci_phy object that has gone link up. - * @do_notify_user: This parameter specifies whether to inform the user (via - * sci_port_link_up()) as to the fact that a new phy as become ready. + * @flags: PF_RESUME, PF_NOTIFY to sci_port_activate_phy * - * Determine if this phy can be assigned to this - * port . If the phy is not a valid PHY for - * this port then the function will notify the user. A PHY can only be - * part of a port if it's attached SAS ADDRESS is the same as all other PHYs in - * the same port. none + * Determine if this phy can be assigned to this port . If the phy is + * not a valid PHY for this port then the function will notify the user. + * A PHY can only be part of a port if it's attached SAS ADDRESS is the + * same as all other PHYs in the same port. */ static void sci_port_general_link_up_handler(struct isci_port *iport, - struct isci_phy *iphy, - bool do_notify_user) + struct isci_phy *iphy, + u8 flags) { struct sci_sas_address port_sas_address; struct sci_sas_address phy_sas_address; @@ -734,7 +740,7 @@ static void sci_port_general_link_up_handler(struct isci_port *iport, iport->active_phy_mask == 0) { struct sci_base_state_machine *sm = &iport->sm; - sci_port_activate_phy(iport, iphy, do_notify_user); + sci_port_activate_phy(iport, iphy, flags); if (sm->current_state_id == SCI_PORT_RESETTING) port_state_machine_change(iport, SCI_PORT_READY); } else @@ -785,11 +791,16 @@ bool sci_port_link_detected( struct isci_phy *iphy) { if ((iport->logical_port_index != SCIC_SDS_DUMMY_PORT) && - (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) && - sci_port_is_wide(iport)) { - sci_port_invalid_link_up(iport, iphy); - - return false; + (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA)) { + if (sci_port_is_wide(iport)) { + sci_port_invalid_link_up(iport, iphy); + return false; + } else { + struct isci_host *ihost = iport->owning_controller; + struct isci_port *dst_port = &(ihost->ports[iphy->phy_index]); + writel(iphy->phy_index, + &dst_port->port_pe_configuration_register[iphy->phy_index]); + } } return true; @@ -979,6 +990,13 @@ static void sci_port_ready_substate_waiting_enter(struct sci_base_state_machine } } +static void scic_sds_port_ready_substate_waiting_exit( + struct sci_base_state_machine *sm) +{ + struct isci_port *iport = container_of(sm, typeof(*iport), sm); + sci_port_resume_port_task_scheduler(iport); +} + static void sci_port_ready_substate_operational_enter(struct sci_base_state_machine *sm) { u32 index; @@ -992,13 +1010,13 @@ static void sci_port_ready_substate_operational_enter(struct sci_base_state_mach writel(iport->physical_port_index, &iport->port_pe_configuration_register[ iport->phy_table[index]->phy_index]); + if (((iport->active_phy_mask^iport->enabled_phy_mask) & (1 << index)) != 0) + sci_port_resume_phy(iport, iport->phy_table[index]); } } sci_port_update_viit_entry(iport); - sci_port_resume_port_task_scheduler(iport); - /* * Post the dummy task for the port so the hardware can schedule * io correctly @@ -1065,20 +1083,9 @@ static void sci_port_ready_substate_configuring_enter(struct sci_base_state_mach if (iport->active_phy_mask == 0) { isci_port_not_ready(ihost, iport); - port_state_machine_change(iport, - SCI_PORT_SUB_WAITING); - } else if (iport->started_request_count == 0) - port_state_machine_change(iport, - SCI_PORT_SUB_OPERATIONAL); -} - -static void sci_port_ready_substate_configuring_exit(struct sci_base_state_machine *sm) -{ - struct isci_port *iport = container_of(sm, typeof(*iport), sm); - - sci_port_suspend_port_task_scheduler(iport); - if (iport->ready_exit) - sci_port_invalidate_dummy_remote_node(iport); + port_state_machine_change(iport, SCI_PORT_SUB_WAITING); + } else + port_state_machine_change(iport, SCI_PORT_SUB_OPERATIONAL); } enum sci_status sci_port_start(struct isci_port *iport) @@ -1256,7 +1263,7 @@ enum sci_status sci_port_add_phy(struct isci_port *iport, if (status != SCI_SUCCESS) return status; - sci_port_general_link_up_handler(iport, iphy, true); + sci_port_general_link_up_handler(iport, iphy, PF_NOTIFY|PF_RESUME); iport->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING; port_state_machine_change(iport, SCI_PORT_SUB_CONFIGURING); @@ -1266,7 +1273,7 @@ enum sci_status sci_port_add_phy(struct isci_port *iport, if (status != SCI_SUCCESS) return status; - sci_port_general_link_up_handler(iport, iphy, true); + sci_port_general_link_up_handler(iport, iphy, PF_NOTIFY); /* Re-enter the configuring state since this may be the last phy in * the port. @@ -1342,13 +1349,13 @@ enum sci_status sci_port_link_up(struct isci_port *iport, /* Since this is the first phy going link up for the port we * can just enable it and continue */ - sci_port_activate_phy(iport, iphy, true); + sci_port_activate_phy(iport, iphy, PF_NOTIFY|PF_RESUME); port_state_machine_change(iport, SCI_PORT_SUB_OPERATIONAL); return SCI_SUCCESS; case SCI_PORT_SUB_OPERATIONAL: - sci_port_general_link_up_handler(iport, iphy, true); + sci_port_general_link_up_handler(iport, iphy, PF_NOTIFY|PF_RESUME); return SCI_SUCCESS; case SCI_PORT_RESETTING: /* TODO We should make sure that the phy that has gone @@ -1365,7 +1372,7 @@ enum sci_status sci_port_link_up(struct isci_port *iport, /* In the resetting state we don't notify the user regarding * link up and link down notifications. */ - sci_port_general_link_up_handler(iport, iphy, false); + sci_port_general_link_up_handler(iport, iphy, PF_RESUME); return SCI_SUCCESS; default: dev_warn(sciport_to_dev(iport), @@ -1588,14 +1595,14 @@ static const struct sci_base_state sci_port_state_table[] = { }, [SCI_PORT_SUB_WAITING] = { .enter_state = sci_port_ready_substate_waiting_enter, + .exit_state = scic_sds_port_ready_substate_waiting_exit, }, [SCI_PORT_SUB_OPERATIONAL] = { .enter_state = sci_port_ready_substate_operational_enter, .exit_state = sci_port_ready_substate_operational_exit }, [SCI_PORT_SUB_CONFIGURING] = { - .enter_state = sci_port_ready_substate_configuring_enter, - .exit_state = sci_port_ready_substate_configuring_exit + .enter_state = sci_port_ready_substate_configuring_enter }, [SCI_PORT_RESETTING] = { .exit_state = sci_port_resetting_state_exit @@ -1613,6 +1620,7 @@ void sci_port_construct(struct isci_port *iport, u8 index, iport->logical_port_index = SCIC_SDS_DUMMY_PORT; iport->physical_port_index = index; iport->active_phy_mask = 0; + iport->enabled_phy_mask = 0; iport->last_active_phy = 0; iport->ready_exit = false; diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index b3a7f12..0811609 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -63,6 +63,9 @@ #define SCIC_SDS_DUMMY_PORT 0xFF +#define PF_NOTIFY (1 << 0) +#define PF_RESUME (1 << 1) + struct isci_phy; struct isci_host; @@ -83,6 +86,8 @@ enum isci_status { * @logical_port_index: software port index * @physical_port_index: hardware port index * @active_phy_mask: identifies phy members + * @enabled_phy_mask: phy mask for the port + * that are already part of the port * @reserved_tag: * @reserved_rni: reserver for port task scheduler workaround * @started_request_count: reference count for outstanding commands @@ -104,6 +109,7 @@ struct isci_port { u8 logical_port_index; u8 physical_port_index; u8 active_phy_mask; + u8 enabled_phy_mask; u8 last_active_phy; u16 reserved_rni; u16 reserved_tag; -- cgit v0.10.2 From 466c08c71a7dc19528e9b336c5bfa5ec41730c7c Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 10 Jan 2012 11:27:01 +0800 Subject: [SCSI] don't change sdev starvation list order without request dispatched The sdev is deleted from starved list and then try to dispatch from this device. It's quite possible the sdev can't eventually dispatch a request, then the sdev will be in starved list tail. This isn't fair. There are two cases here: 1. unplug path. scsi_request_fn() calls to scsi_target_queue_ready(), then the dev is removed from starved list, but quite possible host queue isn't ready, the dev is moved to starved list without dispatching any request. 2. scsi_run_queue path. It deletes the dev from starved list first (both global and local starved lists), then handles the dev. Then we could have the same process like case 1. This patch fixes the first case. Case 2 isn't fixed, because there is a rare case scsi_run_queue finds host isn't busy but scsi_request_fn finds host is busy (other CPU is faster to get host queue depth). Not deleting the dev from starved list in scsi_run_queue will keep scsi_run_queue looping (though this is very rare case, because host will become busy). Fortunately fixing case 1 already gives big improvement for starvation in my test. In a 12 disk JBOD setup, running file creation under EXT4, this gives 12% more throughput. Signed-off-by: Shaohua Li Signed-off-by: James Bottomley diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index f85cfa6..b2c95db 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1316,15 +1316,10 @@ static inline int scsi_target_queue_ready(struct Scsi_Host *shost, } if (scsi_target_is_busy(starget)) { - if (list_empty(&sdev->starved_entry)) - list_add_tail(&sdev->starved_entry, - &shost->starved_list); + list_move_tail(&sdev->starved_entry, &shost->starved_list); return 0; } - /* We're OK to process the command, so we can't be starved */ - if (!list_empty(&sdev->starved_entry)) - list_del_init(&sdev->starved_entry); return 1; } -- cgit v0.10.2 From 7e95fffe080d4dbe826dfe864eb084916cdc6468 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 10 Jan 2012 15:42:34 -0800 Subject: [SCSI] sg: convert to kstrtoul_from_user() Instead of open coding this function use kstrtoul_from_user() directly. Signed-off-by: Stephen Boyd Acked-by: Douglas Gilbert Signed-off-by: Andrew Morton Signed-off-by: James Bottomley diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 02d9998..eacd46b 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2368,16 +2368,15 @@ static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { - int num; - char buff[11]; + int err; + unsigned long num; if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; - num = (count < 10) ? count : 10; - if (copy_from_user(buff, buffer, num)) - return -EFAULT; - buff[num] = '\0'; - sg_allow_dio = simple_strtoul(buff, NULL, 10) ? 1 : 0; + err = kstrtoul_from_user(buffer, count, 0, &num); + if (err) + return err; + sg_allow_dio = num ? 1 : 0; return count; } @@ -2390,17 +2389,15 @@ static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { - int num; + int err; unsigned long k = ULONG_MAX; - char buff[11]; if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; - num = (count < 10) ? count : 10; - if (copy_from_user(buff, buffer, num)) - return -EFAULT; - buff[num] = '\0'; - k = simple_strtoul(buff, NULL, 10); + + err = kstrtoul_from_user(buffer, count, 0, &k); + if (err) + return err; if (k <= 1048576) { /* limit "big buff" to 1 MB */ sg_big_buff = k; return count; -- cgit v0.10.2 From 124dd90f6525ee785b47b59aebadd4a35f797dc1 Mon Sep 17 00:00:00 2001 From: adam radford Date: Tue, 10 Jan 2012 18:07:56 -0800 Subject: [SCSI] megaraid: fix sparse warnings There's a zero day mistake in the megaraid driver in that the code that obtains the version number does a >> 8 on a char quantity. This >>8 causes a sparse warning because it always produces zero. Al Viro suggested these shifts should be >> 4 thus treating the firmware version as a BCD quantity. However, in the interests of safety we've elected to replace the >> 8 quantities with an explicit zero, thus quieting the sparse warning while preserving the same (albeit incorrect) version number as had previously been seen. Signed-off-by: Adam Radford Signed-off-by: James Bottomley diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 5c17764..15eefa1 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -306,19 +306,22 @@ mega_query_adapter(adapter_t *adapter) adapter->host->sg_tablesize = adapter->sglen; - /* use HP firmware and bios version encoding */ + /* use HP firmware and bios version encoding + Note: fw_version[0|1] and bios_version[0|1] were originally shifted + right 8 bits making them zero. This 0 value was hardcoded to fix + sparse warnings. */ if (adapter->product_info.subsysvid == HP_SUBSYS_VID) { sprintf (adapter->fw_version, "%c%d%d.%d%d", adapter->product_info.fw_version[2], - adapter->product_info.fw_version[1] >> 8, + 0, adapter->product_info.fw_version[1] & 0x0f, - adapter->product_info.fw_version[0] >> 8, + 0, adapter->product_info.fw_version[0] & 0x0f); sprintf (adapter->bios_version, "%c%d%d.%d%d", adapter->product_info.bios_version[2], - adapter->product_info.bios_version[1] >> 8, + 0, adapter->product_info.bios_version[1] & 0x0f, - adapter->product_info.bios_version[0] >> 8, + 0, adapter->product_info.bios_version[0] & 0x0f); } else { memcpy(adapter->fw_version, -- cgit v0.10.2 From 4f77083ed0325ceb9cd5701c0f335583df3dded2 Mon Sep 17 00:00:00 2001 From: Mike Hernandez Date: Wed, 11 Jan 2012 02:44:15 -0800 Subject: [SCSI] qla4xxx: Temperature monitoring for ISP82XX core. During watchdog, need to monitor temperature of ISP82XX core and set device state to FAILED when temperature reaches "Panic" level. Signed-off-by: Mike Hernandez Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 22a3ff0..ec48dc3 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -671,6 +671,7 @@ struct scsi_qla_host { uint16_t pri_ddb_idx; uint16_t sec_ddb_idx; int is_reset; + uint16_t temperature; }; struct ql4_task_data { diff --git a/drivers/scsi/qla4xxx/ql4_nx.h b/drivers/scsi/qla4xxx/ql4_nx.h index 35376a1..cfb2f2e 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.h +++ b/drivers/scsi/qla4xxx/ql4_nx.h @@ -19,12 +19,25 @@ #define PHAN_PEG_RCV_INITIALIZED 0xff01 /*CRB_RELATED*/ -#define QLA82XX_CRB_BASE QLA82XX_CAM_RAM(0x200) -#define QLA82XX_REG(X) (QLA82XX_CRB_BASE+(X)) - +#define QLA82XX_CRB_BASE (QLA82XX_CAM_RAM(0x200)) +#define QLA82XX_REG(X) (QLA82XX_CRB_BASE+(X)) #define CRB_CMDPEG_STATE QLA82XX_REG(0x50) #define CRB_RCVPEG_STATE QLA82XX_REG(0x13c) #define CRB_DMA_SHIFT QLA82XX_REG(0xcc) +#define CRB_TEMP_STATE QLA82XX_REG(0x1b4) + +#define qla82xx_get_temp_val(x) ((x) >> 16) +#define qla82xx_get_temp_state(x) ((x) & 0xffff) +#define qla82xx_encode_temp(val, state) (((val) << 16) | (state)) + +/* + * Temperature control. + */ +enum { + QLA82XX_TEMP_NORMAL = 0x1, /* Normal operating range */ + QLA82XX_TEMP_WARN, /* Sound alert, temperature getting high */ + QLA82XX_TEMP_PANIC /* Fatal error, hardware has shut down. */ +}; #define QLA82XX_HW_H0_CH_HUB_ADR 0x05 #define QLA82XX_HW_H1_CH_HUB_ADR 0x0E diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 0048a3f..9d3eab5 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1972,6 +1972,42 @@ mem_alloc_error_exit: } /** + * qla4_8xxx_check_temp - Check the ISP82XX temperature. + * @ha: adapter block pointer. + * + * Note: The caller should not hold the idc lock. + **/ +static int qla4_8xxx_check_temp(struct scsi_qla_host *ha) +{ + uint32_t temp, temp_state, temp_val; + int status = QLA_SUCCESS; + + temp = qla4_8xxx_rd_32(ha, CRB_TEMP_STATE); + + temp_state = qla82xx_get_temp_state(temp); + temp_val = qla82xx_get_temp_val(temp); + + if (temp_state == QLA82XX_TEMP_PANIC) { + ql4_printk(KERN_WARNING, ha, "Device temperature %d degrees C" + " exceeds maximum allowed. Hardware has been shut" + " down.\n", temp_val); + status = QLA_ERROR; + } else if (temp_state == QLA82XX_TEMP_WARN) { + if (ha->temperature == QLA82XX_TEMP_NORMAL) + ql4_printk(KERN_WARNING, ha, "Device temperature %d" + " degrees C exceeds operating range." + " Immediate action needed.\n", temp_val); + } else { + if (ha->temperature == QLA82XX_TEMP_WARN) + ql4_printk(KERN_INFO, ha, "Device temperature is" + " now %d degrees C in normal range.\n", + temp_val); + } + ha->temperature = temp_state; + return status; +} + +/** * qla4_8xxx_check_fw_alive - Check firmware health * @ha: Pointer to host adapter structure. * @@ -2042,7 +2078,11 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha) test_bit(DPC_RESET_HA, &ha->dpc_flags) || test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags))) { dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE); - if (dev_state == QLA82XX_DEV_NEED_RESET && + + if (qla4_8xxx_check_temp(ha)) { + set_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags); + qla4xxx_wake_dpc(ha); + } else if (dev_state == QLA82XX_DEV_NEED_RESET && !test_bit(DPC_RESET_HA, &ha->dpc_flags)) { if (!ql4xdontresethba) { ql4_printk(KERN_INFO, ha, "%s: HW State: " -- cgit v0.10.2 From e6bd0ebd4a8ba9118e970c0214e912e35895c92b Mon Sep 17 00:00:00 2001 From: Giridhar Malavali Date: Wed, 11 Jan 2012 02:44:16 -0800 Subject: [SCSI] qla4xxx: Disable generating pause frames in case of FW hung In case of FW hung ISP82xx generates continuous pause frames which causes switch to disable port. Added fix to disable generating pause frames in case of FW hung Signed-off-by: Giridhar Malavali Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index c259378..e1e66a4 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -219,6 +219,13 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, ha->mailbox_timeout_count++; mbx_sts[0] = (-1); set_bit(DPC_RESET_HA, &ha->dpc_flags); + if (is_qla8022(ha)) { + ql4_printk(KERN_INFO, ha, + "disabling pause transmit on port 0 & 1.\n"); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x98, + CRB_NIU_XG_PAUSE_CTL_P0 | + CRB_NIU_XG_PAUSE_CTL_P1); + } goto mbox_exit; } diff --git a/drivers/scsi/qla4xxx/ql4_nx.h b/drivers/scsi/qla4xxx/ql4_nx.h index cfb2f2e..dc45ac9 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.h +++ b/drivers/scsi/qla4xxx/ql4_nx.h @@ -39,6 +39,9 @@ enum { QLA82XX_TEMP_PANIC /* Fatal error, hardware has shut down. */ }; +#define CRB_NIU_XG_PAUSE_CTL_P0 0x1 +#define CRB_NIU_XG_PAUSE_CTL_P1 0x8 + #define QLA82XX_HW_H0_CH_HUB_ADR 0x05 #define QLA82XX_HW_H1_CH_HUB_ADR 0x0E #define QLA82XX_HW_H2_CH_HUB_ADR 0x03 diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 9d3eab5..e2ef776 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -2080,6 +2080,11 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha) dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE); if (qla4_8xxx_check_temp(ha)) { + ql4_printk(KERN_INFO, ha, "disabling pause" + " transmit on port 0 & 1.\n"); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x98, + CRB_NIU_XG_PAUSE_CTL_P0 | + CRB_NIU_XG_PAUSE_CTL_P1); set_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags); qla4xxx_wake_dpc(ha); } else if (dev_state == QLA82XX_DEV_NEED_RESET && @@ -2099,6 +2104,11 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha) } else { /* Check firmware health */ if (qla4_8xxx_check_fw_alive(ha)) { + ql4_printk(KERN_INFO, ha, "disabling pause" + " transmit on port 0 & 1.\n"); + qla4_8xxx_wr_32(ha, QLA82XX_CRB_NIU + 0x98, + CRB_NIU_XG_PAUSE_CTL_P0 | + CRB_NIU_XG_PAUSE_CTL_P1); halt_status = qla4_8xxx_rd_32(ha, QLA82XX_PEG_HALT_STATUS1); -- cgit v0.10.2 From 527c8b2e962d21baa38a96b22e1bf50a47fdf4fb Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Wed, 11 Jan 2012 02:44:17 -0800 Subject: [SCSI] qla4xxx: Added error logging for firmware abort Added debug print with error code in case of firmware error. Signed-off-by: Nilesh Javali Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index ec48dc3..bfe6854 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -150,6 +150,8 @@ #define QL4_SESS_RECOVERY_TMO 120 /* iSCSI session */ /* recovery timeout */ +#define MSB(x) ((uint8_t)((uint16_t)(x) >> 8)) +#define LSW(x) ((uint16_t)(x)) #define LSDW(x) ((u32)((u64)(x))) #define MSDW(x) ((u32)((((u64)(x)) >> 16) >> 16)) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index e2ef776..b75590a 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -2112,6 +2112,13 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha) halt_status = qla4_8xxx_rd_32(ha, QLA82XX_PEG_HALT_STATUS1); + if (LSW(MSB(halt_status)) == 0x67) + ql4_printk(KERN_ERR, ha, "%s:" + " Firmware aborted with" + " error code 0x00006700." + " Device is being reset\n", + __func__); + /* Since we cannot change dev_state in interrupt * context, set appropriate DPC flag then wakeup * DPC */ -- cgit v0.10.2 From 787649993230eb1d6fc326f13be285a840092538 Mon Sep 17 00:00:00 2001 From: Sarang Radke Date: Wed, 11 Jan 2012 02:44:18 -0800 Subject: [SCSI] qla4xxx: Clear the RISC interrupt bit during FW init This patch fix kernel panic during kdump. Signed-off-by: Sarang Radke Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 1bdfa81..90614f3 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -697,6 +697,9 @@ int qla4xxx_start_firmware(struct scsi_qla_host *ha) writel(set_rmask(CSR_SCSI_PROCESSOR_INTR), &ha->reg->ctrl_status); readl(&ha->reg->ctrl_status); + writel(set_rmask(CSR_SCSI_COMPLETION_INTR), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); spin_unlock_irqrestore(&ha->hardware_lock, flags); if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS) { DEBUG2(printk("scsi%ld: %s: Get firmware " diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 8d6bc1b..78f1111 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -1875,6 +1875,11 @@ exit: int qla4_8xxx_load_risc(struct scsi_qla_host *ha) { int retval; + + /* clear the interrupt */ + writel(0, &ha->qla4_8xxx_reg->host_int); + readl(&ha->qla4_8xxx_reg->host_int); + retval = qla4_8xxx_device_state_handler(ha); if (retval == QLA_SUCCESS && !test_bit(AF_INIT_DONE, &ha->flags)) -- cgit v0.10.2 From 991039f9f40d0809108e8d453c68c2ae965b3ac4 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Wed, 11 Jan 2012 02:44:19 -0800 Subject: [SCSI] qla4xxx: Update license Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/Documentation/scsi/LICENSE.qla4xxx b/Documentation/scsi/LICENSE.qla4xxx index 494980e..ab89959 100644 --- a/Documentation/scsi/LICENSE.qla4xxx +++ b/Documentation/scsi/LICENSE.qla4xxx @@ -1,32 +1,11 @@ Copyright (c) 2003-2011 QLogic Corporation -QLogic Linux iSCSI HBA Driver +QLogic Linux iSCSI Driver This program includes a device driver for Linux 3.x. You may modify and redistribute the device driver code under the GNU General Public License (a copy of which is attached hereto as Exhibit A) published by the Free Software Foundation (version 2). -REGARDLESS OF WHAT LICENSING MECHANISM IS USED OR APPLICABLE, -THIS PROGRAM IS PROVIDED BY QLOGIC CORPORATION "AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -USER ACKNOWLEDGES AND AGREES THAT USE OF THIS PROGRAM WILL NOT -CREATE OR GIVE GROUNDS FOR A LICENSE BY IMPLICATION, ESTOPPEL, OR -OTHERWISE IN ANY INTELLECTUAL PROPERTY RIGHTS (PATENT, COPYRIGHT, -TRADE SECRET, MASK WORK, OR OTHER PROPRIETARY RIGHT) EMBODIED IN -ANY OTHER QLOGIC HARDWARE OR SOFTWARE EITHER SOLELY OR IN -COMBINATION WITH THIS PROGRAM. - EXHIBIT A -- cgit v0.10.2 From a4e8a715a39e5d29f7d35a7289b914ef205a866b Mon Sep 17 00:00:00 2001 From: Karen Higgins Date: Wed, 11 Jan 2012 02:44:20 -0800 Subject: [SCSI] qla4xxx: Cleanup modinfo display Beautify modinfo display. Display correct info for ql4xextended_error_logging [jejb: fixup checkpatch warning] Signed-off-by: Karen Higgins Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index b75590a..ce6d3b7 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -35,43 +35,44 @@ static struct kmem_cache *srb_cachep; int ql4xdisablesysfsboot = 1; module_param(ql4xdisablesysfsboot, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql4xdisablesysfsboot, - "Set to disable exporting boot targets to sysfs\n" - " 0 - Export boot targets\n" - " 1 - Do not export boot targets (Default)"); + " Set to disable exporting boot targets to sysfs.\n" + "\t\t 0 - Export boot targets\n" + "\t\t 1 - Do not export boot targets (Default)"); int ql4xdontresethba = 0; module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql4xdontresethba, - "Don't reset the HBA for driver recovery \n" - " 0 - It will reset HBA (Default)\n" - " 1 - It will NOT reset HBA"); + " Don't reset the HBA for driver recovery.\n" + "\t\t 0 - It will reset HBA (Default)\n" + "\t\t 1 - It will NOT reset HBA"); -int ql4xextended_error_logging = 0; /* 0 = off, 1 = log errors */ +int ql4xextended_error_logging; module_param(ql4xextended_error_logging, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql4xextended_error_logging, - "Option to enable extended error logging, " - "Default is 0 - no logging, 1 - debug logging"); + " Option to enable extended error logging.\n" + "\t\t 0 - no logging (Default)\n" + "\t\t 2 - debug logging"); int ql4xenablemsix = 1; module_param(ql4xenablemsix, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql4xenablemsix, - "Set to enable MSI or MSI-X interrupt mechanism.\n" - " 0 = enable INTx interrupt mechanism.\n" - " 1 = enable MSI-X interrupt mechanism (Default).\n" - " 2 = enable MSI interrupt mechanism."); + " Set to enable MSI or MSI-X interrupt mechanism.\n" + "\t\t 0 = enable INTx interrupt mechanism.\n" + "\t\t 1 = enable MSI-X interrupt mechanism (Default).\n" + "\t\t 2 = enable MSI interrupt mechanism."); #define QL4_DEF_QDEPTH 32 static int ql4xmaxqdepth = QL4_DEF_QDEPTH; module_param(ql4xmaxqdepth, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql4xmaxqdepth, - "Maximum queue depth to report for target devices.\n" - " Default: 32."); + " Maximum queue depth to report for target devices.\n" + "\t\t Default: 32."); static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO; module_param(ql4xsess_recovery_tmo, int, S_IRUGO); MODULE_PARM_DESC(ql4xsess_recovery_tmo, "Target Session Recovery Timeout.\n" - " Default: 120 sec."); + "\t\t Default: 120 sec."); static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha); /* -- cgit v0.10.2 From 19b628dfc60eda4459b65e4b6b7a748932d28929 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Wed, 11 Jan 2012 02:44:21 -0800 Subject: [SCSI] qla4xxx: Update driver version to 5.02.00-k12 Signed-off-by: Vikas Chaudhary Signed-off-by: James Bottomley diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index 7d04eb0..133989b 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,4 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.02.00-k11" +#define QLA4XXX_DRIVER_VERSION "5.02.00-k12" -- cgit v0.10.2 From f10448689d95b9516c656ccd4078839e656656e7 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 11 Jan 2012 05:11:46 +0400 Subject: x86: Get rid of dubious one-bit signed bitfield This very noisy sparse warning appears on almost every file in the kernel: CHECK init/main.c arch/x86/include/asm/thread_info.h:43:55: error: dubious one-bit signed bitfield arch/x86/include/asm/thread_info.h:44:46: error: dubious one-bit signed bitfield Sparse is right and this patch changes sig_on_uaccess_error and uaccess_err flags to unsigned type and thus fixes the warning. Signed-off-by: Anton Vorontsov Acked-by: Andy Lutomirski Cc: Linus Torvalds Cc: H. Peter Anvin Cc: Dan Carpenter Link: http://lkml.kernel.org/r/20120111011146.GA30428@oksana.dev.rtsoft.ru Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 185b719..56a63ff 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -40,8 +40,8 @@ struct thread_info { */ __u8 supervisor_stack[0]; #endif - int sig_on_uaccess_error:1; - int uaccess_err:1; /* uaccess failed */ + unsigned int sig_on_uaccess_error:1; + unsigned int uaccess_err:1; /* uaccess failed */ }; #define INIT_THREAD_INFO(tsk) \ -- cgit v0.10.2 From a762dce41cb5742a143f6aa2d80ee1aac7e1f5eb Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Fri, 13 Jan 2012 17:26:15 -0800 Subject: [SCSI] fcoe: fix regression on offload em matching function for initiator/target This is a regression introduced by commit 1ff9918b625457ce20d450d00f9ed0a12ba191b7 The else statement here is breaking the initiator logic of allocating xid from the offloaded em xid pool for READ I/O only to use DDP, as shown by the snippet of trace below, where the WRITE is using xid 0x5 from the offloaded em xid pool: Protocol VID Len S_ID D_ID OX_ID RX_ID Summary .. *FCP 228 96 0b.08.01 -> 01.0f.00 0x0005 0xffff SCSI: Write(10) LUN: 0x00 FCP 228 76 01.0f.00 -> 0b.08.01 0x0005 0x828d XFER_RDY ... The bug is in the else statement, for both initiator and target, the new command will have FC frame header bit 23 (FC_FC_EX_CTX) cleared as it was originated from the initiator. Also, this is assuming the frame header is already filled up, which is only true for target since for initiator, this is a new frame and oem_match gets called when em tries get xid for this i/o before it is filled up and sent out. The fix is to check if there is a fc_fcp_pkt associated w/ this frame from fr_fsp(fp), since fr_fsp(fp) is NULL for tcm_fc target and non-I/O frame in initiator. This should also return true for target only if it is an FC_RCTL_DD_UNSOL_CMD and rx_id is not allocated. Signed-off-by: Yi Zou Tested-by: Ross Brattain Signed-off-by: Robert Love Signed-off-by: James Bottomley diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index e535f95..507504b 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -756,11 +756,12 @@ bool fcoe_oem_match(struct fc_frame *fp) if (fc_fcp_is_read(fr_fsp(fp)) && (fr_fsp(fp)->data_len > fcoe_ddp_min)) return true; - else if (!(ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)) { + else if ((fr_fsp(fp) == NULL) && + (fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) && + (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)) { fcp = fc_frame_payload_get(fp, sizeof(*fcp)); - if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN && - fcp && (ntohl(fcp->fc_dl) > fcoe_ddp_min) && - (fcp->fc_flags & FCP_CFL_WRDATA)) + if ((fcp->fc_flags & FCP_CFL_WRDATA) && + (ntohl(fcp->fc_dl) > fcoe_ddp_min)) return true; } return false; -- cgit v0.10.2 From c6b21c93c1794113c68f3d43f321968191d87b1b Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 13 Jan 2012 17:26:20 -0800 Subject: [SCSI] libfc: Declare local functions static Avoid that sparse complains about missing declarations for local functions by declaring these static or by adding an #include directive. Add the __percpu annotation where it is missing. Signed-off-by: Bart Van Assche Reviewed-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 7269e92..1d1b0c9 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -61,7 +61,7 @@ static void fc_disc_restart(struct fc_disc *); * Locking Note: This function expects that the lport mutex is locked before * calling it. */ -void fc_disc_stop_rports(struct fc_disc *disc) +static void fc_disc_stop_rports(struct fc_disc *disc) { struct fc_lport *lport; struct fc_rport_priv *rdata; @@ -682,7 +682,7 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp) * fc_disc_stop() - Stop discovery for a given lport * @lport: The local port that discovery should stop on */ -void fc_disc_stop(struct fc_lport *lport) +static void fc_disc_stop(struct fc_lport *lport) { struct fc_disc *disc = &lport->disc; @@ -698,7 +698,7 @@ void fc_disc_stop(struct fc_lport *lport) * This function will block until discovery has been * completely stopped and all rports have been deleted. */ -void fc_disc_stop_final(struct fc_lport *lport) +static void fc_disc_stop_final(struct fc_lport *lport) { fc_disc_stop(lport); lport->tt.rport_flush_queue(); diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c index fb9161d..e17a28d 100644 --- a/drivers/scsi/libfc/fc_elsct.c +++ b/drivers/scsi/libfc/fc_elsct.c @@ -28,6 +28,7 @@ #include #include #include +#include "fc_libfc.h" /** * fc_elsct_send() - Send an ELS or CT frame diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 9de9db2..4d70d96 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -91,7 +91,7 @@ struct fc_exch_pool { * It manages the allocation of exchange IDs. */ struct fc_exch_mgr { - struct fc_exch_pool *pool; + struct fc_exch_pool __percpu *pool; mempool_t *ep_pool; enum fc_class class; struct kref kref; diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index e77094a..83750eb 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -677,7 +677,8 @@ EXPORT_SYMBOL(fc_set_mfs); * @lport: The local port receiving the event * @event: The discovery event */ -void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) +static void fc_lport_disc_callback(struct fc_lport *lport, + enum fc_disc_event event) { switch (event) { case DISC_EV_SUCCESS: @@ -1568,7 +1569,7 @@ EXPORT_SYMBOL(fc_lport_flogi_resp); * Locking Note: The lport lock is expected to be held before calling * this routine. */ -void fc_lport_enter_flogi(struct fc_lport *lport) +static void fc_lport_enter_flogi(struct fc_lport *lport) { struct fc_frame *fp; diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index b9e4348..83aa1ef 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -391,7 +391,7 @@ static void fc_rport_work(struct work_struct *work) * If it appears we are already logged in, ADISC is used to verify * the setup. */ -int fc_rport_login(struct fc_rport_priv *rdata) +static int fc_rport_login(struct fc_rport_priv *rdata) { mutex_lock(&rdata->rp_mutex); @@ -451,7 +451,7 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata, * function will hold the rport lock, call an _enter_* * function and then unlock the rport. */ -int fc_rport_logoff(struct fc_rport_priv *rdata) +static int fc_rport_logoff(struct fc_rport_priv *rdata) { mutex_lock(&rdata->rp_mutex); @@ -653,8 +653,8 @@ static int fc_rport_login_complete(struct fc_rport_priv *rdata, * @fp: The FLOGI response frame * @rp_arg: The remote port that received the FLOGI response */ -void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, - void *rp_arg) +static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, + void *rp_arg) { struct fc_rport_priv *rdata = rp_arg; struct fc_lport *lport = rdata->local_port; @@ -1520,7 +1520,7 @@ reject: * * Locking Note: Called with the lport lock held. */ -void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp) +static void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp) { struct fc_seq_els_data els_data; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 5d1a758..6a3922f 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -857,7 +857,7 @@ struct fc_lport { enum fc_lport_state state; unsigned long boot_time; struct fc_host_statistics host_stats; - struct fcoe_dev_stats *dev_stats; + struct fcoe_dev_stats __percpu *dev_stats; u8 retry_count; /* Fabric information */ -- cgit v0.10.2 From 7c9c684160bc2c6668abbd2701b440e18bb9ef35 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 13 Jan 2012 17:26:25 -0800 Subject: [SCSI] fcoe: Move fcoe_debug_logging from fcoe.h to fcoe.c Move the definition of the global variable fcoe_debug_logging from fcoe.h to fcoe.c. Avoid that sparse complains about missing declarations for local functions or variables by declaring these static. Signed-off-by: Bart Van Assche Reviewed-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 507504b..e959960 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -58,7 +58,11 @@ module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ "Direct Data Placement (DDP)."); -DEFINE_MUTEX(fcoe_config_mutex); +unsigned int fcoe_debug_logging; +module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); + +static DEFINE_MUTEX(fcoe_config_mutex); static struct workqueue_struct *fcoe_wq; @@ -67,8 +71,8 @@ static DECLARE_COMPLETION(fcoe_flush_completion); /* fcoe host list */ /* must only by accessed under the RTNL mutex */ -LIST_HEAD(fcoe_hostlist); -DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); +static LIST_HEAD(fcoe_hostlist); +static DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); /* Function Prototypes */ static int fcoe_reset(struct Scsi_Host *); @@ -157,7 +161,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { .lport_set_port_id = fcoe_set_port_id, }; -struct fc_function_template fcoe_nport_fc_functions = { +static struct fc_function_template fcoe_nport_fc_functions = { .show_host_node_name = 1, .show_host_port_name = 1, .show_host_supported_classes = 1, @@ -197,7 +201,7 @@ struct fc_function_template fcoe_nport_fc_functions = { .bsg_request = fc_lport_bsg_request, }; -struct fc_function_template fcoe_vport_fc_functions = { +static struct fc_function_template fcoe_vport_fc_functions = { .show_host_node_name = 1, .show_host_port_name = 1, .show_host_supported_classes = 1, @@ -433,7 +437,7 @@ static inline void fcoe_interface_put(struct fcoe_interface *fcoe) * * Caller must be holding the RTNL mutex */ -void fcoe_interface_cleanup(struct fcoe_interface *fcoe) +static void fcoe_interface_cleanup(struct fcoe_interface *fcoe) { struct net_device *netdev = fcoe->netdev; struct fcoe_ctlr *fip = &fcoe->ctlr; @@ -748,7 +752,7 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev) * * Returns: True for read types I/O, otherwise returns false. */ -bool fcoe_oem_match(struct fc_frame *fp) +static bool fcoe_oem_match(struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); struct fcp_cmnd *fcp; @@ -1107,7 +1111,7 @@ static int __init fcoe_if_init(void) * * Returns: 0 on success */ -int __exit fcoe_if_exit(void) +static int __exit fcoe_if_exit(void) { fc_release_transport(fcoe_nport_scsi_transport); fc_release_transport(fcoe_vport_scsi_transport); @@ -1296,7 +1300,7 @@ static inline unsigned int fcoe_select_cpu(void) * * Returns: 0 for success */ -int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, +static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, struct packet_type *ptype, struct net_device *olddev) { struct fc_lport *lport; @@ -1452,7 +1456,7 @@ static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen) * * Return: 0 for success */ -int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) +static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) { int wlen; u32 crc; @@ -1728,7 +1732,7 @@ drop: * * Return: 0 for success */ -int fcoe_percpu_receive_thread(void *arg) +static int fcoe_percpu_receive_thread(void *arg) { struct fcoe_percpu_s *p = arg; struct sk_buff *skb; @@ -2146,7 +2150,7 @@ out_nortnl: * Returns: 0 if the ethtool query was successful * -1 if the ethtool query failed */ -int fcoe_link_speed_update(struct fc_lport *lport) +static int fcoe_link_speed_update(struct fc_lport *lport) { struct net_device *netdev = fcoe_netdev(lport); struct ethtool_cmd ecmd; @@ -2180,7 +2184,7 @@ int fcoe_link_speed_update(struct fc_lport *lport) * Returns: 0 if link is UP and OK, -1 if not * */ -int fcoe_link_ok(struct fc_lport *lport) +static int fcoe_link_ok(struct fc_lport *lport) { struct net_device *netdev = fcoe_netdev(lport); @@ -2200,7 +2204,7 @@ int fcoe_link_ok(struct fc_lport *lport) * there no packets that will be handled by the lport, but also that any * threads already handling packet have returned. */ -void fcoe_percpu_clean(struct fc_lport *lport) +static void fcoe_percpu_clean(struct fc_lport *lport) { struct fcoe_percpu_s *pp; struct fcoe_rcv_info *fr; @@ -2251,7 +2255,7 @@ void fcoe_percpu_clean(struct fc_lport *lport) * * Returns: Always 0 (return value required by FC transport template) */ -int fcoe_reset(struct Scsi_Host *shost) +static int fcoe_reset(struct Scsi_Host *shost) { struct fc_lport *lport = shost_priv(shost); struct fcoe_port *port = lport_priv(lport); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 6c6884b..bcc89e6 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -40,9 +40,7 @@ #define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */ #define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */ -unsigned int fcoe_debug_logging; -module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); +extern unsigned int fcoe_debug_logging; #define FCOE_LOGGING 0x01 /* General logging, not categorized */ #define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */ -- cgit v0.10.2 From 76ffe8a3f766358a0ade543153625b3e4e66159d Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Fri, 13 Jan 2012 17:26:30 -0800 Subject: [SCSI] libfc: remove redundant timer init for fcp The fcp timer is already initialized when it gets allocated. Signed-off-by: Yi Zou Tested-by: Ross Brattain Signed-off-by: Robert Love Signed-off-by: James Bottomley diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 221875e..f607314 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -155,6 +155,7 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp) fsp->xfer_ddp = FC_XID_UNKNOWN; atomic_set(&fsp->ref_cnt, 1); init_timer(&fsp->timer); + fsp->timer.data = (unsigned long)fsp; INIT_LIST_HEAD(&fsp->list); spin_lock_init(&fsp->scsi_pkt_lock); } @@ -1850,9 +1851,6 @@ int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd) } put_cpu(); - init_timer(&fsp->timer); - fsp->timer.data = (unsigned long)fsp; - /* * send it to the lower layer * if we get -1 return then put the request in the pending -- cgit v0.10.2 From ffe535edb9a9c5b4d5fe03dfa3d89a1495580f1b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 16 Jan 2012 10:52:20 +0100 Subject: ALSA: HDA: Fix internal microphone on Dell Studio 16 XPS 1645 More than one user reports that changing the model from "both" to "dmic" makes their Internal Mic work. Cc: stable@kernel.org Tested-by: Martin Ling BugLink: https://bugs.launchpad.net/bugs/795823 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 87e684f..3556408 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1596,7 +1596,7 @@ static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd, "Dell Studio 1557", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, - "Dell Studio XPS 1645", STAC_DELL_M6_BOTH), + "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, "Dell Studio 1558", STAC_DELL_M6_DMIC), {} /* terminator */ -- cgit v0.10.2 From 7b32486ca449a20d64fa381262cc5e56893f0ac1 Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Fri, 13 Jan 2012 10:30:08 +0800 Subject: ALSA: Au88x0 - Support 4 channels playback when AC97 codecs has SDAC bit - Check SDAC bit of AC97 codec for supporting 4 channels playback. Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 762bb10..b8bc115 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -268,6 +268,11 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) card->shortname, chip->io, chip->irq); // (4) Alloc components. + err = snd_vortex_mixer(chip); + if (err < 0) { + snd_card_free(card); + return err; + } // ADB pcm. if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) { snd_card_free(card); @@ -299,11 +304,6 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return err; } #endif - // snd_ac97_mixer and Vortex mixer. - if ((err = snd_vortex_mixer(chip)) < 0) { - snd_card_free(card); - return err; - } if ((err = snd_vortex_midi(chip)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 0488633..0ef2f97 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -168,6 +168,7 @@ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) runtime->hw = snd_vortex_playback_hw_adb; #ifdef CHIP_AU8830 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + VORTEX_IS_QUAD(vortex) && VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) { runtime->hw.channels_max = 4; snd_pcm_hw_constraint_list(runtime, 0, -- cgit v0.10.2 From 49b9c40e9b5e47ef64f8601b84981fa45c3c4f86 Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Fri, 13 Jan 2012 10:35:01 +0800 Subject: ALSA: Au88x0 - Reduce the number of playback subdevices of au8830 from 32 to 16 - The maximum number of playback streams depend on the number of sample rate conveters (16) and the number of DMA channels (32). Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index b8bc115..f13ad53 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -274,7 +274,8 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return err; } // ADB pcm. - if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) { + err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM); + if (err < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index 02f6e08..bb93815 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -105,6 +105,7 @@ #define MIX_SPDIF(x) (vortex->mixspdif[x]) #define NR_WTPB 0x20 /* WT channels per each bank. */ +#define NR_PCM 0x10 /* Structs */ typedef struct { -- cgit v0.10.2 From f0e48b6bd4e407459715240cd241ddb6b89bdf81 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sat, 14 Jan 2012 16:42:24 +0100 Subject: ALSA: virtuoso: Xonar DS: fix polarity of front output The two DACs for the front output and the surround/center/LFE/back outputs are wired up out of phase, so when channels are duplicated, their sound can cancel out each other and result in a weaker bass response. To fix this, reverse the polarity of the neutron flow to the front output. Reported-any-tested-by: Daniel Hill Signed-off-by: Clemens Ladisch Cc: 2.6.34+ Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 478303e..63cff90 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -177,6 +177,7 @@ static void wm8776_registers_init(struct oxygen *chip) struct xonar_wm87x6 *data = chip->model_data; wm8776_write(chip, WM8776_RESET, 0); + wm8776_write(chip, WM8776_PHASESWAP, WM8776_PH_MASK); wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN | WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT); wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0); -- cgit v0.10.2 From 0cb64f02b8c8f8181d6eaa6d1931fe0bc4195a25 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 10 Jan 2012 05:04:39 -0300 Subject: [media] cx23885: handle errors from videobuf_dvb_get_frontend() The error handling in the original code wasn't complete so static checkers complained about a potential NULL deference. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 4bbf9bb..c654bdc 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -1550,7 +1550,6 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, struct v4l2_control ctrl; struct videobuf_dvb_frontend *vfe; struct dvb_frontend *fe; - int err = 0; struct analog_parameters params = { .mode = V4L2_TUNER_ANALOG_TV, @@ -1572,8 +1571,10 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, params.frequency, f->tuner, params.std); vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1); - if (!vfe) - err = -EINVAL; + if (!vfe) { + mutex_unlock(&dev->lock); + return -EINVAL; + } fe = vfe->dvb.frontend; -- cgit v0.10.2 From 05834fcf58fe77b0519a8dc40e3c4a5a28dfc88d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Nov 2011 08:59:34 -0300 Subject: [media] v4l2 spec: clarify usage of V4L2_FBUF_FLAG_OVERLAY Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml index 93817f3..7c63815 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml @@ -364,15 +364,20 @@ capability and it is cleared otherwise. V4L2_FBUF_FLAG_OVERLAY 0x0002 - The frame buffer is an overlay surface the same -size as the capture. [?] - - - The purpose of -V4L2_FBUF_FLAG_OVERLAY was never quite clear. -Most drivers seem to ignore this flag. For compatibility with the -bttv driver applications should set the -V4L2_FBUF_FLAG_OVERLAY flag. + If this flag is set for a video capture device, then the +driver will set the initial overlay size to cover the full framebuffer size, +otherwise the existing overlay size (as set by &VIDIOC-S-FMT;) will be used. + +Only one video capture driver (bttv) supports this flag. The use of this flag +for capture devices is deprecated. There is no way to detect which drivers +support this flag, so the only reliable method of setting the overlay size is +through &VIDIOC-S-FMT;. + +If this flag is set for a video output device, then the video output overlay +window is relative to the top-left corner of the framebuffer and restricted +to the size of the framebuffer. If it is cleared, then the video output +overlay window is relative to the video output display. + V4L2_FBUF_FLAG_CHROMAKEY -- cgit v0.10.2 From 20c9fe895a2356924a31a1238f75cf0d05704593 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Nov 2011 08:48:47 -0300 Subject: [media] zoran: do not set V4L2_FBUF_FLAG_OVERLAY The zoran driver does not support this flag, so don't set it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index d4d05d2..f7d236a 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1958,7 +1958,6 @@ static int zoran_g_fbuf(struct file *file, void *__fh, mutex_unlock(&zr->resource_lock); fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; fb->fmt.field = V4L2_FIELD_INTERLACED; - fb->flags = V4L2_FBUF_FLAG_OVERLAY; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; return 0; -- cgit v0.10.2 From 047a01fdc807ffa461db87e2d30d6d28f95c091a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 22 Nov 2011 08:23:19 -0300 Subject: [media] omap_vout: add missing OVERLAY_OUTPUT cap and set V4L2_FBUF_FLAG_OVERLAY The omap_vout driver has an output overlay, but never advertised that capability. The driver should also set the V4L2_FBUF_FLAG_OVERLAY flag. Signed-off-by: Hans Verkuil CC: Archit Taneja CC: Vaibhav Hiremath Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c index 00d6d19..1e7c0d6 100644 --- a/drivers/media/video/omap/omap_vout.c +++ b/drivers/media/video/omap/omap_vout.c @@ -1046,7 +1046,8 @@ static int vidioc_querycap(struct file *file, void *fh, strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); cap->bus_info[0] = '\0'; - cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OUTPUT_OVERLAY; return 0; } @@ -1828,7 +1829,9 @@ static int vidioc_g_fbuf(struct file *file, void *fh, ovid = &vout->vid_info; ovl = ovid->overlays[0]; - a->flags = 0x0; + /* The video overlay must stay within the framebuffer and can't be + positioned independently. */ + a->flags = V4L2_FBUF_FLAG_OVERLAY; a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY | V4L2_FBUF_CAP_SRC_CHROMAKEY; -- cgit v0.10.2 From 6055b12ae0f433314cba98f423fb8c5fc211ca8d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 11 Jan 2012 07:30:55 -0300 Subject: [media] V4L2 Spec: fix extended control documentation Update the spec to the behavior implemented by the control framework. This should have been documented long ago but for some reason it was never done. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 6f1f9a6..b17a7aa 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -183,7 +183,12 @@ applications must set the array to zero. __u32 ctrl_class The control class to which all controls belong, see -. +. Drivers that use a kernel framework for handling +controls will also accept a value of 0 here, meaning that the controls can +belong to any control class. Whether drivers support this can be tested by setting +ctrl_class to 0 and calling VIDIOC_TRY_EXT_CTRLS +with a count of 0. If that succeeds, then the driver +supports this feature. __u32 @@ -194,10 +199,13 @@ also be zero. __u32 error_idx - Set by the driver in case of an error. It is the -index of the control causing the error or equal to 'count' when the -error is not associated with a particular control. Undefined when the -ioctl returns 0 (success). + Set by the driver in case of an error. If it is equal +to count, then no actual changes were made to +controls. In other words, the error was not associated with setting a particular +control. If it is another value, then only the controls up to error_idx-1 +were modified and control error_idx is the one that +caused the error. The error_idx value is undefined +if the ioctl returned 0 (success). __u32 diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 26aa057..e2492a9 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -666,27 +666,6 @@ a control of this type whenever the first control belonging to a new control class is added. -Differences from the Spec -========================= - -There are a few places where the framework acts slightly differently from the -V4L2 Specification. Those differences are described in this section. We will -have to see whether we need to adjust the spec or not. - -1) It is no longer required to have all controls contained in a -v4l2_ext_control array be from the same control class. The framework will be -able to handle any type of control in the array. You need to set ctrl_class -to 0 in order to enable this. If ctrl_class is non-zero, then it will still -check that all controls belong to that control class. - -If you set ctrl_class to 0 and count to 0, then it will only return an error -if there are no controls at all. - -2) Clarified the way error_idx works. For get and set it will be equal to -count if nothing was done yet. If it is less than count then only the controls -up to error_idx-1 were successfully applied. - - Proposals for Extensions ======================== -- cgit v0.10.2 From dacd4fa37dbafe209a90d6d5b729d2257df4b734 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 11 Jan 2012 07:37:54 -0300 Subject: [media] V4L2 Spec: improve the G/S_INPUT/OUTPUT documentation As Rupert pointed out, the phrase "It is good practice" should be replaced with "You must". Signed-off-by: Hans Verkuil Cc: Rupert Eibauer Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-input.xml b/Documentation/DocBook/media/v4l/vidioc-g-input.xml index 08ae82f..1d43065 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-input.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-input.xml @@ -61,8 +61,8 @@ desired input in an integer and call the VIDIOC_S_INPUT ioctl with a pointer to this integer. Side effects are possible. For example inputs may support different video standards, so the driver may implicitly switch the -current standard. It is good practice to select an input before -querying or negotiating any other parameters. +current standard. Because of these possible side effects applications +must select an input before querying or negotiating any other parameters. Information about video inputs is available using the &VIDIOC-ENUMINPUT; ioctl. diff --git a/Documentation/DocBook/media/v4l/vidioc-g-output.xml b/Documentation/DocBook/media/v4l/vidioc-g-output.xml index fd45f1c..4533068 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-output.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-output.xml @@ -61,8 +61,9 @@ desired output in an integer and call the VIDIOC_S_OUTPUT ioctl with a pointer to this integer. Side effects are possible. For example outputs may support different video standards, so the driver may implicitly switch the current -standard. It is good practice to select an output before querying or -negotiating any other parameters. +standard. +standard. Because of these possible side effects applications +must select an output before querying or negotiating any other parameters. Information about video outputs is available using the &VIDIOC-ENUMOUTPUT; ioctl. -- cgit v0.10.2 From aa07eec5320cf1793062c03b8409e7541ca37c31 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Oct 2011 05:12:52 -0300 Subject: [media] v4l2-ioctl: make tuner 'type' check more strict for S_FREQUENCY As per the feature removal document, make the tuner type check more strict so that it is no longer possible to set the radio frequency through a video node or the TV frequency through a radio node. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml b/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml index 1643181..66e9a52 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-frequency.xml @@ -98,8 +98,11 @@ the &v4l2-output; modulator field and the &v4l2-tuner-type; type The tuner type. This is the same value as in the -&v4l2-tuner; type field. The field is not -applicable to modulators, &ie; ignored by drivers. +&v4l2-tuner; type field. The type must be set +to V4L2_TUNER_RADIO for /dev/radioX +device nodes, and to V4L2_TUNER_ANALOG_TV +for all others. The field is not applicable to modulators, &ie; ignored +by drivers. __u32 diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index c4362ef..fed5a34 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -460,17 +460,6 @@ Who: Jean Delvare ---------------------------- -What: For VIDIOC_S_FREQUENCY the type field must match the device node's type. - If not, return -EINVAL. -When: 3.2 -Why: It makes no sense to switch the tuner to radio mode by calling - VIDIOC_S_FREQUENCY on a video node, or to switch the tuner to tv mode by - calling VIDIOC_S_FREQUENCY on a radio node. This is the first step of a - move to more consistent handling of tv and radio tuners. -Who: Hans Verkuil - ----------------------------- - What: Opening a radio device node will no longer automatically switch the tuner mode from tv to radio. When: 3.3 diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 77feeb6..3f62385 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -1871,6 +1871,7 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_S_FREQUENCY: { struct v4l2_frequency *p = arg; + enum v4l2_tuner_type type; if (!ops->vidioc_s_frequency) break; @@ -1878,9 +1879,14 @@ static long __video_do_ioctl(struct file *file, ret = ret_prio; break; } + type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", p->tuner, p->type, p->frequency); - ret = ops->vidioc_s_frequency(file, fh, p); + if (p->type != type) + ret = -EINVAL; + else + ret = ops->vidioc_s_frequency(file, fh, p); break; } case VIDIOC_G_SLICED_VBI_CAP: -- cgit v0.10.2 From 3f3edd7c97d0a5ae355aace36000576acbc2f2cc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Oct 2011 05:18:08 -0300 Subject: [media] ivtv: remove exclusive radio open For some reason the /dev/radio device was implemented as an exclusive open: you could open it only once and not a second time. Remove this limitation. Signed-off-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 38f0522..69a3de9 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -861,20 +861,10 @@ int ivtv_v4l2_close(struct file *filp) IVTV_DEBUG_FILE("close %s\n", s->name); - v4l2_fh_del(fh); - v4l2_fh_exit(fh); - - /* Easy case first: this stream was never claimed by us */ - if (s->id != id->open_id) { - kfree(id); - return 0; - } - - /* 'Unclaim' this stream */ - - /* Stop radio */ mutex_lock(&itv->serialize_lock); - if (id->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Stop radio */ + if (id->type == IVTV_ENC_STREAM_TYPE_RAD && + v4l2_fh_is_singular_file(filp)) { /* Closing radio device, return to TV mode */ ivtv_mute(itv); /* Mark that the radio is no longer in use */ @@ -890,13 +880,26 @@ int ivtv_v4l2_close(struct file *filp) if (atomic_read(&itv->capturing) > 0) { /* Undo video mute */ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, - v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) | - (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); + v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) | + (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); } /* Done! Unmute and continue. */ ivtv_unmute(itv); - ivtv_release_stream(s); - } else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { + } + + v4l2_fh_del(fh); + v4l2_fh_exit(fh); + + /* Easy case first: this stream was never claimed by us */ + if (s->id != id->open_id) { + kfree(id); + mutex_unlock(&itv->serialize_lock); + return 0; + } + + /* 'Unclaim' this stream */ + + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT]; ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); @@ -966,31 +969,20 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) return -ENOMEM; } v4l2_fh_init(&item->fh, s->vdev); - if (res < 0) { - v4l2_fh_exit(&item->fh); - kfree(item); - return res; - } item->itv = itv; item->type = s->type; item->open_id = itv->open_id++; filp->private_data = &item->fh; + v4l2_fh_add(&item->fh); - if (item->type == IVTV_ENC_STREAM_TYPE_RAD) { - /* Try to claim this stream */ - if (ivtv_claim_stream(item, item->type)) { - /* No, it's already in use */ - v4l2_fh_exit(&item->fh); - kfree(item); - return -EBUSY; - } - + if (item->type == IVTV_ENC_STREAM_TYPE_RAD && + v4l2_fh_is_singular_file(filp)) { if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { if (atomic_read(&itv->capturing) > 0) { /* switching to radio while capture is in progress is not polite */ - ivtv_release_stream(s); + v4l2_fh_del(&item->fh); v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; @@ -1022,7 +1014,6 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); itv->yuv_info.stream_size = 0; } - v4l2_fh_add(&item->fh); return 0; } -- cgit v0.10.2 From 4d68e700d6a192a5a8b394b26ac056a1c0fa6ebc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Oct 2011 05:23:36 -0300 Subject: [media] cx18: remove exclusive open of radio device For some reason the cx18 driver could open the radio device only once. Remove this limitation. Signed-off-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 14cb961..4bfd865 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -751,20 +751,10 @@ int cx18_v4l2_close(struct file *filp) CX18_DEBUG_IOCTL("close() of %s\n", s->name); - v4l2_fh_del(fh); - v4l2_fh_exit(fh); - - /* Easy case first: this stream was never claimed by us */ - if (s->id != id->open_id) { - kfree(id); - return 0; - } - - /* 'Unclaim' this stream */ - - /* Stop radio */ mutex_lock(&cx->serialize_lock); - if (id->type == CX18_ENC_STREAM_TYPE_RAD) { + /* Stop radio */ + if (id->type == CX18_ENC_STREAM_TYPE_RAD && + v4l2_fh_is_singular_file(filp)) { /* Closing radio device, return to TV mode */ cx18_mute(cx); /* Mark that the radio is no longer in use */ @@ -781,10 +771,14 @@ int cx18_v4l2_close(struct file *filp) } /* Done! Unmute and continue. */ cx18_unmute(cx); - cx18_release_stream(s); - } else { - cx18_stop_capture(id, 0); } + + v4l2_fh_del(fh); + v4l2_fh_exit(fh); + + /* 'Unclaim' this stream */ + if (s->id == id->open_id) + cx18_stop_capture(id, 0); kfree(id); mutex_unlock(&cx->serialize_lock); return 0; @@ -810,21 +804,15 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) item->open_id = cx->open_id++; filp->private_data = &item->fh; + v4l2_fh_add(&item->fh); - if (item->type == CX18_ENC_STREAM_TYPE_RAD) { - /* Try to claim this stream */ - if (cx18_claim_stream(item, item->type)) { - /* No, it's already in use */ - v4l2_fh_exit(&item->fh); - kfree(item); - return -EBUSY; - } - + if (item->type == CX18_ENC_STREAM_TYPE_RAD && + v4l2_fh_is_singular_file(filp)) { if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { if (atomic_read(&cx->ana_capturing) > 0) { /* switching to radio while capture is in progress is not polite */ - cx18_release_stream(s); + v4l2_fh_del(&item->fh); v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; @@ -842,7 +830,6 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) /* Done! Unmute and continue. */ cx18_unmute(cx); } - v4l2_fh_add(&item->fh); return 0; } -- cgit v0.10.2 From cdc037817cc15caf931cd3476970860d62f1985c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Oct 2011 06:06:58 -0300 Subject: [media] ivtv: switch to the v4l core lock Signed-off-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 69a3de9..116ece4 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -268,11 +268,13 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, } /* wait for more data to arrive */ + mutex_unlock(&itv->serialize_lock); prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ if (!s->q_full.buffers) schedule(); finish_wait(&s->waitq, &wait); + mutex_lock(&itv->serialize_lock); if (signal_pending(current)) { /* return if a signal was received */ IVTV_DEBUG_INFO("User stopped %s\n", s->name); @@ -507,9 +509,7 @@ ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_ IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name); - mutex_lock(&itv->serialize_lock); rc = ivtv_start_capture(id); - mutex_unlock(&itv->serialize_lock); if (rc) return rc; return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); @@ -584,9 +584,7 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c set_bit(IVTV_F_S_APPL_IO, &s->s_flags); /* Start decoder (returns 0 if already started) */ - mutex_lock(&itv->serialize_lock); rc = ivtv_start_decoding(id, itv->speed); - mutex_unlock(&itv->serialize_lock); if (rc) { IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); @@ -627,11 +625,13 @@ retry: break; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; + mutex_unlock(&itv->serialize_lock); prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become free before we were added to the waitqueue */ if (!s->q_free.buffers) schedule(); finish_wait(&s->waitq, &wait); + mutex_lock(&itv->serialize_lock); if (signal_pending(current)) { IVTV_DEBUG_INFO("User stopped %s\n", s->name); return -EINTR; @@ -686,12 +686,14 @@ retry: if (mode == OUT_YUV) ivtv_yuv_setup_stream_frame(itv); + mutex_unlock(&itv->serialize_lock); prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); while (!(got_sig = signal_pending(current)) && test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { schedule(); } finish_wait(&itv->dma_waitq, &wait); + mutex_lock(&itv->serialize_lock); if (got_sig) { IVTV_DEBUG_INFO("User interrupted %s\n", s->name); return -EINTR; @@ -756,9 +758,7 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { int rc; - mutex_lock(&itv->serialize_lock); rc = ivtv_start_capture(id); - mutex_unlock(&itv->serialize_lock); if (rc) { IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n", s->name, rc); @@ -861,7 +861,6 @@ int ivtv_v4l2_close(struct file *filp) IVTV_DEBUG_FILE("close %s\n", s->name); - mutex_lock(&itv->serialize_lock); /* Stop radio */ if (id->type == IVTV_ENC_STREAM_TYPE_RAD && v4l2_fh_is_singular_file(filp)) { @@ -893,7 +892,6 @@ int ivtv_v4l2_close(struct file *filp) /* Easy case first: this stream was never claimed by us */ if (s->id != id->open_id) { kfree(id); - mutex_unlock(&itv->serialize_lock); return 0; } @@ -914,21 +912,25 @@ int ivtv_v4l2_close(struct file *filp) ivtv_stop_capture(id, 0); } kfree(id); - mutex_unlock(&itv->serialize_lock); return 0; } -static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) +int ivtv_v4l2_open(struct file *filp) { -#ifdef CONFIG_VIDEO_ADV_DEBUG struct video_device *vdev = video_devdata(filp); -#endif + struct ivtv_stream *s = video_get_drvdata(vdev); struct ivtv *itv = s->itv; struct ivtv_open_id *item; int res = 0; IVTV_DEBUG_FILE("open %s\n", s->name); + if (ivtv_init_on_first_open(itv)) { + IVTV_ERR("Failed to initialize on device %s\n", + video_device_node_name(vdev)); + return -ENXIO; + } + #ifdef CONFIG_VIDEO_ADV_DEBUG /* Unless ivtv_fw_debug is set, error out if firmware dead. */ if (ivtv_fw_debug) { @@ -1017,28 +1019,6 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) return 0; } -int ivtv_v4l2_open(struct file *filp) -{ - int res; - struct ivtv *itv = NULL; - struct ivtv_stream *s = NULL; - struct video_device *vdev = video_devdata(filp); - - s = video_get_drvdata(vdev); - itv = s->itv; - - mutex_lock(&itv->serialize_lock); - if (ivtv_init_on_first_open(itv)) { - IVTV_ERR("Failed to initialize on device %s\n", - video_device_node_name(vdev)); - mutex_unlock(&itv->serialize_lock); - return -ENXIO; - } - res = ivtv_serialized_open(s, filp); - mutex_unlock(&itv->serialize_lock); - return res; -} - void ivtv_mute(struct ivtv *itv) { if (atomic_read(&itv->capturing)) diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index ecafa69..c4bc481 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -179,6 +179,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed) ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0); /* Wait for any DMA to finish */ + mutex_unlock(&itv->serialize_lock); prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) { got_sig = signal_pending(current); @@ -188,6 +189,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed) schedule(); } finish_wait(&itv->dma_waitq, &wait); + mutex_lock(&itv->serialize_lock); if (got_sig) return -EINTR; @@ -1107,6 +1109,7 @@ void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std) * happens within the first 100 lines of the top field. * Make 4 attempts to sync to the decoder before giving up. */ + mutex_unlock(&itv->serialize_lock); for (f = 0; f < 4; f++) { prepare_to_wait(&itv->vsync_waitq, &wait, TASK_UNINTERRUPTIBLE); @@ -1115,6 +1118,7 @@ void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std) schedule_timeout(msecs_to_jiffies(25)); } finish_wait(&itv->vsync_waitq, &wait); + mutex_lock(&itv->serialize_lock); if (f == 4) IVTV_WARN("Mode change failed to sync to decoder\n"); @@ -1842,8 +1846,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, return 0; } -static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, - unsigned int cmd, unsigned long arg) +long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); long ret; @@ -1855,21 +1858,6 @@ static long ivtv_serialized_ioctl(struct ivtv *itv, struct file *filp, return ret; } -long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct ivtv_open_id *id = fh2id(filp->private_data); - struct ivtv *itv = id->itv; - long res; - - /* DQEVENT can block, so this should not run with the serialize lock */ - if (cmd == VIDIOC_DQEVENT) - return ivtv_serialized_ioctl(itv, filp, cmd, arg); - mutex_lock(&itv->serialize_lock); - res = ivtv_serialized_ioctl(itv, filp, cmd, arg); - mutex_unlock(&itv->serialize_lock); - return res; -} - static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { .vidioc_querycap = ivtv_querycap, .vidioc_s_audio = ivtv_s_audio, diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index e7794dc..d598df0 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -214,6 +214,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->fops = ivtv_stream_info[type].fops; s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; + s->vdev->lock = &itv->serialize_lock; set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ivtv_set_funcs(s->vdev); return 0; diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c index dcbab6a..2ad65eb 100644 --- a/drivers/media/video/ivtv/ivtv-yuv.c +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -1149,23 +1149,37 @@ int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src) { struct yuv_playback_info *yi = &itv->yuv_info; struct ivtv_dma_frame dma_args; + int res; ivtv_yuv_setup_stream_frame(itv); /* We only need to supply source addresses for this */ dma_args.y_source = src; dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31); - return ivtv_yuv_udma_frame(itv, &dma_args); + /* Wait for frame DMA. Note that serialize_lock is locked, + so to allow other processes to access the driver while + we are waiting unlock first and later lock again. */ + mutex_unlock(&itv->serialize_lock); + res = ivtv_yuv_udma_frame(itv, &dma_args); + mutex_lock(&itv->serialize_lock); + return res; } /* IVTV_IOC_DMA_FRAME ioctl handler */ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) { -/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */ + int res; +/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */ ivtv_yuv_next_free(itv); ivtv_yuv_setup_frame(itv, args); - return ivtv_yuv_udma_frame(itv, args); + /* Wait for frame DMA. Note that serialize_lock is locked, + so to allow other processes to access the driver while + we are waiting unlock first and later lock again. */ + mutex_unlock(&itv->serialize_lock); + res = ivtv_yuv_udma_frame(itv, args); + mutex_lock(&itv->serialize_lock); + return res; } void ivtv_yuv_close(struct ivtv *itv) @@ -1174,7 +1188,9 @@ void ivtv_yuv_close(struct ivtv *itv) int h_filter, v_filter_1, v_filter_2; IVTV_DEBUG_YUV("ivtv_yuv_close\n"); + mutex_unlock(&itv->serialize_lock); ivtv_waitq(&itv->vsync_waitq); + mutex_lock(&itv->serialize_lock); yi->running = 0; atomic_set(&yi->next_dma_frame, -1); -- cgit v0.10.2 From 61bb725ef5a646d3fc8a64e41b020a65542cdae1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 12 Oct 2011 05:36:04 -0300 Subject: [media] ivtv: remove open_id/id from the filehandle code Instead of messing around with id's it's much easier to just compare against a filehandle pointer. Signed-off-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 41108a9a..461ae44 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -731,9 +731,6 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) init_kthread_work(&itv->irq_work, ivtv_irq_work_handler); - /* start counting open_id at 1 */ - itv->open_id = 1; - /* Initial settings */ itv->cxhdl.port = CX2341X_PORT_MEMORY; itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI; diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 8f9cc17..06f3d78 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -332,7 +332,7 @@ struct ivtv_stream { const char *name; /* name of the stream */ int type; /* stream type */ - u32 id; + struct v4l2_fh *fh; /* pointer to the streaming filehandle */ spinlock_t qlock; /* locks access to the queues */ unsigned long s_flags; /* status flags, see above */ int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */ @@ -379,7 +379,6 @@ struct ivtv_stream { struct ivtv_open_id { struct v4l2_fh fh; - u32 open_id; /* unique ID for this file descriptor */ int type; /* stream type */ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */ struct ivtv *itv; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 116ece4..2cd6c89 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -50,16 +50,16 @@ static int ivtv_claim_stream(struct ivtv_open_id *id, int type) if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { /* someone already claimed this stream */ - if (s->id == id->open_id) { + if (s->fh == &id->fh) { /* yes, this file descriptor did. So that's OK. */ return 0; } - if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI || + if (s->fh == NULL && (type == IVTV_DEC_STREAM_TYPE_VBI || type == IVTV_ENC_STREAM_TYPE_VBI)) { /* VBI is handled already internally, now also assign the file descriptor to this stream for external reading of the stream. */ - s->id = id->open_id; + s->fh = &id->fh; IVTV_DEBUG_INFO("Start Read VBI\n"); return 0; } @@ -67,7 +67,7 @@ static int ivtv_claim_stream(struct ivtv_open_id *id, int type) IVTV_DEBUG_INFO("Stream %d is busy\n", type); return -EBUSY; } - s->id = id->open_id; + s->fh = &id->fh; if (type == IVTV_DEC_STREAM_TYPE_VBI) { /* Enable reinsertion interrupt */ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); @@ -104,7 +104,7 @@ void ivtv_release_stream(struct ivtv_stream *s) struct ivtv *itv = s->itv; struct ivtv_stream *s_vbi; - s->id = -1; + s->fh = NULL; if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) && test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { /* this stream is still in use internally */ @@ -136,7 +136,7 @@ void ivtv_release_stream(struct ivtv_stream *s) /* was already cleared */ return; } - if (s_vbi->id != -1) { + if (s_vbi->fh) { /* VBI stream still claimed by a file descriptor */ return; } @@ -359,7 +359,7 @@ static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_co size_t tot_written = 0; int single_frame = 0; - if (atomic_read(&itv->capturing) == 0 && s->id == -1) { + if (atomic_read(&itv->capturing) == 0 && s->fh == NULL) { /* shouldn't happen */ IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name); return -EIO; @@ -808,7 +808,7 @@ void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end) id->type == IVTV_ENC_STREAM_TYPE_VBI) && test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { /* Also used internally, don't stop capturing */ - s->id = -1; + s->fh = NULL; } else { ivtv_stop_v4l2_encode_stream(s, gop_end); @@ -890,7 +890,7 @@ int ivtv_v4l2_close(struct file *filp) v4l2_fh_exit(fh); /* Easy case first: this stream was never claimed by us */ - if (s->id != id->open_id) { + if (s->fh != &id->fh) { kfree(id); return 0; } @@ -974,7 +974,6 @@ int ivtv_v4l2_open(struct file *filp) item->itv = itv; item->type = s->type; - item->open_id = itv->open_id++; filp->private_data = &item->fh; v4l2_fh_add(&item->fh); diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index 9c29e96..1b3b957 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -288,13 +288,13 @@ static void dma_post(struct ivtv_stream *s) ivtv_process_vbi_data(itv, buf, 0, s->type); s->q_dma.bytesused += buf->bytesused; } - if (s->id == -1) { + if (s->fh == NULL) { ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); return; } } ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused); - if (s->id != -1) + if (s->fh) wake_up(&s->waitq); } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index d598df0..c6e28b4 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -159,7 +159,6 @@ static void ivtv_stream_init(struct ivtv *itv, int type) s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size; spin_lock_init(&s->qlock); init_waitqueue_head(&s->waitq); - s->id = -1; s->sg_handle = IVTV_DMA_UNMAPPED; ivtv_queue_init(&s->q_free); ivtv_queue_init(&s->q_full); -- cgit v0.10.2 From 412662d204eca981458156fd64d9d5f3b533d7b6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 11 Jan 2012 11:52:17 +1100 Subject: netfilter: xt_hashlimit: fix unused variable warning if IPv6 disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes this warning when CONFIG_IP6_NF_IPTABLES is not enabled: net/netfilter/xt_hashlimit.c: In function ‘hashlimit_init_dst’: net/netfilter/xt_hashlimit.c:448:9: warning: unused variable ‘frag_off’ [-Wunused-variable] Signed-off-by: Stephen Rothwell Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 8e49921..d95f9c9 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -445,7 +445,6 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, { __be16 _ports[2], *ports; u8 nexthdr; - __be16 frag_off; int poff; memset(dst, 0, sizeof(*dst)); @@ -466,6 +465,9 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, break; #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) case NFPROTO_IPV6: + { + __be16 frag_off; + if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP) { memcpy(&dst->ip6.dst, &ipv6_hdr(skb)->daddr, sizeof(dst->ip6.dst)); @@ -485,6 +487,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, if ((int)protoff < 0) return -1; break; + } #endif default: BUG(); -- cgit v0.10.2 From 9bf04646b0b41c5438ed8a27c5f8dbe0ff40d756 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 15 Jan 2012 16:57:12 +0100 Subject: netfilter: revert user-space expectation helper support This patch partially reverts: 3d058d7 netfilter: rework user-space expectation helper support that was applied during the 3.2 development cycle. After this patch, the tree remains just like before patch bc01bef, that initially added the preliminary infrastructure. I decided to partially revert this patch because the approach that I proposed to resolve this problem is broken in NAT setups. Moreover, a new infrastructure will be submitted for the 3.3.x development cycle that resolve the existing issues while providing a neat solution. Since nobody has been seriously using this infrastructure in user-space, the removal of this feature should affect any know FOSS project (to my knowledge). Signed-off-by: Pablo Neira Ayuso diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index 9e3a283..0d3dd66 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -83,10 +83,6 @@ enum ip_conntrack_status { /* Conntrack is a fake untracked entry */ IPS_UNTRACKED_BIT = 12, IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), - - /* Conntrack has a userspace helper. */ - IPS_USERSPACE_HELPER_BIT = 13, - IPS_USERSPACE_HELPER = (1 << IPS_USERSPACE_HELPER_BIT), }; /* Connection tracking event types */ diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h index 6390f09..b56e768 100644 --- a/include/linux/netfilter/xt_CT.h +++ b/include/linux/netfilter/xt_CT.h @@ -3,8 +3,7 @@ #include -#define XT_CT_NOTRACK 0x1 -#define XT_CT_USERSPACE_HELPER 0x2 +#define XT_CT_NOTRACK 0x1 struct xt_ct_target_info { __u16 flags; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 299fec9..bbe23ba 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -121,18 +121,6 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, int ret = 0; if (tmpl != NULL) { - /* we've got a userspace helper. */ - if (tmpl->status & IPS_USERSPACE_HELPER) { - help = nf_ct_helper_ext_add(ct, flags); - if (help == NULL) { - ret = -ENOMEM; - goto out; - } - rcu_assign_pointer(help->helper, NULL); - __set_bit(IPS_USERSPACE_HELPER_BIT, &ct->status); - ret = 0; - goto out; - } help = nfct_help(tmpl); if (help != NULL) helper = help->helper; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 2a4834b..9307b03 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2042,10 +2042,6 @@ ctnetlink_create_expect(struct net *net, u16 zone, } help = nfct_help(ct); if (!help) { - err = -EOPNOTSUPP; - goto out; - } - if (test_bit(IPS_USERSPACE_HELPER_BIT, &ct->status)) { if (!cda[CTA_EXPECT_TIMEOUT]) { err = -EINVAL; goto out; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 8e87123..0221d10 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -62,8 +62,8 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par) int ret = 0; u8 proto; - if (info->flags & ~(XT_CT_NOTRACK | XT_CT_USERSPACE_HELPER)) - return -EOPNOTSUPP; + if (info->flags & ~XT_CT_NOTRACK) + return -EINVAL; if (info->flags & XT_CT_NOTRACK) { ct = nf_ct_untracked_get(); @@ -92,9 +92,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par) GFP_KERNEL)) goto err3; - if (info->flags & XT_CT_USERSPACE_HELPER) { - __set_bit(IPS_USERSPACE_HELPER_BIT, &ct->status); - } else if (info->helper[0]) { + if (info->helper[0]) { ret = -ENOENT; proto = xt_ct_find_proto(par); if (!proto) { -- cgit v0.10.2 From 54d3fb3b11a7c38b112585e54b7af7cb3faa5c91 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Jan 2012 07:19:29 -0300 Subject: [media] pwc: Make fps runtime configurable through s_parm, drop fps module param Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 905d41d..c6dea6b 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -194,8 +194,12 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames, 7 /* 30 */ }; - if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) + if (size < 0 || size > PSZ_CIF) return -EINVAL; + if (frames < 4) + frames = 4; + else if (frames > 25) + frames = 25; frames = frames2frames[frames]; fps = frames2table[frames]; pEntry = &Nala_table[size][fps]; @@ -250,11 +254,14 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, const struct Timon_table_entry *pChoose; int ret, fps; - if (size >= PSZ_MAX || frames < 5 || frames > 30 || - *compression < 0 || *compression > 3) - return -EINVAL; - if (size == PSZ_VGA && frames > 15) + if (size >= PSZ_MAX || *compression < 0 || *compression > 3) return -EINVAL; + if (frames < 5) + frames = 5; + else if (size == PSZ_VGA && frames > 15) + frames = 15; + else if (frames > 30) + frames = 30; fps = (frames / 5) - 1; /* Find a supported framerate with progressively higher compression */ @@ -283,7 +290,7 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, memcpy(pdev->cmd_buf, buf, 13); /* Set various parameters */ - pdev->vframes = frames; + pdev->vframes = (fps + 1) * 5; pdev->valternate = pChoose->alternate; pdev->width = pwc_image_sizes[size][0]; pdev->height = pwc_image_sizes[size][1]; @@ -303,11 +310,14 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int fps, ret; unsigned char buf[12]; - if (size >= PSZ_MAX || frames < 5 || frames > 30 || - *compression < 0 || *compression > 3) - return -EINVAL; - if (size == PSZ_VGA && frames > 15) + if (size >= PSZ_MAX || *compression < 0 || *compression > 3) return -EINVAL; + if (frames < 5) + frames = 5; + else if (size == PSZ_VGA && frames > 15) + frames = 15; + else if (frames > 30) + frames = 30; fps = (frames / 5) - 1; /* Find a supported framerate with progressively higher compression */ @@ -339,7 +349,7 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, pdev->cmd_len = 12; memcpy(pdev->cmd_buf, buf, 12); /* All set and go */ - pdev->vframes = frames; + pdev->vframes = (fps + 1) * 5; pdev->valternate = pChoose->alternate; pdev->width = pwc_image_sizes[size][0]; pdev->height = pwc_image_sizes[size][1]; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 943d37a..5592068 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -128,7 +128,6 @@ static struct usb_driver pwc_driver = { #define MAX_DEV_HINTS 20 #define MAX_ISOC_ERRORS 20 -static int default_fps = 10; #ifdef CONFIG_USB_PWC_DEBUG int pwc_trace = PWC_DEBUG_LEVEL; #endif @@ -1076,7 +1075,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id return -ENOMEM; } pdev->type = type_id; - pdev->vframes = default_fps; pdev->features = features; pwc_construct(pdev); /* set min/max sizes correct */ @@ -1138,8 +1136,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pwc_set_leds(pdev, 0, 0); /* Setup intial videomode */ - rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, pdev->vframes, - &compression); + rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, 30, &compression); if (rc) goto err_free_mem; @@ -1243,13 +1240,11 @@ static void usb_pwc_disconnect(struct usb_interface *intf) * Initialization code & module stuff */ -static int fps; static int leds[2] = { -1, -1 }; static unsigned int leds_nargs; static char *dev_hint[MAX_DEV_HINTS]; static unsigned int dev_hint_nargs; -module_param(fps, int, 0444); #ifdef CONFIG_USB_PWC_DEBUG module_param_named(trace, pwc_trace, int, 0644); #endif @@ -1257,7 +1252,6 @@ module_param(power_save, int, 0644); module_param_array(leds, int, &leds_nargs, 0444); module_param_array(dev_hint, charp, &dev_hint_nargs, 0444); -MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); #ifdef CONFIG_USB_PWC_DEBUG MODULE_PARM_DESC(trace, "For debugging purposes"); #endif @@ -1286,15 +1280,6 @@ static int __init usb_pwc_init(void) } #endif - if (fps) { - if (fps < 4 || fps > 30) { - PWC_ERROR("Framerate out of bounds (4-30).\n"); - return -EINVAL; - } - default_fps = fps; - PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps); - } - if (leds[0] >= 0) led_on = leds[0]; if (leds[1] >= 0) diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 80e2584..ef422d0 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -493,7 +493,7 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) (pixelformat>>24)&255); ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, - pdev->vframes, &compression); + 30, &compression); PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); @@ -1094,6 +1094,63 @@ static int pwc_enum_frameintervals(struct file *file, void *fh, return 0; } +static int pwc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct pwc_device *pdev = video_drvdata(file); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(parm, 0, sizeof(*parm)); + + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.readbuffers = MIN_FRAMES; + parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.denominator = pdev->vframes; + parm->parm.capture.timeperframe.numerator = 1; + + return 0; +} + +static int pwc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct pwc_device *pdev = video_drvdata(file); + int compression = 0; + int ret, fps; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + parm->parm.capture.timeperframe.numerator == 0) + return -EINVAL; + + if (pwc_test_n_set_capt_file(pdev, file)) + return -EBUSY; + + fps = parm->parm.capture.timeperframe.denominator / + parm->parm.capture.timeperframe.numerator; + + mutex_lock(&pdev->udevlock); + if (!pdev->udev) { + ret = -ENODEV; + goto leave; + } + + if (pdev->iso_init) { + ret = -EBUSY; + goto leave; + } + + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, fps, + &compression); + + pwc_g_parm(file, fh, parm); + +leave: + mutex_unlock(&pdev->udevlock); + return ret; +} + static int pwc_log_status(struct file *file, void *priv) { struct pwc_device *pdev = video_drvdata(file); @@ -1120,4 +1177,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_log_status = pwc_log_status, .vidioc_enum_framesizes = pwc_enum_framesizes, .vidioc_enum_frameintervals = pwc_enum_frameintervals, + .vidioc_g_parm = pwc_g_parm, + .vidioc_s_parm = pwc_s_parm, }; -- cgit v0.10.2 From 56ae24aad8f9e25dfef995c3e898e5f394cf0beb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 8 Jan 2012 11:29:19 -0300 Subject: [media] pwc: Make decoder data part of the main pwc struct Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index c6dea6b..34a01b2 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -213,9 +213,7 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames, return ret; } if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf); - if (ret < 0) - return ret; + pwc_dec1_init(pdev, buf); } pdev->cmd_len = 3; @@ -281,9 +279,7 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, return ret; if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - ret = pwc_dec23_init(pdev, pdev->type, buf); - if (ret < 0) - return ret; + pwc_dec23_init(pdev, buf); } pdev->cmd_len = 13; @@ -341,9 +337,7 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, return ret; if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { - ret = pwc_dec23_init(pdev, pdev->type, buf); - if (ret < 0) - return ret; + pwc_dec23_init(pdev, buf); } pdev->cmd_len = 12; @@ -368,7 +362,8 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, { int ret, size; - PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pdev->pixfmt); + PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", + width, height, frames, pdev->pixfmt); size = pwc_get_size(pdev, width, height); PWC_TRACE("decode_size = %d.\n", size); diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c index be0e02c..bac0d83 100644 --- a/drivers/media/video/pwc/pwc-dec1.c +++ b/drivers/media/video/pwc/pwc-dec1.c @@ -22,19 +22,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "pwc-dec1.h" +#include "pwc.h" -int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer) +void pwc_dec1_init(struct pwc_device *pdev, void *buffer) { - struct pwc_dec1_private *pdec; + struct pwc_dec1_private *pdec = &pdev->dec1; - if (pwc->decompress_data == NULL) { - pdec = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL); - if (pdec == NULL) - return -ENOMEM; - pwc->decompress_data = pdec; - } - pdec = pwc->decompress_data; - - return 0; + pdec->version = pdev->release; } diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h index a57d860..6e8f3c5 100644 --- a/drivers/media/video/pwc/pwc-dec1.h +++ b/drivers/media/video/pwc/pwc-dec1.h @@ -25,13 +25,15 @@ #ifndef PWC_DEC1_H #define PWC_DEC1_H -#include "pwc.h" +#include + +struct pwc_device; struct pwc_dec1_private { int version; }; -int pwc_dec1_init(struct pwc_device *pwc, int type, int release, void *buffer); +void pwc_dec1_init(struct pwc_device *pdev, void *buffer); #endif diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c index 2c67091..6d48c1f 100644 --- a/drivers/media/video/pwc/pwc-dec23.c +++ b/drivers/media/video/pwc/pwc-dec23.c @@ -294,22 +294,14 @@ static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; /* If the type or the command change, we rebuild the lookup table */ -int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) +void pwc_dec23_init(struct pwc_device *pdev, unsigned char *cmd) { int flags, version, shift, i; - struct pwc_dec23_private *pdec; - - if (pwc->decompress_data == NULL) { - pdec = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); - if (pdec == NULL) - return -ENOMEM; - pwc->decompress_data = pdec; - } - pdec = pwc->decompress_data; + struct pwc_dec23_private *pdec = &pdev->dec23; mutex_init(&pdec->lock); - if (DEVICE_USE_CODEC3(type)) { + if (DEVICE_USE_CODEC3(pdev->type)) { flags = cmd[2] & 0x18; if (flags == 8) pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ @@ -355,8 +347,6 @@ int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) for (i=0; idecompress_data; + struct pwc_dec23_private *pdec = &pdev->dec23; /* YUV420P image format */ unsigned char *pout_planar_y; @@ -674,23 +664,22 @@ void pwc_dec23_decompress(const struct pwc_device *pwc, mutex_lock(&pdec->lock); - bandlines_left = pwc->height / 4; - bytes_per_block = pwc->width * 4; - plane_size = pwc->height * pwc->width; + bandlines_left = pdev->height / 4; + bytes_per_block = pdev->width * 4; + plane_size = pdev->height * pdev->width; pout_planar_y = dst; pout_planar_u = dst + plane_size; pout_planar_v = dst + plane_size + plane_size / 4; while (bandlines_left--) { - DecompressBand23(pwc->decompress_data, - src, + DecompressBand23(pdec, src, pout_planar_y, pout_planar_u, pout_planar_v, - pwc->width, pwc->width); - src += pwc->vbandlength; + pdev->width, pdev->width); + src += pdev->vbandlength; pout_planar_y += bytes_per_block; - pout_planar_u += pwc->width; - pout_planar_v += pwc->width; + pout_planar_u += pdev->width; + pout_planar_v += pdev->width; } mutex_unlock(&pdec->lock); } diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h index d64a3c2..a29068e 100644 --- a/drivers/media/video/pwc/pwc-dec23.h +++ b/drivers/media/video/pwc/pwc-dec23.h @@ -25,7 +25,7 @@ #ifndef PWC_DEC23_H #define PWC_DEC23_H -#include "pwc.h" +struct pwc_device; struct pwc_dec23_private { @@ -51,8 +51,8 @@ struct pwc_dec23_private }; -int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd); -void pwc_dec23_decompress(const struct pwc_device *pwc, +void pwc_dec23_init(struct pwc_device *pdev, unsigned char *cmd); +void pwc_dec23_decompress(struct pwc_device *pdev, const void *src, void *dst); #endif diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 5592068..250ad4c 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -603,14 +603,6 @@ static void pwc_video_release(struct v4l2_device *v) if (device_hint[hint].pdev == pdev) device_hint[hint].pdev = NULL; - /* Free intermediate decompression buffer & tables */ - if (pdev->decompress_data != NULL) { - PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", - pdev->decompress_data); - kfree(pdev->decompress_data); - pdev->decompress_data = NULL; - } - v4l2_ctrl_handler_free(&pdev->ctrl_handler); kfree(pdev); diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 47c518f..0f3bc1b 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -44,6 +44,8 @@ #ifdef CONFIG_USB_PWC_INPUT_EVDEV #include #endif +#include "pwc-dec1.h" +#include "pwc-dec23.h" /* Version block */ #define PWC_VERSION "10.0.15" @@ -272,7 +274,10 @@ struct pwc_device int frame_total_size; /* including header & trailer */ int drop_frames; - void *decompress_data; /* private data for decompression engine */ + union { /* private data for decompression engine */ + struct pwc_dec1_private dec1; + struct pwc_dec23_private dec23; + }; /* * We have an 'image' and a 'view', where 'image' is the fixed-size img -- cgit v0.10.2 From d167a85c5fb45b1ecdacdb9b7733833a9af78da8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Jan 2012 13:01:41 -0300 Subject: [media] pwc: Fix pixfmt handling Before this patch various code in the mode setting patch checked pdev->pixfmt, but that was not set until the mode setting succeeded, so it was looking at the old pixfmt! This patch fixes this by making the pixfmt a parameter to set_video_mode, and setting it from set_video_mode on success. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 34a01b2..51ab4c5 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -168,8 +168,8 @@ int send_control_msg(struct pwc_device *pdev, request, value, pdev->vcinterface, buf, buflen); } -static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames, - int *compression) +static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression) { unsigned char buf[3]; int ret, fps; @@ -212,14 +212,14 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames, PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); return ret; } - if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { + if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) pwc_dec1_init(pdev, buf); - } pdev->cmd_len = 3; memcpy(pdev->cmd_buf, buf, 3); /* Set various parameters */ + pdev->pixfmt = pixfmt; pdev->vframes = frames; pdev->valternate = pEntry->alternate; pdev->width = pwc_image_sizes[size][0]; @@ -245,8 +245,8 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames, } -static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, - int *compression) +static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression) { unsigned char buf[13]; const struct Timon_table_entry *pChoose; @@ -278,14 +278,14 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, if (ret < 0) return ret; - if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { + if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) pwc_dec23_init(pdev, buf); - } pdev->cmd_len = 13; memcpy(pdev->cmd_buf, buf, 13); /* Set various parameters */ + pdev->pixfmt = pixfmt; pdev->vframes = (fps + 1) * 5; pdev->valternate = pChoose->alternate; pdev->width = pwc_image_sizes[size][0]; @@ -299,8 +299,8 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, } -static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, - int *compression) +static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, + int frames, int *compression) { const struct Kiara_table_entry *pChoose = NULL; int fps, ret; @@ -336,13 +336,13 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, if (ret < 0) return ret; - if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { + if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) pwc_dec23_init(pdev, buf); - } pdev->cmd_len = 12; memcpy(pdev->cmd_buf, buf, 12); /* All set and go */ + pdev->pixfmt = pixfmt; pdev->vframes = (fps + 1) * 5; pdev->valternate = pChoose->alternate; pdev->width = pwc_image_sizes[size][0]; @@ -358,23 +358,24 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, } int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, - int frames, int *compression) + int pixfmt, int frames, int *compression) { int ret, size; PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", - width, height, frames, pdev->pixfmt); + width, height, frames, pixfmt); size = pwc_get_size(pdev, width, height); PWC_TRACE("decode_size = %d.\n", size); if (DEVICE_USE_CODEC1(pdev->type)) { - ret = set_video_mode_Nala(pdev, size, frames, compression); - + ret = set_video_mode_Nala(pdev, size, pixfmt, frames, + compression); } else if (DEVICE_USE_CODEC3(pdev->type)) { - ret = set_video_mode_Kiara(pdev, size, frames, compression); - + ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, + compression); } else { - ret = set_video_mode_Timon(pdev, size, frames, compression); + ret = set_video_mode_Timon(pdev, size, pixfmt, frames, + compression); } if (ret < 0) { PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 250ad4c..1f3386d 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -386,7 +386,7 @@ retry: /* We first try with low compression and then retry with a higher compression setting if there is not enough bandwidth. */ ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, - pdev->vframes, &compression); + pdev->pixfmt, pdev->vframes, &compression); /* Get the current alternate interface, adjust packet size */ intf = usb_ifnum_to_if(udev, 0); @@ -1128,7 +1128,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pwc_set_leds(pdev, 0, 0); /* Setup intial videomode */ - rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, 30, &compression); + rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, + V4L2_PIX_FMT_YUV420, 30, &compression); if (rc) goto err_free_mem; diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c index 23a55b5..9be5adf 100644 --- a/drivers/media/video/pwc/pwc-misc.c +++ b/drivers/media/video/pwc/pwc-misc.c @@ -90,5 +90,4 @@ void pwc_construct(struct pwc_device *pdev) pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; } - pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */ } diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index ef422d0..b275fad 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -493,16 +493,11 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) (pixelformat>>24)&255); ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, - 30, &compression); + pixelformat, 30, &compression); PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); - if (ret == 0) { - pdev->pixfmt = pixelformat; - pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, - pdev->pixfmt); - } - + pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); leave: mutex_unlock(&pdev->udevlock); return ret; @@ -1141,8 +1136,8 @@ static int pwc_s_parm(struct file *file, void *fh, goto leave; } - ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, fps, - &compression); + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, + fps, &compression); pwc_g_parm(file, fh, parm); diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 0f3bc1b..29b6d3d 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -369,7 +369,7 @@ void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, - int frames, int *compression); + int pixfmt, int frames, int *compression); extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); -- cgit v0.10.2 From 938d5b9e7c2e20a7e609ad5874c6e7d8d391e6e9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Jan 2012 13:14:46 -0300 Subject: [media] pwc: Avoid sending mode info to the camera when it is not needed Since we always do a set_video_mode on stream start, there is no need to actually send the mode info to the device on a s_fmt / s_parm ioctl. Not doing this saves us doing (slow) usb io. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 51ab4c5..9c1fb3f 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -169,10 +169,10 @@ int send_control_msg(struct pwc_device *pdev, } static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, - int frames, int *compression) + int frames, int *compression, int send_to_cam) { unsigned char buf[3]; - int ret, fps; + int fps, ret = 0; struct Nala_table_entry *pEntry; int frames2frames[31] = { /* closest match of framerate */ @@ -207,7 +207,8 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, return -EINVAL; memcpy(buf, pEntry->mode, 3); - ret = send_video_command(pdev, pdev->vendpoint, buf, 3); + if (send_to_cam) + ret = send_video_command(pdev, pdev->vendpoint, buf, 3); if (ret < 0) { PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); return ret; @@ -246,11 +247,11 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, - int frames, int *compression) + int frames, int *compression, int send_to_cam) { unsigned char buf[13]; const struct Timon_table_entry *pChoose; - int ret, fps; + int fps, ret = 0; if (size >= PSZ_MAX || *compression < 0 || *compression > 3) return -EINVAL; @@ -274,7 +275,8 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, return -ENOENT; /* Not supported. */ memcpy(buf, pChoose->mode, 13); - ret = send_video_command(pdev, pdev->vendpoint, buf, 13); + if (send_to_cam) + ret = send_video_command(pdev, pdev->vendpoint, buf, 13); if (ret < 0) return ret; @@ -300,10 +302,10 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, - int frames, int *compression) + int frames, int *compression, int send_to_cam) { const struct Kiara_table_entry *pChoose = NULL; - int fps, ret; + int fps, ret = 0; unsigned char buf[12]; if (size >= PSZ_MAX || *compression < 0 || *compression > 3) @@ -332,7 +334,8 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, memcpy(buf, pChoose->mode, 12); /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ - ret = send_video_command(pdev, 4 /* pdev->vendpoint */, buf, 12); + if (send_to_cam) + ret = send_video_command(pdev, 4, buf, 12); if (ret < 0) return ret; @@ -358,7 +361,7 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, } int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, - int pixfmt, int frames, int *compression) + int pixfmt, int frames, int *compression, int send_to_cam) { int ret, size; @@ -369,13 +372,13 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, if (DEVICE_USE_CODEC1(pdev->type)) { ret = set_video_mode_Nala(pdev, size, pixfmt, frames, - compression); + compression, send_to_cam); } else if (DEVICE_USE_CODEC3(pdev->type)) { ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, - compression); + compression, send_to_cam); } else { ret = set_video_mode_Timon(pdev, size, pixfmt, frames, - compression); + compression, send_to_cam); } if (ret < 0) { PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 1f3386d..23eacee 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -385,8 +385,8 @@ static int pwc_isoc_init(struct pwc_device *pdev) retry: /* We first try with low compression and then retry with a higher compression setting if there is not enough bandwidth. */ - ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, - pdev->pixfmt, pdev->vframes, &compression); + ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, + pdev->vframes, &compression, 1); /* Get the current alternate interface, adjust packet size */ intf = usb_ifnum_to_if(udev, 0); @@ -1129,7 +1129,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id /* Setup intial videomode */ rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT, - V4L2_PIX_FMT_YUV420, 30, &compression); + V4L2_PIX_FMT_YUV420, 30, &compression, 1); if (rc) goto err_free_mem; diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index b275fad..46feece 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -493,7 +493,7 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) (pixelformat>>24)&255); ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height, - pixelformat, 30, &compression); + pixelformat, 30, &compression, 0); PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret); @@ -1137,7 +1137,7 @@ static int pwc_s_parm(struct file *file, void *fh, } ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt, - fps, &compression); + fps, &compression, 0); pwc_g_parm(file, fh, parm); diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 29b6d3d..f441999 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -369,7 +369,7 @@ void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, - int pixfmt, int frames, int *compression); + int pixfmt, int frames, int *compression, int send_to_cam); extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); -- cgit v0.10.2 From 1c852201a28601b1379857f615e99071211294c6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Jan 2012 13:23:34 -0300 Subject: [media] pwc: Avoid unnecessarily rebuilding the decoder tables Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c index 6d48c1f..98772ef 100644 --- a/drivers/media/video/pwc/pwc-dec23.c +++ b/drivers/media/video/pwc/pwc-dec23.c @@ -301,6 +301,9 @@ void pwc_dec23_init(struct pwc_device *pdev, unsigned char *cmd) mutex_init(&pdec->lock); + if (pdec->last_cmd_valid && pdec->last_cmd == cmd[2]) + return; + if (DEVICE_USE_CODEC3(pdev->type)) { flags = cmd[2] & 0x18; if (flags == 8) @@ -347,6 +350,9 @@ void pwc_dec23_init(struct pwc_device *pdev, unsigned char *cmd) for (i=0; ilast_cmd = cmd[2]; + pdec->last_cmd_valid = 1; } /* diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h index a29068e..af31d60 100644 --- a/drivers/media/video/pwc/pwc-dec23.h +++ b/drivers/media/video/pwc/pwc-dec23.h @@ -31,11 +31,14 @@ struct pwc_dec23_private { struct mutex lock; + unsigned char last_cmd, last_cmd_valid; + unsigned int scalebits; unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */ unsigned int reservoir; unsigned int nbits_in_reservoir; + const unsigned char *stream; int temp_colors[16]; -- cgit v0.10.2 From 24be689bfbbcd6c047d7918784ff810e97648006 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Jan 2012 17:02:04 -0300 Subject: [media] pwc: Use one shared usb command buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwc driver used to: 1. kmalloc a buffer 2. memcpy data to send over usb there 3. do the usb_control_msg call (which does not work with data on the stack) 4. free the buffer For every usb command send. This patch changes the code to instead malloc a buffer for this purpose once and use it everywhere. [mchehab@redhat.com: Fix a compilation breakage with allyesconfig: drivers/media/video/pwc/pwc-ctrl.c: In function ‘pwc_get_cmos_sensor’: drivers/media/video/pwc/pwc-ctrl.c:546:3: warning: passing argument 4 of ‘recv_control_msg’ makes integer from pointer without a cast [en$ drivers/media/video/pwc/pwc-ctrl.c:107:12: note: expected ‘int’ but argument is of type ‘unsigned char *’ drivers/media/video/pwc/pwc-ctrl.c:546:3: error: too many arguments to function ‘recv_control_msg’ drivers/media/video/pwc/pwc-ctrl.c:107:12: note: declared here] Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 9c1fb3f..1f506fd 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -104,47 +104,16 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = /****************************************************************************/ -static int _send_control_msg(struct pwc_device *pdev, - u8 request, u16 value, int index, void *buf, int buflen) -{ - int rc; - void *kbuf = NULL; - - if (buflen) { - kbuf = kmemdup(buf, buflen, GFP_KERNEL); /* not allowed on stack */ - if (kbuf == NULL) - return -ENOMEM; - } - - rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - kbuf, buflen, USB_CTRL_SET_TIMEOUT); - - kfree(kbuf); - return rc; -} - static int recv_control_msg(struct pwc_device *pdev, - u8 request, u16 value, void *buf, int buflen) + u8 request, u16 value, int recv_count) { int rc; - void *kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ - - if (kbuf == NULL) - return -ENOMEM; rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), request, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - pdev->vcinterface, - kbuf, buflen, USB_CTRL_GET_TIMEOUT); - memcpy(buf, kbuf, buflen); - kfree(kbuf); - + value, pdev->vcinterface, + pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT); if (rc < 0) PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", rc, request, value); @@ -152,26 +121,38 @@ static int recv_control_msg(struct pwc_device *pdev, } static inline int send_video_command(struct pwc_device *pdev, - int index, void *buf, int buflen) + int index, const unsigned char *buf, int buflen) { - return _send_control_msg(pdev, - SET_EP_STREAM_CTL, - VIDEO_OUTPUT_CONTROL_FORMATTER, - index, - buf, buflen); + int rc; + + memcpy(pdev->ctrl_buf, buf, buflen); + + rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, index, + pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT); + if (rc >= 0) + memcpy(pdev->cmd_buf, buf, buflen); + else + PWC_ERROR("send_video_command error %d\n", rc); + + return rc; } int send_control_msg(struct pwc_device *pdev, u8 request, u16 value, void *buf, int buflen) { - return _send_control_msg(pdev, - request, value, pdev->vcinterface, buf, buflen); + return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, pdev->vcinterface, + buf, buflen, USB_CTRL_SET_TIMEOUT); } static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, int frames, int *compression, int send_to_cam) { - unsigned char buf[3]; int fps, ret = 0; struct Nala_table_entry *pEntry; int frames2frames[31] = @@ -206,18 +187,14 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, if (pEntry->alternate == 0) return -EINVAL; - memcpy(buf, pEntry->mode, 3); if (send_to_cam) - ret = send_video_command(pdev, pdev->vendpoint, buf, 3); - if (ret < 0) { - PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); + ret = send_video_command(pdev, pdev->vendpoint, + pEntry->mode, 3); + if (ret < 0) return ret; - } - if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) - pwc_dec1_init(pdev, buf); - pdev->cmd_len = 3; - memcpy(pdev->cmd_buf, buf, 3); + if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) + pwc_dec1_init(pdev, pEntry->mode); /* Set various parameters */ pdev->pixfmt = pixfmt; @@ -249,7 +226,6 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, int frames, int *compression, int send_to_cam) { - unsigned char buf[13]; const struct Timon_table_entry *pChoose; int fps, ret = 0; @@ -274,17 +250,14 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ - memcpy(buf, pChoose->mode, 13); if (send_to_cam) - ret = send_video_command(pdev, pdev->vendpoint, buf, 13); + ret = send_video_command(pdev, pdev->vendpoint, + pChoose->mode, 13); if (ret < 0) return ret; if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) - pwc_dec23_init(pdev, buf); - - pdev->cmd_len = 13; - memcpy(pdev->cmd_buf, buf, 13); + pwc_dec23_init(pdev, pChoose->mode); /* Set various parameters */ pdev->pixfmt = pixfmt; @@ -306,7 +279,6 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, { const struct Kiara_table_entry *pChoose = NULL; int fps, ret = 0; - unsigned char buf[12]; if (size >= PSZ_MAX || *compression < 0 || *compression > 3) return -EINVAL; @@ -328,22 +300,15 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ - PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate); - - /* usb_control_msg won't take staticly allocated arrays as argument?? */ - memcpy(buf, pChoose->mode, 12); - /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ if (send_to_cam) - ret = send_video_command(pdev, 4, buf, 12); + ret = send_video_command(pdev, 4, pChoose->mode, 12); if (ret < 0) return ret; if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) - pwc_dec23_init(pdev, buf); + pwc_dec23_init(pdev, pChoose->mode); - pdev->cmd_len = 12; - memcpy(pdev->cmd_buf, buf, 12); /* All set and go */ pdev->pixfmt = pixfmt; pdev->vframes = (fps + 1) * 5; @@ -445,13 +410,12 @@ unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned i int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { int ret; - u8 buf; - ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, 1); if (ret < 0) return ret; - *data = buf; + *data = pdev->ctrl_buf[0]; return 0; } @@ -459,7 +423,8 @@ int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) { int ret; - ret = send_control_msg(pdev, request, value, &data, sizeof(data)); + pdev->ctrl_buf[0] = data; + ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1); if (ret < 0) return ret; @@ -469,37 +434,34 @@ int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { int ret; - s8 buf; - ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, 1); if (ret < 0) return ret; - *data = buf; + *data = ((s8 *)pdev->ctrl_buf)[0]; return 0; } int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { int ret; - u8 buf[2]; - ret = recv_control_msg(pdev, request, value, buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, 2); if (ret < 0) return ret; - *data = (buf[1] << 8) | buf[0]; + *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; return 0; } int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) { int ret; - u8 buf[2]; - buf[0] = data & 0xff; - buf[1] = data >> 8; - ret = send_control_msg(pdev, request, value, buf, sizeof(buf)); + pdev->ctrl_buf[0] = data & 0xff; + pdev->ctrl_buf[1] = data >> 8; + ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2); if (ret < 0) return ret; @@ -520,7 +482,6 @@ int pwc_button_ctrl(struct pwc_device *pdev, u16 value) /* POWER */ void pwc_camera_power(struct pwc_device *pdev, int power) { - char buf; int r; if (!pdev->power_save) @@ -530,13 +491,11 @@ void pwc_camera_power(struct pwc_device *pdev, int power) return; /* Not supported by Nala or Timon < release 6 */ if (power) - buf = 0x00; /* active */ + pdev->ctrl_buf[0] = 0x00; /* active */ else - buf = 0xFF; /* power save */ - r = send_control_msg(pdev, - SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, - &buf, sizeof(buf)); - + pdev->ctrl_buf[0] = 0xFF; /* power save */ + r = send_control_msg(pdev, SET_STATUS_CTL, + SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1); if (r < 0) PWC_ERROR("Failed to power %s camera (%d)\n", power ? "on" : "off", r); @@ -544,7 +503,6 @@ void pwc_camera_power(struct pwc_device *pdev, int power) int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) { - unsigned char buf[2]; int r; if (pdev->type < 730) @@ -560,11 +518,11 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) if (off_value > 0xff) off_value = 0xff; - buf[0] = on_value; - buf[1] = off_value; + pdev->ctrl_buf[0] = on_value; + pdev->ctrl_buf[1] = off_value; r = send_control_msg(pdev, - SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); + SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2); if (r < 0) PWC_ERROR("Failed to set LED on/off time (%d)\n", r); @@ -574,7 +532,6 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) #ifdef CONFIG_USB_PWC_DEBUG int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) { - unsigned char buf; int ret = -1, request; if (pdev->type < 675) @@ -584,14 +541,13 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) else request = SENSOR_TYPE_FORMATTER2; - ret = recv_control_msg(pdev, - GET_STATUS_CTL, request, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1); if (ret < 0) return ret; if (pdev->type < 675) - *sensor = buf | 0x100; + *sensor = pdev->ctrl_buf[0] | 0x100; else - *sensor = buf; + *sensor = pdev->ctrl_buf[0]; return 0; } #endif diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c index bac0d83..e899036 100644 --- a/drivers/media/video/pwc/pwc-dec1.c +++ b/drivers/media/video/pwc/pwc-dec1.c @@ -24,7 +24,7 @@ */ #include "pwc.h" -void pwc_dec1_init(struct pwc_device *pdev, void *buffer) +void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd) { struct pwc_dec1_private *pdec = &pdev->dec1; diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h index 6e8f3c5..c565ef8 100644 --- a/drivers/media/video/pwc/pwc-dec1.h +++ b/drivers/media/video/pwc/pwc-dec1.h @@ -34,6 +34,6 @@ struct pwc_dec1_private int version; }; -void pwc_dec1_init(struct pwc_device *pdev, void *buffer); +void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd); #endif diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c index 98772ef..3792fed 100644 --- a/drivers/media/video/pwc/pwc-dec23.c +++ b/drivers/media/video/pwc/pwc-dec23.c @@ -294,7 +294,7 @@ static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; /* If the type or the command change, we rebuild the lookup table */ -void pwc_dec23_init(struct pwc_device *pdev, unsigned char *cmd) +void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd) { int flags, version, shift, i; struct pwc_dec23_private *pdec = &pdev->dec23; diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h index af31d60..c655b1c 100644 --- a/drivers/media/video/pwc/pwc-dec23.h +++ b/drivers/media/video/pwc/pwc-dec23.h @@ -54,7 +54,7 @@ struct pwc_dec23_private }; -void pwc_dec23_init(struct pwc_device *pdev, unsigned char *cmd); +void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd); void pwc_dec23_decompress(struct pwc_device *pdev, const void *src, void *dst); diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 23eacee..a07df4e 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -605,6 +605,7 @@ static void pwc_video_release(struct v4l2_device *v) v4l2_ctrl_handler_free(&pdev->ctrl_handler); + kfree(pdev->ctrl_buf); kfree(pdev); } @@ -1115,6 +1116,14 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = pdev; + /* Allocate USB command buffers */ + pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL); + if (!pdev->ctrl_buf) { + PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); + rc = -ENOMEM; + goto err_free_mem; + } + #ifdef CONFIG_USB_PWC_DEBUG /* Query sensor type */ if (pwc_get_cmos_sensor(pdev, &rc) >= 0) { @@ -1199,6 +1208,7 @@ err_free_controls: err_free_mem: if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = NULL; + kfree(pdev->ctrl_buf); kfree(pdev); return rc; } diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 46feece..f495eeb 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -772,33 +772,33 @@ static int pwc_set_autogain_expo(struct pwc_device *pdev) static int pwc_set_motor(struct pwc_device *pdev) { int ret; - u8 buf[4]; - buf[0] = 0; + pdev->ctrl_buf[0] = 0; if (pdev->motor_pan_reset->is_new) - buf[0] |= 0x01; + pdev->ctrl_buf[0] |= 0x01; if (pdev->motor_tilt_reset->is_new) - buf[0] |= 0x02; + pdev->ctrl_buf[0] |= 0x02; if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) { ret = send_control_msg(pdev, SET_MPT_CTL, - PT_RESET_CONTROL_FORMATTER, buf, 1); + PT_RESET_CONTROL_FORMATTER, + pdev->ctrl_buf, 1); if (ret < 0) return ret; } - memset(buf, 0, sizeof(buf)); + memset(pdev->ctrl_buf, 0, 4); if (pdev->motor_pan->is_new) { - buf[0] = pdev->motor_pan->val & 0xFF; - buf[1] = (pdev->motor_pan->val >> 8); + pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF; + pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8); } if (pdev->motor_tilt->is_new) { - buf[2] = pdev->motor_tilt->val & 0xFF; - buf[3] = (pdev->motor_tilt->val >> 8); + pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF; + pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8); } if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) { ret = send_control_msg(pdev, SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, - buf, sizeof(buf)); + pdev->ctrl_buf, 4); if (ret < 0) return ret; } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index f441999..e4d4d71 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -134,9 +134,6 @@ #define DEVICE_USE_CODEC3(x) ((x)>=700) #define DEVICE_USE_CODEC23(x) ((x)>=675) -/* from pwc-dec.h */ -#define PWCX_FLAG_PLANAR 0x0001 - /* Request types: video */ #define SET_LUM_CTL 0x01 #define GET_LUM_CTL 0x02 @@ -250,8 +247,8 @@ struct pwc_device char vmirror; /* for ToUCaM series */ char power_save; /* Do powersaving for this cam */ - int cmd_len; unsigned char cmd_buf[13]; + unsigned char *ctrl_buf; struct urb *urbs[MAX_ISO_BUFS]; char iso_init; -- cgit v0.10.2 From a081c3400ff2695244714293f5f6a4675c3a9493 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Jan 2012 17:47:25 -0300 Subject: [media] pwc: Remove dev_hint module parameter The code for this is rather crufty, and being able to tie a device to a specific minor is not really something we want to support in a modern udev based world. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index a07df4e..a691cf8 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -133,12 +133,6 @@ static struct usb_driver pwc_driver = { #endif static int power_save = -1; static int led_on = 100, led_off; /* defaults to LED that is on while in use */ -static struct { - int type; - char serial_number[30]; - int device_node; - struct pwc_device *pdev; -} device_hint[MAX_DEV_HINTS]; /***/ @@ -596,15 +590,8 @@ leave: static void pwc_video_release(struct v4l2_device *v) { struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - int hint; - - /* search device_hint[] table if we occupy a slot, by any chance */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) - if (device_hint[hint].pdev == pdev) - device_hint[hint].pdev = NULL; v4l2_ctrl_handler_free(&pdev->ctrl_handler); - kfree(pdev->ctrl_buf); kfree(pdev); } @@ -805,10 +792,9 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id struct usb_device *udev = interface_to_usbdev(intf); struct pwc_device *pdev = NULL; int vendor_id, product_id, type_id; - int hint, rc; + int rc; int features = 0; int compression = 0; - int video_nr = -1; /* default: use next available device */ int my_power_save = power_save; char serial_number[30], *name; @@ -1098,24 +1084,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); PWC_DEBUG_PROBE("Release: %04x\n", pdev->release); - /* Now search device_hint[] table for a match, so we can hint a node number. */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) { - if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && - (device_hint[hint].pdev == NULL)) { - /* so far, so good... try serial number */ - if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { - /* match! */ - video_nr = device_hint[hint].device_node; - PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr); - break; - } - } - } - - /* occupy slot */ - if (hint < MAX_DEV_HINTS) - device_hint[hint].pdev = pdev; - /* Allocate USB command buffers */ pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL); if (!pdev->ctrl_buf) { @@ -1163,7 +1131,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; pdev->vdev.v4l2_dev = &pdev->v4l2_dev; - rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr); + rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { PWC_ERROR("Failed to register as video device (%d).\n", rc); goto err_unregister_v4l2_dev; @@ -1206,8 +1174,6 @@ err_unregister_v4l2_dev: err_free_controls: v4l2_ctrl_handler_free(&pdev->ctrl_handler); err_free_mem: - if (hint < MAX_DEV_HINTS) - device_hint[hint].pdev = NULL; kfree(pdev->ctrl_buf); kfree(pdev); return rc; @@ -1245,22 +1211,18 @@ static void usb_pwc_disconnect(struct usb_interface *intf) static int leds[2] = { -1, -1 }; static unsigned int leds_nargs; -static char *dev_hint[MAX_DEV_HINTS]; -static unsigned int dev_hint_nargs; #ifdef CONFIG_USB_PWC_DEBUG module_param_named(trace, pwc_trace, int, 0644); #endif module_param(power_save, int, 0644); module_param_array(leds, int, &leds_nargs, 0444); -module_param_array(dev_hint, charp, &dev_hint_nargs, 0444); #ifdef CONFIG_USB_PWC_DEBUG MODULE_PARM_DESC(trace, "For debugging purposes"); #endif MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off"); MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); -MODULE_PARM_DESC(dev_hint, "Device node hints"); MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); MODULE_AUTHOR("Luc Saillard "); @@ -1270,105 +1232,18 @@ MODULE_VERSION( PWC_VERSION ); static int __init usb_pwc_init(void) { - int i; - -#ifdef CONFIG_USB_PWC_DEBUG - PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n"); - PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); - PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); - PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); - - if (pwc_trace >= 0) { - PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); - } -#endif - if (leds[0] >= 0) led_on = leds[0]; if (leds[1] >= 0) led_off = leds[1]; - /* Big device node whoopla. Basically, it allows you to assign a - device node (/dev/videoX) to a camera, based on its type - & serial number. The format is [type[.serialnumber]:]node. - - Any camera that isn't matched by these rules gets the next - available free device node. - */ - for (i = 0; i < MAX_DEV_HINTS; i++) { - char *s, *colon, *dot; - - /* This loop also initializes the array */ - device_hint[i].pdev = NULL; - s = dev_hint[i]; - if (s != NULL && *s != '\0') { - device_hint[i].type = -1; /* wildcard */ - strcpy(device_hint[i].serial_number, "*"); - - /* parse string: chop at ':' & '/' */ - colon = dot = s; - while (*colon != '\0' && *colon != ':') - colon++; - while (*dot != '\0' && *dot != '.') - dot++; - /* Few sanity checks */ - if (*dot != '\0' && dot > colon) { - PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n"); - return -EINVAL; - } - - if (*colon == '\0') { - /* No colon */ - if (*dot != '\0') { - PWC_ERROR("Malformed camera hint: no colon + device node given.\n"); - return -EINVAL; - } - else { - /* No type or serial number specified, just a number. */ - device_hint[i].device_node = - simple_strtol(s, NULL, 10); - } - } - else { - /* There's a colon, so we have at least a type and a device node */ - device_hint[i].type = - simple_strtol(s, NULL, 10); - device_hint[i].device_node = - simple_strtol(colon + 1, NULL, 10); - if (*dot != '\0') { - /* There's a serial number as well */ - int k; - - dot++; - k = 0; - while (*dot != ':' && k < 29) { - device_hint[i].serial_number[k++] = *dot; - dot++; - } - device_hint[i].serial_number[k] = '\0'; - } - } - PWC_TRACE("device_hint[%d]:\n", i); - PWC_TRACE(" type : %d\n", device_hint[i].type); - PWC_TRACE(" serial# : %s\n", device_hint[i].serial_number); - PWC_TRACE(" node : %d\n", device_hint[i].device_node); - } - else - device_hint[i].type = 0; /* not filled */ - } /* ..for MAX_DEV_HINTS */ - - PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver); return usb_register(&pwc_driver); } static void __exit usb_pwc_exit(void) { - PWC_DEBUG_MODULE("Deregistering driver.\n"); usb_deregister(&pwc_driver); - PWC_INFO("Philips webcam module removed.\n"); } module_init(usb_pwc_init); module_exit(usb_pwc_exit); - -/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ -- cgit v0.10.2 From c24e13713762a30f22468ac2c3d053abacb7672d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Jan 2012 17:51:12 -0300 Subject: [media] pwc: Simplify leds parameter parsing This reduces our module init to a simple usb_register() call, so that we can make use of the new upcoming macro's for this. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index a691cf8..122fbd0 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -132,7 +132,7 @@ static struct usb_driver pwc_driver = { int pwc_trace = PWC_DEBUG_LEVEL; #endif static int power_save = -1; -static int led_on = 100, led_off; /* defaults to LED that is on while in use */ +static int leds[2] = { 100, 0 }; /***/ @@ -737,7 +737,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); - pwc_set_leds(pdev, led_on, led_off); + pwc_set_leds(pdev, leds[0], leds[1]); r = pwc_isoc_init(pdev); if (r) { @@ -1209,7 +1209,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) * Initialization code & module stuff */ -static int leds[2] = { -1, -1 }; static unsigned int leds_nargs; #ifdef CONFIG_USB_PWC_DEBUG @@ -1232,11 +1231,6 @@ MODULE_VERSION( PWC_VERSION ); static int __init usb_pwc_init(void) { - if (leds[0] >= 0) - led_on = leds[0]; - if (leds[1] >= 0) - led_off = leds[1]; - return usb_register(&pwc_driver); } -- cgit v0.10.2 From 82033bc52abeb266cb3403e783a276d4a3d6cf9f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Jan 2012 02:28:34 -0300 Subject: [media] mb86a20s: fix off by one checks Clearly ">=" was intended here instead of ">". Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/mb86a20s.c b/drivers/media/dvb/frontends/mb86a20s.c index 7fa3e47..fade566 100644 --- a/drivers/media/dvb/frontends/mb86a20s.c +++ b/drivers/media/dvb/frontends/mb86a20s.c @@ -402,7 +402,7 @@ static int mb86a20s_get_modulation(struct mb86a20s_state *state, [2] = 0x8e, /* Layer C */ }; - if (layer > ARRAY_SIZE(reg)) + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) @@ -435,7 +435,7 @@ static int mb86a20s_get_fec(struct mb86a20s_state *state, [2] = 0x8f, /* Layer C */ }; - if (layer > ARRAY_SIZE(reg)) + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) @@ -470,7 +470,7 @@ static int mb86a20s_get_interleaving(struct mb86a20s_state *state, [2] = 0x90, /* Layer C */ }; - if (layer > ARRAY_SIZE(reg)) + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) @@ -494,7 +494,7 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, [2] = 0x91, /* Layer C */ }; - if (layer > ARRAY_SIZE(reg)) + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) -- cgit v0.10.2 From 6c2cc5b575ed09d81507ec86cc5cfad022d9236b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Jan 2012 02:34:00 -0300 Subject: [media] cx231xx: dereferencing NULL after allocation failure "dev" is NULL here so we should use "nr" instead of "dev->devno". Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 627829f..7d8edb4 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -1052,7 +1052,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { cx231xx_err(DRIVER_NAME ": out of memory!\n"); - clear_bit(dev->devno, &cx231xx_devused); + clear_bit(nr, &cx231xx_devused); return -ENOMEM; } -- cgit v0.10.2 From a7c8aadad39428b64d26c3971d967f8314e2397d Mon Sep 17 00:00:00 2001 From: Miroslav Slugen Date: Sun, 11 Dec 2011 18:57:58 -0300 Subject: [media] cx23885-dvb: check if dvb_attach() succeded Fix possible null dereference for Leadtek DTV 3200H XC4000 tuner when no firmware file available. Cc: stable@kernel.org Signed-off-by: Miroslav Slugen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index af8a225..6835eb1 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -943,6 +943,11 @@ static int dvb_register(struct cx23885_tsport *port) fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->i2c_bus[1].i2c_adap, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc4000 attach failed\n", + dev->name); + goto frontend_detach; + } } break; case CX23885_BOARD_TBS_6920: -- cgit v0.10.2 From 9ee8537f60590c63d74dc5724c921bc3eb6ef558 Mon Sep 17 00:00:00 2001 From: Miroslav Slugen Date: Sun, 11 Dec 2011 20:19:34 -0300 Subject: [media] cx23885: Don't duplicate xc4000 entry for radio All radio tuners in cx23885 driver using same address for radio and tuner, so there is no need to probe it twice for same tuner and we can use radio_type UNSET. Be aware radio support in cx23885 is not yet committed, so this is only minor fix for future support. Signed-off-by: Miroslav Slugen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 3c01be9..19b5499 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -213,8 +213,8 @@ struct cx23885_board cx23885_boards[] = { .portc = CX23885_MPEG_DVB, .tuner_type = TUNER_XC4000, .tuner_addr = 0x61, - .radio_type = TUNER_XC4000, - .radio_addr = 0x61, + .radio_type = UNSET, + .radio_addr = ADDR_UNSET, .input = {{ .type = CX23885_VMUX_TELEVISION, .vmux = CX25840_VIN2_CH1 | -- cgit v0.10.2 From b6854e3f31402476bcc9d2f41570389fa491de17 Mon Sep 17 00:00:00 2001 From: Miroslav Slugen Date: Sun, 11 Dec 2011 19:00:06 -0300 Subject: [media] cx88: fix: don't duplicate xc4000 entry for radio All radio tuners in cx88 driver using same address for radio and tuner, so there is no need to probe it twice for same tuner and we can use radio_type UNSET, this also fix broken radio since kernel 2.6.39-rc1 for those tuners. Cc: stable@kernel.org Signed-off-by: Miroslav Slugen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 62c7ad0..cbd5d11 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1573,8 +1573,8 @@ static const struct cx88_board cx88_boards[] = { .name = "Pinnacle Hybrid PCTV", .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, - .radio_type = TUNER_XC2028, - .radio_addr = 0x61, + .radio_type = UNSET, + .radio_addr = ADDR_UNSET, .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -1611,8 +1611,8 @@ static const struct cx88_board cx88_boards[] = { .name = "Leadtek TV2000 XP Global", .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, - .radio_type = TUNER_XC2028, - .radio_addr = 0x61, + .radio_type = UNSET, + .radio_addr = ADDR_UNSET, .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -2115,8 +2115,8 @@ static const struct cx88_board cx88_boards[] = { .name = "Terratec Cinergy HT PCI MKII", .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, - .radio_type = TUNER_XC2028, - .radio_addr = 0x61, + .radio_type = UNSET, + .radio_addr = ADDR_UNSET, .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -2154,9 +2154,9 @@ static const struct cx88_board cx88_boards[] = { [CX88_BOARD_WINFAST_DTV1800H] = { .name = "Leadtek WinFast DTV1800 Hybrid", .tuner_type = TUNER_XC2028, - .radio_type = TUNER_XC2028, + .radio_type = UNSET, .tuner_addr = 0x61, - .radio_addr = 0x61, + .radio_addr = ADDR_UNSET, /* * GPIO setting * @@ -2195,9 +2195,9 @@ static const struct cx88_board cx88_boards[] = { [CX88_BOARD_WINFAST_DTV1800H_XC4000] = { .name = "Leadtek WinFast DTV1800 H (XC4000)", .tuner_type = TUNER_XC4000, - .radio_type = TUNER_XC4000, + .radio_type = UNSET, .tuner_addr = 0x61, - .radio_addr = 0x61, + .radio_addr = ADDR_UNSET, /* * GPIO setting * @@ -2236,9 +2236,9 @@ static const struct cx88_board cx88_boards[] = { [CX88_BOARD_WINFAST_DTV2000H_PLUS] = { .name = "Leadtek WinFast DTV2000 H PLUS", .tuner_type = TUNER_XC4000, - .radio_type = TUNER_XC4000, + .radio_type = UNSET, .tuner_addr = 0x61, - .radio_addr = 0x61, + .radio_addr = ADDR_UNSET, /* * GPIO * 2: 1: mute audio -- cgit v0.10.2 From cd4ca7afc61d3b18fcd635002459fb6b1d701099 Mon Sep 17 00:00:00 2001 From: Miroslav Slugen Date: Sun, 11 Dec 2011 18:47:32 -0300 Subject: [media] tuner: Fix numberspace conflict between xc4000 and pti 5nf05 tuners Update xc4000 tuner definition, number 81 is already in use by TUNER_PARTSNIC_PTI_5NF05. Cc: stable@kernel.org Signed-off-by: Miroslav Slugen Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/tuner.h b/include/media/tuner.h index 89c290b..29e1920 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -127,7 +127,6 @@ #define TUNER_PHILIPS_FMD1216MEX_MK3 78 #define TUNER_PHILIPS_FM1216MK5 79 #define TUNER_PHILIPS_FQ1216LME_MK3 80 /* Active loopthrough, no FM */ -#define TUNER_XC4000 81 /* Xceive Silicon Tuner */ #define TUNER_PARTSNIC_PTI_5NF05 81 #define TUNER_PHILIPS_CU1216L 82 @@ -136,6 +135,8 @@ #define TUNER_PHILIPS_FQ1236_MK5 85 /* NTSC, TDA9885, no FM radio */ #define TUNER_TENA_TNF_5337 86 +#define TUNER_XC4000 87 /* Xceive Silicon Tuner */ + /* tv card specific */ #define TDA9887_PRESENT (1<<0) #define TDA9887_PORT1_INACTIVE (1<<1) -- cgit v0.10.2 From 8a538a8855aa95e5a12ae232a140e6b30c56d0c5 Mon Sep 17 00:00:00 2001 From: Miroslav Slugen Date: Tue, 20 Dec 2011 21:18:38 -0300 Subject: [media] xc4000: add support for signal strength measures In xc4000 chipsets real signal and noise level is stored in register 0x0A and 0x0B,so we can use those registers to monitor signal strength. I tested this patch on 2 different cards Leadtek DVR3200 and DTV2000H Plus, both with same results, I used special antenna hubs (toner 4x, 6x, 8x and 12x) with mesured signal lost, both registers are in dB value, first represent signal with limit value -113.5dB (should be -114dB) and exactly match with test results. Second represents noise level also in dB and there is no maximum value, but from tests we can drop everything above 32dB which tuner realy can't use, signal was usable till 20dB noise level. In digital mode we can take signal strength but sadly noise level is not relevant and real value is stored in demodulator for now just zl10353, also digital mode is just for testing, because it needs changing other parts of code which reads data only from demodulator. In analog mode I was able to test only FM radio, signal level is not important, it says something about cable and hub losts, but nothing about real quality of reception, so even if we have signal level at minimum 113dB we can still here radio, because of that it is displaied only in debug mode, but for real signal level is used noise register which is again very accurate, radio noise level was betwen 6-20dB for good signal, 20-25dB for medium signal, and above 25dB signal is unusable. For now real benefit of this patch is only for FM radio mode. Signed-off-by: Miroslav Slugen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c index d218c1d..6839711 100644 --- a/drivers/media/common/tuners/xc4000.c +++ b/drivers/media/common/tuners/xc4000.c @@ -154,6 +154,8 @@ struct xc4000_priv { #define XREG_SNR 0x06 #define XREG_VERSION 0x07 #define XREG_PRODUCT_ID 0x08 +#define XREG_SIGNAL_LEVEL 0x0A +#define XREG_NOISE_LEVEL 0x0B /* Basic firmware description. This will remain with @@ -486,6 +488,16 @@ static int xc_get_quality(struct xc4000_priv *priv, u16 *quality) return xc4000_readreg(priv, XREG_QUALITY, quality); } +static int xc_get_signal_level(struct xc4000_priv *priv, u16 *signal) +{ + return xc4000_readreg(priv, XREG_SIGNAL_LEVEL, signal); +} + +static int xc_get_noise_level(struct xc4000_priv *priv, u16 *noise) +{ + return xc4000_readreg(priv, XREG_NOISE_LEVEL, noise); +} + static u16 xc_wait_for_lock(struct xc4000_priv *priv) { u16 lock_state = 0; @@ -1089,6 +1101,8 @@ static void xc_debug_dump(struct xc4000_priv *priv) u32 hsync_freq_hz = 0; u16 frame_lines; u16 quality; + u16 signal = 0; + u16 noise = 0; u8 hw_majorversion = 0, hw_minorversion = 0; u8 fw_majorversion = 0, fw_minorversion = 0; @@ -1119,6 +1133,12 @@ static void xc_debug_dump(struct xc4000_priv *priv) xc_get_quality(priv, &quality); dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality); + + xc_get_signal_level(priv, &signal); + dprintk(1, "*** Signal level = -%ddB (%d)\n", signal >> 8, signal); + + xc_get_noise_level(priv, &noise); + dprintk(1, "*** Noise level = %ddB (%d)\n", noise >> 8, noise); } static int xc4000_set_params(struct dvb_frontend *fe) @@ -1432,6 +1452,71 @@ fail: return ret; } +static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength) +{ + struct xc4000_priv *priv = fe->tuner_priv; + u16 value = 0; + int rc; + + mutex_lock(&priv->lock); + rc = xc4000_readreg(priv, XREG_SIGNAL_LEVEL, &value); + mutex_unlock(&priv->lock); + + if (rc < 0) + goto ret; + + /* Informations from real testing of DVB-T and radio part, + coeficient for one dB is 0xff. + */ + tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value); + + /* all known digital modes */ + if ((priv->video_standard == XC4000_DTV6) || + (priv->video_standard == XC4000_DTV7) || + (priv->video_standard == XC4000_DTV7_8) || + (priv->video_standard == XC4000_DTV8)) + goto digital; + + /* Analog mode has NOISE LEVEL important, signal + depends only on gain of antenna and amplifiers, + but it doesn't tell anything about real quality + of reception. + */ + mutex_lock(&priv->lock); + rc = xc4000_readreg(priv, XREG_NOISE_LEVEL, &value); + mutex_unlock(&priv->lock); + + tuner_dbg("Noise level: %ddB (%05d)\n", value >> 8, value); + + /* highest noise level: 32dB */ + if (value >= 0x2000) { + value = 0; + } else { + value = ~value << 3; + } + + goto ret; + + /* Digital mode has SIGNAL LEVEL important and real + noise level is stored in demodulator registers. + */ +digital: + /* best signal: -50dB */ + if (value <= 0x3200) { + value = 0xffff; + /* minimum: -114dB - should be 0x7200 but real zero is 0x713A */ + } else if (value >= 0x713A) { + value = 0; + } else { + value = ~(value - 0x3200) << 2; + } + +ret: + *strength = value; + + return rc; +} + static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc4000_priv *priv = fe->tuner_priv; @@ -1559,6 +1644,7 @@ static const struct dvb_tuner_ops xc4000_tuner_ops = { .set_params = xc4000_set_params, .set_analog_params = xc4000_set_analog_params, .get_frequency = xc4000_get_frequency, + .get_rf_strength = xc4000_get_signal, .get_bandwidth = xc4000_get_bandwidth, .get_status = xc4000_get_status }; -- cgit v0.10.2 From 304bce414efce196f4051f2a5d11418c2c2ef7f9 Mon Sep 17 00:00:00 2001 From: Miroslav Slugen Date: Sun, 11 Dec 2011 20:19:34 -0300 Subject: [media] Add registers names to XC2028 tuner from datahseet and use them Signed-off-by: Miroslav Slugen Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index 2755599..b5ee3eb 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -24,6 +24,21 @@ #include #include "dvb_frontend.h" +/* Registers (Write-only) */ +#define XREG_INIT 0x00 +#define XREG_RF_FREQ 0x02 +#define XREG_POWER_DOWN 0x08 + +/* Registers (Read-only) */ +#define XREG_FREQ_ERROR 0x01 +#define XREG_LOCK 0x02 +#define XREG_VERSION 0x04 +#define XREG_PRODUCT_ID 0x08 +#define XREG_HSYNC_FREQ 0x10 +#define XREG_FRAME_LINES 0x20 +#define XREG_SNR 0x40 + +#define XREG_ADC_ENV 0x0100 static int debug; module_param(debug, int, 0644); @@ -885,7 +900,7 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) mutex_lock(&priv->lock); /* Sync Lock Indicator */ - rc = xc2028_get_reg(priv, 0x0002, &frq_lock); + rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); if (rc < 0) goto ret; @@ -894,7 +909,7 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) signal = 1 << 11; /* Get SNR of the video signal */ - rc = xc2028_get_reg(priv, 0x0040, &signal); + rc = xc2028_get_reg(priv, XREG_SNR, &signal); if (rc < 0) goto ret; @@ -1019,9 +1034,9 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, /* CMD= Set frequency */ if (priv->firm_version < 0x0202) - rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00}); + rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00}); else - rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00}); + rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00}); if (rc < 0) goto ret; @@ -1201,9 +1216,9 @@ static int xc2028_sleep(struct dvb_frontend *fe) mutex_lock(&priv->lock); if (priv->firm_version < 0x0202) - rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00}); + rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00}); else - rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00}); + rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00}); priv->cur_fw.type = 0; /* need firmware reload */ -- cgit v0.10.2 From 764a579fb56a9205a9212250e625efe2b11e3f96 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 15 Dec 2011 14:02:33 +0000 Subject: ARM: versatile: Add missing ENDPROC to headsmp.S Once the ENDPROC is in place, BSYM() in not longer necessary to get correct pointer to versatile_secondary_startup(). Tested-by: Jon Medhurst Signed-off-by: Pawel Moll Acked-by: Dave Martin diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c index e83c654..17c878d 100644 --- a/arch/arm/mach-realview/platsmp.c +++ b/arch/arm/mach-realview/platsmp.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -75,6 +74,6 @@ void __init platform_smp_prepare_cpus(unsigned int max_cpus) * until it receives a soft interrupt, and then the * secondary CPU branches to this address. */ - __raw_writel(BSYM(virt_to_phys(versatile_secondary_startup)), + __raw_writel(virt_to_phys(versatile_secondary_startup), __io_address(REALVIEW_SYS_FLAGSSET)); } diff --git a/arch/arm/mach-vexpress/platsmp.c b/arch/arm/mach-vexpress/platsmp.c index 2b5f7ac..124ffb1 100644 --- a/arch/arm/mach-vexpress/platsmp.c +++ b/arch/arm/mach-vexpress/platsmp.c @@ -13,8 +13,6 @@ #include #include -#include - #include #define V2M_PA_CS7 0x10000000 @@ -46,6 +44,6 @@ void __init platform_smp_prepare_cpus(unsigned int max_cpus) * secondary CPU branches to this address. */ writel(~0, MMIO_P2V(V2M_SYS_FLAGSCLR)); - writel(BSYM(virt_to_phys(versatile_secondary_startup)), + writel(virt_to_phys(versatile_secondary_startup), MMIO_P2V(V2M_SYS_FLAGSSET)); } diff --git a/arch/arm/plat-versatile/headsmp.S b/arch/arm/plat-versatile/headsmp.S index d397a1f..dd703ef 100644 --- a/arch/arm/plat-versatile/headsmp.S +++ b/arch/arm/plat-versatile/headsmp.S @@ -38,3 +38,4 @@ pen: ldr r7, [r6] .align 1: .long . .long pen_release +ENDPROC(versatile_secondary_startup) -- cgit v0.10.2 From 0397e4505910b44be0066d1b2ce201f0c03bae4f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 15 Jan 2012 07:25:57 -0300 Subject: [media] saa7164: remove duplicate initialization These were initialized twice by mistake. They were defined the same way both times so this doesn't change how the code works. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c index 971591d..5b72da5 100644 --- a/drivers/media/video/saa7164/saa7164-cards.c +++ b/drivers/media/video/saa7164/saa7164-cards.c @@ -269,8 +269,6 @@ struct saa7164_board saa7164_boards[] = { .portb = SAA7164_MPEG_DVB, .portc = SAA7164_MPEG_ENCODER, .portd = SAA7164_MPEG_ENCODER, - .portc = SAA7164_MPEG_ENCODER, - .portd = SAA7164_MPEG_ENCODER, .porte = SAA7164_MPEG_VBI, .portf = SAA7164_MPEG_VBI, .chiprev = SAA7164_CHIP_REV3, @@ -333,8 +331,6 @@ struct saa7164_board saa7164_boards[] = { .portd = SAA7164_MPEG_ENCODER, .porte = SAA7164_MPEG_VBI, .portf = SAA7164_MPEG_VBI, - .porte = SAA7164_MPEG_VBI, - .portf = SAA7164_MPEG_VBI, .chiprev = SAA7164_CHIP_REV3, .unit = {{ .id = 0x28, -- cgit v0.10.2 From 8e0f549b23006e90172a4be188ba94f0944f08f8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 12 Jan 2012 08:46:23 -0600 Subject: ARM: msm: Add missing ENDPROC to headsmp.S Once the ENDPROC is in place, BSYM() in not longer necessary to get correct pointer to msm_secondary_startup(). Signed-off-by: Rob Herring Cc: David Brown Cc: Daniel Walker diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S index 0c631a9..bcd5af2 100644 --- a/arch/arm/mach-msm/headsmp.S +++ b/arch/arm/mach-msm/headsmp.S @@ -34,6 +34,7 @@ pen: ldr r7, [r6] * should now contain the SVC stack for this core */ b secondary_startup +ENDPROC(msm_secondary_startup) .align 1: .long . -- cgit v0.10.2 From 09f759f66b82bbfe21e165acf0e57e7725466312 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 12 Jan 2012 08:46:23 -0600 Subject: ARM: ux500: add missing ENDPROC to headsmp.S Once the ENDPROC is in place, BSYM() in not longer necessary to get correct pointer to u8500_secondary_startup(). Signed-off-by: Rob Herring Cc: Srinidhi Kasagar Cc: Linus Walleij diff --git a/arch/arm/mach-ux500/headsmp.S b/arch/arm/mach-ux500/headsmp.S index 64fa451..08da5589 100644 --- a/arch/arm/mach-ux500/headsmp.S +++ b/arch/arm/mach-ux500/headsmp.S @@ -32,6 +32,8 @@ pen: ldr r7, [r6] * should now contain the SVC stack for this core */ b secondary_startup +ENDPROC(u8500_secondary_startup) + .align 2 1: .long . .long pen_release -- cgit v0.10.2 From f7597c02a2e6fada7a065b03efe283ae7ef0e0bc Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Jan 2012 15:39:19 -0600 Subject: ARM: exynos: remove incorrect BSYM usage BSYM macro is only needed for assembly files and its usage in c files is wrong, so remove it. The linker will correctly set bit 0 for Thumb2 kernels. Signed-off-by: Rob Herring Cc: Dave Martin Cc: Kukjin Kim diff --git a/arch/arm/mach-exynos/headsmp.S b/arch/arm/mach-exynos/headsmp.S index 3cdeb36..5364d4b 100644 --- a/arch/arm/mach-exynos/headsmp.S +++ b/arch/arm/mach-exynos/headsmp.S @@ -36,6 +36,8 @@ pen: ldr r7, [r6] * should now contain the SVC stack for this core */ b secondary_startup +ENDPROC(exynos4_secondary_startup) + .align 2 1: .long . .long pen_release diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 69ffb2f..b89bfa5 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -163,7 +162,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) while (time_before(jiffies, timeout)) { smp_rmb(); - __raw_writel(BSYM(virt_to_phys(exynos4_secondary_startup)), + __raw_writel(virt_to_phys(exynos4_secondary_startup), CPU1_BOOT_REG); gic_raise_softirq(cpumask_of(cpu), 1); @@ -218,6 +217,6 @@ void __init platform_smp_prepare_cpus(unsigned int max_cpus) * until it receives a soft interrupt, and then the * secondary CPU branches to this address. */ - __raw_writel(BSYM(virt_to_phys(exynos4_secondary_startup)), + __raw_writel(virt_to_phys(exynos4_secondary_startup), CPU1_BOOT_REG); } -- cgit v0.10.2 From e9ae4d4f028cd9ff9e7d06430cc678d7926df9e0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 15 Jan 2012 07:32:19 -0300 Subject: [media] tlg2300: fix up check_firmware() return The caller doesn't check the return value of check_firmware() but static checkers complain. It currently returns negative error codes, or zero or greater on success but since the return type is boolean the values are truncated to one or zero. I've changed it to return an int, negative on error and zero on success. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index 129f135..c096b3f 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -374,7 +374,7 @@ static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) } #endif -static bool check_firmware(struct usb_device *udev, int *down_firmware) +static int check_firmware(struct usb_device *udev, int *down_firmware) { void *buf; int ret; @@ -398,7 +398,7 @@ static bool check_firmware(struct usb_device *udev, int *down_firmware) *down_firmware = 1; return firmware_download(udev); } - return ret; + return 0; } static int poseidon_probe(struct usb_interface *interface, -- cgit v0.10.2 From faf27976a745ac126943a500c48b30c0dd1082d3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 15 Jan 2012 14:20:50 -0300 Subject: [media] anysee: do not attach same frontend twice cxd2820r implements only one frontend currently which handles all the standards. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index df46015..ecc3add 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -877,24 +877,18 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) case ANYSEE_HW_508T2C: /* 20 */ /* E7 T2C */ + if (state->fe_id) + break; + /* enable DVB-T/T2/C demod on IOE[5] */ ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 5), 0x20); if (ret) goto error; - if (state->fe_id == 0) { - /* DVB-T/T2 */ - adap->fe_adap[state->fe_id].fe = - dvb_attach(cxd2820r_attach, - &anysee_cxd2820r_config, - &adap->dev->i2c_adap, NULL); - } else { - /* DVB-C */ - adap->fe_adap[state->fe_id].fe = - dvb_attach(cxd2820r_attach, - &anysee_cxd2820r_config, - &adap->dev->i2c_adap, adap->fe_adap[0].fe); - } + /* attach demod */ + adap->fe_adap[state->fe_id].fe = dvb_attach(cxd2820r_attach, + &anysee_cxd2820r_config, &adap->dev->i2c_adap, + NULL); state->has_ci = true; -- cgit v0.10.2 From d04ca8df70f0e1c3fe6ee2aeb1114b03a3a3de88 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 15 Jan 2012 15:08:30 -0300 Subject: [media] cxd2820r: do not switch to DVB-T when DVB-C fails Fix another bug introduced by recent multi-frontend to single-frontend change. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index 93e1b12..b789a90 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -476,10 +476,10 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); /* switch between DVB-T and DVB-T2 when tune fails */ - if (priv->last_tune_failed && (priv->delivery_system != SYS_DVBC_ANNEX_A)) { + if (priv->last_tune_failed) { if (priv->delivery_system == SYS_DVBT) c->delivery_system = SYS_DVBT2; - else + else if (priv->delivery_system == SYS_DVBT2) c->delivery_system = SYS_DVBT; } -- cgit v0.10.2 From 016961e0d9c5446017610a10fe7db14bf5f5400c Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 15 Jan 2012 16:39:13 -0300 Subject: [media] tda18271c2dd: Remove pointless linux/version.h include As pointed out by 'make versioncheck', there's no need for drivers/media/dvb/frontends/tda18271c2dd.c to Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb/frontends/tda18271c2dd.c index 86da3d8..ad7c72e 100644 --- a/drivers/media/dvb/frontends/tda18271c2dd.c +++ b/drivers/media/dvb/frontends/tda18271c2dd.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "dvb_frontend.h" -- cgit v0.10.2 From 1f64972937d84f48a8b4d16e7e0b4a5ee7904c96 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 15 Jan 2012 16:50:11 -0300 Subject: [media] cxd2820r: wait demod lock for DVB-C too Fix yet another bug introduced be recent cxd2820r multi-frontend to single-frontend change. Finally, we have at least almost working picture for DVB-C too. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index b789a90..372a4e7 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -492,6 +492,7 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) /* frontend lock wait loop count */ switch (priv->delivery_system) { case SYS_DVBT: + case SYS_DVBC_ANNEX_A: i = 20; break; case SYS_DVBT2: -- cgit v0.10.2 From 3b6a567afa3250abf280ef535000dc0dc3ed9d52 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 15 Jan 2012 18:30:36 -0300 Subject: [media] cxd2820r: do not allow get_frontend() when demod is not initialized This fixes bug introduced by multi-frontend to single-frontend change. Finally HAS_LOCK is got back! We are not allowed to access hardware in sleep mode... Chip did not like when .get_frontend() reads some registers while chip was sleeping and due to that HAS_LOCK bit was never gained. TODO: We should add logic for dvb-core to drop out illegal calls like that. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index 372a4e7..caae7f7 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -309,9 +309,14 @@ static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status) static int cxd2820r_get_frontend(struct dvb_frontend *fe) { + struct cxd2820r_priv *priv = fe->demodulator_priv; int ret; dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); + + if (priv->delivery_system == SYS_UNDEFINED) + return 0; + switch (fe->dtv_property_cache.delivery_system) { case SYS_DVBT: ret = cxd2820r_get_frontend_t(fe); -- cgit v0.10.2 From 0a60cb14a58696100481674682eb07ffb2a8eac7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Jan 2012 15:41:40 -0600 Subject: ARM: imx: remove incorrect BSYM usage BSYM macro is only needed for assembly files and its usage in c files is wrong, so remove it. The linker will correctly set bit 0 for Thumb2 kernels. Signed-off-by: Rob Herring Cc: Sascha Hauer Acked-by: Shawn Guo Acked-by: Dave Martin diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c index a8e3368..74a4b5d 100644 --- a/arch/arm/mach-imx/src.c +++ b/arch/arm/mach-imx/src.c @@ -15,7 +15,6 @@ #include #include #include -#include #define SRC_SCR 0x000 #define SRC_GPR1 0x020 @@ -42,7 +41,7 @@ void imx_enable_cpu(int cpu, bool enable) void imx_set_cpu_jump(int cpu, void *jump_addr) { cpu = cpu_logical_map(cpu); - writel_relaxed(BSYM(virt_to_phys(jump_addr)), + writel_relaxed(virt_to_phys(jump_addr), src_base + SRC_GPR1 + cpu * 8); } -- cgit v0.10.2 From adf55f7f5bde24149455efdee8d055645de96006 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Jan 2012 15:41:58 -0600 Subject: ARM: highbank: remove incorrect BSYM usage BSYM macro is only needed for assembly files and its usage in c files is wrong, so remove it. The linker will correctly set bit 0 for Thumb2 kernels. Signed-off-by: Rob Herring Acked-by: Dave Martin diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index 88660d5..48293f8 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -76,7 +75,7 @@ void highbank_set_cpu_jump(int cpu, void *jump_addr) #ifdef CONFIG_SMP cpu = cpu_logical_map(cpu); #endif - writel(BSYM(virt_to_phys(jump_addr)), HB_JUMP_TABLE_VIRT(cpu)); + writel(virt_to_phys(jump_addr), HB_JUMP_TABLE_VIRT(cpu)); __cpuc_flush_dcache_area(HB_JUMP_TABLE_VIRT(cpu), 16); outer_clean_range(HB_JUMP_TABLE_PHYS(cpu), HB_JUMP_TABLE_PHYS(cpu) + 15); -- cgit v0.10.2 From efb963dcd9854c70667cdba9b5854b7290f1cefd Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 9 Jan 2012 15:43:20 -0600 Subject: ARM: make BSYM macro assembly only BSYM macro is only needed for assembly files and its usage in c files is wrong, so only define it for assembly. Signed-off-by: Rob Herring Acked-by: Dave Martin diff --git a/arch/arm/include/asm/unified.h b/arch/arm/include/asm/unified.h index bc63116..f5989f4 100644 --- a/arch/arm/include/asm/unified.h +++ b/arch/arm/include/asm/unified.h @@ -37,8 +37,8 @@ #define THUMB(x...) x #ifdef __ASSEMBLY__ #define W(instr) instr.w -#endif #define BSYM(sym) sym + 1 +#endif #else /* !CONFIG_THUMB2_KERNEL */ @@ -49,8 +49,8 @@ #define THUMB(x...) #ifdef __ASSEMBLY__ #define W(instr) instr -#endif #define BSYM(sym) sym +#endif #endif /* CONFIG_THUMB2_KERNEL */ -- cgit v0.10.2 From f7cb8a4be1cb46b03ccf517d505b3474c810853d Mon Sep 17 00:00:00 2001 From: Zeng Zhaoming Date: Mon, 16 Jan 2012 15:18:11 +0800 Subject: ASoC: sgtl5000: update author email address Update MODULE_AUTHOR email address. Signed-off-by: Zeng Zhaoming Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index fc9b127..a809d70 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1457,5 +1457,5 @@ static void __exit sgtl5000_exit(void) module_exit(sgtl5000_exit); MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver"); -MODULE_AUTHOR("Zeng Zhaoming "); +MODULE_AUTHOR("Zeng Zhaoming "); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From e7c80e2a8b403c10d805f48596efac741cce754d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 16 Jan 2012 15:23:31 +0000 Subject: ASoC: dapm - Fix check for codec context in dapm_power_widgets(). Fixes a NULL pointer dereference in dapm_power_widgets() if the dapm context has no codec. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3ad1f59..1f55ded 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1426,7 +1426,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) dapm->target_bias_level = SND_SOC_BIAS_ON; break; case SND_SOC_DAPM_STREAM_STOP: - if (dapm->codec->active) + if (dapm->codec && dapm->codec->active) dapm->target_bias_level = SND_SOC_BIAS_ON; else dapm->target_bias_level = SND_SOC_BIAS_STANDBY; -- cgit v0.10.2 From 675c496ba4e751ea5b895c83219e3bb034f648ba Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 16 Jan 2012 15:25:37 +0000 Subject: ASoC: core - Free platform DAPM context at platform removal. Fix platform removal by freeing the platform DAPM resources and remove it from the DAPM list. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3986520..b5ecf6d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -907,6 +907,10 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order) if (err < 0) printk(KERN_ERR "asoc: failed to remove %s\n", platform->name); } + + /* Make sure all DAPM widgets are freed */ + snd_soc_dapm_free(&platform->dapm); + platform->probed = 0; list_del(&platform->card_list); module_put(platform->dev->driver->owner); -- cgit v0.10.2 From c4addcb55417a246b4419cbb075a2e4f412a8645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Sun, 15 Jan 2012 11:32:30 +0100 Subject: gpio-pch: cleanup NULL pointer checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch will remove the following sparse warning ("make C=1"): * warning: Using plain integer as NULL pointer Signed-off-by: Márton Németh Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index f060329..7f008fb 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -376,7 +376,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, } chip->base = pci_iomap(pdev, 1, 0); - if (chip->base == 0) { + if (!chip->base) { dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__); ret = -ENOMEM; goto err_iomap; -- cgit v0.10.2 From cd7bf8a554fdfaadcee6d66900c7710651dc58f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Sun, 15 Jan 2012 11:32:24 +0100 Subject: gpio-pch: cleanup __iomem annotation usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The __iomem annotation is to be used together with pointers used as iowrite32() parameter. For more details see [1] and [2]. This patch will remove the following sparse warnings ("make C=1"): * warning: incorrect type in assignment (different address spaces) * warning: incorrect type in argument 1 (different address spaces) * warning: incorrect type in argument 2 (different address spaces) References: [1] A new I/O memory access mechanism (Sep 15, 2004) http://lwn.net/Articles/102232/ [2] Being more anal about iospace accesses (Sep 15, 2004) http://lwn.net/Articles/102240/ Signed-off-by: Márton Németh Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 7f008fb..68fa55e 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -231,7 +231,7 @@ static void pch_gpio_setup(struct pch_gpio *chip) static int pch_irq_type(struct irq_data *d, unsigned int type) { u32 im; - u32 *im_reg; + u32 __iomem *im_reg; u32 ien; u32 im_pos; int ch; -- cgit v0.10.2 From 2bd1c85e855c94da564e151cb54553e6cfc2f04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Sun, 15 Jan 2012 10:57:43 +0100 Subject: gpio-ml-ioh: cleanup NULL pointer checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch will remove the following sparse warning ("make C=1"): * warning: Using plain integer as NULL pointer Signed-off-by: Márton Németh Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 461958f..af6d4f3 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -428,7 +428,7 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, } base = pci_iomap(pdev, 1, 0); - if (base == 0) { + if (!base) { dev_err(&pdev->dev, "%s : pci_iomap failed", __func__); ret = -ENOMEM; goto err_iomap; -- cgit v0.10.2 From dd9328a6b026060699d4136d8f926e39281c8a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Sun, 15 Jan 2012 10:57:34 +0100 Subject: gpio-ml-ioh: cleanup __iomem annotation usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The __iomem annotation is to be used together with pointers used in iowrite32() but not for pointers returned by kzalloc(). For more details see [1] and [2]. This patch will remove the following sparse warning (i.e. when copiling with "make C=1"): * warning: incorrect type in assignment (different address spaces) References: [1] A new I/O memory access mechanism (Sep 15, 2004) http://lwn.net/Articles/102232/ [2] Being more anal about iospace accesses (Sep 15, 2004) http://lwn.net/Articles/102240/ Signed-off-by: Márton Németh Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index af6d4f3..03d6dd5 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -248,7 +248,7 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) static int ioh_irq_type(struct irq_data *d, unsigned int type) { u32 im; - u32 *im_reg; + void __iomem *im_reg; u32 ien; u32 im_pos; int ch; @@ -412,7 +412,7 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, int i, j; struct ioh_gpio *chip; void __iomem *base; - void __iomem *chip_save; + void *chip_save; int irq_base; ret = pci_enable_device(pdev); @@ -521,7 +521,7 @@ static void __devexit ioh_gpio_remove(struct pci_dev *pdev) int err; int i; struct ioh_gpio *chip = pci_get_drvdata(pdev); - void __iomem *chip_save; + void *chip_save; chip_save = chip; -- cgit v0.10.2 From c10076c4304083af15a41f6bc5e657e781c1f9a6 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 13 Jan 2012 21:40:59 -0500 Subject: tracepoints/module: Fix disabling tracepoints with taint CRAP or OOT Tracepoints are disabled for tainted modules, which is usually because the module is either proprietary or was forced, and we don't want either of them using kernel tracepoints. But, a module can also be tainted by being in the staging directory or compiled out of tree. Either is fine for use with tracepoints, no need to punish them. I found this out when I noticed that my sample trace event module, when done out of tree, stopped working. Cc: stable@vger.kernel.org # 3.2 Cc: Mathieu Desnoyers Cc: Ben Hutchings Cc: Dave Jones Cc: Greg Kroah-Hartman Cc: Rusty Russell Signed-off-by: Steven Rostedt diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index db110b8..f1539de 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -634,10 +634,11 @@ static int tracepoint_module_coming(struct module *mod) int ret = 0; /* - * We skip modules that tain the kernel, especially those with different - * module header (for forced load), to make sure we don't cause a crash. + * We skip modules that taint the kernel, especially those with different + * module headers (for forced load), to make sure we don't cause a crash. + * Staging and out-of-tree GPL modules are fine. */ - if (mod->taints) + if (mod->taints & ~((1 << TAINT_OOT_MODULE) | (1 << TAINT_CRAP))) return 0; mutex_lock(&tracepoints_mutex); tp_mod = kmalloc(sizeof(struct tp_module), GFP_KERNEL); -- cgit v0.10.2 From d8e8ed95cda1dfd6813588333d36552935eba4a1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 15 Dec 2011 13:43:24 +1030 Subject: rcu: Make rcutorture bool parameters really bool (core code) module_param(bool) used to counter-intuitively take an int. In fddd5201 (mid-2009) we allowed bool or int/unsigned int using a messy trick. It's time to remove the int/unsigned int option. For this version it'll simply give a warning, but it'll break next kernel version. This commit makes this change to rcutorture. Signed-off-by: Rusty Russell Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 88f17b8..e29edc3 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -56,8 +56,8 @@ static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ static int nfakewriters = 4; /* # fake writer threads */ static int stat_interval; /* Interval between stats, in seconds. */ /* Defaults to "only at end of test". */ -static int verbose; /* Print more debug info. */ -static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ +static bool verbose; /* Print more debug info. */ +static bool test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/ static int stutter = 5; /* Start/stop testing interval (in sec) */ static int irqreader = 1; /* RCU readers from irq (timers). */ -- cgit v0.10.2 From 7061ca3b6c99fc78115560b9a10227c8c5fafc45 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 20 Dec 2011 08:20:46 -0800 Subject: sched: Add "const" to is_idle_task() parameter This patch fixes a build warning in -next due to a const pointer being passed to is_idle_task(). Because is_idle_task() does not modify anything, this commit adds the "const" to is_idle_task()'s argument declaration. Reported-by: Stephen Rothwell Signed-off-by: Paul E. McKenney diff --git a/include/linux/sched.h b/include/linux/sched.h index 4a7e4d3..56fa25a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2074,7 +2074,7 @@ extern struct task_struct *idle_task(int cpu); * is_idle_task - is the specified task an idle task? * @tsk: the task in question. */ -static inline bool is_idle_task(struct task_struct *p) +static inline bool is_idle_task(const struct task_struct *p) { return p->pid == 0; } -- cgit v0.10.2 From 4410030646be072b82ec1892ad5cc7d91af384d8 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 27 Dec 2011 15:04:26 +0100 Subject: rcu: Add missing __cpuinit annotation in rcutorture code "rcu: Add rcutorture CPU-hotplug capability" adds cpu hotplug operations to the rcutorture code but produces a false positive warning about section mismatches: WARNING: vmlinux.o(.text+0x1e420c): Section mismatch in reference from the function rcu_torture_onoff() to the function .cpuinit.text:cpu_up() The function rcu_torture_onoff() references the function __cpuinit cpu_up(). This is often because rcu_torture_onoff lacks a __cpuinit annotation or the annotation of cpu_up is wrong. This commit therefore adds a __cpuinit annotation so the warning goes away. Signed-off-by: Heiko Carstens Signed-off-by: Paul E. McKenney diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index e29edc3..a58ac28 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -1399,7 +1399,7 @@ rcu_torture_shutdown(void *arg) * Execute random CPU-hotplug operations at the interval specified * by the onoff_interval. */ -static int +static int __cpuinit rcu_torture_onoff(void *arg) { int cpu; @@ -1447,7 +1447,7 @@ rcu_torture_onoff(void *arg) return 0; } -static int +static int __cpuinit rcu_torture_onoff_init(void) { if (onoff_interval <= 0) -- cgit v0.10.2 From c9678d867216790f961ea06867b3ccbdb3484542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 13 Jan 2012 22:55:05 +0100 Subject: MAINTAINERS: move BCMA to keep the list sorted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville diff --git a/MAINTAINERS b/MAINTAINERS index 1094edf..442d2a7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1587,6 +1587,13 @@ L: linux-scsi@vger.kernel.org S: Supported F: drivers/scsi/bnx2fc/ +BROADCOM SPECIFIC AMBA DRIVER (BCMA) +M: RafaÅ‚ MiÅ‚ecki +L: linux-wireless@vger.kernel.org +S: Maintained +F: drivers/bcma/ +F: include/linux/bcma/ + BROCADE BFA FC SCSI DRIVER M: Jing Huang L: linux-scsi@vger.kernel.org @@ -6099,13 +6106,6 @@ S: Maintained F: drivers/ssb/ F: include/linux/ssb/ -BROADCOM SPECIFIC AMBA DRIVER (BCMA) -M: RafaÅ‚ MiÅ‚ecki -L: linux-wireless@vger.kernel.org -S: Maintained -F: drivers/bcma/ -F: include/linux/bcma/ - SONY VAIO CONTROL DEVICE DRIVER M: Mattia Dongili L: platform-driver-x86@vger.kernel.org -- cgit v0.10.2 From 8675b505a4df40394096948453a17cc5445f6538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 13 Jan 2012 22:55:06 +0100 Subject: MAINTAINERS: add b43 mailing list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville diff --git a/MAINTAINERS b/MAINTAINERS index 442d2a7..6e68daa1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1411,6 +1411,7 @@ F: net/ax25/ B43 WIRELESS DRIVER M: Stefano Brivio L: linux-wireless@vger.kernel.org +L: b43-dev@lists.infradead.org (moderated for non-subscribers) W: http://linuxwireless.org/en/users/Drivers/b43 S: Maintained F: drivers/net/wireless/b43/ -- cgit v0.10.2 From 28e7d218da975f6ae1751e293aed938952c55c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 13 Jan 2012 23:58:38 +0100 Subject: bcma: invalidate the mapped core over suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This clears the currently mapped core when suspending, to force re-mapping after resume. Without that we were touching default core registers believing some other core is mapped. Such a behaviour resulted in lockups on some machines. Cc: stable@vger.kernel.org Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index 443b83a..c1ca9e3 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -237,11 +237,14 @@ static void bcma_host_pci_remove(struct pci_dev *dev) #ifdef CONFIG_PM static int bcma_host_pci_suspend(struct pci_dev *dev, pm_message_t state) { + struct bcma_bus *bus = pci_get_drvdata(dev); + /* Host specific */ pci_save_state(dev); pci_disable_device(dev); pci_set_power_state(dev, pci_choose_state(dev, state)); + bus->mapped_core = NULL; return 0; } -- cgit v0.10.2 From 7a532fe7131216a02c81a6c1b1f8632da1195a58 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 14 Jan 2012 15:08:34 +0100 Subject: ath9k_hw: fix interpretation of the rx KeyMiss flag Documentation states that the KeyMiss flag is only valid if RxFrameOK is unset, however empirical evidence has shown that this is false. When KeyMiss is set (and RxFrameOK is 1), the hardware passes a valid frame which has not been decrypted. The driver then falsely marks the frame as decrypted, and when using CCMP this corrupts the rx CCMP PN, leading to connection hangs. Signed-off-by: Felix Fietkau Cc: stable@kernel.org Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 88c81c5..09b8c9d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -557,10 +557,11 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status |= ATH9K_RXERR_DECRYPT; else if (rxsp->status11 & AR_MichaelErr) rxs->rs_status |= ATH9K_RXERR_MIC; - if (rxsp->status11 & AR_KeyMiss) - rxs->rs_status |= ATH9K_RXERR_KEYMISS; } + if (rxsp->status11 & AR_KeyMiss) + rxs->rs_status |= ATH9K_RXERR_KEYMISS; + return 0; } EXPORT_SYMBOL(ath9k_hw_process_rxdesc_edma); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index fd3f19c..e196aba 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -618,10 +618,11 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status |= ATH9K_RXERR_DECRYPT; else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; - if (ads.ds_rxstatus8 & AR_KeyMiss) - rs->rs_status |= ATH9K_RXERR_KEYMISS; } + if (ads.ds_rxstatus8 & AR_KeyMiss) + rs->rs_status |= ATH9K_RXERR_KEYMISS; + return 0; } EXPORT_SYMBOL(ath9k_hw_rxprocdesc); -- cgit v0.10.2 From 74b8cc3d592e5e884123eb99960a9f064223eaab Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 14 Jan 2012 21:52:17 +0100 Subject: Net, mac80211: Fix resource leak in ieee80211_rx_h_mesh_fwding() We may leak the 'fwd_skb' we skb_copy() in ieee80211_rx_h_mesh_fwding() if we take the 'else' branch in the 'if' statement just below. If we take that branch we'll end up returning from the function and since we've not assigned 'fwd_skb' to anything at that point, we leak it when the variable goes out of scope. The simple fix seems to be to just kfree_skb(fwd_skb); just before we return. That is what this patch does. Signed-off-by: Jesper Juhl Signed-off-by: John W. Linville diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f407427..7514091 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1979,6 +1979,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, 0, reason, fwd_hdr->addr2, sdata); IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); + kfree_skb(fwd_skb); return RX_DROP_MONITOR; } -- cgit v0.10.2 From 331d9301ed4d0fe33e9762c62f2870243bcf77eb Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 16 Jan 2012 00:47:12 -0200 Subject: wireless: iwlwifi: iwl-scan.c: Fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following build warning: drivers/net/wireless/iwlwifi/iwl-scan.c: In function ‘iwlagn_request_scan’: drivers/net/wireless/iwlwifi/iwl-scan.c:572: warning: ‘cmd_len’ may be used uninitialized in this function Signed-off-by: Fabio Estevam Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 084aa2c..a645472 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -569,7 +569,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) struct iwl_scan_cmd *scan; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; u32 rate_flags = 0; - u16 cmd_len; + u16 cmd_len = 0; u16 rx_chain = 0; enum ieee80211_band band; u8 n_probes = 0; -- cgit v0.10.2 From d32a102819eef99d4d87019266bea208447be0a0 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Mon, 16 Jan 2012 15:18:59 +0200 Subject: mac80211: fix tx->skb NULL pointer dereference In function ieee80211_tx_h_encrypt the var info was initialized from tx->skb, since the fucntion is called after the function ieee80211_tx_h_fragment tx->skb is not valid anymore. Signed-off-by: Yoni Divinsky Signed-off-by: John W. Linville diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index edcd1c7..e05667c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1001,8 +1001,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - if (!tx->key) return TX_CONTINUE; @@ -1017,13 +1015,7 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) case WLAN_CIPHER_SUITE_AES_CMAC: return ieee80211_crypto_aes_cmac_encrypt(tx); default: - /* handle hw-only algorithm */ - if (info->control.hw_key) { - ieee80211_tx_set_protected(tx); - return TX_CONTINUE; - } - break; - + return ieee80211_crypto_hw_encrypt(tx); } return TX_DROP; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 422b798..b758350 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -643,3 +643,22 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) return RX_CONTINUE; } + +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) +{ + struct sk_buff *skb; + struct ieee80211_tx_info *info = NULL; + + skb_queue_walk(&tx->skbs, skb) { + info = IEEE80211_SKB_CB(skb); + + /* handle hw-only algorithm */ + if (!info->control.hw_key) + return TX_DROP; + } + + ieee80211_tx_set_protected(tx); + + return TX_CONTINUE; +} diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h index baba060..07e33f8 100644 --- a/net/mac80211/wpa.h +++ b/net/mac80211/wpa.h @@ -32,5 +32,7 @@ ieee80211_tx_result ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx); #endif /* WPA_H */ -- cgit v0.10.2 From 6fef8df1dcb9b586268caff66df1d71ce8610132 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: get rid of *_alloc_profile fields {data,metadata,system}_alloc_profile fields have been unused for a long time now. Get rid of them. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6738503..f5434ad 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1135,9 +1135,6 @@ struct btrfs_fs_info { u64 avail_data_alloc_bits; u64 avail_metadata_alloc_bits; u64 avail_system_alloc_bits; - u64 data_alloc_profile; - u64 metadata_alloc_profile; - u64 system_alloc_profile; unsigned data_chunk_allocations; unsigned metadata_ratio; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 3f9d555..ce9d0fb 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2321,9 +2321,6 @@ retry_root_backup: fs_info->generation = generation; fs_info->last_trans_committed = generation; - fs_info->data_alloc_profile = (u64)-1; - fs_info->metadata_alloc_profile = (u64)-1; - fs_info->system_alloc_profile = fs_info->metadata_alloc_profile; ret = btrfs_init_space_info(fs_info); if (ret) { diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 8603ee4..f0591fd 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3067,14 +3067,12 @@ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) static u64 get_alloc_profile(struct btrfs_root *root, u64 flags) { if (flags & BTRFS_BLOCK_GROUP_DATA) - flags |= root->fs_info->avail_data_alloc_bits & - root->fs_info->data_alloc_profile; + flags |= root->fs_info->avail_data_alloc_bits; else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) - flags |= root->fs_info->avail_system_alloc_bits & - root->fs_info->system_alloc_profile; + flags |= root->fs_info->avail_system_alloc_bits; else if (flags & BTRFS_BLOCK_GROUP_METADATA) - flags |= root->fs_info->avail_metadata_alloc_bits & - root->fs_info->metadata_alloc_profile; + flags |= root->fs_info->avail_metadata_alloc_bits; + return btrfs_reduce_alloc_profile(root, flags); } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f4b839f..89096f6 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2752,8 +2752,7 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans, return ret; alloc_profile = BTRFS_BLOCK_GROUP_METADATA | - (fs_info->metadata_alloc_profile & - fs_info->avail_metadata_alloc_bits); + fs_info->avail_metadata_alloc_bits; alloc_profile = btrfs_reduce_alloc_profile(root, alloc_profile); ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size, @@ -2763,8 +2762,7 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans, sys_chunk_offset = chunk_offset + chunk_size; alloc_profile = BTRFS_BLOCK_GROUP_SYSTEM | - (fs_info->system_alloc_profile & - fs_info->avail_system_alloc_bits); + fs_info->avail_system_alloc_bits; alloc_profile = btrfs_reduce_alloc_profile(root, alloc_profile); ret = __btrfs_alloc_chunk(trans, extent_root, &sys_map, -- cgit v0.10.2 From 52ba692972532f8d652080214b6599ece3dd51b9 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: introduce masks for chunk type and profile Chunk's type and profile are encoded in u64 flags field. Introduce masks to easily access them. Also fix the type of BTRFS_BLOCK_GROUP_* constants, it should be ULL. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index f5434ad..4370a56 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -751,15 +751,23 @@ struct btrfs_csum_item { } __attribute__ ((__packed__)); /* different types of block groups (and chunks) */ -#define BTRFS_BLOCK_GROUP_DATA (1 << 0) -#define BTRFS_BLOCK_GROUP_SYSTEM (1 << 1) -#define BTRFS_BLOCK_GROUP_METADATA (1 << 2) -#define BTRFS_BLOCK_GROUP_RAID0 (1 << 3) -#define BTRFS_BLOCK_GROUP_RAID1 (1 << 4) -#define BTRFS_BLOCK_GROUP_DUP (1 << 5) -#define BTRFS_BLOCK_GROUP_RAID10 (1 << 6) -#define BTRFS_NR_RAID_TYPES 5 - +#define BTRFS_BLOCK_GROUP_DATA (1ULL << 0) +#define BTRFS_BLOCK_GROUP_SYSTEM (1ULL << 1) +#define BTRFS_BLOCK_GROUP_METADATA (1ULL << 2) +#define BTRFS_BLOCK_GROUP_RAID0 (1ULL << 3) +#define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4) +#define BTRFS_BLOCK_GROUP_DUP (1ULL << 5) +#define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6) +#define BTRFS_NR_RAID_TYPES 5 + +#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \ + BTRFS_BLOCK_GROUP_SYSTEM | \ + BTRFS_BLOCK_GROUP_METADATA) + +#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \ + BTRFS_BLOCK_GROUP_RAID1 | \ + BTRFS_BLOCK_GROUP_DUP | \ + BTRFS_BLOCK_GROUP_RAID10) struct btrfs_block_group_item { __le64 used; __le64 chunk_objectid; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f0591fd..a8d8204 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -618,8 +618,7 @@ static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info, struct list_head *head = &info->space_info; struct btrfs_space_info *found; - flags &= BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_SYSTEM | - BTRFS_BLOCK_GROUP_METADATA; + flags &= BTRFS_BLOCK_GROUP_TYPE_MASK; rcu_read_lock(); list_for_each_entry_rcu(found, head, list) { @@ -2993,9 +2992,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, INIT_LIST_HEAD(&found->block_groups[i]); init_rwsem(&found->groups_sem); spin_lock_init(&found->lock); - found->flags = flags & (BTRFS_BLOCK_GROUP_DATA | - BTRFS_BLOCK_GROUP_SYSTEM | - BTRFS_BLOCK_GROUP_METADATA); + found->flags = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; found->total_bytes = total_bytes; found->disk_total = total_bytes * factor; found->bytes_used = bytes_used; @@ -3016,10 +3013,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) { - u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 | - BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_RAID10 | - BTRFS_BLOCK_GROUP_DUP); + u64 extra_flags = flags & BTRFS_BLOCK_GROUP_PROFILE_MASK; if (extra_flags) { if (flags & BTRFS_BLOCK_GROUP_DATA) fs_info->avail_data_alloc_bits |= extra_flags; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 89096f6..d5fdee5 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2949,12 +2949,8 @@ again: } } if (rw & REQ_DISCARD) { - if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | - BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_DUP | - BTRFS_BLOCK_GROUP_RAID10)) { + if (map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK) stripes_required = map->num_stripes; - } } if (bbio_ret && (rw & (REQ_WRITE | REQ_DISCARD)) && stripes_allocated < stripes_required) { @@ -2978,10 +2974,7 @@ again: if (rw & REQ_DISCARD) *length = min_t(u64, em->len - offset, *length); - else if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | - BTRFS_BLOCK_GROUP_RAID1 | - BTRFS_BLOCK_GROUP_RAID10 | - BTRFS_BLOCK_GROUP_DUP)) { + else if (map->type & BTRFS_BLOCK_GROUP_PROFILE_MASK) { /* we limit the length of each bio to what fits in a stripe */ *length = min_t(u64, em->len - offset, map->stripe_len - stripe_offset); -- cgit v0.10.2 From a46d11a8b06dd0431a3888fbc4856ea13a8e634f Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: add BTRFS_AVAIL_ALLOC_BIT_SINGLE bit Right now on-disk BTRFS_BLOCK_GROUP_* profile bits are used for avail_{data,metadata,system}_alloc_bits fields, which gather info about available allocation profiles in the FS. When chunk is created or read from disk, its profile is OR'ed with the corresponding avail_alloc_bits field. Since SINGLE is denoted by 0 in the on-disk format, currently there is no way to tell when such chunks become avaialble. Restriper needs that information, so add a separate bit for SINGLE profile. This bit is going to be in-memory only, it should never be written out to disk, so it's not a disk format change. However to avoid remappings in future, reserve corresponding on-disk bit. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 4370a56..3f8f11e 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -758,6 +758,7 @@ struct btrfs_csum_item { #define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4) #define BTRFS_BLOCK_GROUP_DUP (1ULL << 5) #define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6) +#define BTRFS_BLOCK_GROUP_RESERVED BTRFS_AVAIL_ALLOC_BIT_SINGLE #define BTRFS_NR_RAID_TYPES 5 #define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \ @@ -768,6 +769,15 @@ struct btrfs_csum_item { BTRFS_BLOCK_GROUP_RAID1 | \ BTRFS_BLOCK_GROUP_DUP | \ BTRFS_BLOCK_GROUP_RAID10) +/* + * We need a bit for restriper to be able to tell when chunks of type + * SINGLE are available. This "extended" profile format is used in + * fs_info->avail_*_alloc_bits (in-memory) and balance item fields + * (on-disk). The corresponding on-disk bit in chunk.type is reserved + * to avoid remappings between two formats in future. + */ +#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48) + struct btrfs_block_group_item { __le64 used; __le64 chunk_objectid; @@ -1140,6 +1150,11 @@ struct btrfs_fs_info { spinlock_t ref_cache_lock; u64 total_ref_cache_size; + /* + * these three are in extended format (availability of single + * chunks is denoted by BTRFS_AVAIL_ALLOC_BIT_SINGLE bit, other + * types are denoted by corresponding BTRFS_BLOCK_GROUP_* bits) + */ u64 avail_data_alloc_bits; u64 avail_metadata_alloc_bits; u64 avail_system_alloc_bits; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a8d8204..15a2294 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3014,16 +3014,24 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) { u64 extra_flags = flags & BTRFS_BLOCK_GROUP_PROFILE_MASK; - if (extra_flags) { - if (flags & BTRFS_BLOCK_GROUP_DATA) - fs_info->avail_data_alloc_bits |= extra_flags; - if (flags & BTRFS_BLOCK_GROUP_METADATA) - fs_info->avail_metadata_alloc_bits |= extra_flags; - if (flags & BTRFS_BLOCK_GROUP_SYSTEM) - fs_info->avail_system_alloc_bits |= extra_flags; - } + + /* chunk -> extended profile */ + if (extra_flags == 0) + extra_flags = BTRFS_AVAIL_ALLOC_BIT_SINGLE; + + if (flags & BTRFS_BLOCK_GROUP_DATA) + fs_info->avail_data_alloc_bits |= extra_flags; + if (flags & BTRFS_BLOCK_GROUP_METADATA) + fs_info->avail_metadata_alloc_bits |= extra_flags; + if (flags & BTRFS_BLOCK_GROUP_SYSTEM) + fs_info->avail_system_alloc_bits |= extra_flags; } +/* + * @flags: available profiles in extended format (see ctree.h) + * + * Returns reduced profile in chunk format. + */ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) { /* @@ -3053,8 +3061,12 @@ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) if ((flags & BTRFS_BLOCK_GROUP_RAID0) && ((flags & BTRFS_BLOCK_GROUP_RAID1) | (flags & BTRFS_BLOCK_GROUP_RAID10) | - (flags & BTRFS_BLOCK_GROUP_DUP))) + (flags & BTRFS_BLOCK_GROUP_DUP))) { flags &= ~BTRFS_BLOCK_GROUP_RAID0; + } + + /* extended -> chunk profile */ + flags &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE; return flags; } -- cgit v0.10.2 From 10ea00f55a07f8f9536d9112b95108a86f700bab Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: make avail_*_alloc_bits fields dynamic Currently when new chunks are created respective avail_alloc_bits field is updated to reflect profiles of all chunks present in the system. However when chunks are removed profile bits are never cleared. This patch clears profile bit of respective avail_alloc_bits field when the last chunk with that profile is removed. Restriper needs this to properly operate when "downgrading". Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 15a2294..946b067 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7469,6 +7469,22 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, return 0; } +static void clear_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) +{ + u64 extra_flags = flags & BTRFS_BLOCK_GROUP_PROFILE_MASK; + + /* chunk -> extended profile */ + if (extra_flags == 0) + extra_flags = BTRFS_AVAIL_ALLOC_BIT_SINGLE; + + if (flags & BTRFS_BLOCK_GROUP_DATA) + fs_info->avail_data_alloc_bits &= ~extra_flags; + if (flags & BTRFS_BLOCK_GROUP_METADATA) + fs_info->avail_metadata_alloc_bits &= ~extra_flags; + if (flags & BTRFS_BLOCK_GROUP_SYSTEM) + fs_info->avail_system_alloc_bits &= ~extra_flags; +} + int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 group_start) { @@ -7479,6 +7495,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct btrfs_key key; struct inode *inode; int ret; + int index; int factor; root = root->fs_info->extent_root; @@ -7494,6 +7511,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, free_excluded_extents(root, block_group); memcpy(&key, &block_group->key, sizeof(key)); + index = get_block_group_index(block_group); if (block_group->flags & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10)) @@ -7568,6 +7586,8 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, * are still on the list after taking the semaphore */ list_del_init(&block_group->list); + if (list_empty(&block_group->space_info->block_groups[index])) + clear_avail_alloc_bits(root->fs_info, block_group->flags); up_write(&block_group->space_info->groups_sem); if (block_group->cached == BTRFS_CACHE_STARTED) -- cgit v0.10.2 From c9e9f97bdfb64d06e9520f8e4f37674ac21cc9bc Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: add basic restriper infrastructure Add basic restriper infrastructure: extended balancing ioctl and all related ioctl data structures, add data structure for tracking restriper's state to fs_info, etc. The semantics of the old balancing ioctl are fully preserved. Explicitly disallow any volume operations when balance is in progress. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3f8f11e..c4d98c8 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -934,6 +934,7 @@ struct btrfs_block_group_cache { struct reloc_control; struct btrfs_device; struct btrfs_fs_devices; +struct btrfs_balance_control; struct btrfs_delayed_root; struct btrfs_fs_info { u8 fsid[BTRFS_FSID_SIZE]; @@ -1159,6 +1160,11 @@ struct btrfs_fs_info { u64 avail_metadata_alloc_bits; u64 avail_system_alloc_bits; + /* restriper state */ + spinlock_t balance_lock; + struct mutex balance_mutex; + struct btrfs_balance_control *balance_ctl; + unsigned data_chunk_allocations; unsigned metadata_ratio; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index ce9d0fb..190a1b2 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2002,6 +2002,10 @@ struct btrfs_root *open_ctree(struct super_block *sb, init_rwsem(&fs_info->scrub_super_lock); fs_info->scrub_workers_refcnt = 0; + spin_lock_init(&fs_info->balance_lock); + mutex_init(&fs_info->balance_mutex); + fs_info->balance_ctl = NULL; + sb->s_blocksize = 4096; sb->s_blocksize_bits = blksize_bits(4096); sb->s_bdi = &fs_info->bdi; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c04f02c..d838d2cf 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1203,13 +1203,21 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, if (!capable(CAP_SYS_ADMIN)) return -EPERM; + mutex_lock(&root->fs_info->volume_mutex); + if (root->fs_info->balance_ctl) { + printk(KERN_INFO "btrfs: balance in progress\n"); + ret = -EINVAL; + goto out; + } + vol_args = memdup_user(arg, sizeof(*vol_args)); - if (IS_ERR(vol_args)) - return PTR_ERR(vol_args); + if (IS_ERR(vol_args)) { + ret = PTR_ERR(vol_args); + goto out; + } vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; - mutex_lock(&root->fs_info->volume_mutex); sizestr = vol_args->name; devstr = strchr(sizestr, ':'); if (devstr) { @@ -1226,7 +1234,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, printk(KERN_INFO "btrfs: resizer unable to find device %llu\n", (unsigned long long)devid); ret = -EINVAL; - goto out_unlock; + goto out_free; } if (!strcmp(sizestr, "max")) new_size = device->bdev->bd_inode->i_size; @@ -1241,7 +1249,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, new_size = memparse(sizestr, NULL); if (new_size == 0) { ret = -EINVAL; - goto out_unlock; + goto out_free; } } @@ -1250,7 +1258,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, if (mod < 0) { if (new_size > old_size) { ret = -EINVAL; - goto out_unlock; + goto out_free; } new_size = old_size - new_size; } else if (mod > 0) { @@ -1259,11 +1267,11 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, if (new_size < 256 * 1024 * 1024) { ret = -EINVAL; - goto out_unlock; + goto out_free; } if (new_size > device->bdev->bd_inode->i_size) { ret = -EFBIG; - goto out_unlock; + goto out_free; } do_div(new_size, root->sectorsize); @@ -1276,7 +1284,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); - goto out_unlock; + goto out_free; } ret = btrfs_grow_device(trans, device, new_size); btrfs_commit_transaction(trans, root); @@ -1284,9 +1292,10 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, ret = btrfs_shrink_device(device, new_size); } -out_unlock: - mutex_unlock(&root->fs_info->volume_mutex); +out_free: kfree(vol_args); +out: + mutex_unlock(&root->fs_info->volume_mutex); return ret; } @@ -2052,14 +2061,25 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + mutex_lock(&root->fs_info->volume_mutex); + if (root->fs_info->balance_ctl) { + printk(KERN_INFO "btrfs: balance in progress\n"); + ret = -EINVAL; + goto out; + } + vol_args = memdup_user(arg, sizeof(*vol_args)); - if (IS_ERR(vol_args)) - return PTR_ERR(vol_args); + if (IS_ERR(vol_args)) { + ret = PTR_ERR(vol_args); + goto out; + } vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; ret = btrfs_init_new_device(root, vol_args->name); kfree(vol_args); +out: + mutex_unlock(&root->fs_info->volume_mutex); return ret; } @@ -2074,14 +2094,25 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg) if (root->fs_info->sb->s_flags & MS_RDONLY) return -EROFS; + mutex_lock(&root->fs_info->volume_mutex); + if (root->fs_info->balance_ctl) { + printk(KERN_INFO "btrfs: balance in progress\n"); + ret = -EINVAL; + goto out; + } + vol_args = memdup_user(arg, sizeof(*vol_args)); - if (IS_ERR(vol_args)) - return PTR_ERR(vol_args); + if (IS_ERR(vol_args)) { + ret = PTR_ERR(vol_args); + goto out; + } vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; ret = btrfs_rm_device(root, vol_args->name); kfree(vol_args); +out: + mutex_unlock(&root->fs_info->volume_mutex); return ret; } @@ -3034,6 +3065,76 @@ out: return ret; } +void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_balance_args *bargs) +{ + struct btrfs_balance_control *bctl = fs_info->balance_ctl; + + bargs->flags = bctl->flags; + + memcpy(&bargs->data, &bctl->data, sizeof(bargs->data)); + memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); + memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys)); +} + +static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_ioctl_balance_args *bargs; + struct btrfs_balance_control *bctl; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (fs_info->sb->s_flags & MS_RDONLY) + return -EROFS; + + mutex_lock(&fs_info->volume_mutex); + mutex_lock(&fs_info->balance_mutex); + + if (arg) { + bargs = memdup_user(arg, sizeof(*bargs)); + if (IS_ERR(bargs)) { + ret = PTR_ERR(bargs); + goto out; + } + } else { + bargs = NULL; + } + + bctl = kzalloc(sizeof(*bctl), GFP_NOFS); + if (!bctl) { + ret = -ENOMEM; + goto out_bargs; + } + + bctl->fs_info = fs_info; + if (arg) { + memcpy(&bctl->data, &bargs->data, sizeof(bctl->data)); + memcpy(&bctl->meta, &bargs->meta, sizeof(bctl->meta)); + memcpy(&bctl->sys, &bargs->sys, sizeof(bctl->sys)); + + bctl->flags = bargs->flags; + } + + ret = btrfs_balance(bctl, bargs); + /* + * bctl is freed in __cancel_balance + */ + if (arg) { + if (copy_to_user(arg, bargs, sizeof(*bargs))) + ret = -EFAULT; + } + +out_bargs: + kfree(bargs); +out: + mutex_unlock(&fs_info->balance_mutex); + mutex_unlock(&fs_info->volume_mutex); + return ret; +} + long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -3078,7 +3179,7 @@ long btrfs_ioctl(struct file *file, unsigned int case BTRFS_IOC_DEV_INFO: return btrfs_ioctl_dev_info(root, argp); case BTRFS_IOC_BALANCE: - return btrfs_balance(root->fs_info->dev_root); + return btrfs_ioctl_balance(root, NULL); case BTRFS_IOC_CLONE: return btrfs_ioctl_clone(file, arg, 0, 0, 0); case BTRFS_IOC_CLONE_RANGE: @@ -3110,6 +3211,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_scrub_cancel(root, argp); case BTRFS_IOC_SCRUB_PROGRESS: return btrfs_ioctl_scrub_progress(root, argp); + case BTRFS_IOC_BALANCE_V2: + return btrfs_ioctl_balance(root, argp); } return -ENOTTY; diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index 252ae99..c8b37d2 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -109,6 +109,47 @@ struct btrfs_ioctl_fs_info_args { __u64 reserved[124]; /* pad to 1k */ }; +/* + * this is packed, because it should be exactly the same as its disk + * byte order counterpart (struct btrfs_disk_balance_args) + */ +struct btrfs_balance_args { + __u64 profiles; + __u64 usage; + __u64 devid; + __u64 pstart; + __u64 pend; + __u64 vstart; + __u64 vend; + + __u64 target; + + __u64 flags; + + __u64 unused[8]; +} __attribute__ ((__packed__)); + +/* report balance progress to userspace */ +struct btrfs_balance_progress { + __u64 expected; /* estimated # of chunks that will be + * relocated to fulfill the request */ + __u64 considered; /* # of chunks we have considered so far */ + __u64 completed; /* # of chunks relocated so far */ +}; + +struct btrfs_ioctl_balance_args { + __u64 flags; /* in/out */ + __u64 state; /* out */ + + struct btrfs_balance_args data; /* in/out */ + struct btrfs_balance_args meta; /* in/out */ + struct btrfs_balance_args sys; /* in/out */ + + struct btrfs_balance_progress stat; /* out */ + + __u64 unused[72]; /* pad to 1k */ +}; + #define BTRFS_INO_LOOKUP_PATH_MAX 4080 struct btrfs_ioctl_ino_lookup_args { __u64 treeid; @@ -272,6 +313,8 @@ struct btrfs_ioctl_logical_ino_args { struct btrfs_ioctl_dev_info_args) #define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ struct btrfs_ioctl_fs_info_args) +#define BTRFS_IOC_BALANCE_V2 _IOWR(BTRFS_IOCTL_MAGIC, 32, \ + struct btrfs_ioctl_balance_args) #define BTRFS_IOC_INO_PATHS _IOWR(BTRFS_IOCTL_MAGIC, 35, \ struct btrfs_ioctl_ino_path_args) #define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d5fdee5..9fc06e6 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1282,7 +1282,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) bool clear_super = false; mutex_lock(&uuid_mutex); - mutex_lock(&root->fs_info->volume_mutex); all_avail = root->fs_info->avail_data_alloc_bits | root->fs_info->avail_system_alloc_bits | @@ -1452,7 +1451,6 @@ error_close: if (bdev) blkdev_put(bdev, FMODE_READ | FMODE_EXCL); out: - mutex_unlock(&root->fs_info->volume_mutex); mutex_unlock(&uuid_mutex); return ret; error_undo: @@ -1629,7 +1627,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) } filemap_write_and_wait(bdev->bd_inode->i_mapping); - mutex_lock(&root->fs_info->volume_mutex); devices = &root->fs_info->fs_devices->devices; /* @@ -1757,8 +1754,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ret = btrfs_relocate_sys_chunks(root); BUG_ON(ret); } -out: - mutex_unlock(&root->fs_info->volume_mutex); + return ret; error: blkdev_put(bdev, FMODE_EXCL); @@ -1766,7 +1762,7 @@ error: mutex_unlock(&uuid_mutex); up_write(&sb->s_umount); } - goto out; + return ret; } static noinline int btrfs_update_device(struct btrfs_trans_handle *trans, @@ -2077,6 +2073,35 @@ error: return ret; } +/* + * Should be called with both balance and volume mutexes held to + * serialize other volume operations (add_dev/rm_dev/resize) with + * restriper. Same goes for unset_balance_control. + */ +static void set_balance_control(struct btrfs_balance_control *bctl) +{ + struct btrfs_fs_info *fs_info = bctl->fs_info; + + BUG_ON(fs_info->balance_ctl); + + spin_lock(&fs_info->balance_lock); + fs_info->balance_ctl = bctl; + spin_unlock(&fs_info->balance_lock); +} + +static void unset_balance_control(struct btrfs_fs_info *fs_info) +{ + struct btrfs_balance_control *bctl = fs_info->balance_ctl; + + BUG_ON(!fs_info->balance_ctl); + + spin_lock(&fs_info->balance_lock); + fs_info->balance_ctl = NULL; + spin_unlock(&fs_info->balance_lock); + + kfree(bctl); +} + static u64 div_factor(u64 num, int factor) { if (factor == 10) @@ -2086,29 +2111,23 @@ static u64 div_factor(u64 num, int factor) return num; } -int btrfs_balance(struct btrfs_root *dev_root) +static int __btrfs_balance(struct btrfs_fs_info *fs_info) { - int ret; - struct list_head *devices = &dev_root->fs_info->fs_devices->devices; + struct btrfs_root *chunk_root = fs_info->chunk_root; + struct btrfs_root *dev_root = fs_info->dev_root; + struct list_head *devices; struct btrfs_device *device; u64 old_size; u64 size_to_free; struct btrfs_path *path; struct btrfs_key key; - struct btrfs_root *chunk_root = dev_root->fs_info->chunk_root; - struct btrfs_trans_handle *trans; struct btrfs_key found_key; - - if (dev_root->fs_info->sb->s_flags & MS_RDONLY) - return -EROFS; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - mutex_lock(&dev_root->fs_info->volume_mutex); - dev_root = dev_root->fs_info->dev_root; + struct btrfs_trans_handle *trans; + int ret; + int enospc_errors = 0; /* step one make some room on all the devices */ + devices = &fs_info->fs_devices->devices; list_for_each_entry(device, devices, dev_list) { old_size = device->total_bytes; size_to_free = div_factor(old_size, 1); @@ -2151,12 +2170,14 @@ int btrfs_balance(struct btrfs_root *dev_root) * failed */ if (ret == 0) - break; + BUG(); /* FIXME break ? */ ret = btrfs_previous_item(chunk_root, path, 0, BTRFS_CHUNK_ITEM_KEY); - if (ret) + if (ret) { + ret = 0; break; + } btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); @@ -2174,12 +2195,63 @@ int btrfs_balance(struct btrfs_root *dev_root) found_key.offset); if (ret && ret != -ENOSPC) goto error; + if (ret == -ENOSPC) + enospc_errors++; key.offset = found_key.offset - 1; } - ret = 0; + error: btrfs_free_path(path); - mutex_unlock(&dev_root->fs_info->volume_mutex); + if (enospc_errors) { + printk(KERN_INFO "btrfs: %d enospc errors during balance\n", + enospc_errors); + if (!ret) + ret = -ENOSPC; + } + + return ret; +} + +static void __cancel_balance(struct btrfs_fs_info *fs_info) +{ + unset_balance_control(fs_info); +} + +void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, + struct btrfs_ioctl_balance_args *bargs); + +/* + * Should be called with both balance and volume mutexes held + */ +int btrfs_balance(struct btrfs_balance_control *bctl, + struct btrfs_ioctl_balance_args *bargs) +{ + struct btrfs_fs_info *fs_info = bctl->fs_info; + int ret; + + if (btrfs_fs_closing(fs_info)) { + ret = -EINVAL; + goto out; + } + + set_balance_control(bctl); + + mutex_unlock(&fs_info->balance_mutex); + + ret = __btrfs_balance(fs_info); + + mutex_lock(&fs_info->balance_mutex); + + if (bargs) { + memset(bargs, 0, sizeof(*bargs)); + update_ioctl_balance_args(fs_info, bargs); + } + + __cancel_balance(fs_info); + + return ret; +out: + kfree(bctl); return ret; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 78f2d4d..8825823 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -186,6 +186,17 @@ struct map_lookup { #define map_lookup_size(n) (sizeof(struct map_lookup) + \ (sizeof(struct btrfs_bio_stripe) * (n))) +struct btrfs_balance_args; +struct btrfs_balance_control { + struct btrfs_fs_info *fs_info; + + struct btrfs_balance_args data; + struct btrfs_balance_args meta; + struct btrfs_balance_args sys; + + u64 flags; +}; + int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start, u64 end, u64 *length); @@ -228,7 +239,8 @@ struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid, u8 *uuid, u8 *fsid); int btrfs_shrink_device(struct btrfs_device *device, u64 new_size); int btrfs_init_new_device(struct btrfs_root *root, char *path); -int btrfs_balance(struct btrfs_root *dev_root); +int btrfs_balance(struct btrfs_balance_control *bctl, + struct btrfs_ioctl_balance_args *bargs); int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); int find_free_dev_extent(struct btrfs_trans_handle *trans, struct btrfs_device *device, u64 num_bytes, -- cgit v0.10.2 From f43ffb60fd94e98be02780944e182ade6653b916 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: add basic infrastructure for selective balancing This allows to have a separate set of filters for each chunk type (data,meta,sys). The code however is generic and switch on chunk type is only done once. This commit also adds a type filter: it allows to balance for example meta and system chunks w/o touching data ones. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index d838d2cf..29b3a94 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3116,6 +3116,9 @@ static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) memcpy(&bctl->sys, &bargs->sys, sizeof(bctl->sys)); bctl->flags = bargs->flags; + } else { + /* balance everything - no filters */ + bctl->flags |= BTRFS_BALANCE_TYPE_MASK; } ret = btrfs_balance(bctl, bargs); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9fc06e6..91bbf6e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2102,6 +2102,30 @@ static void unset_balance_control(struct btrfs_fs_info *fs_info) kfree(bctl); } +static int should_balance_chunk(struct btrfs_root *root, + struct extent_buffer *leaf, + struct btrfs_chunk *chunk, u64 chunk_offset) +{ + struct btrfs_balance_control *bctl = root->fs_info->balance_ctl; + struct btrfs_balance_args *bargs = NULL; + u64 chunk_type = btrfs_chunk_type(leaf, chunk); + + /* type filter */ + if (!((chunk_type & BTRFS_BLOCK_GROUP_TYPE_MASK) & + (bctl->flags & BTRFS_BALANCE_TYPE_MASK))) { + return 0; + } + + if (chunk_type & BTRFS_BLOCK_GROUP_DATA) + bargs = &bctl->data; + else if (chunk_type & BTRFS_BLOCK_GROUP_SYSTEM) + bargs = &bctl->sys; + else if (chunk_type & BTRFS_BLOCK_GROUP_METADATA) + bargs = &bctl->meta; + + return 1; +} + static u64 div_factor(u64 num, int factor) { if (factor == 10) @@ -2119,10 +2143,13 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) struct btrfs_device *device; u64 old_size; u64 size_to_free; + struct btrfs_chunk *chunk; struct btrfs_path *path; struct btrfs_key key; struct btrfs_key found_key; struct btrfs_trans_handle *trans; + struct extent_buffer *leaf; + int slot; int ret; int enospc_errors = 0; @@ -2179,8 +2206,10 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) break; } - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &found_key, slot); + if (found_key.objectid != key.objectid) break; @@ -2188,7 +2217,14 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) if (found_key.offset == 0) break; + chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); + + ret = should_balance_chunk(chunk_root, leaf, chunk, + found_key.offset); btrfs_release_path(path); + if (!ret) + goto loop; + ret = btrfs_relocate_chunk(chunk_root, chunk_root->root_key.objectid, found_key.objectid, @@ -2197,6 +2233,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) goto error; if (ret == -ENOSPC) enospc_errors++; +loop: key.offset = found_key.offset - 1; } @@ -2227,6 +2264,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, struct btrfs_ioctl_balance_args *bargs) { struct btrfs_fs_info *fs_info = bctl->fs_info; + u64 allowed; int ret; if (btrfs_fs_closing(fs_info)) { @@ -2234,6 +2272,23 @@ int btrfs_balance(struct btrfs_balance_control *bctl, goto out; } + /* + * In case of mixed groups both data and meta should be picked, + * and identical options should be given for both of them. + */ + allowed = btrfs_super_incompat_flags(fs_info->super_copy); + if ((allowed & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) && + (bctl->flags & (BTRFS_BALANCE_DATA | BTRFS_BALANCE_METADATA))) { + if (!(bctl->flags & BTRFS_BALANCE_DATA) || + !(bctl->flags & BTRFS_BALANCE_METADATA) || + memcmp(&bctl->data, &bctl->meta, sizeof(bctl->data))) { + printk(KERN_ERR "btrfs: with mixed groups data and " + "metadata balance options must be the same\n"); + ret = -EINVAL; + goto out; + } + } + set_balance_control(bctl); mutex_unlock(&fs_info->balance_mutex); diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 8825823..003e542 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -186,6 +186,17 @@ struct map_lookup { #define map_lookup_size(n) (sizeof(struct map_lookup) + \ (sizeof(struct btrfs_bio_stripe) * (n))) +/* + * Restriper's general type filter + */ +#define BTRFS_BALANCE_DATA (1ULL << 0) +#define BTRFS_BALANCE_SYSTEM (1ULL << 1) +#define BTRFS_BALANCE_METADATA (1ULL << 2) + +#define BTRFS_BALANCE_TYPE_MASK (BTRFS_BALANCE_DATA | \ + BTRFS_BALANCE_SYSTEM | \ + BTRFS_BALANCE_METADATA) + struct btrfs_balance_args; struct btrfs_balance_control { struct btrfs_fs_info *fs_info; -- cgit v0.10.2 From ed25e9b26f898d8d63ae4a836489f1923534143b Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: profiles filter Select chunks based on a given profile mask. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 91bbf6e..447bd42 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2102,6 +2102,24 @@ static void unset_balance_control(struct btrfs_fs_info *fs_info) kfree(bctl); } +/* + * Balance filters. Return 1 if chunk should be filtered out + * (should not be balanced). + */ +static int chunk_profiles_filter(u64 chunk_profile, + struct btrfs_balance_args *bargs) +{ + chunk_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK; + + if (chunk_profile == 0) + chunk_profile = BTRFS_AVAIL_ALLOC_BIT_SINGLE; + + if (bargs->profiles & chunk_profile) + return 0; + + return 1; +} + static int should_balance_chunk(struct btrfs_root *root, struct extent_buffer *leaf, struct btrfs_chunk *chunk, u64 chunk_offset) @@ -2123,6 +2141,12 @@ static int should_balance_chunk(struct btrfs_root *root, else if (chunk_type & BTRFS_BLOCK_GROUP_METADATA) bargs = &bctl->meta; + /* profiles filter */ + if ((bargs->flags & BTRFS_BALANCE_ARGS_PROFILES) && + chunk_profiles_filter(chunk_type, bargs)) { + return 0; + } + return 1; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 003e542..fb20d77 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -196,6 +196,10 @@ struct map_lookup { #define BTRFS_BALANCE_TYPE_MASK (BTRFS_BALANCE_DATA | \ BTRFS_BALANCE_SYSTEM | \ BTRFS_BALANCE_METADATA) +/* + * Balance filters + */ +#define BTRFS_BALANCE_ARGS_PROFILES (1ULL << 0) struct btrfs_balance_args; struct btrfs_balance_control { -- cgit v0.10.2 From 5ce5b3c0916ba3a2e34cf648b94044adc5ef9e76 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: usage filter Select chunks that are less than X percent full. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 447bd42..b858242 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2120,6 +2120,36 @@ static int chunk_profiles_filter(u64 chunk_profile, return 1; } +static u64 div_factor_fine(u64 num, int factor) +{ + if (factor <= 0) + return 0; + if (factor >= 100) + return num; + + num *= factor; + do_div(num, 100); + return num; +} + +static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset, + struct btrfs_balance_args *bargs) +{ + struct btrfs_block_group_cache *cache; + u64 chunk_used, user_thresh; + int ret = 1; + + cache = btrfs_lookup_block_group(fs_info, chunk_offset); + chunk_used = btrfs_block_group_used(&cache->item); + + user_thresh = div_factor_fine(cache->key.offset, bargs->usage); + if (chunk_used < user_thresh) + ret = 0; + + btrfs_put_block_group(cache); + return ret; +} + static int should_balance_chunk(struct btrfs_root *root, struct extent_buffer *leaf, struct btrfs_chunk *chunk, u64 chunk_offset) @@ -2147,6 +2177,12 @@ static int should_balance_chunk(struct btrfs_root *root, return 0; } + /* usage filter */ + if ((bargs->flags & BTRFS_BALANCE_ARGS_USAGE) && + chunk_usage_filter(bctl->fs_info, chunk_offset, bargs)) { + return 0; + } + return 1; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index fb20d77..eee77fc 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -200,6 +200,7 @@ struct map_lookup { * Balance filters */ #define BTRFS_BALANCE_ARGS_PROFILES (1ULL << 0) +#define BTRFS_BALANCE_ARGS_USAGE (1ULL << 1) struct btrfs_balance_args; struct btrfs_balance_control { -- cgit v0.10.2 From 409d404b461afa9738619f249fd7f62a366b68c2 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:47 +0200 Subject: Btrfs: devid filter Relocate chunks which have at least one stripe located on a device with devid X. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index b858242..9ff5cd0 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2150,6 +2150,23 @@ static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset, return ret; } +static int chunk_devid_filter(struct extent_buffer *leaf, + struct btrfs_chunk *chunk, + struct btrfs_balance_args *bargs) +{ + struct btrfs_stripe *stripe; + int num_stripes = btrfs_chunk_num_stripes(leaf, chunk); + int i; + + for (i = 0; i < num_stripes; i++) { + stripe = btrfs_stripe_nr(chunk, i); + if (btrfs_stripe_devid(leaf, stripe) == bargs->devid) + return 0; + } + + return 1; +} + static int should_balance_chunk(struct btrfs_root *root, struct extent_buffer *leaf, struct btrfs_chunk *chunk, u64 chunk_offset) @@ -2183,6 +2200,12 @@ static int should_balance_chunk(struct btrfs_root *root, return 0; } + /* devid filter */ + if ((bargs->flags & BTRFS_BALANCE_ARGS_DEVID) && + chunk_devid_filter(leaf, chunk, bargs)) { + return 0; + } + return 1; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index eee77fc..7cfec03 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -201,6 +201,7 @@ struct map_lookup { */ #define BTRFS_BALANCE_ARGS_PROFILES (1ULL << 0) #define BTRFS_BALANCE_ARGS_USAGE (1ULL << 1) +#define BTRFS_BALANCE_ARGS_DEVID (1ULL << 2) struct btrfs_balance_args; struct btrfs_balance_control { -- cgit v0.10.2 From 94e60d5a5c4b98a32b1077dec88df09ada712376 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: devid subset filter Select chunks which have at least one byte of at least one stripe located on a device with devid X in a given [pstart,pend) physical address range. This filter only works when devid filter is turned on. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9ff5cd0..c60071a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2167,6 +2167,46 @@ static int chunk_devid_filter(struct extent_buffer *leaf, return 1; } +/* [pstart, pend) */ +static int chunk_drange_filter(struct extent_buffer *leaf, + struct btrfs_chunk *chunk, + u64 chunk_offset, + struct btrfs_balance_args *bargs) +{ + struct btrfs_stripe *stripe; + int num_stripes = btrfs_chunk_num_stripes(leaf, chunk); + u64 stripe_offset; + u64 stripe_length; + int factor; + int i; + + if (!(bargs->flags & BTRFS_BALANCE_ARGS_DEVID)) + return 0; + + if (btrfs_chunk_type(leaf, chunk) & (BTRFS_BLOCK_GROUP_DUP | + BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10)) + factor = 2; + else + factor = 1; + factor = num_stripes / factor; + + for (i = 0; i < num_stripes; i++) { + stripe = btrfs_stripe_nr(chunk, i); + if (btrfs_stripe_devid(leaf, stripe) != bargs->devid) + continue; + + stripe_offset = btrfs_stripe_offset(leaf, stripe); + stripe_length = btrfs_chunk_length(leaf, chunk); + do_div(stripe_length, factor); + + if (stripe_offset < bargs->pend && + stripe_offset + stripe_length > bargs->pstart) + return 0; + } + + return 1; +} + static int should_balance_chunk(struct btrfs_root *root, struct extent_buffer *leaf, struct btrfs_chunk *chunk, u64 chunk_offset) @@ -2206,6 +2246,12 @@ static int should_balance_chunk(struct btrfs_root *root, return 0; } + /* drange filter, makes sense only with devid filter */ + if ((bargs->flags & BTRFS_BALANCE_ARGS_DRANGE) && + chunk_drange_filter(leaf, chunk, chunk_offset, bargs)) { + return 0; + } + return 1; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 7cfec03..844b08e 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -202,6 +202,7 @@ struct map_lookup { #define BTRFS_BALANCE_ARGS_PROFILES (1ULL << 0) #define BTRFS_BALANCE_ARGS_USAGE (1ULL << 1) #define BTRFS_BALANCE_ARGS_DEVID (1ULL << 2) +#define BTRFS_BALANCE_ARGS_DRANGE (1ULL << 3) struct btrfs_balance_args; struct btrfs_balance_control { -- cgit v0.10.2 From ea67176ae8c024f64d85ec33873e5eadf1af7247 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: virtual address space subset filter Select chunks which have at least one byte located inside a given [vstart, vend) virtual address space range. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c60071a..e86c9e4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2207,6 +2207,20 @@ static int chunk_drange_filter(struct extent_buffer *leaf, return 1; } +/* [vstart, vend) */ +static int chunk_vrange_filter(struct extent_buffer *leaf, + struct btrfs_chunk *chunk, + u64 chunk_offset, + struct btrfs_balance_args *bargs) +{ + if (chunk_offset < bargs->vend && + chunk_offset + btrfs_chunk_length(leaf, chunk) > bargs->vstart) + /* at least part of the chunk is inside this vrange */ + return 0; + + return 1; +} + static int should_balance_chunk(struct btrfs_root *root, struct extent_buffer *leaf, struct btrfs_chunk *chunk, u64 chunk_offset) @@ -2252,6 +2266,12 @@ static int should_balance_chunk(struct btrfs_root *root, return 0; } + /* vrange filter */ + if ((bargs->flags & BTRFS_BALANCE_ARGS_VRANGE) && + chunk_vrange_filter(leaf, chunk, chunk_offset, bargs)) { + return 0; + } + return 1; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 844b08e..eac26c3 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -203,6 +203,7 @@ struct map_lookup { #define BTRFS_BALANCE_ARGS_USAGE (1ULL << 1) #define BTRFS_BALANCE_ARGS_DEVID (1ULL << 2) #define BTRFS_BALANCE_ARGS_DRANGE (1ULL << 3) +#define BTRFS_BALANCE_ARGS_VRANGE (1ULL << 4) struct btrfs_balance_args; struct btrfs_balance_control { -- cgit v0.10.2 From 70922617b0099f420deceb53d5dc7f4fb30d08d0 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: do not reduce profile in do_chunk_alloc() Every caller of do_chunk_alloc() feeds it the reduced allocation profile, so stop trying to reduce it one more time. Instead check the validity of the passed profile. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index c4d98c8..1e7aea6 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2536,6 +2536,24 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info) kfree(fs_info->super_for_commit); kfree(fs_info); } +/** + * profile_is_valid - tests whether a given profile is valid and reduced + * @flags: profile to validate + * @extended: if true @flags is treated as an extended profile + */ +static inline int profile_is_valid(u64 flags, int extended) +{ + u64 mask = ~BTRFS_BLOCK_GROUP_PROFILE_MASK; + + flags &= ~BTRFS_BLOCK_GROUP_TYPE_MASK; + if (extended) + mask &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE; + + if (flags & mask) + return 0; + /* true if zero or exactly one bit set */ + return (flags & (~flags + 1)) == flags; +} /* root-item.c */ int btrfs_find_root_ref(struct btrfs_root *tree_root, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 946b067..a1a18ea 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3295,7 +3295,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, int wait_for_alloc = 0; int ret = 0; - flags = btrfs_reduce_alloc_profile(extent_root, flags); + BUG_ON(!profile_is_valid(flags, 0)); space_info = __find_space_info(extent_root->fs_info, flags); if (!space_info) { -- cgit v0.10.2 From e4d8ec0f65b91bfb4984a4927632ded95f9825ad Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: implement online profile changing Profile changing is done by launching a balance with BTRFS_BALANCE_CONVERT bits set and target fields of respective btrfs_balance_args structs initialized. Profile reducing code in this case will pick restriper's target profile if it's available instead of doing a blind reduce. If target profile is not yet available it goes back to a plain reduce. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a1a18ea..e6a832e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3030,7 +3030,9 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) /* * @flags: available profiles in extended format (see ctree.h) * - * Returns reduced profile in chunk format. + * Returns reduced profile in chunk format. If profile changing is in + * progress (either running or paused) picks the target profile (if it's + * already available), otherwise falls back to plain reducing. */ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) { @@ -3042,6 +3044,34 @@ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) u64 num_devices = root->fs_info->fs_devices->rw_devices + root->fs_info->fs_devices->missing_devices; + /* pick restriper's target profile if it's available */ + spin_lock(&root->fs_info->balance_lock); + if (root->fs_info->balance_ctl) { + struct btrfs_balance_control *bctl = root->fs_info->balance_ctl; + u64 tgt = 0; + + if ((flags & BTRFS_BLOCK_GROUP_DATA) && + (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) && + (flags & bctl->data.target)) { + tgt = BTRFS_BLOCK_GROUP_DATA | bctl->data.target; + } else if ((flags & BTRFS_BLOCK_GROUP_SYSTEM) && + (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) && + (flags & bctl->sys.target)) { + tgt = BTRFS_BLOCK_GROUP_SYSTEM | bctl->sys.target; + } else if ((flags & BTRFS_BLOCK_GROUP_METADATA) && + (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) && + (flags & bctl->meta.target)) { + tgt = BTRFS_BLOCK_GROUP_METADATA | bctl->meta.target; + } + + if (tgt) { + spin_unlock(&root->fs_info->balance_lock); + flags = tgt; + goto out; + } + } + spin_unlock(&root->fs_info->balance_lock); + if (num_devices == 1) flags &= ~(BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID0); if (num_devices < 4) @@ -3065,6 +3095,7 @@ u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags) flags &= ~BTRFS_BLOCK_GROUP_RAID0; } +out: /* extended -> chunk profile */ flags &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE; return flags; @@ -6795,6 +6826,29 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags) u64 stripped = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10; + if (root->fs_info->balance_ctl) { + struct btrfs_balance_control *bctl = root->fs_info->balance_ctl; + u64 tgt = 0; + + /* pick restriper's target profile and return */ + if (flags & BTRFS_BLOCK_GROUP_DATA && + bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) { + tgt = BTRFS_BLOCK_GROUP_DATA | bctl->data.target; + } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM && + bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) { + tgt = BTRFS_BLOCK_GROUP_SYSTEM | bctl->sys.target; + } else if (flags & BTRFS_BLOCK_GROUP_METADATA && + bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) { + tgt = BTRFS_BLOCK_GROUP_METADATA | bctl->meta.target; + } + + if (tgt) { + /* extended -> chunk profile */ + tgt &= ~BTRFS_AVAIL_ALLOC_BIT_SINGLE; + return tgt; + } + } + /* * we add in the count of missing devices because we want * to make sure that any RAID levels on a degraded FS diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index e86c9e4..f08210e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2438,6 +2438,75 @@ int btrfs_balance(struct btrfs_balance_control *bctl, } } + /* + * Profile changing sanity checks. Skip them if a simple + * balance is requested. + */ + if (!((bctl->data.flags | bctl->sys.flags | bctl->meta.flags) & + BTRFS_BALANCE_ARGS_CONVERT)) + goto do_balance; + + allowed = BTRFS_AVAIL_ALLOC_BIT_SINGLE; + if (fs_info->fs_devices->num_devices == 1) + allowed |= BTRFS_BLOCK_GROUP_DUP; + else if (fs_info->fs_devices->num_devices < 4) + allowed |= (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1); + else + allowed |= (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_RAID10); + + if (!profile_is_valid(bctl->data.target, 1) || + bctl->data.target & ~allowed) { + printk(KERN_ERR "btrfs: unable to start balance with target " + "data profile %llu\n", + (unsigned long long)bctl->data.target); + ret = -EINVAL; + goto out; + } + if (!profile_is_valid(bctl->meta.target, 1) || + bctl->meta.target & ~allowed) { + printk(KERN_ERR "btrfs: unable to start balance with target " + "metadata profile %llu\n", + (unsigned long long)bctl->meta.target); + ret = -EINVAL; + goto out; + } + if (!profile_is_valid(bctl->sys.target, 1) || + bctl->sys.target & ~allowed) { + printk(KERN_ERR "btrfs: unable to start balance with target " + "system profile %llu\n", + (unsigned long long)bctl->sys.target); + ret = -EINVAL; + goto out; + } + + if (bctl->data.target & BTRFS_BLOCK_GROUP_DUP) { + printk(KERN_ERR "btrfs: dup for data is not allowed\n"); + ret = -EINVAL; + goto out; + } + + /* allow to reduce meta or sys integrity only if force set */ + allowed = BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1 | + BTRFS_BLOCK_GROUP_RAID10; + if (((bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) && + (fs_info->avail_system_alloc_bits & allowed) && + !(bctl->sys.target & allowed)) || + ((bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) && + (fs_info->avail_metadata_alloc_bits & allowed) && + !(bctl->meta.target & allowed))) { + if (bctl->flags & BTRFS_BALANCE_FORCE) { + printk(KERN_INFO "btrfs: force reducing metadata " + "integrity\n"); + } else { + printk(KERN_ERR "btrfs: balance will reduce metadata " + "integrity, use force if you want this\n"); + ret = -EINVAL; + goto out; + } + } + +do_balance: set_balance_control(bctl); mutex_unlock(&fs_info->balance_mutex); diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index eac26c3..79ee9c3 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -196,6 +196,9 @@ struct map_lookup { #define BTRFS_BALANCE_TYPE_MASK (BTRFS_BALANCE_DATA | \ BTRFS_BALANCE_SYSTEM | \ BTRFS_BALANCE_METADATA) + +#define BTRFS_BALANCE_FORCE (1ULL << 3) + /* * Balance filters */ @@ -205,6 +208,8 @@ struct map_lookup { #define BTRFS_BALANCE_ARGS_DRANGE (1ULL << 3) #define BTRFS_BALANCE_ARGS_VRANGE (1ULL << 4) +#define BTRFS_BALANCE_ARGS_CONVERT (1ULL << 8) + struct btrfs_balance_args; struct btrfs_balance_control { struct btrfs_fs_info *fs_info; -- cgit v0.10.2 From cfa4c961cc69ffb7bda450972320a25cbd413e19 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: soft profile changing mode (aka soft convert) When doing convert from one profile to another if soft mode is on restriper won't touch chunks that already have the profile we are converting to. This is useful if e.g. half of the FS was converted earlier. The soft mode switch is (like every other filter) per-type. This means that we can convert for example meta chunks the "hard" way while converting data chunks selectively with soft switch. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index f08210e..98b4067 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2221,6 +2221,23 @@ static int chunk_vrange_filter(struct extent_buffer *leaf, return 1; } +static int chunk_soft_convert_filter(u64 chunk_profile, + struct btrfs_balance_args *bargs) +{ + if (!(bargs->flags & BTRFS_BALANCE_ARGS_CONVERT)) + return 0; + + chunk_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK; + + if (chunk_profile == 0) + chunk_profile = BTRFS_AVAIL_ALLOC_BIT_SINGLE; + + if (bargs->target & chunk_profile) + return 1; + + return 0; +} + static int should_balance_chunk(struct btrfs_root *root, struct extent_buffer *leaf, struct btrfs_chunk *chunk, u64 chunk_offset) @@ -2272,6 +2289,12 @@ static int should_balance_chunk(struct btrfs_root *root, return 0; } + /* soft profile changing mode */ + if ((bargs->flags & BTRFS_BALANCE_ARGS_SOFT) && + chunk_soft_convert_filter(chunk_type, bargs)) { + return 0; + } + return 1; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 79ee9c3..6c143c98 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -208,7 +208,13 @@ struct map_lookup { #define BTRFS_BALANCE_ARGS_DRANGE (1ULL << 3) #define BTRFS_BALANCE_ARGS_VRANGE (1ULL << 4) +/* + * Profile changing flags. When SOFT is set we won't relocate chunk if + * it already has the target profile (even though it may be + * half-filled). + */ #define BTRFS_BALANCE_ARGS_CONVERT (1ULL << 8) +#define BTRFS_BALANCE_ARGS_SOFT (1ULL << 9) struct btrfs_balance_args; struct btrfs_balance_control { -- cgit v0.10.2 From 0940ebf6b92ea10a6f30ae5ac3993a3b75745da6 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: save balance parameters to disk Introduce a new btree objectid for storing balance item. The reason is to be able to resume restriper after a crash with the same parameters. Balance item has a very high objectid and goes into tree of tree roots. The key for the new item is as follows: [ BTRFS_BALANCE_OBJECTID ; BTRFS_BALANCE_ITEM_KEY ; 0 ] Older kernels simply ignore it so it's safe to mount with an older kernel and then go back to the newer one. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 1e7aea6..9997a59 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -86,6 +86,9 @@ struct btrfs_ordered_sum; /* holds checksums of all the data extents */ #define BTRFS_CSUM_TREE_OBJECTID 7ULL +/* for storing balance parameters in the root tree */ +#define BTRFS_BALANCE_OBJECTID -4ULL + /* orhpan objectid for tracking unlinked/truncated files */ #define BTRFS_ORPHAN_OBJECTID -5ULL @@ -692,6 +695,54 @@ struct btrfs_root_ref { __le16 name_len; } __attribute__ ((__packed__)); +struct btrfs_disk_balance_args { + /* + * profiles to operate on, single is denoted by + * BTRFS_AVAIL_ALLOC_BIT_SINGLE + */ + __le64 profiles; + + /* usage filter */ + __le64 usage; + + /* devid filter */ + __le64 devid; + + /* devid subset filter [pstart..pend) */ + __le64 pstart; + __le64 pend; + + /* btrfs virtual address space subset filter [vstart..vend) */ + __le64 vstart; + __le64 vend; + + /* + * profile to convert to, single is denoted by + * BTRFS_AVAIL_ALLOC_BIT_SINGLE + */ + __le64 target; + + /* BTRFS_BALANCE_ARGS_* */ + __le64 flags; + + __le64 unused[8]; +} __attribute__ ((__packed__)); + +/* + * store balance parameters to disk so that balance can be properly + * resumed after crash or unmount + */ +struct btrfs_balance_item { + /* BTRFS_BALANCE_* */ + __le64 flags; + + struct btrfs_disk_balance_args data; + struct btrfs_disk_balance_args meta; + struct btrfs_disk_balance_args sys; + + __le64 unused[4]; +} __attribute__ ((__packed__)); + #define BTRFS_FILE_EXTENT_INLINE 0 #define BTRFS_FILE_EXTENT_REG 1 #define BTRFS_FILE_EXTENT_PREALLOC 2 @@ -1409,6 +1460,8 @@ struct btrfs_ioctl_defrag_range_args { #define BTRFS_DEV_ITEM_KEY 216 #define BTRFS_CHUNK_ITEM_KEY 228 +#define BTRFS_BALANCE_ITEM_KEY 248 + /* * string items are for debugging. They just store a short string of * data in the FS @@ -2103,8 +2156,86 @@ BTRFS_SETGET_STACK_FUNCS(backup_bytes_used, struct btrfs_root_backup, BTRFS_SETGET_STACK_FUNCS(backup_num_devices, struct btrfs_root_backup, num_devices, 64); -/* struct btrfs_super_block */ +/* struct btrfs_balance_item */ +BTRFS_SETGET_FUNCS(balance_flags, struct btrfs_balance_item, flags, 64); + +static inline void btrfs_balance_data(struct extent_buffer *eb, + struct btrfs_balance_item *bi, + struct btrfs_disk_balance_args *ba) +{ + read_eb_member(eb, bi, struct btrfs_balance_item, data, ba); +} + +static inline void btrfs_set_balance_data(struct extent_buffer *eb, + struct btrfs_balance_item *bi, + struct btrfs_disk_balance_args *ba) +{ + write_eb_member(eb, bi, struct btrfs_balance_item, data, ba); +} + +static inline void btrfs_balance_meta(struct extent_buffer *eb, + struct btrfs_balance_item *bi, + struct btrfs_disk_balance_args *ba) +{ + read_eb_member(eb, bi, struct btrfs_balance_item, meta, ba); +} + +static inline void btrfs_set_balance_meta(struct extent_buffer *eb, + struct btrfs_balance_item *bi, + struct btrfs_disk_balance_args *ba) +{ + write_eb_member(eb, bi, struct btrfs_balance_item, meta, ba); +} + +static inline void btrfs_balance_sys(struct extent_buffer *eb, + struct btrfs_balance_item *bi, + struct btrfs_disk_balance_args *ba) +{ + read_eb_member(eb, bi, struct btrfs_balance_item, sys, ba); +} + +static inline void btrfs_set_balance_sys(struct extent_buffer *eb, + struct btrfs_balance_item *bi, + struct btrfs_disk_balance_args *ba) +{ + write_eb_member(eb, bi, struct btrfs_balance_item, sys, ba); +} +static inline void +btrfs_disk_balance_args_to_cpu(struct btrfs_balance_args *cpu, + struct btrfs_disk_balance_args *disk) +{ + memset(cpu, 0, sizeof(*cpu)); + + cpu->profiles = le64_to_cpu(disk->profiles); + cpu->usage = le64_to_cpu(disk->usage); + cpu->devid = le64_to_cpu(disk->devid); + cpu->pstart = le64_to_cpu(disk->pstart); + cpu->pend = le64_to_cpu(disk->pend); + cpu->vstart = le64_to_cpu(disk->vstart); + cpu->vend = le64_to_cpu(disk->vend); + cpu->target = le64_to_cpu(disk->target); + cpu->flags = le64_to_cpu(disk->flags); +} + +static inline void +btrfs_cpu_balance_args_to_disk(struct btrfs_disk_balance_args *disk, + struct btrfs_balance_args *cpu) +{ + memset(disk, 0, sizeof(*disk)); + + disk->profiles = cpu_to_le64(cpu->profiles); + disk->usage = cpu_to_le64(cpu->usage); + disk->devid = cpu_to_le64(cpu->devid); + disk->pstart = cpu_to_le64(cpu->pstart); + disk->pend = cpu_to_le64(cpu->pend); + disk->vstart = cpu_to_le64(cpu->vstart); + disk->vend = cpu_to_le64(cpu->vend); + disk->target = cpu_to_le64(cpu->target); + disk->flags = cpu_to_le64(cpu->flags); +} + +/* struct btrfs_super_block */ BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64); BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64); BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 98b4067..4c60ca0 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2073,6 +2073,97 @@ error: return ret; } +static int insert_balance_item(struct btrfs_root *root, + struct btrfs_balance_control *bctl) +{ + struct btrfs_trans_handle *trans; + struct btrfs_balance_item *item; + struct btrfs_disk_balance_args disk_bargs; + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_key key; + int ret, err; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + btrfs_free_path(path); + return PTR_ERR(trans); + } + + key.objectid = BTRFS_BALANCE_OBJECTID; + key.type = BTRFS_BALANCE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_insert_empty_item(trans, root, path, &key, + sizeof(*item)); + if (ret) + goto out; + + leaf = path->nodes[0]; + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_balance_item); + + memset_extent_buffer(leaf, 0, (unsigned long)item, sizeof(*item)); + + btrfs_cpu_balance_args_to_disk(&disk_bargs, &bctl->data); + btrfs_set_balance_data(leaf, item, &disk_bargs); + btrfs_cpu_balance_args_to_disk(&disk_bargs, &bctl->meta); + btrfs_set_balance_meta(leaf, item, &disk_bargs); + btrfs_cpu_balance_args_to_disk(&disk_bargs, &bctl->sys); + btrfs_set_balance_sys(leaf, item, &disk_bargs); + + btrfs_set_balance_flags(leaf, item, bctl->flags); + + btrfs_mark_buffer_dirty(leaf); +out: + btrfs_free_path(path); + err = btrfs_commit_transaction(trans, root); + if (err && !ret) + ret = err; + return ret; +} + +static int del_balance_item(struct btrfs_root *root) +{ + struct btrfs_trans_handle *trans; + struct btrfs_path *path; + struct btrfs_key key; + int ret, err; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + btrfs_free_path(path); + return PTR_ERR(trans); + } + + key.objectid = BTRFS_BALANCE_OBJECTID; + key.type = BTRFS_BALANCE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret < 0) + goto out; + if (ret > 0) { + ret = -ENOENT; + goto out; + } + + ret = btrfs_del_item(trans, root, path); +out: + btrfs_free_path(path); + err = btrfs_commit_transaction(trans, root); + if (err && !ret) + ret = err; + return ret; +} + /* * Should be called with both balance and volume mutexes held to * serialize other volume operations (add_dev/rm_dev/resize) with @@ -2423,7 +2514,11 @@ error: static void __cancel_balance(struct btrfs_fs_info *fs_info) { + int ret; + unset_balance_control(fs_info); + ret = del_balance_item(fs_info->tree_root); + BUG_ON(ret); } void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, @@ -2530,6 +2625,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl, } do_balance: + ret = insert_balance_item(fs_info->tree_root, bctl); + if (ret) + goto out; + set_balance_control(bctl); mutex_unlock(&fs_info->balance_mutex); -- cgit v0.10.2 From 596410151ed71819b9e8a8018c6c9992796b256d Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: recover balance on mount On mount, if balance item is found, resume balance in a separate kernel thread. Try to be smart to continue roughly where previous balance (or convert) was interrupted. For chunk types that were being converted to some profile we turn on soft convert, in case of a simple balance we turn on usage filter and relocate only less-than-90%-full chunks of that type. These are just heuristics but they help quite a bit, and can be improved in future. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 190a1b2..eb7a11a 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2427,6 +2427,10 @@ retry_root_backup: if (!err) err = btrfs_orphan_cleanup(fs_info->tree_root); up_read(&fs_info->cleanup_work_sem); + + if (!err) + err = btrfs_recover_balance(fs_info->tree_root); + if (err) { close_ctree(tree_root); return ERR_PTR(err); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 4c60ca0..17e5653 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "compat.h" #include "ctree.h" @@ -2165,6 +2166,46 @@ out: } /* + * This is a heuristic used to reduce the number of chunks balanced on + * resume after balance was interrupted. + */ +static void update_balance_args(struct btrfs_balance_control *bctl) +{ + /* + * Turn on soft mode for chunk types that were being converted. + */ + if (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) + bctl->data.flags |= BTRFS_BALANCE_ARGS_SOFT; + if (bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT) + bctl->sys.flags |= BTRFS_BALANCE_ARGS_SOFT; + if (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) + bctl->meta.flags |= BTRFS_BALANCE_ARGS_SOFT; + + /* + * Turn on usage filter if is not already used. The idea is + * that chunks that we have already balanced should be + * reasonably full. Don't do it for chunks that are being + * converted - that will keep us from relocating unconverted + * (albeit full) chunks. + */ + if (!(bctl->data.flags & BTRFS_BALANCE_ARGS_USAGE) && + !(bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT)) { + bctl->data.flags |= BTRFS_BALANCE_ARGS_USAGE; + bctl->data.usage = 90; + } + if (!(bctl->sys.flags & BTRFS_BALANCE_ARGS_USAGE) && + !(bctl->sys.flags & BTRFS_BALANCE_ARGS_CONVERT)) { + bctl->sys.flags |= BTRFS_BALANCE_ARGS_USAGE; + bctl->sys.usage = 90; + } + if (!(bctl->meta.flags & BTRFS_BALANCE_ARGS_USAGE) && + !(bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT)) { + bctl->meta.flags |= BTRFS_BALANCE_ARGS_USAGE; + bctl->meta.usage = 90; + } +} + +/* * Should be called with both balance and volume mutexes held to * serialize other volume operations (add_dev/rm_dev/resize) with * restriper. Same goes for unset_balance_control. @@ -2626,10 +2667,18 @@ int btrfs_balance(struct btrfs_balance_control *bctl, do_balance: ret = insert_balance_item(fs_info->tree_root, bctl); - if (ret) + if (ret && ret != -EEXIST) goto out; - set_balance_control(bctl); + if (!(bctl->flags & BTRFS_BALANCE_RESUME)) { + BUG_ON(ret == -EEXIST); + set_balance_control(bctl); + } else { + BUG_ON(ret != -EEXIST); + spin_lock(&fs_info->balance_lock); + update_balance_args(bctl); + spin_unlock(&fs_info->balance_lock); + } mutex_unlock(&fs_info->balance_mutex); @@ -2646,7 +2695,89 @@ do_balance: return ret; out: + if (bctl->flags & BTRFS_BALANCE_RESUME) + __cancel_balance(fs_info); + else + kfree(bctl); + return ret; +} + +static int balance_kthread(void *data) +{ + struct btrfs_balance_control *bctl = + (struct btrfs_balance_control *)data; + struct btrfs_fs_info *fs_info = bctl->fs_info; + int ret; + + mutex_lock(&fs_info->volume_mutex); + mutex_lock(&fs_info->balance_mutex); + + set_balance_control(bctl); + + printk(KERN_INFO "btrfs: continuing balance\n"); + ret = btrfs_balance(bctl, NULL); + + mutex_unlock(&fs_info->balance_mutex); + mutex_unlock(&fs_info->volume_mutex); + return ret; +} + +int btrfs_recover_balance(struct btrfs_root *tree_root) +{ + struct task_struct *tsk; + struct btrfs_balance_control *bctl; + struct btrfs_balance_item *item; + struct btrfs_disk_balance_args disk_bargs; + struct btrfs_path *path; + struct extent_buffer *leaf; + struct btrfs_key key; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + bctl = kzalloc(sizeof(*bctl), GFP_NOFS); + if (!bctl) { + ret = -ENOMEM; + goto out; + } + + key.objectid = BTRFS_BALANCE_OBJECTID; + key.type = BTRFS_BALANCE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0); + if (ret < 0) + goto out_bctl; + if (ret > 0) { /* ret = -ENOENT; */ + ret = 0; + goto out_bctl; + } + + leaf = path->nodes[0]; + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_balance_item); + + bctl->fs_info = tree_root->fs_info; + bctl->flags = btrfs_balance_flags(leaf, item) | BTRFS_BALANCE_RESUME; + + btrfs_balance_data(leaf, item, &disk_bargs); + btrfs_disk_balance_args_to_cpu(&bctl->data, &disk_bargs); + btrfs_balance_meta(leaf, item, &disk_bargs); + btrfs_disk_balance_args_to_cpu(&bctl->meta, &disk_bargs); + btrfs_balance_sys(leaf, item, &disk_bargs); + btrfs_disk_balance_args_to_cpu(&bctl->sys, &disk_bargs); + + tsk = kthread_run(balance_kthread, bctl, "btrfs-balance"); + if (IS_ERR(tsk)) + ret = PTR_ERR(tsk); + else + goto out; + +out_bctl: kfree(bctl); +out: + btrfs_free_path(path); return ret; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 6c143c98..cd25ea5 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -198,6 +198,7 @@ struct map_lookup { BTRFS_BALANCE_METADATA) #define BTRFS_BALANCE_FORCE (1ULL << 3) +#define BTRFS_BALANCE_RESUME (1ULL << 4) /* * Balance filters @@ -271,6 +272,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size); int btrfs_init_new_device(struct btrfs_root *root, char *path); int btrfs_balance(struct btrfs_balance_control *bctl, struct btrfs_ioctl_balance_args *bargs); +int btrfs_recover_balance(struct btrfs_root *tree_root); int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); int find_free_dev_extent(struct btrfs_trans_handle *trans, struct btrfs_device *device, u64 num_bytes, -- cgit v0.10.2 From 9555c6c180600b40f6e86bd4dc53bf47e06ed663 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:48 +0200 Subject: Btrfs: add skip_balance mount option Since restriper kthread starts involuntarily on mount and can suck cpu and memory bandwidth add a mount option to forcefully skip it. The restriper in that case hangs around in paused state and can be resumed from userspace when it's convenient. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 9997a59..99eb2bc 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1492,6 +1492,7 @@ struct btrfs_ioctl_defrag_range_args { #define BTRFS_MOUNT_AUTO_DEFRAG (1 << 16) #define BTRFS_MOUNT_INODE_MAP_CACHE (1 << 17) #define BTRFS_MOUNT_RECOVERY (1 << 18) +#define BTRFS_MOUNT_SKIP_BALANCE (1 << 19) #define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt) #define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 34a8b61..063b521 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -164,8 +164,9 @@ enum { Opt_compress_type, Opt_compress_force, Opt_compress_force_type, Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_discard, Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed, - Opt_enospc_debug, Opt_subvolrootid, Opt_defrag, - Opt_inode_cache, Opt_no_space_cache, Opt_recovery, Opt_err, + Opt_enospc_debug, Opt_subvolrootid, Opt_defrag, Opt_inode_cache, + Opt_no_space_cache, Opt_recovery, Opt_skip_balance, + Opt_err, }; static match_table_t tokens = { @@ -200,6 +201,7 @@ static match_table_t tokens = { {Opt_inode_cache, "inode_cache"}, {Opt_no_space_cache, "nospace_cache"}, {Opt_recovery, "recovery"}, + {Opt_skip_balance, "skip_balance"}, {Opt_err, NULL}, }; @@ -398,6 +400,9 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) printk(KERN_INFO "btrfs: enabling auto recovery"); btrfs_set_opt(info->mount_opt, RECOVERY); break; + case Opt_skip_balance: + btrfs_set_opt(info->mount_opt, SKIP_BALANCE); + break; case Opt_err: printk(KERN_INFO "btrfs: unrecognized mount option " "'%s'\n", p); @@ -723,6 +728,8 @@ static int btrfs_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_puts(seq, ",autodefrag"); if (btrfs_test_opt(root, INODE_MAP_CACHE)) seq_puts(seq, ",inode_cache"); + if (btrfs_test_opt(root, SKIP_BALANCE)) + seq_puts(seq, ",skip_balance"); return 0; } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 17e5653..e016060 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2707,15 +2707,19 @@ static int balance_kthread(void *data) struct btrfs_balance_control *bctl = (struct btrfs_balance_control *)data; struct btrfs_fs_info *fs_info = bctl->fs_info; - int ret; + int ret = 0; mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); set_balance_control(bctl); - printk(KERN_INFO "btrfs: continuing balance\n"); - ret = btrfs_balance(bctl, NULL); + if (btrfs_test_opt(fs_info->tree_root, SKIP_BALANCE)) { + printk(KERN_INFO "btrfs: force skipping balance\n"); + } else { + printk(KERN_INFO "btrfs: continuing balance\n"); + ret = btrfs_balance(bctl, NULL); + } mutex_unlock(&fs_info->balance_mutex); mutex_unlock(&fs_info->volume_mutex); -- cgit v0.10.2 From 837d5b6e46d1a4af5b6cc8f2fe83cb5de79a2961 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:49 +0200 Subject: Btrfs: allow for pausing restriper Implement an ioctl for pausing restriper. This pauses the relocation, but balance is still considered to be "in progress": balance item is not deleted, other volume operations cannot be started, etc. If paused in the middle of profile changing operation we will continue making allocations with the target profile. Add a hook to close_ctree() to pause restriper and free its data structures on unmount. (It's safe to unmount when restriper is in "paused" state, we will resume with the same parameters on the next mount) Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 99eb2bc..1afda75 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1214,7 +1214,10 @@ struct btrfs_fs_info { /* restriper state */ spinlock_t balance_lock; struct mutex balance_mutex; + atomic_t balance_running; + atomic_t balance_pause_req; struct btrfs_balance_control *balance_ctl; + wait_queue_head_t balance_wait_q; unsigned data_chunk_allocations; unsigned metadata_ratio; @@ -2658,6 +2661,7 @@ static inline int btrfs_fs_closing(struct btrfs_fs_info *fs_info) } static inline void free_fs_info(struct btrfs_fs_info *fs_info) { + kfree(fs_info->balance_ctl); kfree(fs_info->delayed_root); kfree(fs_info->extent_root); kfree(fs_info->tree_root); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index eb7a11a..8ce8374 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2004,7 +2004,10 @@ struct btrfs_root *open_ctree(struct super_block *sb, spin_lock_init(&fs_info->balance_lock); mutex_init(&fs_info->balance_mutex); + atomic_set(&fs_info->balance_running, 0); + atomic_set(&fs_info->balance_pause_req, 0); fs_info->balance_ctl = NULL; + init_waitqueue_head(&fs_info->balance_wait_q); sb->s_blocksize = 4096; sb->s_blocksize_bits = blksize_bits(4096); @@ -2980,6 +2983,9 @@ int close_ctree(struct btrfs_root *root) fs_info->closing = 1; smp_mb(); + /* pause restriper - we want to resume on mount */ + btrfs_pause_balance(root->fs_info); + btrfs_scrub_cancel(root); /* wait for any defraggers to finish */ diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 29b3a94..f572c53 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3072,6 +3072,11 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, bargs->flags = bctl->flags; + if (atomic_read(&fs_info->balance_running)) + bargs->state |= BTRFS_BALANCE_STATE_RUNNING; + if (atomic_read(&fs_info->balance_pause_req)) + bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ; + memcpy(&bargs->data, &bctl->data, sizeof(bargs->data)); memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys)); @@ -3103,6 +3108,11 @@ static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) bargs = NULL; } + if (fs_info->balance_ctl) { + ret = -EINPROGRESS; + goto out_bargs; + } + bctl = kzalloc(sizeof(*bctl), GFP_NOFS); if (!bctl) { ret = -ENOMEM; @@ -3123,7 +3133,8 @@ static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) ret = btrfs_balance(bctl, bargs); /* - * bctl is freed in __cancel_balance + * bctl is freed in __cancel_balance or in free_fs_info if + * restriper was paused all the way until unmount */ if (arg) { if (copy_to_user(arg, bargs, sizeof(*bargs))) @@ -3138,6 +3149,19 @@ out: return ret; } +static long btrfs_ioctl_balance_ctl(struct btrfs_root *root, int cmd) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + switch (cmd) { + case BTRFS_BALANCE_CTL_PAUSE: + return btrfs_pause_balance(root->fs_info); + } + + return -EINVAL; +} + long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -3216,6 +3240,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_scrub_progress(root, argp); case BTRFS_IOC_BALANCE_V2: return btrfs_ioctl_balance(root, argp); + case BTRFS_IOC_BALANCE_CTL: + return btrfs_ioctl_balance_ctl(root, arg); } return -ENOTTY; diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index c8b37d2..e972e11 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -109,6 +109,9 @@ struct btrfs_ioctl_fs_info_args { __u64 reserved[124]; /* pad to 1k */ }; +/* balance control ioctl modes */ +#define BTRFS_BALANCE_CTL_PAUSE 1 + /* * this is packed, because it should be exactly the same as its disk * byte order counterpart (struct btrfs_disk_balance_args) @@ -137,6 +140,9 @@ struct btrfs_balance_progress { __u64 completed; /* # of chunks relocated so far */ }; +#define BTRFS_BALANCE_STATE_RUNNING (1ULL << 0) +#define BTRFS_BALANCE_STATE_PAUSE_REQ (1ULL << 1) + struct btrfs_ioctl_balance_args { __u64 flags; /* in/out */ __u64 state; /* out */ @@ -315,6 +321,7 @@ struct btrfs_ioctl_logical_ino_args { struct btrfs_ioctl_fs_info_args) #define BTRFS_IOC_BALANCE_V2 _IOWR(BTRFS_IOCTL_MAGIC, 32, \ struct btrfs_ioctl_balance_args) +#define BTRFS_IOC_BALANCE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int) #define BTRFS_IOC_INO_PATHS _IOWR(BTRFS_IOCTL_MAGIC, 35, \ struct btrfs_ioctl_ino_path_args) #define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index e016060..d32660c 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2492,6 +2492,11 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) key.type = BTRFS_CHUNK_ITEM_KEY; while (1) { + if (atomic_read(&fs_info->balance_pause_req)) { + ret = -ECANCELED; + goto error; + } + ret = btrfs_search_slot(NULL, chunk_root, &key, path, 0, 0); if (ret < 0) goto error; @@ -2553,6 +2558,11 @@ error: return ret; } +static inline int balance_need_close(struct btrfs_fs_info *fs_info) +{ + return atomic_read(&fs_info->balance_pause_req) == 0; +} + static void __cancel_balance(struct btrfs_fs_info *fs_info) { int ret; @@ -2575,7 +2585,8 @@ int btrfs_balance(struct btrfs_balance_control *bctl, u64 allowed; int ret; - if (btrfs_fs_closing(fs_info)) { + if (btrfs_fs_closing(fs_info) || + atomic_read(&fs_info->balance_pause_req)) { ret = -EINVAL; goto out; } @@ -2680,18 +2691,25 @@ do_balance: spin_unlock(&fs_info->balance_lock); } + atomic_inc(&fs_info->balance_running); mutex_unlock(&fs_info->balance_mutex); ret = __btrfs_balance(fs_info); mutex_lock(&fs_info->balance_mutex); + atomic_dec(&fs_info->balance_running); if (bargs) { memset(bargs, 0, sizeof(*bargs)); update_ioctl_balance_args(fs_info, bargs); } - __cancel_balance(fs_info); + if ((ret && ret != -ECANCELED && ret != -ENOSPC) || + balance_need_close(fs_info)) { + __cancel_balance(fs_info); + } + + wake_up(&fs_info->balance_wait_q); return ret; out: @@ -2785,6 +2803,35 @@ out: return ret; } +int btrfs_pause_balance(struct btrfs_fs_info *fs_info) +{ + int ret = 0; + + mutex_lock(&fs_info->balance_mutex); + if (!fs_info->balance_ctl) { + mutex_unlock(&fs_info->balance_mutex); + return -ENOTCONN; + } + + if (atomic_read(&fs_info->balance_running)) { + atomic_inc(&fs_info->balance_pause_req); + mutex_unlock(&fs_info->balance_mutex); + + wait_event(fs_info->balance_wait_q, + atomic_read(&fs_info->balance_running) == 0); + + mutex_lock(&fs_info->balance_mutex); + /* we are good with balance_ctl ripped off from under us */ + BUG_ON(atomic_read(&fs_info->balance_running)); + atomic_dec(&fs_info->balance_pause_req); + } else { + ret = -ENOTCONN; + } + + mutex_unlock(&fs_info->balance_mutex); + return ret; +} + /* * shrinking a device means finding all of the device extents past * the new size, and then following the back refs to the chunks. diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index cd25ea5..80953af 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -273,6 +273,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *path); int btrfs_balance(struct btrfs_balance_control *bctl, struct btrfs_ioctl_balance_args *bargs); int btrfs_recover_balance(struct btrfs_root *tree_root); +int btrfs_pause_balance(struct btrfs_fs_info *fs_info); int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); int find_free_dev_extent(struct btrfs_trans_handle *trans, struct btrfs_device *device, u64 num_bytes, -- cgit v0.10.2 From a7e99c691af553fc15ac46a51f130b7c59a65f76 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:49 +0200 Subject: Btrfs: allow for canceling restriper Implement an ioctl for canceling restriper. Currently we wait until relocation of the current block group is finished, in future this can be done by triggering a commit. Balance item is deleted and no memory about the interrupted balance is kept. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 1afda75..dfc136c 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1216,6 +1216,7 @@ struct btrfs_fs_info { struct mutex balance_mutex; atomic_t balance_running; atomic_t balance_pause_req; + atomic_t balance_cancel_req; struct btrfs_balance_control *balance_ctl; wait_queue_head_t balance_wait_q; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 8ce8374..c23b82d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2006,6 +2006,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, mutex_init(&fs_info->balance_mutex); atomic_set(&fs_info->balance_running, 0); atomic_set(&fs_info->balance_pause_req, 0); + atomic_set(&fs_info->balance_cancel_req, 0); fs_info->balance_ctl = NULL; init_waitqueue_head(&fs_info->balance_wait_q); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f572c53..6085221 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3076,6 +3076,8 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, bargs->state |= BTRFS_BALANCE_STATE_RUNNING; if (atomic_read(&fs_info->balance_pause_req)) bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ; + if (atomic_read(&fs_info->balance_cancel_req)) + bargs->state |= BTRFS_BALANCE_STATE_CANCEL_REQ; memcpy(&bargs->data, &bctl->data, sizeof(bargs->data)); memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); @@ -3157,6 +3159,8 @@ static long btrfs_ioctl_balance_ctl(struct btrfs_root *root, int cmd) switch (cmd) { case BTRFS_BALANCE_CTL_PAUSE: return btrfs_pause_balance(root->fs_info); + case BTRFS_BALANCE_CTL_CANCEL: + return btrfs_cancel_balance(root->fs_info); } return -EINVAL; diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index e972e11..cd19d10 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -111,6 +111,7 @@ struct btrfs_ioctl_fs_info_args { /* balance control ioctl modes */ #define BTRFS_BALANCE_CTL_PAUSE 1 +#define BTRFS_BALANCE_CTL_CANCEL 2 /* * this is packed, because it should be exactly the same as its disk @@ -142,6 +143,7 @@ struct btrfs_balance_progress { #define BTRFS_BALANCE_STATE_RUNNING (1ULL << 0) #define BTRFS_BALANCE_STATE_PAUSE_REQ (1ULL << 1) +#define BTRFS_BALANCE_STATE_CANCEL_REQ (1ULL << 2) struct btrfs_ioctl_balance_args { __u64 flags; /* in/out */ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d32660c..c326673 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2492,7 +2492,8 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) key.type = BTRFS_CHUNK_ITEM_KEY; while (1) { - if (atomic_read(&fs_info->balance_pause_req)) { + if (atomic_read(&fs_info->balance_pause_req) || + atomic_read(&fs_info->balance_cancel_req)) { ret = -ECANCELED; goto error; } @@ -2560,7 +2561,10 @@ error: static inline int balance_need_close(struct btrfs_fs_info *fs_info) { - return atomic_read(&fs_info->balance_pause_req) == 0; + /* cancel requested || normal exit path */ + return atomic_read(&fs_info->balance_cancel_req) || + (atomic_read(&fs_info->balance_pause_req) == 0 && + atomic_read(&fs_info->balance_cancel_req) == 0); } static void __cancel_balance(struct btrfs_fs_info *fs_info) @@ -2586,7 +2590,8 @@ int btrfs_balance(struct btrfs_balance_control *bctl, int ret; if (btrfs_fs_closing(fs_info) || - atomic_read(&fs_info->balance_pause_req)) { + atomic_read(&fs_info->balance_pause_req) || + atomic_read(&fs_info->balance_cancel_req)) { ret = -EINVAL; goto out; } @@ -2832,6 +2837,42 @@ int btrfs_pause_balance(struct btrfs_fs_info *fs_info) return ret; } +int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) +{ + mutex_lock(&fs_info->balance_mutex); + if (!fs_info->balance_ctl) { + mutex_unlock(&fs_info->balance_mutex); + return -ENOTCONN; + } + + atomic_inc(&fs_info->balance_cancel_req); + /* + * if we are running just wait and return, balance item is + * deleted in btrfs_balance in this case + */ + if (atomic_read(&fs_info->balance_running)) { + mutex_unlock(&fs_info->balance_mutex); + wait_event(fs_info->balance_wait_q, + atomic_read(&fs_info->balance_running) == 0); + mutex_lock(&fs_info->balance_mutex); + } else { + /* __cancel_balance needs volume_mutex */ + mutex_unlock(&fs_info->balance_mutex); + mutex_lock(&fs_info->volume_mutex); + mutex_lock(&fs_info->balance_mutex); + + if (fs_info->balance_ctl) + __cancel_balance(fs_info); + + mutex_unlock(&fs_info->volume_mutex); + } + + BUG_ON(fs_info->balance_ctl || atomic_read(&fs_info->balance_running)); + atomic_dec(&fs_info->balance_cancel_req); + mutex_unlock(&fs_info->balance_mutex); + return 0; +} + /* * shrinking a device means finding all of the device extents past * the new size, and then following the back refs to the chunks. diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 80953af..caa9abd 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -274,6 +274,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, struct btrfs_ioctl_balance_args *bargs); int btrfs_recover_balance(struct btrfs_root *tree_root); int btrfs_pause_balance(struct btrfs_fs_info *fs_info); +int btrfs_cancel_balance(struct btrfs_fs_info *fs_info); int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); int find_free_dev_extent(struct btrfs_trans_handle *trans, struct btrfs_device *device, u64 num_bytes, -- cgit v0.10.2 From de322263d3a6d4ffd4ed7c4d0c6536e9497aec9b Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:49 +0200 Subject: Btrfs: allow for resuming restriper after it was paused Recognize BTRFS_BALANCE_RESUME flag passed from userspace. We use the same heuristics used when recovering balance after a crash to try to start where we left off last time. Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 6085221..85e546f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3106,6 +3106,20 @@ static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) ret = PTR_ERR(bargs); goto out; } + + if (bargs->flags & BTRFS_BALANCE_RESUME) { + if (!fs_info->balance_ctl) { + ret = -ENOTCONN; + goto out_bargs; + } + + bctl = fs_info->balance_ctl; + spin_lock(&fs_info->balance_lock); + bctl->flags |= BTRFS_BALANCE_RESUME; + spin_unlock(&fs_info->balance_lock); + + goto do_balance; + } } else { bargs = NULL; } @@ -3133,6 +3147,7 @@ static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) bctl->flags |= BTRFS_BALANCE_TYPE_MASK; } +do_balance: ret = btrfs_balance(bctl, bargs); /* * bctl is freed in __cancel_balance or in free_fs_info if -- cgit v0.10.2 From 19a39dce3b9bf0244d19a446718ad6f7605ff099 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 16 Jan 2012 22:04:49 +0200 Subject: Btrfs: add balance progress reporting Signed-off-by: Ilya Dryomov diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 85e546f..1e7a9ba 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3065,7 +3065,7 @@ out: return ret; } -void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, +void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, struct btrfs_ioctl_balance_args *bargs) { struct btrfs_balance_control *bctl = fs_info->balance_ctl; @@ -3082,6 +3082,14 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, memcpy(&bargs->data, &bctl->data, sizeof(bargs->data)); memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys)); + + if (lock) { + spin_lock(&fs_info->balance_lock); + memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); + spin_unlock(&fs_info->balance_lock); + } else { + memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); + } } static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) @@ -3181,6 +3189,39 @@ static long btrfs_ioctl_balance_ctl(struct btrfs_root *root, int cmd) return -EINVAL; } +static long btrfs_ioctl_balance_progress(struct btrfs_root *root, + void __user *arg) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_ioctl_balance_args *bargs; + int ret = 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + mutex_lock(&fs_info->balance_mutex); + if (!fs_info->balance_ctl) { + ret = -ENOTCONN; + goto out; + } + + bargs = kzalloc(sizeof(*bargs), GFP_NOFS); + if (!bargs) { + ret = -ENOMEM; + goto out; + } + + update_ioctl_balance_args(fs_info, 1, bargs); + + if (copy_to_user(arg, bargs, sizeof(*bargs))) + ret = -EFAULT; + + kfree(bargs); +out: + mutex_unlock(&fs_info->balance_mutex); + return ret; +} + long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -3261,6 +3302,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_balance(root, argp); case BTRFS_IOC_BALANCE_CTL: return btrfs_ioctl_balance_ctl(root, arg); + case BTRFS_IOC_BALANCE_PROGRESS: + return btrfs_ioctl_balance_progress(root, argp); } return -ENOTTY; diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index cd19d10..4f69028 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -324,6 +324,8 @@ struct btrfs_ioctl_logical_ino_args { #define BTRFS_IOC_BALANCE_V2 _IOWR(BTRFS_IOCTL_MAGIC, 32, \ struct btrfs_ioctl_balance_args) #define BTRFS_IOC_BALANCE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int) +#define BTRFS_IOC_BALANCE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 34, \ + struct btrfs_ioctl_balance_args) #define BTRFS_IOC_INO_PATHS _IOWR(BTRFS_IOCTL_MAGIC, 35, \ struct btrfs_ioctl_ino_path_args) #define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c326673..d73439b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2441,6 +2441,7 @@ static u64 div_factor(u64 num, int factor) static int __btrfs_balance(struct btrfs_fs_info *fs_info) { + struct btrfs_balance_control *bctl = fs_info->balance_ctl; struct btrfs_root *chunk_root = fs_info->chunk_root; struct btrfs_root *dev_root = fs_info->dev_root; struct list_head *devices; @@ -2456,6 +2457,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) int slot; int ret; int enospc_errors = 0; + bool counting = true; /* step one make some room on all the devices */ devices = &fs_info->fs_devices->devices; @@ -2487,12 +2489,18 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) ret = -ENOMEM; goto error; } + + /* zero out stat counters */ + spin_lock(&fs_info->balance_lock); + memset(&bctl->stat, 0, sizeof(bctl->stat)); + spin_unlock(&fs_info->balance_lock); +again: key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; key.offset = (u64)-1; key.type = BTRFS_CHUNK_ITEM_KEY; while (1) { - if (atomic_read(&fs_info->balance_pause_req) || + if ((!counting && atomic_read(&fs_info->balance_pause_req)) || atomic_read(&fs_info->balance_cancel_req)) { ret = -ECANCELED; goto error; @@ -2529,24 +2537,47 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); + if (!counting) { + spin_lock(&fs_info->balance_lock); + bctl->stat.considered++; + spin_unlock(&fs_info->balance_lock); + } + ret = should_balance_chunk(chunk_root, leaf, chunk, found_key.offset); btrfs_release_path(path); if (!ret) goto loop; + if (counting) { + spin_lock(&fs_info->balance_lock); + bctl->stat.expected++; + spin_unlock(&fs_info->balance_lock); + goto loop; + } + ret = btrfs_relocate_chunk(chunk_root, chunk_root->root_key.objectid, found_key.objectid, found_key.offset); if (ret && ret != -ENOSPC) goto error; - if (ret == -ENOSPC) + if (ret == -ENOSPC) { enospc_errors++; + } else { + spin_lock(&fs_info->balance_lock); + bctl->stat.completed++; + spin_unlock(&fs_info->balance_lock); + } loop: key.offset = found_key.offset - 1; } + if (counting) { + btrfs_release_path(path); + counting = false; + goto again; + } error: btrfs_free_path(path); if (enospc_errors) { @@ -2576,7 +2607,7 @@ static void __cancel_balance(struct btrfs_fs_info *fs_info) BUG_ON(ret); } -void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, +void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, struct btrfs_ioctl_balance_args *bargs); /* @@ -2706,7 +2737,7 @@ do_balance: if (bargs) { memset(bargs, 0, sizeof(*bargs)); - update_ioctl_balance_args(fs_info, bargs); + update_ioctl_balance_args(fs_info, 0, bargs); } if ((ret && ret != -ECANCELED && ret != -ENOSPC) || diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index caa9abd..6faec9d 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -218,6 +218,7 @@ struct map_lookup { #define BTRFS_BALANCE_ARGS_SOFT (1ULL << 9) struct btrfs_balance_args; +struct btrfs_balance_progress; struct btrfs_balance_control { struct btrfs_fs_info *fs_info; @@ -226,6 +227,8 @@ struct btrfs_balance_control { struct btrfs_balance_args sys; u64 flags; + + struct btrfs_balance_progress stat; }; int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start, -- cgit v0.10.2 From 7ad85bb76a61801362701b77c5cee5aa09f35369 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 12 Jan 2012 19:10:12 -0500 Subject: Btrfs: do not use btrfs_end_transaction_throttle everywhere A user reported a problem where things like open with O_CREAT would take up to 30 seconds when he had nfs activity on the same mount. This is because all of our quick metadata operations, like create, symlink etc all do btrfs_end_transaction_throttle, which if the transaction is blocked will wait for the commit to complete before it returns. This adds a ridiculous amount of latency and isn't really needed. The normal btrfs_end_transaction will mark the transaction as blocked and wake the transaction kthread up if it thinks the transaction needs to end (this being in the running out of global reserve space scenario), and this is all that is really needed since we've already done everything we're going to do, we just need to return. This should help people with the latency they were seeing when using synchronous heavy workloads. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index acc4ff3..5f8ba21 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2845,7 +2845,7 @@ static void __unlink_end_trans(struct btrfs_trans_handle *trans, BUG_ON(!root->fs_info->enospc_unlink); root->fs_info->enospc_unlink = 0; } - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); } static int btrfs_unlink(struct inode *dir, struct dentry *dentry) @@ -3434,7 +3434,7 @@ static int btrfs_setsize(struct inode *inode, loff_t newsize) i_size_write(inode, newsize); btrfs_ordered_update_i_size(inode, i_size_read(inode), NULL); ret = btrfs_update_inode(trans, root, inode); - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); } else { /* @@ -4655,7 +4655,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry, } out_unlock: nr = trans->blocks_used; - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); btrfs_btree_balance_dirty(root, nr); if (drop_inode) { inode_dec_link_count(inode); @@ -4723,7 +4723,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, } out_unlock: nr = trans->blocks_used; - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); if (drop_inode) { inode_dec_link_count(inode); iput(inode); @@ -4782,7 +4782,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, } nr = trans->blocks_used; - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); fail: if (drop_inode) { inode_dec_link_count(inode); @@ -4848,7 +4848,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) out_fail: nr = trans->blocks_used; - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); if (drop_on_err) iput(inode); btrfs_btree_balance_dirty(root, nr); @@ -6668,7 +6668,7 @@ end_trans: err = ret; nr = trans->blocks_used; - ret = btrfs_end_transaction_throttle(trans, root); + ret = btrfs_end_transaction(trans, root); btrfs_btree_balance_dirty(root, nr); } @@ -7075,7 +7075,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, btrfs_end_log_trans(root); } out_fail: - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); out_notrans: if (old_ino == BTRFS_FIRST_FREE_OBJECTID) up_read(&root->fs_info->subvol_sem); @@ -7247,7 +7247,7 @@ out_unlock: if (!err) d_instantiate(dentry, inode); nr = trans->blocks_used; - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); if (drop_inode) { inode_dec_link_count(inode); iput(inode); diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index 3848b04..e7a5659 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c @@ -200,7 +200,7 @@ int __btrfs_setxattr(struct btrfs_trans_handle *trans, ret = btrfs_update_inode(trans, root, inode); BUG_ON(ret); out: - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction(trans, root); return ret; } -- cgit v0.10.2 From f70a9a6b94af86fca069a7552ab672c31b457786 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 12 Jan 2012 19:10:12 -0500 Subject: Btrfs: fix btrfsck error 400 when truncating a compressed Reproduce steps: # mkfs.btrfs /dev/sdb5 # mount /dev/sdb5 -o compress=lzo /mnt # dd if=/dev/zero of=/mnt/tmpfile bs=128K count=1 # sync # truncate -s 64K /mnt/tmpfile root 5 inode 257 errors 400 This is because of the wrong if condition, which is used to check if we should subtract the bytes of the dropped range from i_blocks/i_bytes of i-node or not. When we truncate a compressed extent, btrfs substracts the bytes of the whole extent, it's wrong. We should substract the real size that we truncate, no matter it is a compressed extent or not. Fix it. Signed-off-by: Miao Xie Signed-off-by: Chris Mason diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5f8ba21..946a7f1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3009,7 +3009,6 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, int pending_del_nr = 0; int pending_del_slot = 0; int extent_type = -1; - int encoding; int ret; int err = 0; u64 ino = btrfs_ino(inode); @@ -3059,7 +3058,6 @@ search_again: leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); found_type = btrfs_key_type(&found_key); - encoding = 0; if (found_key.objectid != ino) break; @@ -3072,10 +3070,6 @@ search_again: fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); extent_type = btrfs_file_extent_type(leaf, fi); - encoding = btrfs_file_extent_compression(leaf, fi); - encoding |= btrfs_file_extent_encryption(leaf, fi); - encoding |= btrfs_file_extent_other_encoding(leaf, fi); - if (extent_type != BTRFS_FILE_EXTENT_INLINE) { item_end += btrfs_file_extent_num_bytes(leaf, fi); @@ -3103,7 +3097,7 @@ search_again: if (extent_type != BTRFS_FILE_EXTENT_INLINE) { u64 num_dec; extent_start = btrfs_file_extent_disk_bytenr(leaf, fi); - if (!del_item && !encoding) { + if (!del_item) { u64 orig_num_bytes = btrfs_file_extent_num_bytes(leaf, fi); extent_num_bytes = new_size - -- cgit v0.10.2 From ec39e180fd3188c983c94603634bfcd019f42ae7 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 12 Jan 2012 19:10:12 -0500 Subject: Btrfs: release space on error in page_mkwrite If updating the inode gave us an ENOSPC we were just returning in page_mkwrite, which is a problem since we make our reservation right before trying to update the inode, so fix the out label so that we actually free our reservation. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 946a7f1..85fd86e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6488,8 +6488,8 @@ out_unlock: if (!ret) return VM_FAULT_LOCKED; unlock_page(page); - btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE); out: + btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE); return ret; } -- cgit v0.10.2 From 45a8090e626ab470c91142954431a93846030b0d Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 12 Jan 2012 19:10:12 -0500 Subject: Btrfs: don't call btrfs_throttle in file write Btrfs_throttle will make us wait if there is a currently committing transaction until we can open new transactions, which is ridiculous since we don't actually start any transactions within the file write path anyway, so all this does is introduce big latencies if we have a sync/fsync heavy workload going on while somebody else is trying to do work. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index fc97b00..0f61e11 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1273,7 +1273,6 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, dirty_pages); if (dirty_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1) btrfs_btree_balance_dirty(root, 1); - btrfs_throttle(root); pos += copied; num_written += copied; -- cgit v0.10.2 From 3f7de037fb3727b20bc27332cdcf2488b702394c Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 10 Nov 2011 08:29:20 -0500 Subject: Btrfs: add allocator tracepoints I used these tracepoints when figuring out what the cluster stuff was doing, so add them to mainline in case we need to profile this stuff again. Thanks, Signed-off-by: Josef Bacik diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a44072a..ad1a20b 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5256,6 +5256,8 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, ins->objectid = 0; ins->offset = 0; + trace_find_free_extent(orig_root, num_bytes, empty_size, data); + space_info = __find_space_info(root->fs_info, data); if (!space_info) { printk(KERN_ERR "No space info for %llu\n", data); @@ -5432,6 +5434,8 @@ alloc: if (offset) { /* we have a block, we're done */ spin_unlock(&last_ptr->refill_lock); + trace_btrfs_reserve_extent_cluster(root, + block_group, search_start, num_bytes); goto checks; } @@ -5490,6 +5494,9 @@ refill_cluster: if (offset) { /* we found one, proceed */ spin_unlock(&last_ptr->refill_lock); + trace_btrfs_reserve_extent_cluster(root, + block_group, search_start, + num_bytes); goto checks; } } else if (!cached && loop > LOOP_CACHING_NOWAIT @@ -5576,6 +5583,8 @@ checks: ins->objectid = search_start; ins->offset = num_bytes; + trace_btrfs_reserve_extent(orig_root, block_group, + search_start, num_bytes); if (offset < search_start) btrfs_add_free_space(used_block_group, offset, search_start - offset); diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 6c7887a..efe2003 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2346,6 +2346,8 @@ again: &entry->offset_index, 1); BUG_ON(ret); + trace_btrfs_setup_cluster(block_group, cluster, + total_found * block_group->sectorsize, 1); return 0; } @@ -2368,6 +2370,7 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, u64 window_start; u64 window_free; u64 max_extent; + u64 total_size = 0; entry = tree_search_offset(ctl, offset, 0, 1); if (!entry) @@ -2433,11 +2436,12 @@ setup_cluster_no_bitmap(struct btrfs_block_group_cache *block_group, rb_erase(&entry->offset_index, &ctl->free_space_offset); ret = tree_insert_offset(&cluster->root, entry->offset, &entry->offset_index, 0); + total_size += entry->bytes; BUG_ON(ret); } while (node && entry != last); cluster->max_size = max_extent; - + trace_btrfs_setup_cluster(block_group, cluster, total_size, 0); return 0; } @@ -2542,6 +2546,10 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, goto out; } + trace_btrfs_find_cluster(block_group, offset, bytes, empty_size, + min_bytes); + + INIT_LIST_HEAD(&bitmaps); ret = setup_cluster_no_bitmap(block_group, cluster, &bitmaps, offset, bytes + empty_size, cont1_bytes, min_bytes); @@ -2559,6 +2567,8 @@ int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, list_add_tail(&cluster->block_group_list, &block_group->cluster_list); cluster->block_group = block_group; + } else { + trace_btrfs_failed_cluster_setup(block_group); } out: spin_unlock(&cluster->lock); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index b31702a..1750c0e 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -16,6 +16,8 @@ struct btrfs_delayed_ref_node; struct btrfs_delayed_tree_ref; struct btrfs_delayed_data_ref; struct btrfs_delayed_ref_head; +struct btrfs_block_group_cache; +struct btrfs_free_cluster; struct map_lookup; struct extent_buffer; @@ -44,6 +46,15 @@ struct extent_buffer; obj, ((obj >= BTRFS_DATA_RELOC_TREE_OBJECTID) || \ (obj <= BTRFS_CSUM_TREE_OBJECTID )) ? __show_root_type(obj) : "-" +#define BTRFS_GROUP_FLAGS \ + { BTRFS_BLOCK_GROUP_DATA, "DATA"}, \ + { BTRFS_BLOCK_GROUP_SYSTEM, "SYSTEM"}, \ + { BTRFS_BLOCK_GROUP_METADATA, "METADATA"}, \ + { BTRFS_BLOCK_GROUP_RAID0, "RAID0"}, \ + { BTRFS_BLOCK_GROUP_RAID1, "RAID1"}, \ + { BTRFS_BLOCK_GROUP_DUP, "DUP"}, \ + { BTRFS_BLOCK_GROUP_RAID10, "RAID10"} + TRACE_EVENT(btrfs_transaction_commit, TP_PROTO(struct btrfs_root *root), @@ -659,6 +670,168 @@ DEFINE_EVENT(btrfs__reserved_extent, btrfs_reserved_extent_free, TP_ARGS(root, start, len) ); +TRACE_EVENT(find_free_extent, + + TP_PROTO(struct btrfs_root *root, u64 num_bytes, u64 empty_size, + u64 data), + + TP_ARGS(root, num_bytes, empty_size, data), + + TP_STRUCT__entry( + __field( u64, root_objectid ) + __field( u64, num_bytes ) + __field( u64, empty_size ) + __field( u64, data ) + ), + + TP_fast_assign( + __entry->root_objectid = root->root_key.objectid; + __entry->num_bytes = num_bytes; + __entry->empty_size = empty_size; + __entry->data = data; + ), + + TP_printk("root = %Lu(%s), len = %Lu, empty_size = %Lu, " + "flags = %Lu(%s)", show_root_type(__entry->root_objectid), + __entry->num_bytes, __entry->empty_size, __entry->data, + __print_flags((unsigned long)__entry->data, "|", + BTRFS_GROUP_FLAGS)) +); + +DECLARE_EVENT_CLASS(btrfs__reserve_extent, + + TP_PROTO(struct btrfs_root *root, + struct btrfs_block_group_cache *block_group, u64 start, + u64 len), + + TP_ARGS(root, block_group, start, len), + + TP_STRUCT__entry( + __field( u64, root_objectid ) + __field( u64, bg_objectid ) + __field( u64, flags ) + __field( u64, start ) + __field( u64, len ) + ), + + TP_fast_assign( + __entry->root_objectid = root->root_key.objectid; + __entry->bg_objectid = block_group->key.objectid; + __entry->flags = block_group->flags; + __entry->start = start; + __entry->len = len; + ), + + TP_printk("root = %Lu(%s), block_group = %Lu, flags = %Lu(%s), " + "start = %Lu, len = %Lu", + show_root_type(__entry->root_objectid), __entry->bg_objectid, + __entry->flags, __print_flags((unsigned long)__entry->flags, + "|", BTRFS_GROUP_FLAGS), + __entry->start, __entry->len) +); + +DEFINE_EVENT(btrfs__reserve_extent, btrfs_reserve_extent, + + TP_PROTO(struct btrfs_root *root, + struct btrfs_block_group_cache *block_group, u64 start, + u64 len), + + TP_ARGS(root, block_group, start, len) +); + +DEFINE_EVENT(btrfs__reserve_extent, btrfs_reserve_extent_cluster, + + TP_PROTO(struct btrfs_root *root, + struct btrfs_block_group_cache *block_group, u64 start, + u64 len), + + TP_ARGS(root, block_group, start, len) +); + +TRACE_EVENT(btrfs_find_cluster, + + TP_PROTO(struct btrfs_block_group_cache *block_group, u64 start, + u64 bytes, u64 empty_size, u64 min_bytes), + + TP_ARGS(block_group, start, bytes, empty_size, min_bytes), + + TP_STRUCT__entry( + __field( u64, bg_objectid ) + __field( u64, flags ) + __field( u64, start ) + __field( u64, bytes ) + __field( u64, empty_size ) + __field( u64, min_bytes ) + ), + + TP_fast_assign( + __entry->bg_objectid = block_group->key.objectid; + __entry->flags = block_group->flags; + __entry->start = start; + __entry->bytes = bytes; + __entry->empty_size = empty_size; + __entry->min_bytes = min_bytes; + ), + + TP_printk("block_group = %Lu, flags = %Lu(%s), start = %Lu, len = %Lu," + " empty_size = %Lu, min_bytes = %Lu", __entry->bg_objectid, + __entry->flags, + __print_flags((unsigned long)__entry->flags, "|", + BTRFS_GROUP_FLAGS), __entry->start, + __entry->bytes, __entry->empty_size, __entry->min_bytes) +); + +TRACE_EVENT(btrfs_failed_cluster_setup, + + TP_PROTO(struct btrfs_block_group_cache *block_group), + + TP_ARGS(block_group), + + TP_STRUCT__entry( + __field( u64, bg_objectid ) + ), + + TP_fast_assign( + __entry->bg_objectid = block_group->key.objectid; + ), + + TP_printk("block_group = %Lu", __entry->bg_objectid) +); + +TRACE_EVENT(btrfs_setup_cluster, + + TP_PROTO(struct btrfs_block_group_cache *block_group, + struct btrfs_free_cluster *cluster, u64 size, int bitmap), + + TP_ARGS(block_group, cluster, size, bitmap), + + TP_STRUCT__entry( + __field( u64, bg_objectid ) + __field( u64, flags ) + __field( u64, start ) + __field( u64, max_size ) + __field( u64, size ) + __field( int, bitmap ) + ), + + TP_fast_assign( + __entry->bg_objectid = block_group->key.objectid; + __entry->flags = block_group->flags; + __entry->start = cluster->window_start; + __entry->max_size = cluster->max_size; + __entry->size = size; + __entry->bitmap = bitmap; + ), + + TP_printk("block_group = %Lu, flags = %Lu(%s), window_start = %Lu, " + "size = %Lu, max_size = %Lu, bitmap = %d", + __entry->bg_objectid, + __entry->flags, + __print_flags((unsigned long)__entry->flags, "|", + BTRFS_GROUP_FLAGS), __entry->start, + __entry->size, __entry->max_size, __entry->bitmap) +); + #endif /* _TRACE_BTRFS_H */ /* This part must be outside protection */ -- cgit v0.10.2 From 90290e19820e3323ce6b9c2888eeb68bf29c278b Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 2 Dec 2011 15:44:12 -0500 Subject: Btrfs: protect orphan block rsv with spin_lock We've been seeing warnings coming out of the orphan commit stuff forever from ceph. Turns out it's because we're racing with checking if the orphan block reserve is set, because we clear it outside of the spin_lock. So leave the normal fastpath checks where they are, but take the spin_lock and _recheck_ to make sure we haven't had an orphan block rsv added in the meantime. Then clear the root's orphan block rsv and release the lock. With this patch a user said the warnings went away and they usually showed up pretty soon after he started ceph. Thanks, Signed-off-by: Josef Bacik diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 85fd86e..619742d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1951,12 +1951,28 @@ enum btrfs_orphan_cleanup_state { void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, struct btrfs_root *root) { + struct btrfs_block_rsv *block_rsv; int ret; if (!list_empty(&root->orphan_list) || root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE) return; + spin_lock(&root->orphan_lock); + if (!list_empty(&root->orphan_list)) { + spin_unlock(&root->orphan_lock); + return; + } + + if (root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE) { + spin_unlock(&root->orphan_lock); + return; + } + + block_rsv = root->orphan_block_rsv; + root->orphan_block_rsv = NULL; + spin_unlock(&root->orphan_lock); + if (root->orphan_item_inserted && btrfs_root_refs(&root->root_item) > 0) { ret = btrfs_del_orphan_item(trans, root->fs_info->tree_root, @@ -1965,10 +1981,9 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, root->orphan_item_inserted = 0; } - if (root->orphan_block_rsv) { - WARN_ON(root->orphan_block_rsv->size > 0); - btrfs_free_block_rsv(root, root->orphan_block_rsv); - root->orphan_block_rsv = NULL; + if (block_rsv) { + WARN_ON(block_rsv->size > 0); + btrfs_free_block_rsv(root, block_rsv); } } -- cgit v0.10.2 From 8c2a3ca20f6233677ac3222c6506174010eb414f Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 10 Jan 2012 10:31:31 -0500 Subject: Btrfs: space leak tracepoints This in addition to a script in my btrfs-tracing tree will help track down space leaks when we're getting space left over in block groups on umount. Thanks, Signed-off-by: Josef Bacik diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 9c1eccc..fe4cd0f 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -595,8 +595,12 @@ static int btrfs_delayed_item_reserve_metadata(struct btrfs_trans_handle *trans, num_bytes = btrfs_calc_trans_metadata_size(root, 1); ret = btrfs_block_rsv_migrate(src_rsv, dst_rsv, num_bytes); - if (!ret) + if (!ret) { + trace_btrfs_space_reservation(root->fs_info, "delayed_item", + item->key.objectid, + num_bytes, 1); item->bytes_reserved = num_bytes; + } return ret; } @@ -610,6 +614,9 @@ static void btrfs_delayed_item_release_metadata(struct btrfs_root *root, return; rsv = &root->fs_info->delayed_block_rsv; + trace_btrfs_space_reservation(root->fs_info, "delayed_item", + item->key.objectid, item->bytes_reserved, + 0); btrfs_block_rsv_release(root, rsv, item->bytes_reserved); } @@ -624,7 +631,7 @@ static int btrfs_delayed_inode_reserve_metadata( struct btrfs_block_rsv *dst_rsv; u64 num_bytes; int ret; - int release = false; + bool release = false; src_rsv = trans->block_rsv; dst_rsv = &root->fs_info->delayed_block_rsv; @@ -651,8 +658,13 @@ static int btrfs_delayed_inode_reserve_metadata( */ if (ret == -EAGAIN) ret = -ENOSPC; - if (!ret) + if (!ret) { node->bytes_reserved = num_bytes; + trace_btrfs_space_reservation(root->fs_info, + "delayed_inode", + btrfs_ino(inode), + num_bytes, 1); + } return ret; } else if (src_rsv == &root->fs_info->delalloc_block_rsv) { spin_lock(&BTRFS_I(inode)->lock); @@ -707,11 +719,17 @@ out: * reservation here. I think it may be time for a documentation page on * how block rsvs. work. */ - if (!ret) + if (!ret) { + trace_btrfs_space_reservation(root->fs_info, "delayed_inode", + btrfs_ino(inode), num_bytes, 1); node->bytes_reserved = num_bytes; + } - if (release) + if (release) { + trace_btrfs_space_reservation(root->fs_info, "delalloc", + btrfs_ino(inode), num_bytes, 0); btrfs_block_rsv_release(root, src_rsv, num_bytes); + } return ret; } @@ -725,6 +743,8 @@ static void btrfs_delayed_inode_release_metadata(struct btrfs_root *root, return; rsv = &root->fs_info->delayed_block_rsv; + trace_btrfs_space_reservation(root->fs_info, "delayed_inode", + node->inode_id, node->bytes_reserved, 0); btrfs_block_rsv_release(root, rsv, node->bytes_reserved); node->bytes_reserved = 0; @@ -1372,13 +1392,6 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, goto release_node; } - ret = btrfs_delayed_item_reserve_metadata(trans, root, delayed_item); - /* - * we have reserved enough space when we start a new transaction, - * so reserving metadata failure is impossible - */ - BUG_ON(ret); - delayed_item->key.objectid = btrfs_ino(dir); btrfs_set_key_type(&delayed_item->key, BTRFS_DIR_INDEX_KEY); delayed_item->key.offset = index; @@ -1391,6 +1404,14 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, dir_item->type = type; memcpy((char *)(dir_item + 1), name, name_len); + ret = btrfs_delayed_item_reserve_metadata(trans, root, delayed_item); + /* + * we have reserved enough space when we start a new transaction, + * so reserving metadata failure is impossible + */ + BUG_ON(ret); + + mutex_lock(&delayed_node->mutex); ret = __btrfs_add_delayed_insertion_item(delayed_node, delayed_item); if (unlikely(ret)) { diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index ad1a20b..556f9aa 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3310,6 +3310,8 @@ commit_trans: return -ENOSPC; } data_sinfo->bytes_may_use += bytes; + trace_btrfs_space_reservation(root->fs_info, "space_info", + (u64)data_sinfo, bytes, 1); spin_unlock(&data_sinfo->lock); return 0; @@ -3329,6 +3331,8 @@ void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes) data_sinfo = BTRFS_I(inode)->space_info; spin_lock(&data_sinfo->lock); data_sinfo->bytes_may_use -= bytes; + trace_btrfs_space_reservation(root->fs_info, "space_info", + (u64)data_sinfo, bytes, 0); spin_unlock(&data_sinfo->lock); } @@ -3686,6 +3690,10 @@ again: if (used <= space_info->total_bytes) { if (used + orig_bytes <= space_info->total_bytes) { space_info->bytes_may_use += orig_bytes; + trace_btrfs_space_reservation(root->fs_info, + "space_info", + (u64)space_info, + orig_bytes, 1); ret = 0; } else { /* @@ -3753,6 +3761,10 @@ again: if (used + num_bytes < space_info->total_bytes + avail) { space_info->bytes_may_use += orig_bytes; + trace_btrfs_space_reservation(root->fs_info, + "space_info", + (u64)space_info, + orig_bytes, 1); ret = 0; } else { wait_ordered = true; @@ -3859,7 +3871,8 @@ static void block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv, spin_unlock(&block_rsv->lock); } -static void block_rsv_release_bytes(struct btrfs_block_rsv *block_rsv, +static void block_rsv_release_bytes(struct btrfs_fs_info *fs_info, + struct btrfs_block_rsv *block_rsv, struct btrfs_block_rsv *dest, u64 num_bytes) { struct btrfs_space_info *space_info = block_rsv->space_info; @@ -3895,6 +3908,9 @@ static void block_rsv_release_bytes(struct btrfs_block_rsv *block_rsv, if (num_bytes) { spin_lock(&space_info->lock); space_info->bytes_may_use -= num_bytes; + trace_btrfs_space_reservation(fs_info, "space_info", + (u64)space_info, + num_bytes, 0); space_info->reservation_progress++; spin_unlock(&space_info->lock); } @@ -4051,7 +4067,8 @@ void btrfs_block_rsv_release(struct btrfs_root *root, if (global_rsv->full || global_rsv == block_rsv || block_rsv->space_info != global_rsv->space_info) global_rsv = NULL; - block_rsv_release_bytes(block_rsv, global_rsv, num_bytes); + block_rsv_release_bytes(root->fs_info, block_rsv, global_rsv, + num_bytes); } /* @@ -4110,11 +4127,15 @@ static void update_global_block_rsv(struct btrfs_fs_info *fs_info) num_bytes = sinfo->total_bytes - num_bytes; block_rsv->reserved += num_bytes; sinfo->bytes_may_use += num_bytes; + trace_btrfs_space_reservation(fs_info, "space_info", + (u64)sinfo, num_bytes, 1); } if (block_rsv->reserved >= block_rsv->size) { num_bytes = block_rsv->reserved - block_rsv->size; sinfo->bytes_may_use -= num_bytes; + trace_btrfs_space_reservation(fs_info, "space_info", + (u64)sinfo, num_bytes, 0); sinfo->reservation_progress++; block_rsv->reserved = block_rsv->size; block_rsv->full = 1; @@ -4149,7 +4170,8 @@ static void init_global_block_rsv(struct btrfs_fs_info *fs_info) static void release_global_block_rsv(struct btrfs_fs_info *fs_info) { - block_rsv_release_bytes(&fs_info->global_block_rsv, NULL, (u64)-1); + block_rsv_release_bytes(fs_info, &fs_info->global_block_rsv, NULL, + (u64)-1); WARN_ON(fs_info->delalloc_block_rsv.size > 0); WARN_ON(fs_info->delalloc_block_rsv.reserved > 0); WARN_ON(fs_info->trans_block_rsv.size > 0); @@ -4166,6 +4188,8 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans, if (!trans->bytes_reserved) return; + trace_btrfs_space_reservation(root->fs_info, "transaction", (u64)trans, + trans->bytes_reserved, 0); btrfs_block_rsv_release(root, trans->block_rsv, trans->bytes_reserved); trans->bytes_reserved = 0; } @@ -4183,6 +4207,8 @@ int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, * when we are truly done with the orphan item. */ u64 num_bytes = btrfs_calc_trans_metadata_size(root, 1); + trace_btrfs_space_reservation(root->fs_info, "orphan", + btrfs_ino(inode), num_bytes, 1); return block_rsv_migrate_bytes(src_rsv, dst_rsv, num_bytes); } @@ -4190,6 +4216,8 @@ void btrfs_orphan_release_metadata(struct inode *inode) { struct btrfs_root *root = BTRFS_I(inode)->root; u64 num_bytes = btrfs_calc_trans_metadata_size(root, 1); + trace_btrfs_space_reservation(root->fs_info, "orphan", + btrfs_ino(inode), num_bytes, 0); btrfs_block_rsv_release(root, root->orphan_block_rsv, num_bytes); } @@ -4370,8 +4398,13 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) if (dropped) to_free += btrfs_calc_trans_metadata_size(root, dropped); - if (to_free) + if (to_free) { btrfs_block_rsv_release(root, block_rsv, to_free); + trace_btrfs_space_reservation(root->fs_info, + "delalloc", + btrfs_ino(inode), + to_free, 0); + } return ret; } @@ -4383,6 +4416,9 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) BTRFS_I(inode)->reserved_extents += nr_extents; spin_unlock(&BTRFS_I(inode)->lock); + if (to_reserve) + trace_btrfs_space_reservation(root->fs_info,"delalloc", + btrfs_ino(inode), to_reserve, 1); block_rsv_add_bytes(block_rsv, to_reserve, 1); return 0; @@ -4412,6 +4448,8 @@ void btrfs_delalloc_release_metadata(struct inode *inode, u64 num_bytes) if (dropped > 0) to_free += btrfs_calc_trans_metadata_size(root, dropped); + trace_btrfs_space_reservation(root->fs_info, "delalloc", + btrfs_ino(inode), to_free, 0); btrfs_block_rsv_release(root, &root->fs_info->delalloc_block_rsv, to_free); } @@ -4666,7 +4704,10 @@ static int btrfs_update_reserved_bytes(struct btrfs_block_group_cache *cache, cache->reserved += num_bytes; space_info->bytes_reserved += num_bytes; if (reserve == RESERVE_ALLOC) { - BUG_ON(space_info->bytes_may_use < num_bytes); + trace_btrfs_space_reservation(cache->fs_info, + "space_info", + (u64)space_info, + num_bytes, 0); space_info->bytes_may_use -= num_bytes; } } @@ -6126,10 +6167,11 @@ use_block_rsv(struct btrfs_trans_handle *trans, return ERR_PTR(-ENOSPC); } -static void unuse_block_rsv(struct btrfs_block_rsv *block_rsv, u32 blocksize) +static void unuse_block_rsv(struct btrfs_fs_info *fs_info, + struct btrfs_block_rsv *block_rsv, u32 blocksize) { block_rsv_add_bytes(block_rsv, blocksize, 0); - block_rsv_release_bytes(block_rsv, NULL, 0); + block_rsv_release_bytes(fs_info, block_rsv, NULL, 0); } /* @@ -6159,7 +6201,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, ret = btrfs_reserve_extent(trans, root, blocksize, blocksize, empty_size, hint, (u64)-1, &ins, 0); if (ret) { - unuse_block_rsv(block_rsv, blocksize); + unuse_block_rsv(root->fs_info, block_rsv, blocksize); return ERR_PTR(ret); } diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c index f8962a9..213ffa8 100644 --- a/fs/btrfs/inode-map.c +++ b/fs/btrfs/inode-map.c @@ -438,6 +438,8 @@ int btrfs_save_ino_cache(struct btrfs_root *root, trans->bytes_reserved); if (ret) goto out; + trace_btrfs_space_reservation(root->fs_info, "ino_cache", (u64)trans, + trans->bytes_reserved, 1); again: inode = lookup_free_ino_inode(root, path); if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) { @@ -498,6 +500,8 @@ again: out_put: iput(inode); out_release: + trace_btrfs_space_reservation(root->fs_info, "ino_cache", (u64)trans, + trans->bytes_reserved, 0); btrfs_block_rsv_release(root, trans->block_rsv, trans->bytes_reserved); out: trans->block_rsv = rsv; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index d5f987b..287a672 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -326,6 +326,8 @@ again: } if (num_bytes) { + trace_btrfs_space_reservation(root->fs_info, "transaction", + (u64)h, num_bytes, 1); h->block_rsv = &root->fs_info->trans_block_rsv; h->bytes_reserved = num_bytes; } diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 1750c0e..84f3001 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -55,6 +55,8 @@ struct extent_buffer; { BTRFS_BLOCK_GROUP_DUP, "DUP"}, \ { BTRFS_BLOCK_GROUP_RAID10, "RAID10"} +#define BTRFS_UUID_SIZE 16 + TRACE_EVENT(btrfs_transaction_commit, TP_PROTO(struct btrfs_root *root), @@ -632,6 +634,34 @@ TRACE_EVENT(btrfs_cow_block, __entry->cow_level) ); +TRACE_EVENT(btrfs_space_reservation, + + TP_PROTO(struct btrfs_fs_info *fs_info, char *type, u64 val, + u64 bytes, int reserve), + + TP_ARGS(fs_info, type, val, bytes, reserve), + + TP_STRUCT__entry( + __array( u8, fsid, BTRFS_UUID_SIZE ) + __string( type, type ) + __field( u64, val ) + __field( u64, bytes ) + __field( int, reserve ) + ), + + TP_fast_assign( + memcpy(__entry->fsid, fs_info->fsid, BTRFS_UUID_SIZE); + __assign_str(type, type); + __entry->val = val; + __entry->bytes = bytes; + __entry->reserve = reserve; + ), + + TP_printk("%pU: %s: %Lu %s %Lu", __entry->fsid, __get_str(type), + __entry->val, __entry->reserve ? "reserve" : "release", + __entry->bytes) +); + DECLARE_EVENT_CLASS(btrfs__reserved_extent, TP_PROTO(struct btrfs_root *root, u64 start, u64 len), -- cgit v0.10.2 From f248679e86fead40cc78e724c7181d6bec1a2046 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 13 Jan 2012 12:09:22 -0500 Subject: Btrfs: add a delalloc mutex to inodes for delalloc reservations I was using i_mutex for this, but we're getting bogus lockdep warnings by doing that and theres no real way to get rid of those, so just stop using i_mutex to protect delalloc metadata reservations and use a delalloc mutex instead. This shouldn't be contended often at all, only if you are writing and mmap writing to the file at the same time. Thanks, Signed-off-by: Josef Bacik diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 634608d2..9b9b15f 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -51,6 +51,9 @@ struct btrfs_inode { /* held while logging the inode in tree-log.c */ struct mutex log_mutex; + /* held while doing delalloc reservations */ + struct mutex delalloc_mutex; + /* used to order data wrt metadata */ struct btrfs_ordered_inode_tree ordered_tree; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 556f9aa..e0ad5f0 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4345,12 +4345,11 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) /* Need to be holding the i_mutex here if we aren't free space cache */ if (btrfs_is_free_space_inode(root, inode)) flush = 0; - else - WARN_ON(!mutex_is_locked(&inode->i_mutex)); if (flush && btrfs_transaction_in_commit(root->fs_info)) schedule_timeout(1); + mutex_lock(&BTRFS_I(inode)->delalloc_mutex); num_bytes = ALIGN(num_bytes, root->sectorsize); spin_lock(&BTRFS_I(inode)->lock); @@ -4405,6 +4404,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) btrfs_ino(inode), to_free, 0); } + mutex_unlock(&BTRFS_I(inode)->delalloc_mutex); return ret; } @@ -4415,6 +4415,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) } BTRFS_I(inode)->reserved_extents += nr_extents; spin_unlock(&BTRFS_I(inode)->lock); + mutex_unlock(&BTRFS_I(inode)->delalloc_mutex); if (to_reserve) trace_btrfs_space_reservation(root->fs_info,"delalloc", diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 619742d..5977987 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2239,14 +2239,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) continue; } nr_truncate++; - /* - * Need to hold the imutex for reservation purposes, not - * a huge deal here but I have a WARN_ON in - * btrfs_delalloc_reserve_space to catch offenders. - */ - mutex_lock(&inode->i_mutex); ret = btrfs_truncate(inode); - mutex_unlock(&inode->i_mutex); } else { nr_unlink++; } @@ -6411,10 +6404,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) u64 page_start; u64 page_end; - /* Need this to keep space reservations serialized */ - mutex_lock(&inode->i_mutex); ret = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE); - mutex_unlock(&inode->i_mutex); if (!ret) ret = btrfs_update_time(vma->vm_file); if (ret) { @@ -6758,6 +6748,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) extent_io_tree_init(&ei->io_tree, &inode->i_data); extent_io_tree_init(&ei->io_failure_tree, &inode->i_data); mutex_init(&ei->log_mutex); + mutex_init(&ei->delalloc_mutex); btrfs_ordered_inode_tree_init(&ei->ordered_tree); INIT_LIST_HEAD(&ei->i_orphan); INIT_LIST_HEAD(&ei->delalloc_inodes); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 7fdf22c..6834be4 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -868,10 +868,8 @@ static int cluster_pages_for_defrag(struct inode *inode, return 0; file_end = (isize - 1) >> PAGE_CACHE_SHIFT; - mutex_lock(&inode->i_mutex); ret = btrfs_delalloc_reserve_space(inode, num_pages << PAGE_CACHE_SHIFT); - mutex_unlock(&inode->i_mutex); if (ret) return ret; again: diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index efe9f79..8c1aae2 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2949,9 +2949,7 @@ static int relocate_file_extent_cluster(struct inode *inode, index = (cluster->start - offset) >> PAGE_CACHE_SHIFT; last_index = (cluster->end - offset) >> PAGE_CACHE_SHIFT; while (index <= last_index) { - mutex_lock(&inode->i_mutex); ret = btrfs_delalloc_reserve_metadata(inode, PAGE_CACHE_SIZE); - mutex_unlock(&inode->i_mutex); if (ret) goto out; -- cgit v0.10.2 From 96bdc7dc61fb1b1e8e858dafb13abee8482ba064 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Mon, 16 Jan 2012 08:13:11 -0500 Subject: Btrfs: use larger system chunks system chunks by default are very small. This makes them slightly larger and also fixes the conditional checks to make sure we don't allocate a billion of them at once. Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index e0ad5f0..700879e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3384,6 +3384,9 @@ static int should_alloc_chunk(struct btrfs_root *root, /* 256MB or 2% of the FS */ thresh = max_t(u64, 256 * 1024 * 1024, div_factor_fine(thresh, 2)); + /* system chunks need a much small threshold */ + if (sinfo->flags & BTRFS_BLOCK_GROUP_SYSTEM) + thresh = 32 * 1024 * 1024; if (num_bytes > thresh && sinfo->bytes_used < div_factor(num_bytes, 8)) return 0; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 59e878f..7ffdb15 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3166,7 +3166,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, max_stripe_size = 256 * 1024 * 1024; max_chunk_size = max_stripe_size; } else if (type & BTRFS_BLOCK_GROUP_SYSTEM) { - max_stripe_size = 8 * 1024 * 1024; + max_stripe_size = 32 * 1024 * 1024; max_chunk_size = 2 * max_stripe_size; } else { printk(KERN_ERR "btrfs: invalid chunk type 0x%llx requested\n", -- cgit v0.10.2 From da87c937e5a2374686edd58df06cfd5050b125fa Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Mon, 16 Jan 2012 15:17:50 -0600 Subject: x86/UV2: Fix new UV2 hardware by using native UV2 broadcast mode Update the use of the Broadcast Assist Unit on SGI Altix UV2 to the use of native UV2 mode on new hardware (not the legacy mode). UV2 native mode has a different format for a broadcast message. We also need quick differentiaton between UV1 and UV2. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20120116211750.GA5767@sgi.com Cc: Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index 8e862aa..4a46b27 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -65,7 +65,7 @@ * UV2: Bit 19 selects between * (0): 10 microsecond timebase and * (1): 80 microseconds - * we're using 655us, similar to UV1: 65 units of 10us + * we're using 560us, similar to UV1: 65 units of 10us */ #define UV1_INTD_SOFT_ACK_TIMEOUT_PERIOD (9UL) #define UV2_INTD_SOFT_ACK_TIMEOUT_PERIOD (15UL) @@ -235,10 +235,10 @@ struct bau_msg_payload { /* - * Message header: 16 bytes (128 bits) (bytes 0x30-0x3f of descriptor) + * UV1 Message header: 16 bytes (128 bits) (bytes 0x30-0x3f of descriptor) * see table 4.2.3.0.1 in broacast_assist spec. */ -struct bau_msg_header { +struct uv1_bau_msg_header { unsigned int dest_subnodeid:6; /* must be 0x10, for the LB */ /* bits 5:0 */ unsigned int base_dest_nasid:15; /* nasid of the first bit */ @@ -318,19 +318,87 @@ struct bau_msg_header { }; /* + * UV2 Message header: 16 bytes (128 bits) (bytes 0x30-0x3f of descriptor) + * see figure 9-2 of harp_sys.pdf + */ +struct uv2_bau_msg_header { + unsigned int base_dest_nasid:15; /* nasid of the first bit */ + /* bits 14:0 */ /* in uvhub map */ + unsigned int dest_subnodeid:5; /* must be 0x10, for the LB */ + /* bits 19:15 */ + unsigned int rsvd_1:1; /* must be zero */ + /* bit 20 */ + /* Address bits 59:21 */ + /* bits 25:2 of address (44:21) are payload */ + /* these next 24 bits become bytes 12-14 of msg */ + /* bits 28:21 land in byte 12 */ + unsigned int replied_to:1; /* sent as 0 by the source to + byte 12 */ + /* bit 21 */ + unsigned int msg_type:3; /* software type of the + message */ + /* bits 24:22 */ + unsigned int canceled:1; /* message canceled, resource + is to be freed*/ + /* bit 25 */ + unsigned int payload_1:3; /* not currently used */ + /* bits 28:26 */ + + /* bits 36:29 land in byte 13 */ + unsigned int payload_2a:3; /* not currently used */ + unsigned int payload_2b:5; /* not currently used */ + /* bits 36:29 */ + + /* bits 44:37 land in byte 14 */ + unsigned int payload_3:8; /* not currently used */ + /* bits 44:37 */ + + unsigned int rsvd_2:7; /* reserved */ + /* bits 51:45 */ + unsigned int swack_flag:1; /* software acknowledge flag */ + /* bit 52 */ + unsigned int rsvd_3a:3; /* must be zero */ + unsigned int rsvd_3b:8; /* must be zero */ + unsigned int rsvd_3c:8; /* must be zero */ + unsigned int rsvd_3d:3; /* must be zero */ + /* bits 74:53 */ + unsigned int fairness:3; /* usually zero */ + /* bits 77:75 */ + + unsigned int sequence:16; /* message sequence number */ + /* bits 93:78 Suppl_A */ + unsigned int chaining:1; /* next descriptor is part of + this activation*/ + /* bit 94 */ + unsigned int multilevel:1; /* multi-level multicast + format */ + /* bit 95 */ + unsigned int rsvd_4:24; /* ordered / source node / + source subnode / aging + must be zero */ + /* bits 119:96 */ + unsigned int command:8; /* message type */ + /* bits 127:120 */ +}; + +/* * The activation descriptor: * The format of the message to send, plus all accompanying control * Should be 64 bytes */ struct bau_desc { - struct pnmask distribution; + struct pnmask distribution; /* * message template, consisting of header and payload: */ - struct bau_msg_header header; - struct bau_msg_payload payload; + union bau_msg_header { + struct uv1_bau_msg_header uv1_hdr; + struct uv2_bau_msg_header uv2_hdr; + } header; + + struct bau_msg_payload payload; }; -/* +/* UV1: * -payload-- ---------header------ * bytes 0-11 bits 41-56 bits 58-81 * A B (2) C (3) @@ -340,6 +408,16 @@ struct bau_desc { * bytes 0-11 bytes 12-14 bytes 16-17 (byte 15 filled in by hw as vector) * ------------payload queue----------- */ +/* UV2: + * -payload-- ---------header------ + * bytes 0-11 bits 70-78 bits 21-44 + * A B (2) C (3) + * + * A/B/C are moved to: + * A C B + * bytes 0-11 bytes 12-14 bytes 16-17 (byte 15 filled in by hw as vector) + * ------------payload queue----------- + */ /* * The payload queue on the destination side is an array of these. @@ -511,6 +589,7 @@ struct bau_control { short osnode; short uvhub_cpu; short uvhub; + short uvhub_version; short cpus_in_socket; short cpus_in_uvhub; short partition_base_pnode; diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 5b55219..1341a2e 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -573,7 +573,7 @@ static int wait_completion(struct bau_desc *bau_desc, right_shift = ((cpu - UV_CPUS_PER_AS) * UV_ACT_STATUS_SIZE); } - if (is_uv1_hub()) + if (bcp->uvhub_version == 1) return uv1_wait_completion(bau_desc, mmr_offset, right_shift, bcp, try); else @@ -757,15 +757,22 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc, { int seq_number = 0; int completion_stat = 0; + int uv1 = 0; long try = 0; unsigned long index; cycles_t time1; cycles_t time2; struct ptc_stats *stat = bcp->statp; struct bau_control *hmaster = bcp->uvhub_master; + struct uv1_bau_msg_header *uv1_hdr = NULL; + struct uv2_bau_msg_header *uv2_hdr = NULL; - if (is_uv1_hub()) + if (bcp->uvhub_version == 1) { + uv1 = 1; uv1_throttle(hmaster, stat); + uv1_hdr = &bau_desc->header.uv1_hdr; + } else + uv2_hdr = &bau_desc->header.uv2_hdr; while (hmaster->uvhub_quiesce) cpu_relax(); @@ -773,14 +780,23 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc, time1 = get_cycles(); do { if (try == 0) { - bau_desc->header.msg_type = MSG_REGULAR; + if (uv1) + uv1_hdr->msg_type = MSG_REGULAR; + else + uv2_hdr->msg_type = MSG_REGULAR; seq_number = bcp->message_number++; } else { - bau_desc->header.msg_type = MSG_RETRY; + if (uv1) + uv1_hdr->msg_type = MSG_RETRY; + else + uv2_hdr->msg_type = MSG_RETRY; stat->s_retry_messages++; } - bau_desc->header.sequence = seq_number; + if (uv1) + uv1_hdr->sequence = seq_number; + else + uv2_hdr->sequence = seq_number; index = (1UL << AS_PUSH_SHIFT) | bcp->uvhub_cpu; bcp->send_message = get_cycles(); @@ -967,7 +983,7 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, stat->s_ntargself++; bau_desc = bcp->descriptor_base; - bau_desc += ITEMS_PER_DESC * bcp->uvhub_cpu; + bau_desc += (ITEMS_PER_DESC * bcp->uvhub_cpu); bau_uvhubs_clear(&bau_desc->distribution, UV_DISTRIBUTION_SIZE); if (set_distrib_bits(flush_mask, bcp, bau_desc, &locals, &remotes)) return NULL; @@ -1083,7 +1099,7 @@ static void __init enable_timeouts(void) */ mmr_image |= (1L << SOFTACK_MSHIFT); if (is_uv2_hub()) { - mmr_image |= (1L << UV2_LEG_SHFT); + mmr_image &= ~(1L << UV2_LEG_SHFT); mmr_image |= (1L << UV2_EXT_SHFT); } write_mmr_misc_control(pnode, mmr_image); @@ -1432,12 +1448,15 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) { int i; int cpu; + int uv1 = 0; unsigned long gpa; unsigned long m; unsigned long n; size_t dsize; struct bau_desc *bau_desc; struct bau_desc *bd2; + struct uv1_bau_msg_header *uv1_hdr; + struct uv2_bau_msg_header *uv2_hdr; struct bau_control *bcp; /* @@ -1451,6 +1470,8 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) gpa = uv_gpa(bau_desc); n = uv_gpa_to_gnode(gpa); m = uv_gpa_to_offset(gpa); + if (is_uv1_hub()) + uv1 = 1; /* the 14-bit pnode */ write_mmr_descriptor_base(pnode, (n << UV_DESC_PSHIFT | m)); @@ -1461,21 +1482,33 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) */ for (i = 0, bd2 = bau_desc; i < (ADP_SZ * ITEMS_PER_DESC); i++, bd2++) { memset(bd2, 0, sizeof(struct bau_desc)); - bd2->header.swack_flag = 1; - /* - * The base_dest_nasid set in the message header is the nasid - * of the first uvhub in the partition. The bit map will - * indicate destination pnode numbers relative to that base. - * They may not be consecutive if nasid striding is being used. - */ - bd2->header.base_dest_nasid = UV_PNODE_TO_NASID(base_pnode); - bd2->header.dest_subnodeid = UV_LB_SUBNODEID; - bd2->header.command = UV_NET_ENDPOINT_INTD; - bd2->header.int_both = 1; - /* - * all others need to be set to zero: - * fairness chaining multilevel count replied_to - */ + if (uv1) { + uv1_hdr = &bd2->header.uv1_hdr; + uv1_hdr->swack_flag = 1; + /* + * The base_dest_nasid set in the message header + * is the nasid of the first uvhub in the partition. + * The bit map will indicate destination pnode numbers + * relative to that base. They may not be consecutive + * if nasid striding is being used. + */ + uv1_hdr->base_dest_nasid = + UV_PNODE_TO_NASID(base_pnode); + uv1_hdr->dest_subnodeid = UV_LB_SUBNODEID; + uv1_hdr->command = UV_NET_ENDPOINT_INTD; + uv1_hdr->int_both = 1; + /* + * all others need to be set to zero: + * fairness chaining multilevel count replied_to + */ + } else { + uv2_hdr = &bd2->header.uv2_hdr; + uv2_hdr->swack_flag = 1; + uv2_hdr->base_dest_nasid = + UV_PNODE_TO_NASID(base_pnode); + uv2_hdr->dest_subnodeid = UV_LB_SUBNODEID; + uv2_hdr->command = UV_NET_ENDPOINT_INTD; + } } for_each_present_cpu(cpu) { if (pnode != uv_blade_to_pnode(uv_cpu_to_blade_id(cpu))) @@ -1728,6 +1761,14 @@ static int scan_sock(struct socket_desc *sdp, struct uvhub_desc *bdp, bcp->cpus_in_socket = sdp->num_cpus; bcp->socket_master = *smasterp; bcp->uvhub = bdp->uvhub; + if (is_uv1_hub()) + bcp->uvhub_version = 1; + else if (is_uv2_hub()) + bcp->uvhub_version = 2; + else { + printk(KERN_EMERG "uvhub version not 1 or 2\n"); + return 1; + } bcp->uvhub_master = *hmasterp; bcp->uvhub_cpu = uv_cpu_hub_info(cpu)->blade_processor_id; if (bcp->uvhub_cpu >= MAX_CPUS_PER_UVHUB) { @@ -1867,7 +1908,8 @@ static int __init uv_bau_init(void) val = 1L << 63; write_gmmr_activation(pnode, val); mmr = 1; /* should be 1 to broadcast to both sockets */ - write_mmr_data_broadcast(pnode, mmr); + if (!is_uv1_hub()) + write_mmr_data_broadcast(pnode, mmr); } } -- cgit v0.10.2 From d059f9fa84a30e04279c6ff615e9e2cf3b260191 Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Mon, 16 Jan 2012 15:18:48 -0600 Subject: x86/UV2: Fix BAU destination timeout initialization Move the call to enable_timeouts() forward so that BAU_MISC_CONTROL is initialized before using it in calculate_destination_timeout(). Fix the calculation of a BAU destination timeout for UV2 (in calculate_destination_timeout()). Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20120116211848.GB5767@sgi.com Cc: Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 1341a2e..c425ff1 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1617,14 +1617,14 @@ static int calculate_destination_timeout(void) ts_ns = base * mult1 * mult2; ret = ts_ns / 1000; } else { - /* 4 bits 0/1 for 10/80us, 3 bits of multiplier */ - mmr_image = uv_read_local_mmr(UVH_AGING_PRESCALE_SEL); + /* 4 bits 0/1 for 10/80us base, 3 bits of multiplier */ + mmr_image = uv_read_local_mmr(UVH_LB_BAU_MISC_CONTROL); mmr_image = (mmr_image & UV_SA_MASK) >> UV_SA_SHFT; if (mmr_image & (1L << UV2_ACK_UNITS_SHFT)) - mult1 = 80; + base = 80; else - mult1 = 10; - base = mmr_image & UV2_ACK_MASK; + base = 10; + mult1 = mmr_image & UV2_ACK_MASK; ret = mult1 * base; } return ret; @@ -1886,6 +1886,8 @@ static int __init uv_bau_init(void) uv_base_pnode = uv_blade_to_pnode(uvhub); } + enable_timeouts(); + if (init_per_cpu(nuvhubs, uv_base_pnode)) { nobau = 1; return 0; @@ -1896,7 +1898,6 @@ static int __init uv_bau_init(void) if (uv_blade_nr_possible_cpus(uvhub)) init_uvhub(uvhub, vector, uv_base_pnode); - enable_timeouts(); alloc_intr_gate(vector, uv_bau_message_intr1); for_each_possible_blade(uvhub) { -- cgit v0.10.2 From c5d35d399e685acccc85a675e8765c26b2a9813a Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Mon, 16 Jan 2012 15:19:47 -0600 Subject: x86/UV2: Work around BAU bug This patch implements a workaround for a UV2 hardware bug. The bug is a non-atomic update of a memory-mapped register. When hardware message delivery and software message acknowledge occur simultaneously the pending message acknowledge for the arriving message may be lost. This causes the sender's message status to stay busy. Part of the workaround is to not acknowledge a completed message until it is verified that no other message is actually using the resource that is mistakenly recorded in the completed message. Part of the workaround is to test for long elapsed time in such a busy condition, then handle it by using a spare sending descriptor. The stay-busy condition is eventually timed out by hardware, and then the original sending descriptor can be re-used. Most of that logic change is in keeping track of the current descriptor and the state of the spares. The occurrences of the workaround are added to the BAU statistics. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20120116211947.GC5767@sgi.com Cc: Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index 4a46b27..1b82f7e 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -167,6 +167,7 @@ #define FLUSH_RETRY_TIMEOUT 2 #define FLUSH_GIVEUP 3 #define FLUSH_COMPLETE 4 +#define FLUSH_RETRY_BUSYBUG 5 /* * tuning the action when the numalink network is extremely delayed @@ -463,7 +464,6 @@ struct bau_pq_entry { struct msg_desc { struct bau_pq_entry *msg; int msg_slot; - int swack_slot; struct bau_pq_entry *queue_first; struct bau_pq_entry *queue_last; }; @@ -517,6 +517,9 @@ struct ptc_stats { unsigned long s_retry_messages; /* retry broadcasts */ unsigned long s_bau_reenabled; /* for bau enable/disable */ unsigned long s_bau_disabled; /* for bau enable/disable */ + unsigned long s_uv2_wars; /* uv2 workaround, perm. busy */ + unsigned long s_uv2_wars_hw; /* uv2 workaround, hiwater */ + unsigned long s_uv2_war_waits; /* uv2 workaround, long waits */ /* destination statistics */ unsigned long d_alltlb; /* times all tlb's on this cpu were flushed */ @@ -593,6 +596,8 @@ struct bau_control { short cpus_in_socket; short cpus_in_uvhub; short partition_base_pnode; + short using_desc; /* an index, like uvhub_cpu */ + unsigned int inuse_map; unsigned short message_number; unsigned short uvhub_quiesce; short socket_acknowledge_count[DEST_Q_SIZE]; @@ -610,6 +615,7 @@ struct bau_control { int cong_response_us; int cong_reps; int cong_period; + unsigned long clocks_per_100_usec; cycles_t period_time; long period_requests; struct hub_and_pnode *thp; @@ -670,6 +676,11 @@ static inline void write_mmr_sw_ack(unsigned long mr) uv_write_local_mmr(UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_ALIAS, mr); } +static inline void write_gmmr_sw_ack(int pnode, unsigned long mr) +{ + write_gmmr(pnode, UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_ALIAS, mr); +} + static inline unsigned long read_mmr_sw_ack(void) { return read_lmmr(UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE); diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index c425ff1..9010ca7 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -157,13 +157,14 @@ static int __init uvhub_to_first_apicid(int uvhub) * clear of the Timeout bit (as well) will free the resource. No reply will * be sent (the hardware will only do one reply per message). */ -static void reply_to_message(struct msg_desc *mdp, struct bau_control *bcp) +static void reply_to_message(struct msg_desc *mdp, struct bau_control *bcp, + int do_acknowledge) { unsigned long dw; struct bau_pq_entry *msg; msg = mdp->msg; - if (!msg->canceled) { + if (!msg->canceled && do_acknowledge) { dw = (msg->swack_vec << UV_SW_ACK_NPENDING) | msg->swack_vec; write_mmr_sw_ack(dw); } @@ -212,8 +213,8 @@ static void bau_process_retry_msg(struct msg_desc *mdp, if (mmr & (msg_res << UV_SW_ACK_NPENDING)) { unsigned long mr; /* - * is the resource timed out? - * make everyone ignore the cancelled message. + * Is the resource timed out? + * Make everyone ignore the cancelled message. */ msg2->canceled = 1; stat->d_canceled++; @@ -231,8 +232,8 @@ static void bau_process_retry_msg(struct msg_desc *mdp, * Do all the things a cpu should do for a TLB shootdown message. * Other cpu's may come here at the same time for this message. */ -static void bau_process_message(struct msg_desc *mdp, - struct bau_control *bcp) +static void bau_process_message(struct msg_desc *mdp, struct bau_control *bcp, + int do_acknowledge) { short socket_ack_count = 0; short *sp; @@ -284,8 +285,9 @@ static void bau_process_message(struct msg_desc *mdp, if (msg_ack_count == bcp->cpus_in_uvhub) { /* * All cpus in uvhub saw it; reply + * (unless we are in the UV2 workaround) */ - reply_to_message(mdp, bcp); + reply_to_message(mdp, bcp, do_acknowledge); } } @@ -491,27 +493,138 @@ static int uv1_wait_completion(struct bau_desc *bau_desc, /* * UV2 has an extra bit of status in the ACTIVATION_STATUS_2 register. */ -static unsigned long uv2_read_status(unsigned long offset, int rshft, int cpu) +static unsigned long uv2_read_status(unsigned long offset, int rshft, int desc) { unsigned long descriptor_status; unsigned long descriptor_status2; descriptor_status = ((read_lmmr(offset) >> rshft) & UV_ACT_STATUS_MASK); - descriptor_status2 = (read_mmr_uv2_status() >> cpu) & 0x1UL; + descriptor_status2 = (read_mmr_uv2_status() >> desc) & 0x1UL; descriptor_status = (descriptor_status << 1) | descriptor_status2; return descriptor_status; } +/* + * Return whether the status of the descriptor that is normally used for this + * cpu (the one indexed by its hub-relative cpu number) is busy. + * The status of the original 32 descriptors is always reflected in the 64 + * bits of UVH_LB_BAU_SB_ACTIVATION_STATUS_0. + * The bit provided by the activation_status_2 register is irrelevant to + * the status if it is only being tested for busy or not busy. + */ +int normal_busy(struct bau_control *bcp) +{ + int cpu = bcp->uvhub_cpu; + int mmr_offset; + int right_shift; + + mmr_offset = UVH_LB_BAU_SB_ACTIVATION_STATUS_0; + right_shift = cpu * UV_ACT_STATUS_SIZE; + return (((((read_lmmr(mmr_offset) >> right_shift) & + UV_ACT_STATUS_MASK)) << 1) == UV2H_DESC_BUSY); +} + +/* + * Entered when a bau descriptor has gone into a permanent busy wait because + * of a hardware bug. + * Workaround the bug. + */ +int handle_uv2_busy(struct bau_control *bcp) +{ + int busy_one = bcp->using_desc; + int normal = bcp->uvhub_cpu; + int selected = -1; + int i; + unsigned long descriptor_status; + unsigned long status; + int mmr_offset; + struct bau_desc *bau_desc_old; + struct bau_desc *bau_desc_new; + struct bau_control *hmaster = bcp->uvhub_master; + struct ptc_stats *stat = bcp->statp; + cycles_t ttm; + + stat->s_uv2_wars++; + spin_lock(&hmaster->uvhub_lock); + /* try for the original first */ + if (busy_one != normal) { + if (!normal_busy(bcp)) + selected = normal; + } + if (selected < 0) { + /* can't use the normal, select an alternate */ + mmr_offset = UVH_LB_BAU_SB_ACTIVATION_STATUS_1; + descriptor_status = read_lmmr(mmr_offset); + + /* scan available descriptors 32-63 */ + for (i = 0; i < UV_CPUS_PER_AS; i++) { + if ((hmaster->inuse_map & (1 << i)) == 0) { + status = ((descriptor_status >> + (i * UV_ACT_STATUS_SIZE)) & + UV_ACT_STATUS_MASK) << 1; + if (status != UV2H_DESC_BUSY) { + selected = i + UV_CPUS_PER_AS; + break; + } + } + } + } + + if (busy_one != normal) + /* mark the busy alternate as not in-use */ + hmaster->inuse_map &= ~(1 << (busy_one - UV_CPUS_PER_AS)); + + if (selected >= 0) { + /* switch to the selected descriptor */ + if (selected != normal) { + /* set the selected alternate as in-use */ + hmaster->inuse_map |= + (1 << (selected - UV_CPUS_PER_AS)); + if (selected > stat->s_uv2_wars_hw) + stat->s_uv2_wars_hw = selected; + } + bau_desc_old = bcp->descriptor_base; + bau_desc_old += (ITEMS_PER_DESC * busy_one); + bcp->using_desc = selected; + bau_desc_new = bcp->descriptor_base; + bau_desc_new += (ITEMS_PER_DESC * selected); + *bau_desc_new = *bau_desc_old; + } else { + /* + * All are busy. Wait for the normal one for this cpu to + * free up. + */ + stat->s_uv2_war_waits++; + spin_unlock(&hmaster->uvhub_lock); + ttm = get_cycles(); + do { + cpu_relax(); + } while (normal_busy(bcp)); + spin_lock(&hmaster->uvhub_lock); + /* switch to the original descriptor */ + bcp->using_desc = normal; + bau_desc_old = bcp->descriptor_base; + bau_desc_old += (ITEMS_PER_DESC * bcp->using_desc); + bcp->using_desc = (ITEMS_PER_DESC * normal); + bau_desc_new = bcp->descriptor_base; + bau_desc_new += (ITEMS_PER_DESC * normal); + *bau_desc_new = *bau_desc_old; /* copy the entire descriptor */ + } + spin_unlock(&hmaster->uvhub_lock); + return FLUSH_RETRY_BUSYBUG; +} + static int uv2_wait_completion(struct bau_desc *bau_desc, unsigned long mmr_offset, int right_shift, struct bau_control *bcp, long try) { unsigned long descriptor_stat; cycles_t ttm; - int cpu = bcp->uvhub_cpu; + int desc = bcp->using_desc; + long busy_reps = 0; struct ptc_stats *stat = bcp->statp; - descriptor_stat = uv2_read_status(mmr_offset, right_shift, cpu); + descriptor_stat = uv2_read_status(mmr_offset, right_shift, desc); /* spin on the status MMR, waiting for it to go idle */ while (descriptor_stat != UV2H_DESC_IDLE) { @@ -542,12 +655,23 @@ static int uv2_wait_completion(struct bau_desc *bau_desc, bcp->conseccompletes = 0; return FLUSH_RETRY_TIMEOUT; } else { + busy_reps++; + if (busy_reps > 1000000) { + /* not to hammer on the clock */ + busy_reps = 0; + ttm = get_cycles(); + if ((ttm - bcp->send_message) > + (bcp->clocks_per_100_usec)) { + return handle_uv2_busy(bcp); + } + } /* * descriptor_stat is still BUSY */ cpu_relax(); } - descriptor_stat = uv2_read_status(mmr_offset, right_shift, cpu); + descriptor_stat = uv2_read_status(mmr_offset, right_shift, + desc); } bcp->conseccompletes++; return FLUSH_COMPLETE; @@ -563,14 +687,14 @@ static int wait_completion(struct bau_desc *bau_desc, { int right_shift; unsigned long mmr_offset; - int cpu = bcp->uvhub_cpu; + int desc = bcp->using_desc; - if (cpu < UV_CPUS_PER_AS) { + if (desc < UV_CPUS_PER_AS) { mmr_offset = UVH_LB_BAU_SB_ACTIVATION_STATUS_0; - right_shift = cpu * UV_ACT_STATUS_SIZE; + right_shift = desc * UV_ACT_STATUS_SIZE; } else { mmr_offset = UVH_LB_BAU_SB_ACTIVATION_STATUS_1; - right_shift = ((cpu - UV_CPUS_PER_AS) * UV_ACT_STATUS_SIZE); + right_shift = ((desc - UV_CPUS_PER_AS) * UV_ACT_STATUS_SIZE); } if (bcp->uvhub_version == 1) @@ -752,8 +876,7 @@ static void handle_cmplt(int completion_status, struct bau_desc *bau_desc, * Returns 1 if it gives up entirely and the original cpu mask is to be * returned to the kernel. */ -int uv_flush_send_and_wait(struct bau_desc *bau_desc, - struct cpumask *flush_mask, struct bau_control *bcp) +int uv_flush_send_and_wait(struct cpumask *flush_mask, struct bau_control *bcp) { int seq_number = 0; int completion_stat = 0; @@ -766,20 +889,24 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc, struct bau_control *hmaster = bcp->uvhub_master; struct uv1_bau_msg_header *uv1_hdr = NULL; struct uv2_bau_msg_header *uv2_hdr = NULL; + struct bau_desc *bau_desc; - if (bcp->uvhub_version == 1) { - uv1 = 1; + if (bcp->uvhub_version == 1) uv1_throttle(hmaster, stat); - uv1_hdr = &bau_desc->header.uv1_hdr; - } else - uv2_hdr = &bau_desc->header.uv2_hdr; while (hmaster->uvhub_quiesce) cpu_relax(); time1 = get_cycles(); do { - if (try == 0) { + bau_desc = bcp->descriptor_base; + bau_desc += (ITEMS_PER_DESC * bcp->using_desc); + if (bcp->uvhub_version == 1) { + uv1 = 1; + uv1_hdr = &bau_desc->header.uv1_hdr; + } else + uv2_hdr = &bau_desc->header.uv2_hdr; + if ((try == 0) || (completion_stat == FLUSH_RETRY_BUSYBUG)) { if (uv1) uv1_hdr->msg_type = MSG_REGULAR; else @@ -797,13 +924,14 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc, uv1_hdr->sequence = seq_number; else uv2_hdr->sequence = seq_number; - index = (1UL << AS_PUSH_SHIFT) | bcp->uvhub_cpu; + index = (1UL << AS_PUSH_SHIFT) | bcp->using_desc; bcp->send_message = get_cycles(); write_mmr_activation(index); try++; completion_stat = wait_completion(bau_desc, bcp, try); + /* UV2: wait_completion() may change the bcp->using_desc */ handle_cmplt(completion_stat, bau_desc, bcp, hmaster, stat); @@ -814,6 +942,7 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc, } cpu_relax(); } while ((completion_stat == FLUSH_RETRY_PLUGGED) || + (completion_stat == FLUSH_RETRY_BUSYBUG) || (completion_stat == FLUSH_RETRY_TIMEOUT)); time2 = get_cycles(); @@ -828,6 +957,7 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc, record_send_stats(time1, time2, bcp, stat, completion_stat, try); if (completion_stat == FLUSH_GIVEUP) + /* FLUSH_GIVEUP will fall back to using IPI's for tlb flush */ return 1; return 0; } @@ -983,7 +1113,7 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, stat->s_ntargself++; bau_desc = bcp->descriptor_base; - bau_desc += (ITEMS_PER_DESC * bcp->uvhub_cpu); + bau_desc += (ITEMS_PER_DESC * bcp->using_desc); bau_uvhubs_clear(&bau_desc->distribution, UV_DISTRIBUTION_SIZE); if (set_distrib_bits(flush_mask, bcp, bau_desc, &locals, &remotes)) return NULL; @@ -996,13 +1126,86 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, * uv_flush_send_and_wait returns 0 if all cpu's were messaged, * or 1 if it gave up and the original cpumask should be returned. */ - if (!uv_flush_send_and_wait(bau_desc, flush_mask, bcp)) + if (!uv_flush_send_and_wait(flush_mask, bcp)) return NULL; else return cpumask; } /* + * Search the message queue for any 'other' message with the same software + * acknowledge resource bit vector. + */ +struct bau_pq_entry *find_another_by_swack(struct bau_pq_entry *msg, + struct bau_control *bcp, unsigned char swack_vec) +{ + struct bau_pq_entry *msg_next = msg + 1; + + if (msg_next > bcp->queue_last) + msg_next = bcp->queue_first; + while ((msg_next->swack_vec != 0) && (msg_next != msg)) { + if (msg_next->swack_vec == swack_vec) + return msg_next; + msg_next++; + if (msg_next > bcp->queue_last) + msg_next = bcp->queue_first; + } + return NULL; +} + +/* + * UV2 needs to work around a bug in which an arriving message has not + * set a bit in the UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE register. + * Such a message must be ignored. + */ +void process_uv2_message(struct msg_desc *mdp, struct bau_control *bcp) +{ + unsigned long mmr_image; + unsigned char swack_vec; + struct bau_pq_entry *msg = mdp->msg; + struct bau_pq_entry *other_msg; + + mmr_image = read_mmr_sw_ack(); + swack_vec = msg->swack_vec; + + if ((swack_vec & mmr_image) == 0) { + /* + * This message was assigned a swack resource, but no + * reserved acknowlegment is pending. + * The bug has prevented this message from setting the MMR. + * And no other message has used the same sw_ack resource. + * Do the requested shootdown but do not reply to the msg. + * (the 0 means make no acknowledge) + */ + bau_process_message(mdp, bcp, 0); + return; + } + + /* + * Some message has set the MMR 'pending' bit; it might have been + * another message. Look for that message. + */ + other_msg = find_another_by_swack(msg, bcp, msg->swack_vec); + if (other_msg) { + /* There is another. Do not ack the current one. */ + bau_process_message(mdp, bcp, 0); + /* + * Let the natural processing of that message acknowledge + * it. Don't get the processing of sw_ack's out of order. + */ + return; + } + + /* + * There is no other message using this sw_ack, so it is safe to + * acknowledge it. + */ + bau_process_message(mdp, bcp, 1); + + return; +} + +/* * The BAU message interrupt comes here. (registered by set_intr_gate) * See entry_64.S * @@ -1038,9 +1241,11 @@ void uv_bau_message_interrupt(struct pt_regs *regs) count++; msgdesc.msg_slot = msg - msgdesc.queue_first; - msgdesc.swack_slot = ffs(msg->swack_vec) - 1; msgdesc.msg = msg; - bau_process_message(&msgdesc, bcp); + if (bcp->uvhub_version == 2) + process_uv2_message(&msgdesc, bcp); + else + bau_process_message(&msgdesc, bcp, 1); msg++; if (msg > msgdesc.queue_last) @@ -1158,7 +1363,7 @@ static int ptc_seq_show(struct seq_file *file, void *data) seq_printf(file, "all one mult none retry canc nocan reset rcan "); seq_printf(file, - "disable enable\n"); + "disable enable wars warshw warwaits\n"); } if (cpu < num_possible_cpus() && cpu_online(cpu)) { stat = &per_cpu(ptcstats, cpu); @@ -1189,8 +1394,10 @@ static int ptc_seq_show(struct seq_file *file, void *data) stat->d_nomsg, stat->d_retries, stat->d_canceled, stat->d_nocanceled, stat->d_resets, stat->d_rcanceled); - seq_printf(file, "%ld %ld\n", - stat->s_bau_disabled, stat->s_bau_reenabled); + seq_printf(file, "%ld %ld %ld %ld %ld\n", + stat->s_bau_disabled, stat->s_bau_reenabled, + stat->s_uv2_wars, stat->s_uv2_wars_hw, + stat->s_uv2_war_waits); } return 0; } @@ -1564,6 +1771,7 @@ static void pq_init(int node, int pnode) write_mmr_payload_first(pnode, pn_first); write_mmr_payload_tail(pnode, first); write_mmr_payload_last(pnode, last); + write_gmmr_sw_ack(pnode, 0xffffUL); /* in effect, all msg_type's are set to MSG_NOOP */ memset(pqp, 0, sizeof(struct bau_pq_entry) * DEST_Q_SIZE); @@ -1651,6 +1859,7 @@ static void __init init_per_cpu_tunables(void) bcp->cong_response_us = congested_respns_us; bcp->cong_reps = congested_reps; bcp->cong_period = congested_period; + bcp->clocks_per_100_usec = usec_2_cycles(100); } } @@ -1771,6 +1980,7 @@ static int scan_sock(struct socket_desc *sdp, struct uvhub_desc *bdp, } bcp->uvhub_master = *hmasterp; bcp->uvhub_cpu = uv_cpu_hub_info(cpu)->blade_processor_id; + bcp->using_desc = bcp->uvhub_cpu; if (bcp->uvhub_cpu >= MAX_CPUS_PER_UVHUB) { printk(KERN_EMERG "%d cpus per uvhub invalid\n", bcp->uvhub_cpu); -- cgit v0.10.2 From 478c6e529e7bd7c6ef8994c55bd252c287c35893 Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Mon, 16 Jan 2012 15:20:50 -0600 Subject: x86/UV2: Remove stale no-resources test for UV2 BAU This patch removes an unnecessary test for a no-destination-resources-available condition that looks like a destination timeout in UV1, but is separately distinguishable in UV2. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20120116212050.GD5767@sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 9010ca7..affea50 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -642,16 +642,6 @@ static int uv2_wait_completion(struct bau_desc *bau_desc, } else if (descriptor_stat == UV2H_DESC_DEST_TIMEOUT) { stat->s_dtimeout++; ttm = get_cycles(); - /* - * Our retries may be blocked by all destination - * swack resources being consumed, and a timeout - * pending. In that case hardware returns the - * ERROR that looks like a destination timeout. - */ - if (cycles_2_us(ttm - bcp->send_message) < timeout_us) { - bcp->conseccompletes = 0; - return FLUSH_RETRY_PLUGGED; - } bcp->conseccompletes = 0; return FLUSH_RETRY_TIMEOUT; } else { -- cgit v0.10.2 From 88ed9dd7f63c3ae71c1984d99ee2dced0b386dea Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Mon, 16 Jan 2012 15:21:46 -0600 Subject: x86/UV2: Ack BAU interrupt earlier This patch moves the ack of the BAU interrupt to the beginning of the interrupt handler so that there is less possibility of a lost interrupt and slower response to a shootdown message. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20120116212146.GE5767@sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index affea50..4686bf1 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1218,6 +1218,7 @@ void uv_bau_message_interrupt(struct pt_regs *regs) struct ptc_stats *stat; struct msg_desc msgdesc; + ack_APIC_irq(); time_start = get_cycles(); bcp = &per_cpu(bau_control, smp_processor_id()); @@ -1247,8 +1248,6 @@ void uv_bau_message_interrupt(struct pt_regs *regs) stat->d_nomsg++; else if (count > 1) stat->d_multmsg++; - - ack_APIC_irq(); } /* -- cgit v0.10.2 From b54bd9be35f4084edb3eb9ee054a43f722a67483 Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Mon, 16 Jan 2012 15:22:38 -0600 Subject: x86/UV2: Add accounting for BAU strong nacks This patch adds separate accounting of UV2 message "strong nack's" in the BAU statistics. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20120116212238.GF5767@sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index 1b82f7e..becf47b 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -483,6 +483,7 @@ struct ptc_stats { requests */ unsigned long s_stimeout; /* source side timeouts */ unsigned long s_dtimeout; /* destination side timeouts */ + unsigned long s_strongnacks; /* number of strong nack's */ unsigned long s_time; /* time spent in sending side */ unsigned long s_retriesok; /* successful retries */ unsigned long s_ntargcpu; /* total number of cpu's diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 4686bf1..9be4cff 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -635,13 +635,15 @@ static int uv2_wait_completion(struct bau_desc *bau_desc, * our message and its state will stay IDLE. */ if ((descriptor_stat == UV2H_DESC_SOURCE_TIMEOUT) || - (descriptor_stat == UV2H_DESC_DEST_STRONG_NACK) || (descriptor_stat == UV2H_DESC_DEST_PUT_ERR)) { stat->s_stimeout++; return FLUSH_GIVEUP; + } else if (descriptor_stat == UV2H_DESC_DEST_STRONG_NACK) { + stat->s_strongnacks++; + bcp->conseccompletes = 0; + return FLUSH_GIVEUP; } else if (descriptor_stat == UV2H_DESC_DEST_TIMEOUT) { stat->s_dtimeout++; - ttm = get_cycles(); bcp->conseccompletes = 0; return FLUSH_RETRY_TIMEOUT; } else { @@ -1346,7 +1348,7 @@ static int ptc_seq_show(struct seq_file *file, void *data) seq_printf(file, "remotehub numuvhubs numuvhubs16 numuvhubs8 "); seq_printf(file, - "numuvhubs4 numuvhubs2 numuvhubs1 dto retries rok "); + "numuvhubs4 numuvhubs2 numuvhubs1 dto snacks retries rok "); seq_printf(file, "resetp resett giveup sto bz throt swack recv rtime "); seq_printf(file, @@ -1364,10 +1366,10 @@ static int ptc_seq_show(struct seq_file *file, void *data) stat->s_ntargremotes, stat->s_ntargcpu, stat->s_ntarglocaluvhub, stat->s_ntargremoteuvhub, stat->s_ntarguvhub, stat->s_ntarguvhub16); - seq_printf(file, "%ld %ld %ld %ld %ld ", + seq_printf(file, "%ld %ld %ld %ld %ld %ld ", stat->s_ntarguvhub8, stat->s_ntarguvhub4, stat->s_ntarguvhub2, stat->s_ntarguvhub1, - stat->s_dtimeout); + stat->s_dtimeout, stat->s_strongnacks); seq_printf(file, "%ld %ld %ld %ld %ld %ld %ld %ld ", stat->s_retry_messages, stat->s_retriesok, stat->s_resets_plug, stat->s_resets_timeout, -- cgit v0.10.2 From da4d8b287abe783d30e968155614531a0937d090 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Tue, 29 Nov 2011 22:13:35 +0800 Subject: ACPICA: Put back the call to acpi_os_validate_address The call to acpi_os_validate_address in acpi_ds_get_region_arguments was removed by mistake in commit 9ad19ac(ACPICA: Split large dsopcode and dsload.c files). Put it back. Cc: stable@vger.kernel.org # 2.6.38+ Reported-and-bisected-by: Luca Tettamanti Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index 8c7b997..42163d8 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -387,5 +387,29 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) status = acpi_ds_execute_arguments(node, node->parent, extra_desc->extra.aml_length, extra_desc->extra.aml_start); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Validate the region address/length via the host OS */ + + status = acpi_os_validate_address(obj_desc->region.space_id, + obj_desc->region.address, + (acpi_size) obj_desc->region.length, + acpi_ut_get_node_name(node)); + + if (ACPI_FAILURE(status)) { + /* + * Invalid address/length. We will emit an error message and mark + * the region as invalid, so that it will cause an additional error if + * it is ever used. Then return AE_OK. + */ + ACPI_EXCEPTION((AE_INFO, status, + "During address validation of OpRegion [%4.4s]", + node->name.ascii)); + obj_desc->common.flags |= AOPOBJ_INVALID; + status = AE_OK; + } + return_ACPI_STATUS(status); } -- cgit v0.10.2 From 42f5284306465dc263ce227b0c5bd2c7dcd43cdf Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 28 Nov 2011 09:43:52 +0800 Subject: ACPICA: Update for GCC 4 Fixes several issues with GCC 4.6 related to the new checks for unused variables. http://www.acpica.org/bugzilla/show_bug.cgi?id=935 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 7d797e2..302c74e 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -293,14 +293,10 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) { - acpi_thread_id this_thread_id; - ACPI_FUNCTION_NAME(ut_release_mutex); - this_thread_id = acpi_os_get_thread_id(); - ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %u releasing Mutex [%s]\n", - (u32)this_thread_id, + (u32)acpi_os_get_thread_id(), acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { @@ -329,7 +325,8 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) * the ACPI subsystem code. */ for (i = mutex_id; i < ACPI_NUM_MUTEX; i++) { - if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) { + if (acpi_gbl_mutex_info[i].thread_id == + acpi_os_get_thread_id()) { if (i == mutex_id) { continue; } -- cgit v0.10.2 From 8931d9ea78848b073bf299594f148b83abde4a5e Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 28 Nov 2011 09:46:02 +0800 Subject: ACPICA: Fix to allow region arguments to reference other scopes Allow referenced objects to be in a different scope. http://www.acpica.org/bugzilla/show_bug.cgi?id=937 http://marc.info/?l=linux-acpi&m=131636632718222&w=2 ACPI Error: [RAMB] Namespace lookup failure, AE_NOT_FOUND (20110112/psargs-359) ACPI Exception: AE_NOT_FOUND, Could not execute arguments for [RAMW] (Region) (20110112/nsinit-349) Scope (_SB) { Name (RAMB, 0xDF5A1018) OperationRegion (\RAMW, SystemMemory, RAMB, 0x00010000) } For above ASL code, we need to save scope node(\_SB) to lookup the argument node(\_SB.RAMB). Reported-by: Jim Green Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 1055769..6d276c2 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -358,6 +358,7 @@ typedef enum { */ struct acpi_object_extra { ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ + struct acpi_namespace_node *scope_node; void *region_context; /* Region-specific data */ u8 *aml_start; u32 aml_length; diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index 42163d8..d69e4a5 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -384,7 +384,7 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) /* Execute the argument AML */ - status = acpi_ds_execute_arguments(node, node->parent, + status = acpi_ds_execute_arguments(node, extra_desc->extra.scope_node, extra_desc->extra.aml_length, extra_desc->extra.aml_start); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 110711a..8a06dc5 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -330,6 +330,12 @@ acpi_ex_create_region(u8 * aml_start, region_obj2 = obj_desc->common.next_object; region_obj2->extra.aml_start = aml_start; region_obj2->extra.aml_length = aml_length; + if (walk_state->scope_info) { + region_obj2->extra.scope_node = + walk_state->scope_info->scope.node; + } else { + region_obj2->extra.scope_node = node; + } /* Init the region from the operands */ -- cgit v0.10.2 From 8519bc9f1e6db43a9b95b70ef0c7b61cb36d58e4 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 28 Nov 2011 10:08:03 +0800 Subject: ACPICA: Add error msg for unsupported I/O requests (not 8/16/32 bit length) Found during ACPICA debugging. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index 5f16058..f8fbea9 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -134,6 +134,8 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) /* Supported widths are 8/16/32 */ if ((bit_width != 8) && (bit_width != 16) && (bit_width != 32)) { + ACPI_ERROR((AE_INFO, + "Bad BitWidth parameter: %8.8X", bit_width)); return AE_BAD_PARAMETER; } -- cgit v0.10.2 From 46dfb09c024a1a92ead63b40f6993dcb5eb7d153 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 28 Nov 2011 10:10:10 +0800 Subject: ACPICA: Add error msg for Index/Bank field registers out-of-range Supplement the exception code with an actual message. Found during ACPICA debugging. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index f915a7f..19df8ce 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -316,6 +316,7 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc, static u8 acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value) { + ACPI_FUNCTION_NAME(ex_register_overflow); if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) { /* @@ -330,6 +331,11 @@ acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value) * The Value is larger than the maximum value that can fit into * the register. */ + ACPI_ERROR((AE_INFO, + "Index value 0x%8.8X%8.8X overflows field width 0x%X", + ACPI_FORMAT_UINT64(value), + obj_desc->common_field.bit_length)); + return (TRUE); } -- cgit v0.10.2 From ec4636669bf9c6ff157121ab42709650a9e0cc2a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Nov 2011 09:35:05 +0800 Subject: ACPICA: Do not abort table load on invalid space ID Ignore an invalid space ID during a table load. Instead, detect it if a control method attempts access - then abort the method. http://www.acpica.org/bugzilla/show_bug.cgi?id=925 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index 3731e1c..4ee9058 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -468,6 +468,8 @@ void acpi_ex_eisa_id_to_string(char *dest, u64 compressed_id); void acpi_ex_integer_to_string(char *dest, u64 value); +u8 acpi_is_valid_space_id(u8 space_id); + /* * exregion - default op_region handlers */ diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 8a06dc5..c66562b 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -267,7 +267,7 @@ acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state) * * PARAMETERS: aml_start - Pointer to the region declaration AML * aml_length - Max length of the declaration AML - * region_space - space_iD for the region + * space_id - Address space ID for the region * walk_state - Current state * * RETURN: Status @@ -279,7 +279,7 @@ acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state) acpi_status acpi_ex_create_region(u8 * aml_start, u32 aml_length, - u8 region_space, struct acpi_walk_state *walk_state) + u8 space_id, struct acpi_walk_state *walk_state) { acpi_status status; union acpi_operand_object *obj_desc; @@ -304,16 +304,19 @@ acpi_ex_create_region(u8 * aml_start, * Space ID must be one of the predefined IDs, or in the user-defined * range */ - if ((region_space >= ACPI_NUM_PREDEFINED_REGIONS) && - (region_space < ACPI_USER_REGION_BEGIN) && - (region_space != ACPI_ADR_SPACE_DATA_TABLE)) { - ACPI_ERROR((AE_INFO, "Invalid AddressSpace type 0x%X", - region_space)); - return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); + if (!acpi_is_valid_space_id(space_id)) { + /* + * Print an error message, but continue. We don't want to abort + * a table load for this exception. Instead, if the region is + * actually used at runtime, abort the executing method. + */ + ACPI_ERROR((AE_INFO, + "Invalid/unknown Address Space ID: 0x%2.2X", + space_id)); } ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Region Type - %s (0x%X)\n", - acpi_ut_get_region_name(region_space), region_space)); + acpi_ut_get_region_name(space_id), space_id)); /* Create the region descriptor */ @@ -339,7 +342,7 @@ acpi_ex_create_region(u8 * aml_start, /* Init the region from the operands */ - obj_desc->region.space_id = region_space; + obj_desc->region.space_id = space_id; obj_desc->region.address = 0; obj_desc->region.length = 0; obj_desc->region.node = node; diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 19df8ce..2a524fc 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -86,6 +86,7 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, { acpi_status status = AE_OK; union acpi_operand_object *rgn_desc; + u8 space_id; ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset); @@ -101,6 +102,17 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } + space_id = rgn_desc->region.space_id; + + /* Validate the Space ID */ + + if (!acpi_is_valid_space_id(space_id)) { + ACPI_ERROR((AE_INFO, + "Invalid/unknown Address Space ID: 0x%2.2X", + space_id)); + return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); + } + /* * If the Region Address and Length have not been previously evaluated, * evaluate them now and save the results. @@ -122,8 +134,8 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, * Exit now for SMBus or IPMI address space, it has a non-linear * address space and the request cannot be directly validated */ - if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS || - rgn_desc->region.space_id == ACPI_ADR_SPACE_IPMI) { + if (space_id == ACPI_ADR_SPACE_SMBUS || + space_id == ACPI_ADR_SPACE_IPMI) { /* SMBus or IPMI has a non-linear address space */ diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index 8ad9314..3993aa5 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -435,4 +435,29 @@ void acpi_ex_integer_to_string(char *out_string, u64 value) } } +/******************************************************************************* + * + * FUNCTION: acpi_is_valid_space_id + * + * PARAMETERS: space_id - ID to be validated + * + * RETURN: TRUE if valid/supported ID. + * + * DESCRIPTION: Validate an operation region space_iD. + * + ******************************************************************************/ + +u8 acpi_is_valid_space_id(u8 space_id) +{ + + if ((space_id >= ACPI_NUM_PREDEFINED_REGIONS) && + (space_id < ACPI_USER_REGION_BEGIN) && + (space_id != ACPI_ADR_SPACE_DATA_TABLE) && + (space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) { + return (FALSE); + } + + return (TRUE); +} + #endif -- cgit v0.10.2 From 2355e10f07b2c550c671f98b68aedf323c299aeb Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 10:42:09 +0800 Subject: ACPI 5.0: Basic support for FADT version 5 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 6f5588e..536fd2b 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -277,12 +277,12 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) { /* * Check if the FADT is larger than the largest table that we expect - * (the ACPI 2.0/3.0 version). If so, truncate the table, and issue + * (the ACPI 5.0 version). If so, truncate the table, and issue * a warning. */ if (length > sizeof(struct acpi_table_fadt)) { ACPI_WARNING((AE_INFO, - "FADT (revision %u) is longer than ACPI 2.0 version, " + "FADT (revision %u) is longer than ACPI 5.0 version, " "truncating length %u to %u", table->revision, length, (u32)sizeof(struct acpi_table_fadt))); diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index f138028..7d0e051 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -255,6 +255,8 @@ struct acpi_table_fadt { struct acpi_generic_address xpm_timer_block; /* 64-bit Extended Power Mgt Timer Ctrl Reg Blk address */ struct acpi_generic_address xgpe0_block; /* 64-bit Extended General Purpose Event 0 Reg Blk address */ struct acpi_generic_address xgpe1_block; /* 64-bit Extended General Purpose Event 1 Reg Blk address */ + struct acpi_generic_address sleep_control; /* 64-bit Sleep Control register */ + struct acpi_generic_address sleep_status; /* 64-bit Sleep Status register */ }; /* Masks for FADT Boot Architecture Flags (boot_flags) */ @@ -264,6 +266,7 @@ struct acpi_table_fadt { #define ACPI_FADT_NO_VGA (1<<2) /* 02: [V4] It is not safe to probe for VGA hardware */ #define ACPI_FADT_NO_MSI (1<<3) /* 03: [V4] Message Signaled Interrupts (MSI) must not be enabled */ #define ACPI_FADT_NO_ASPM (1<<4) /* 04: [V4] PCIe ASPM control must not be enabled */ +#define ACPI_FADT_NO_CMOS_RTC (1<<5) /* 05: [V5] No CMOS real-time clock present */ #define FADT2_REVISION_ID 3 @@ -289,6 +292,8 @@ struct acpi_table_fadt { #define ACPI_FADT_REMOTE_POWER_ON (1<<17) /* 17: [V4] System is compatible with remote power on (ACPI 3.0) */ #define ACPI_FADT_APIC_CLUSTER (1<<18) /* 18: [V4] All local APICs must use cluster model (ACPI 3.0) */ #define ACPI_FADT_APIC_PHYSICAL (1<<19) /* 19: [V4] All local x_aPICs must use physical dest mode (ACPI 3.0) */ +#define ACPI_FADT_HW_REDUCED (1<<20) /* 20: [V5] ACPI hardware is not implemented (ACPI 5.0) */ +#define ACPI_FADT_LOW_POWER_S0 (1<<21) /* 21: [V5] S0 power savings are equal or better than S3 (ACPI 5.0) */ /* Values for preferred_profile (Preferred Power Management Profiles) */ @@ -299,14 +304,16 @@ enum acpi_prefered_pm_profiles { PM_WORKSTATION = 3, PM_ENTERPRISE_SERVER = 4, PM_SOHO_SERVER = 5, - PM_APPLIANCE_PC = 6 + PM_APPLIANCE_PC = 6, + PM_PERFORMANCE_SERVER = 7, + PM_TABLET = 8 }; /* Reset to default packing */ #pragma pack() -#define ACPI_FADT_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_fadt, f) +#define ACPI_FADT_OFFSET(f) (u16) ACPI_OFFSET (struct acpi_table_fadt, f) /* * Internal table-related structures @@ -351,12 +358,15 @@ struct acpi_table_desc { * FADT is the bottom line as to what the version really is. * * For reference, the values below are as follows: - * FADT V1 size: 0x74 - * FADT V2 size: 0x84 - * FADT V3+ size: 0xF4 + * FADT V1 size: 0x074 + * FADT V2 size: 0x084 + * FADT V3 size: 0x0F4 + * FADT V4 size: 0x0F4 + * FADT V5 size: 0x10C */ #define ACPI_FADT_V1_SIZE (u32) (ACPI_FADT_OFFSET (flags) + 4) #define ACPI_FADT_V2_SIZE (u32) (ACPI_FADT_OFFSET (reserved4[0]) + 3) -#define ACPI_FADT_V3_SIZE (u32) (sizeof (struct acpi_table_fadt)) +#define ACPI_FADT_V3_SIZE (u32) (ACPI_FADT_OFFSET (sleep_control)) +#define ACPI_FADT_V5_SIZE (u32) (sizeof (struct acpi_table_fadt)) #endif /* __ACTBL_H__ */ -- cgit v0.10.2 From 22e5b40ab21fcac21db0ff25fbb844ffecc73a4a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 10:57:28 +0800 Subject: ACPI 5.0: Implement hardware-reduced option If HW-reduced flag is set in the FADT, do not attempt to access or initialize any ACPI hardware, including SCI and global lock. No FACS will be present. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 76dc02f..25912a9 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -140,8 +140,19 @@ u32 acpi_gbl_trace_flags; acpi_name acpi_gbl_trace_method_name; u8 acpi_gbl_system_awake_and_running; +/* + * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning + * that the ACPI hardware is no longer required. A flag in the FADT indicates + * a reduced HW machine, and that flag is duplicated here for convenience. + */ +u8 acpi_gbl_reduced_hardware; + #endif +/* Do not disassemble buffers to resource descriptors */ + +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_no_resource_disassembly, FALSE); + /***************************************************************************** * * Debug support @@ -207,7 +218,7 @@ ACPI_EXTERN struct acpi_rw_lock acpi_gbl_namespace_rw_lock; /***************************************************************************** * - * Mutual exlusion within ACPICA subsystem + * Mutual exclusion within ACPICA subsystem * ****************************************************************************/ diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index d458b04..1f43be4 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -71,6 +71,12 @@ acpi_status acpi_ev_initialize_events(void) ACPI_FUNCTION_TRACE(ev_initialize_events); + /* If Hardware Reduced flag is set, there are no fixed events */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + /* * Initialize the Fixed and General Purpose Events. This is done prior to * enabling SCIs to prevent interrupts from occurring before the handlers @@ -111,6 +117,12 @@ acpi_status acpi_ev_install_xrupt_handlers(void) ACPI_FUNCTION_TRACE(ev_install_xrupt_handlers); + /* If Hardware Reduced flag is set, there is no ACPI h/w */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + /* Install the SCI handler */ status = acpi_ev_install_sci_handler(); diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c index 56a562a..af1e71a 100644 --- a/drivers/acpi/acpica/evglock.c +++ b/drivers/acpi/acpica/evglock.c @@ -70,6 +70,12 @@ acpi_status acpi_ev_init_global_lock_handler(void) ACPI_FUNCTION_TRACE(ev_init_global_lock_handler); + /* If Hardware Reduced flag is set, there is no global lock */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + /* Attempt installation of the global lock handler */ status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL, diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 536fd2b..1e1a7de 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -253,8 +253,13 @@ void acpi_tb_parse_fadt(u32 table_index) acpi_tb_install_table((acpi_physical_address) acpi_gbl_FADT.Xdsdt, ACPI_SIG_DSDT, ACPI_TABLE_INDEX_DSDT); - acpi_tb_install_table((acpi_physical_address) acpi_gbl_FADT.Xfacs, - ACPI_SIG_FACS, ACPI_TABLE_INDEX_FACS); + /* If Hardware Reduced flag is set, there is no FACS */ + + if (!acpi_gbl_reduced_hardware) { + acpi_tb_install_table((acpi_physical_address) acpi_gbl_FADT. + Xfacs, ACPI_SIG_FACS, + ACPI_TABLE_INDEX_FACS); + } } /******************************************************************************* @@ -297,6 +302,13 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) ACPI_MEMCPY(&acpi_gbl_FADT, table, ACPI_MIN(length, sizeof(struct acpi_table_fadt))); + /* Take a copy of the Hardware Reduced flag */ + + acpi_gbl_reduced_hardware = FALSE; + if (acpi_gbl_FADT.flags & ACPI_FADT_HW_REDUCED) { + acpi_gbl_reduced_hardware = TRUE; + } + /* Convert the local copy of the FADT to the common internal format */ acpi_tb_convert_fadt(); @@ -502,6 +514,12 @@ static void acpi_tb_validate_fadt(void) acpi_gbl_FADT.Xdsdt = (u64) acpi_gbl_FADT.dsdt; } + /* If Hardware Reduced flag is set, we are all done */ + + if (acpi_gbl_reduced_hardware) { + return; + } + /* Examine all of the 64-bit extended address fields (X fields) */ for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 0f2d395..da56a19 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -135,6 +135,13 @@ acpi_status acpi_tb_initialize_facs(void) { acpi_status status; + /* If Hardware Reduced flag is set, there is no FACS */ + + if (acpi_gbl_reduced_hardware) { + acpi_gbl_FACS = NULL; + return (AE_OK); + } + status = acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, ACPI_CAST_INDIRECT_PTR(struct acpi_table_header, diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index f554a93..9ba2e3a 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -74,6 +74,7 @@ extern u8 acpi_gbl_disable_auto_repair; extern u32 acpi_current_gpe_count; extern struct acpi_table_fadt acpi_gbl_FADT; extern u8 acpi_gbl_system_awake_and_running; +extern u8 acpi_gbl_reduced_hardware; /* ACPI 5.0 */ extern u32 acpi_rsdt_forced; /* -- cgit v0.10.2 From c5bd6537329e66a8b36234f19a36d94b72d07394 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 11:04:00 +0800 Subject: ACPI 5.0: Add new/changed tables to headers Adds new file, actbl3.h Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 7d0e051..8e1b92f 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -349,6 +349,7 @@ struct acpi_table_desc { #include #include +#include /* * Sizes of the various flavors of FADT. We need to look closely diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 7504bc9..71e747b 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -228,7 +228,8 @@ enum acpi_einj_actions { ACPI_EINJ_EXECUTE_OPERATION = 5, ACPI_EINJ_CHECK_BUSY_STATUS = 6, ACPI_EINJ_GET_COMMAND_STATUS = 7, - ACPI_EINJ_ACTION_RESERVED = 8, /* 8 and greater are reserved */ + ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS = 8, + ACPI_EINJ_ACTION_RESERVED = 9, /* 9 and greater are reserved */ ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */ }; @@ -240,7 +241,27 @@ enum acpi_einj_instructions { ACPI_EINJ_WRITE_REGISTER = 2, ACPI_EINJ_WRITE_REGISTER_VALUE = 3, ACPI_EINJ_NOOP = 4, - ACPI_EINJ_INSTRUCTION_RESERVED = 5 /* 5 and greater are reserved */ + ACPI_EINJ_FLUSH_CACHELINE = 5, + ACPI_EINJ_INSTRUCTION_RESERVED = 6 /* 6 and greater are reserved */ +}; + +struct acpi_einj_error_type_with_addr { + u32 error_type; + u32 vendor_struct_offset; + u32 flags; + u32 apic_id; + u64 address; + u64 range; + u32 pcie_id; +}; + +struct acpi_einj_vendor { + u32 length; + u32 pcie_id; + u16 vendor_id; + u16 device_id; + u8 revision_id; + u8 reserved[3]; }; /* EINJ Trigger Error Action Table */ @@ -275,6 +296,7 @@ enum acpi_einj_command_status { #define ACPI_EINJ_PLATFORM_CORRECTABLE (1<<9) #define ACPI_EINJ_PLATFORM_UNCORRECTABLE (1<<10) #define ACPI_EINJ_PLATFORM_FATAL (1<<11) +#define ACPI_EINJ_VENDOR_DEFINED (1<<31) /******************************************************************************* * @@ -631,7 +653,9 @@ enum acpi_madt_type { ACPI_MADT_TYPE_INTERRUPT_SOURCE = 8, ACPI_MADT_TYPE_LOCAL_X2APIC = 9, ACPI_MADT_TYPE_LOCAL_X2APIC_NMI = 10, - ACPI_MADT_TYPE_RESERVED = 11 /* 11 and greater are reserved */ + ACPI_MADT_TYPE_GENERIC_INTERRUPT = 11, + ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR = 12, + ACPI_MADT_TYPE_RESERVED = 13 /* 13 and greater are reserved */ }; /* @@ -752,11 +776,36 @@ struct acpi_madt_local_x2apic_nmi { u8 reserved[3]; }; +/* 11: Generic Interrupt (ACPI 5.0) */ + +struct acpi_madt_generic_interrupt { + struct acpi_subtable_header header; + u16 reserved; /* Reserved - must be zero */ + u32 gic_id; + u32 uid; + u32 flags; + u32 parking_version; + u32 performance_interrupt; + u64 parked_address; + u64 base_address; +}; + +/* 12: Generic Distributor (ACPI 5.0) */ + +struct acpi_madt_generic_distributor { + struct acpi_subtable_header header; + u16 reserved; /* Reserved - must be zero */ + u32 gic_id; + u64 base_address; + u32 global_irq_base; + u32 reserved2; /* Reserved - must be zero */ +}; + /* * Common flags fields for MADT subtables */ -/* MADT Local APIC flags (lapic_flags) */ +/* MADT Local APIC flags (lapic_flags) and GIC flags */ #define ACPI_MADT_ENABLED (1) /* 00: Processor is usable if set */ diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h new file mode 100644 index 0000000..c22ce80 --- /dev/null +++ b/include/acpi/actbl3.h @@ -0,0 +1,552 @@ +/****************************************************************************** + * + * Name: actbl3.h - ACPI Table Definitions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2011, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACTBL3_H__ +#define __ACTBL3_H__ + +/******************************************************************************* + * + * Additional ACPI Tables (3) + * + * These tables are not consumed directly by the ACPICA subsystem, but are + * included here to support device drivers and the AML disassembler. + * + * The tables in this file are fully defined within the ACPI specification. + * + ******************************************************************************/ + +/* + * Values for description table header signatures for tables defined in this + * file. Useful because they make it more difficult to inadvertently type in + * the wrong signature. + */ +#define ACPI_SIG_BGRT "BGRT" /* Boot Graphics Resource Table */ +#define ACPI_SIG_DRTM "DRTM" /* Dynamic Root of Trust for Measurement table */ +#define ACPI_SIG_FPDT "FPDT" /* Firmware Performance Data Table */ +#define ACPI_SIG_GTDT "GTDT" /* Generic Timer Description Table */ +#define ACPI_SIG_MPST "MPST" /* Memory Power State Table */ +#define ACPI_SIG_PCCT "PCCT" /* Platform Communications Channel Table */ +#define ACPI_SIG_PMTT "PMTT" /* Platform Memory Topology Table */ +#define ACPI_SIG_RASF "RASF" /* RAS Feature table */ + +#define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ +#define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ + +/* Reserved table signatures */ + +#define ACPI_SIG_CSRT "CSRT" /* Core System Resources Table */ +#define ACPI_SIG_DBG2 "DBG2" /* Debug Port table 2 */ +#define ACPI_SIG_MATR "MATR" /* Memory Address Translation Table */ +#define ACPI_SIG_MSDM "MSDM" /* Microsoft Data Management Table */ +#define ACPI_SIG_WPBT "WPBT" /* Windows Platform Binary Table */ + +/* + * All tables must be byte-packed to match the ACPI specification, since + * the tables are provided by the system BIOS. + */ +#pragma pack(1) + +/* + * Note about bitfields: The u8 type is used for bitfields in ACPI tables. + * This is the only type that is even remotely portable. Anything else is not + * portable, so do not use any other bitfield types. + */ + +/******************************************************************************* + * + * BGRT - Boot Graphics Resource Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_bgrt { + struct acpi_table_header header; /* Common ACPI table header */ + u16 version; + u8 status; + u8 image_type; + u64 image_address; + u32 image_offset_x; + u32 image_offset_y; +}; + +/******************************************************************************* + * + * DRTM - Dynamic Root of Trust for Measurement table + * + ******************************************************************************/ + +struct acpi_table_drtm { + struct acpi_table_header header; /* Common ACPI table header */ + u64 entry_base_address; + u64 entry_length; + u32 entry_address32; + u64 entry_address64; + u64 exit_address; + u64 log_area_address; + u32 log_area_length; + u64 arch_dependent_address; + u32 flags; +}; + +/* 1) Validated Tables List */ + +struct acpi_drtm_vtl_list { + u32 validated_table_list_count; +}; + +/* 2) Resources List */ + +struct acpi_drtm_resource_list { + u32 resource_list_count; +}; + +/* 3) Platform-specific Identifiers List */ + +struct acpi_drtm_id_list { + u32 id_list_count; +}; + +/******************************************************************************* + * + * FPDT - Firmware Performance Data Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_fpdt { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* FPDT subtable header */ + +struct acpi_fpdt_header { + u16 type; + u8 length; + u8 revision; +}; + +/* Values for Type field above */ + +enum acpi_fpdt_type { + ACPI_FPDT_TYPE_BOOT = 0, + ACPI_FPDT_TYPE_S3PERF = 1, +}; + +/* + * FPDT subtables + */ + +/* 0: Firmware Basic Boot Performance Record */ + +struct acpi_fpdt_boot { + struct acpi_fpdt_header header; + u8 reserved[4]; + u64 reset_end; + u64 load_start; + u64 startup_start; + u64 exit_services_entry; + u64 exit_services_exit; +}; + +/* 1: S3 Performance Table Pointer Record */ + +struct acpi_fpdt_s3pt_ptr { + struct acpi_fpdt_header header; + u8 reserved[4]; + u64 address; +}; + +/* + * S3PT - S3 Performance Table. This table is pointed to by the + * FPDT S3 Pointer Record above. + */ +struct acpi_table_s3pt { + u8 signature[4]; /* "S3PT" */ + u32 length; +}; + +/* + * S3PT Subtables + */ +struct acpi_s3pt_header { + u16 type; + u8 length; + u8 revision; +}; + +/* Values for Type field above */ + +enum acpi_s3pt_type { + ACPI_S3PT_TYPE_RESUME = 0, + ACPI_S3PT_TYPE_SUSPEND = 1, +}; + +struct acpi_s3pt_resume { + struct acpi_s3pt_header header; + u32 resume_count; + u64 full_resume; + u64 average_resume; +}; + +struct acpi_s3pt_suspend { + struct acpi_s3pt_header header; + u64 suspend_start; + u64 suspend_end; +}; + +/******************************************************************************* + * + * GTDT - Generic Timer Description Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_gtdt { + struct acpi_table_header header; /* Common ACPI table header */ + u64 address; + u32 flags; + u32 secure_pl1_interrupt; + u32 secure_pl1_flags; + u32 non_secure_pl1_interrupt; + u32 non_secure_pl1_flags; + u32 virtual_timer_interrupt; + u32 virtual_timer_flags; + u32 non_secure_pl2_interrupt; + u32 non_secure_pl2_flags; +}; + +/* Values for Flags field above */ + +#define ACPI_GTDT_MAPPED_BLOCK_PRESENT 1 + +/* Values for all "TimerFlags" fields above */ + +#define ACPI_GTDT_INTERRUPT_MODE 1 +#define ACPI_GTDT_INTERRUPT_POLARITY 2 + +/******************************************************************************* + * + * MPST - Memory Power State Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +#define ACPI_MPST_CHANNEL_INFO \ + u16 reserved1; \ + u8 channel_id; \ + u8 reserved2; \ + u16 power_node_count; + +/* Main table */ + +struct acpi_table_mpst { + struct acpi_table_header header; /* Common ACPI table header */ + ACPI_MPST_CHANNEL_INFO /* Platform Communication Channel */ +}; + +/* Memory Platform Communication Channel Info */ + +struct acpi_mpst_channel { + ACPI_MPST_CHANNEL_INFO /* Platform Communication Channel */ +}; + +/* Memory Power Node Structure */ + +struct acpi_mpst_power_node { + u8 flags; + u8 reserved1; + u16 node_id; + u32 length; + u64 range_address; + u64 range_length; + u8 num_power_states; + u8 num_physical_components; + u16 reserved2; +}; + +/* Values for Flags field above */ + +#define ACPI_MPST_ENABLED 1 +#define ACPI_MPST_POWER_MANAGED 2 +#define ACPI_MPST_HOT_PLUG_CAPABLE 4 + +/* Memory Power State Structure (follows POWER_NODE above) */ + +struct acpi_mpst_power_state { + u8 power_state; + u8 info_index; +}; + +/* Physical Component ID Structure (follows POWER_STATE above) */ + +struct acpi_mpst_component { + u16 component_id; +}; + +/* Memory Power State Characteristics Structure (follows all POWER_NODEs) */ + +struct acpi_mpst_data_hdr { + u16 characteristics_count; +}; + +struct acpi_mpst_power_data { + u8 revision; + u8 flags; + u16 reserved1; + u32 average_power; + u32 power_saving; + u64 exit_latency; + u64 reserved2; +}; + +/* Values for Flags field above */ + +#define ACPI_MPST_PRESERVE 1 +#define ACPI_MPST_AUTOENTRY 2 +#define ACPI_MPST_AUTOEXIT 4 + +/* Shared Memory Region (not part of an ACPI table) */ + +struct acpi_mpst_shared { + u32 signature; + u16 pcc_command; + u16 pcc_status; + u16 command_register; + u16 status_register; + u16 power_state_id; + u16 power_node_id; + u64 energy_consumed; + u64 average_power; +}; + +/******************************************************************************* + * + * PCCT - Platform Communications Channel Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_pcct { + struct acpi_table_header header; /* Common ACPI table header */ + u32 flags; + u32 latency; + u32 reserved; +}; + +/* Values for Flags field above */ + +#define ACPI_PCCT_DOORBELL 1 + +/* + * PCCT subtables + */ + +/* 0: Generic Communications Subspace */ + +struct acpi_pcct_subspace { + struct acpi_subtable_header header; + u8 reserved[6]; + u64 base_address; + u64 length; + struct acpi_generic_address doorbell_register; + u64 preserve_mask; + u64 write_mask; +}; + +/* + * PCC memory structures (not part of the ACPI table) + */ + +/* Shared Memory Region */ + +struct acpi_pcct_shared_memory { + u32 signature; + u16 command; + u16 status; +}; + +/******************************************************************************* + * + * PMTT - Platform Memory Topology Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_pmtt { + struct acpi_table_header header; /* Common ACPI table header */ + u32 reserved; +}; + +/* Common header for PMTT subtables that follow main table */ + +struct acpi_pmtt_header { + u8 type; + u8 reserved1; + u16 length; + u16 flags; + u16 reserved2; +}; + +/* Values for Type field above */ + +#define ACPI_PMTT_TYPE_SOCKET 0 +#define ACPI_PMTT_TYPE_CONTROLLER 1 +#define ACPI_PMTT_TYPE_DIMM 2 +#define ACPI_PMTT_TYPE_RESERVED 3 /* 0x03-0xFF are reserved */ + +/* Values for Flags field above */ + +#define ACPI_PMTT_TOP_LEVEL 0x0001 +#define ACPI_PMTT_PHYSICAL 0x0002 +#define ACPI_PMTT_MEMORY_TYPE 0x000C + +/* + * PMTT subtables, correspond to Type in struct acpi_pmtt_header + */ + +/* 0: Socket Structure */ + +struct acpi_pmtt_socket { + struct acpi_pmtt_header header; + u16 socket_id; + u16 reserved; +}; + +/* 1: Memory Controller subtable */ + +struct acpi_pmtt_controller { + struct acpi_pmtt_header header; + u32 read_latency; + u32 write_latency; + u32 read_bandwidth; + u32 write_bandwidth; + u16 access_width; + u16 alignment; + u16 reserved; + u16 domain_count; +}; + +/* 1a: Proximity Domain substructure */ + +struct acpi_pmtt_domain { + u32 proximity_domain; +}; + +/* 2: Physical Component Identifier (DIMM) */ + +struct acpi_pmtt_physical_component { + struct acpi_pmtt_header header; + u16 component_id; + u16 reserved; + u32 memory_size; + u32 bios_handle; +}; + +/******************************************************************************* + * + * RASF - RAS Feature Table (ACPI 5.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_rasf { + struct acpi_table_header header; /* Common ACPI table header */ + u8 channel_id[12]; +}; + +/* RASF Platform Communication Channel Shared Memory Region */ + +struct acpi_rasf_shared_memory { + u32 signature; + u16 command; + u16 status; + u64 requested_address; + u64 requested_length; + u64 actual_address; + u64 actual_length; + u16 flags; + u8 speed; +}; + +/* Masks for Flags and Speed fields above */ + +#define ACPI_RASF_SCRUBBER_RUNNING 1 +#define ACPI_RASF_SPEED (7<<1) + +/* Channel Commands */ + +enum acpi_rasf_commands { + ACPI_RASF_GET_RAS_CAPABILITIES = 1, + ACPI_RASF_GET_PATROL_PARAMETERS = 2, + ACPI_RASF_START_PATROL_SCRUBBER = 3, + ACPI_RASF_STOP_PATROL_SCRUBBER = 4 +}; + +/* Channel Command flags */ + +#define ACPI_RASF_GENERATE_SCI (1<<15) + +/* Status values */ + +enum acpi_rasf_status { + ACPI_RASF_SUCCESS = 0, + ACPI_RASF_NOT_VALID = 1, + ACPI_RASF_NOT_SUPPORTED = 2, + ACPI_RASF_BUSY = 3, + ACPI_RASF_FAILED = 4, + ACPI_RASF_ABORTED = 5, + ACPI_RASF_INVALID_DATA = 6 +}; + +/* Status flags */ + +#define ACPI_RASF_COMMAND_COMPLETE (1) +#define ACPI_RASF_SCI_DOORBELL (1<<1) +#define ACPI_RASF_ERROR (1<<2) +#define ACPI_RASF_STATUS (0x1F<<3) + +/* Reset to default packing */ + +#pragma pack() + +#endif /* __ACTBL3_H__ */ -- cgit v0.10.2 From ffef68273b6278e98a99dd4051671d4854b20fe0 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Wed, 16 Nov 2011 11:08:30 +0800 Subject: ACPI 5.0: New interfaces to allow driver access to AML mutex objects Adds acpi_acquire_mutex, acpi_release_mutex external interfaces. New file, utxfmutex.c. Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 301bd2d..1bd4661 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -45,4 +45,4 @@ acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o \ - utosi.o utxferror.o utdecode.o + utosi.o utxferror.o utdecode.o utxfmutex.o diff --git a/drivers/acpi/acpica/utxfmutex.c b/drivers/acpi/acpica/utxfmutex.c new file mode 100644 index 0000000..1ea016b --- /dev/null +++ b/drivers/acpi/acpica/utxfmutex.c @@ -0,0 +1,187 @@ +/******************************************************************************* + * + * Module Name: utxfmutex - external AML mutex access functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2011, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utxfmutex") + +/* Local prototypes */ +static acpi_status +acpi_ut_get_mutex_object(acpi_handle handle, + acpi_string pathname, + union acpi_operand_object **ret_obj); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_mutex_object + * + * PARAMETERS: Handle - Mutex or prefix handle (optional) + * Pathname - Mutex pathname (optional) + * ret_obj - Where the mutex object is returned + * + * RETURN: Status + * + * DESCRIPTION: Get an AML mutex object. The mutex node is pointed to by + * Handle:Pathname. Either Handle or Pathname can be NULL, but + * not both. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_get_mutex_object(acpi_handle handle, + acpi_string pathname, + union acpi_operand_object **ret_obj) +{ + struct acpi_namespace_node *mutex_node; + union acpi_operand_object *mutex_obj; + acpi_status status; + + /* Parameter validation */ + + if (!ret_obj || (!handle && !pathname)) { + return (AE_BAD_PARAMETER); + } + + /* Get a the namespace node for the mutex */ + + mutex_node = handle; + if (pathname != NULL) { + status = acpi_get_handle(handle, pathname, + ACPI_CAST_PTR(acpi_handle, + &mutex_node)); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + /* Ensure that we actually have a Mutex object */ + + if (!mutex_node || (mutex_node->type != ACPI_TYPE_MUTEX)) { + return (AE_TYPE); + } + + /* Get the low-level mutex object */ + + mutex_obj = acpi_ns_get_attached_object(mutex_node); + if (!mutex_obj) { + return (AE_NULL_OBJECT); + } + + *ret_obj = mutex_obj; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_acquire_mutex + * + * PARAMETERS: Handle - Mutex or prefix handle (optional) + * Pathname - Mutex pathname (optional) + * Timeout - Max time to wait for the lock (millisec) + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex. This is a device driver interface to + * AML mutex objects, and allows for transaction locking between + * drivers and AML code. The mutex node is pointed to by + * Handle:Pathname. Either Handle or Pathname can be NULL, but + * not both. + * + ******************************************************************************/ + +acpi_status +acpi_acquire_mutex(acpi_handle handle, acpi_string pathname, u16 timeout) +{ + acpi_status status; + union acpi_operand_object *mutex_obj; + + /* Get the low-level mutex associated with Handle:Pathname */ + + status = acpi_ut_get_mutex_object(handle, pathname, &mutex_obj); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Acquire the OS mutex */ + + status = acpi_os_acquire_mutex(mutex_obj->mutex.os_mutex, timeout); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_release_mutex + * + * PARAMETERS: Handle - Mutex or prefix handle (optional) + * Pathname - Mutex pathname (optional) + * + * RETURN: Status + * + * DESCRIPTION: Release an AML mutex. This is a device driver interface to + * AML mutex objects, and allows for transaction locking between + * drivers and AML code. The mutex node is pointed to by + * Handle:Pathname. Either Handle or Pathname can be NULL, but + * not both. + * + ******************************************************************************/ + +acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname) +{ + acpi_status status; + union acpi_operand_object *mutex_obj; + + /* Get the low-level mutex associated with Handle:Pathname */ + + status = acpi_ut_get_mutex_object(handle, pathname, &mutex_obj); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Release the OS mutex */ + + acpi_os_release_mutex(mutex_obj->mutex.os_mutex); + return (AE_OK); +} diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 9ba2e3a..dd86610 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -277,12 +277,23 @@ acpi_status acpi_install_exception_handler(acpi_exception_handler handler); acpi_status acpi_install_interface_handler(acpi_interface_handler handler); /* - * Event interfaces + * Global Lock interfaces */ acpi_status acpi_acquire_global_lock(u16 timeout, u32 * handle); acpi_status acpi_release_global_lock(u32 handle); +/* + * Interfaces to AML mutex objects + */ +acpi_status +acpi_acquire_mutex(acpi_handle handle, acpi_string pathname, u16 timeout); + +acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname); + +/* + * Fixed Event interfaces + */ acpi_status acpi_enable_event(u32 event, u32 flags); acpi_status acpi_disable_event(u32 event, u32 flags); @@ -292,7 +303,7 @@ acpi_status acpi_clear_event(u32 event); acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status); /* - * GPE Interfaces + * General Purpose Event (GPE) Interfaces */ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number); -- cgit v0.10.2 From 9ce81784c9c0396a6a6be05248928a71134fe60b Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 13:39:07 +0800 Subject: ACPI 5.0: Implement Connection() and AccessAs() changes Support within the interpreter and operation region dispatch. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index bea3b48..f763b88 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -162,6 +162,7 @@ acpi_status acpi_ev_initialize_op_regions(void); acpi_status acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + union acpi_operand_object *field_obj, u32 function, u32 region_offset, u32 bit_width, u64 *value); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 5552125..52c4ebd 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -53,7 +53,7 @@ typedef u32 acpi_mutex_handle; /* Total number of aml opcodes defined */ -#define AML_NUM_OPCODES 0x7F +#define AML_NUM_OPCODES 0x81 /* Forward declarations */ @@ -249,12 +249,16 @@ struct acpi_create_field_info { struct acpi_namespace_node *field_node; struct acpi_namespace_node *register_node; struct acpi_namespace_node *data_register_node; + struct acpi_namespace_node *connection_node; + u8 *resource_buffer; u32 bank_value; u32 field_bit_position; u32 field_bit_length; + u16 resource_length; u8 field_flags; u8 attribute; u8 field_type; + u8 access_length; }; typedef diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 6d276c2..435dd2f 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -254,6 +254,7 @@ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; u32 base_byte_offset; /* Byte offset within containing object */\ u32 value; /* Value to store into the Bank or Index register */\ u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ + u8 access_length; /* For serial regions/fields */ struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */ @@ -261,7 +262,9 @@ struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and }; struct acpi_object_region_field { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing op_region object */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length; + union acpi_operand_object *region_obj; /* Containing op_region object */ + u8 *resource_buffer; /* resource_template for serial regions/fields */ }; struct acpi_object_bank_field { diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index bb2ccfa..9896c77 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -93,6 +93,7 @@ #define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) #define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) #define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME) +#define ARGP_CONNECTFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) #define ARGP_CONTINUE_OP ARG_NONE #define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME) #define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) @@ -164,6 +165,7 @@ #define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG) #define ARGP_REVISION_OP ARG_NONE #define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST) +#define ARGP_SERIALFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) #define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) #define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) #define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME) @@ -223,6 +225,7 @@ #define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA, ARGI_TARGETREF) #define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF) #define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF) +#define ARGI_CONNECTFIELD_OP ARGI_INVALID_OPCODE #define ARGI_CONTINUE_OP ARGI_INVALID_OPCODE #define ARGI_COPY_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_SIMPLE_TARGET) #define ARGI_CREATE_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) @@ -294,6 +297,7 @@ #define ARGI_RETURN_OP ARGI_INVALID_OPCODE #define ARGI_REVISION_OP ARG_NONE #define ARGI_SCOPE_OP ARGI_INVALID_OPCODE +#define ARGI_SERIALFIELD_OP ARGI_INVALID_OPCODE #define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT) diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index 1077f17..adf7a71 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -189,6 +189,14 @@ #define AML_LNOTEQUAL_OP (u16) 0x9293 /* + * Opcodes for "Field" operators + */ +#define AML_FIELD_OFFSET_OP (u8) 0x00 +#define AML_FIELD_ACCESS_OP (u8) 0x01 +#define AML_FIELD_CONNECTION_OP (u8) 0x02 /* ACPI 5.0 */ +#define AML_FIELD_EXT_ACCESS_OP (u8) 0x03 /* ACPI 5.0 */ + +/* * Internal opcodes * Use only "Unknown" AML opcodes, don't attempt to use * any valid ACPI ASCII values (A-Z, 0-9, '-') @@ -202,6 +210,8 @@ #define AML_INT_METHODCALL_OP (u16) 0x0035 #define AML_INT_RETURN_VALUE_OP (u16) 0x0036 #define AML_INT_EVAL_SUBTREE_OP (u16) 0x0037 +#define AML_INT_CONNECTION_OP (u16) 0x0038 +#define AML_INT_EXTACCESSFIELD_OP (u16) 0x0039 #define ARG_NONE 0x0 diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index 34be60c..aa880d9 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -221,6 +221,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info, { acpi_status status; u64 position; + union acpi_parse_object *child; ACPI_FUNCTION_TRACE_PTR(ds_get_field_names, info); @@ -232,10 +233,11 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info, while (arg) { /* - * Three types of field elements are handled: - * 1) Offset - specifies a bit offset - * 2) access_as - changes the access mode - * 3) Name - Enters a new named field into the namespace + * Four types of field elements are handled: + * 1) Name - Enters a new named field into the namespace + * 2) Offset - specifies a bit offset + * 3) access_as - changes the access mode/attributes + * 4) Connection - Associate a resource template with the field */ switch (arg->common.aml_opcode) { case AML_INT_RESERVEDFIELD_OP: @@ -253,21 +255,70 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info, break; case AML_INT_ACCESSFIELD_OP: - + case AML_INT_EXTACCESSFIELD_OP: /* - * Get a new access_type and access_attribute -- to be used for all - * field units that follow, until field end or another access_as - * keyword. + * Get new access_type, access_attribute, and access_length fields + * -- to be used for all field units that follow, until the + * end-of-field or another access_as keyword is encountered. + * NOTE. These three bytes are encoded in the integer value + * of the parseop for convenience. * * In field_flags, preserve the flag bits other than the - * ACCESS_TYPE bits + * ACCESS_TYPE bits. */ + + /* access_type (byte_acc, word_acc, etc.) */ + info->field_flags = (u8) ((info-> field_flags & ~(AML_FIELD_ACCESS_TYPE_MASK)) | - ((u8) ((u32) arg->common.value.integer >> 8))); + ((u8)((u32)(arg->common.value.integer & 0x07)))); + + /* access_attribute (attrib_quick, attrib_byte, etc.) */ + + info->attribute = + (u8)((arg->common.value.integer >> 8) & 0xFF); + + /* access_length (for serial/buffer protocols) */ + + info->access_length = + (u8)((arg->common.value.integer >> 16) & 0xFF); + break; + + case AML_INT_CONNECTION_OP: + /* + * Clear any previous connection. New connection is used for all + * fields that follow, similar to access_as + */ + info->resource_buffer = NULL; + info->connection_node = NULL; - info->attribute = (u8) (arg->common.value.integer); + /* + * A Connection() is either an actual resource descriptor (buffer) + * or a named reference to a resource template + */ + child = arg->common.value.arg; + if (child->common.aml_opcode == AML_INT_BYTELIST_OP) { + info->resource_buffer = child->named.data; + info->resource_length = + (u16)child->named.value.integer; + } else { + /* Lookup the Connection() namepath, it should already exist */ + + status = acpi_ns_lookup(walk_state->scope_info, + child->common.value. + name, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, + ACPI_NS_DONT_OPEN_SCOPE, + walk_state, + &info->connection_node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(child->common. + value.name, + status); + return_ACPI_STATUS(status); + } + } break; case AML_INT_NAMEDFIELD_OP: @@ -374,6 +425,8 @@ acpi_ds_create_field(union acpi_parse_object *op, } } + ACPI_MEMSET(&info, 0, sizeof(struct acpi_create_field_info)); + /* Second arg is the field flags */ arg = arg->common.next; @@ -386,7 +439,6 @@ acpi_ds_create_field(union acpi_parse_object *op, info.region_node = region_node; status = acpi_ds_get_field_names(&info, walk_state, arg->common.next); - return_ACPI_STATUS(status); } @@ -474,8 +526,8 @@ acpi_ds_init_field_objects(union acpi_parse_object *op, */ while (arg) { /* - * Ignore OFFSET and ACCESSAS terms here; we are only interested in the - * field names in order to enter them into the namespace. + * Ignore OFFSET/ACCESSAS/CONNECTION terms here; we are only interested + * in the field names in order to enter them into the namespace. */ if (arg->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { status = acpi_ns_lookup(walk_state->scope_info, @@ -651,6 +703,5 @@ acpi_ds_create_index_field(union acpi_parse_object *op, info.region_node = region_node; status = acpi_ds_get_field_names(&info, walk_state, arg->common.next); - return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index f0edf5c..dcdc7e4 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -329,6 +329,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) * FUNCTION: acpi_ev_address_space_dispatch * * PARAMETERS: region_obj - Internal region object + * field_obj - Corresponding field. Can be NULL. * Function - Read or Write operation * region_offset - Where in the region to read or write * bit_width - Field width in bits (8, 16, 32, or 64) @@ -344,6 +345,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) acpi_status acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + union acpi_operand_object *field_obj, u32 function, u32 region_offset, u32 bit_width, u64 *value) { @@ -353,6 +355,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, union acpi_operand_object *handler_desc; union acpi_operand_object *region_obj2; void *region_context = NULL; + struct acpi_connection_info *context; ACPI_FUNCTION_TRACE(ev_address_space_dispatch); @@ -375,6 +378,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, return_ACPI_STATUS(AE_NOT_EXIST); } + context = handler_desc->address_space.context; + /* * It may be the case that the region has never been initialized. * Some types of regions require special init code @@ -404,8 +409,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, acpi_ex_exit_interpreter(); status = region_setup(region_obj, ACPI_REGION_ACTIVATE, - handler_desc->address_space.context, - ®ion_context); + context, ®ion_context); /* Re-enter the interpreter */ @@ -455,6 +459,25 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, acpi_ut_get_region_name(region_obj->region. space_id))); + /* + * Special handling for generic_serial_bus and general_purpose_io: + * There are three extra parameters that must be passed to the + * handler via the context: + * 1) Connection buffer, a resource template from Connection() op. + * 2) Length of the above buffer. + * 3) Actual access length from the access_as() op. + */ + if (((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) || + (region_obj->region.space_id == ACPI_ADR_SPACE_GPIO)) && + context && field_obj) { + + /* Get the Connection (resource_template) buffer */ + + context->connection = field_obj->field.resource_buffer; + context->length = field_obj->field.resource_length; + context->access_length = field_obj->field.access_length; + } + if (!(handler_desc->address_space.handler_flags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { /* @@ -469,7 +492,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, status = handler(function, (region_obj->region.address + region_offset), - bit_width, value, handler_desc->address_space.context, + bit_width, value, context, region_obj2->extra.region_context); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 745a42b..efe41f7 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -297,9 +297,9 @@ acpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer) /* Bytewise reads */ for (i = 0; i < length; i++) { - status = acpi_ev_address_space_dispatch(obj_desc, ACPI_READ, - region_offset, 8, - &value); + status = + acpi_ev_address_space_dispatch(obj_desc, NULL, ACPI_READ, + region_offset, 8, &value); if (ACPI_FAILURE(status)) { return status; } diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index 61b8c0e..c7064f4 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -192,10 +192,13 @@ static struct acpi_exdump_info acpi_ex_dump_buffer_field[3] = { "Buffer Object"} }; -static struct acpi_exdump_info acpi_ex_dump_region_field[3] = { +static struct acpi_exdump_info acpi_ex_dump_region_field[5] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_region_field), NULL}, {ACPI_EXD_FIELD, 0, NULL}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.region_obj), "Region Object"} + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(field.access_length), "AccessLength"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.region_obj), "Region Object"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.resource_buffer), + "ResourceBuffer"} }; static struct acpi_exdump_info acpi_ex_dump_bank_field[5] = { diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 2a524fc..cc9c5df 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -283,11 +283,12 @@ acpi_ex_access_region(union acpi_operand_object *obj_desc, /* Invoke the appropriate address_space/op_region handler */ - status = - acpi_ev_address_space_dispatch(rgn_desc, function, region_offset, - ACPI_MUL_8(obj_desc->common_field. - access_byte_width), - value); + status = acpi_ev_address_space_dispatch(rgn_desc, obj_desc, + function, region_offset, + ACPI_MUL_8(obj_desc-> + common_field. + access_byte_width), + value); if (ACPI_FAILURE(status)) { if (status == AE_NOT_IMPLEMENTED) { diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index cc95e20..18b75fe 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -47,6 +47,7 @@ #include "acinterp.h" #include "amlcode.h" #include "acnamesp.h" +#include "acdispat.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exprep") @@ -455,6 +456,30 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) obj_desc->field.region_obj = acpi_ns_get_attached_object(info->region_node); + /* Fields specific to generic_serial_bus fields */ + + obj_desc->field.access_length = info->access_length; + + if (info->connection_node) { + second_desc = info->connection_node->object; + if (!(second_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = + acpi_ds_get_buffer_arguments(second_desc); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(status); + } + } + + obj_desc->field.resource_buffer = + second_desc->buffer.pointer; + obj_desc->field.resource_length = + (u16)second_desc->buffer.length; + } else if (info->resource_buffer) { + obj_desc->field.resource_buffer = info->resource_buffer; + obj_desc->field.resource_length = info->resource_length; + } + /* Allow full data read from EC address space */ if ((obj_desc->field.region_obj->region.space_id == diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index e1fad0e..4037f90 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -484,34 +484,54 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state *parser_state) { - u32 aml_offset = (u32) - ACPI_PTR_DIFF(parser_state->aml, - parser_state->aml_start); + u32 aml_offset; union acpi_parse_object *field; + union acpi_parse_object *arg = NULL; u16 opcode; u32 name; + u8 access_type; + u8 access_attribute; + u8 access_length; + u32 pkg_length; + u8 *pkg_end; + u32 buffer_length; ACPI_FUNCTION_TRACE(ps_get_next_field); + aml_offset = + (u32)ACPI_PTR_DIFF(parser_state->aml, parser_state->aml_start); + /* Determine field type */ switch (ACPI_GET8(parser_state->aml)) { - default: + case AML_FIELD_OFFSET_OP: - opcode = AML_INT_NAMEDFIELD_OP; + opcode = AML_INT_RESERVEDFIELD_OP; + parser_state->aml++; break; - case 0x00: + case AML_FIELD_ACCESS_OP: - opcode = AML_INT_RESERVEDFIELD_OP; + opcode = AML_INT_ACCESSFIELD_OP; parser_state->aml++; break; - case 0x01: + case AML_FIELD_CONNECTION_OP: - opcode = AML_INT_ACCESSFIELD_OP; + opcode = AML_INT_CONNECTION_OP; + parser_state->aml++; + break; + + case AML_FIELD_EXT_ACCESS_OP: + + opcode = AML_INT_EXTACCESSFIELD_OP; parser_state->aml++; break; + + default: + + opcode = AML_INT_NAMEDFIELD_OP; + break; } /* Allocate a new field op */ @@ -549,16 +569,111 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state break; case AML_INT_ACCESSFIELD_OP: + case AML_INT_EXTACCESSFIELD_OP: /* * Get access_type and access_attrib and merge into the field Op - * access_type is first operand, access_attribute is second + * access_type is first operand, access_attribute is second. stuff + * these bytes into the node integer value for convenience. */ - field->common.value.integer = - (((u32) ACPI_GET8(parser_state->aml) << 8)); + + /* Get the two bytes (Type/Attribute) */ + + access_type = ACPI_GET8(parser_state->aml); parser_state->aml++; - field->common.value.integer |= ACPI_GET8(parser_state->aml); + access_attribute = ACPI_GET8(parser_state->aml); parser_state->aml++; + + field->common.value.integer = (u8)access_type; + field->common.value.integer |= (u16)(access_attribute << 8); + + /* This opcode has a third byte, access_length */ + + if (opcode == AML_INT_EXTACCESSFIELD_OP) { + access_length = ACPI_GET8(parser_state->aml); + parser_state->aml++; + + field->common.value.integer |= + (u32)(access_length << 16); + } + break; + + case AML_INT_CONNECTION_OP: + + /* + * Argument for Connection operator can be either a Buffer + * (resource descriptor), or a name_string. + */ + if (ACPI_GET8(parser_state->aml) == AML_BUFFER_OP) { + parser_state->aml++; + + pkg_end = parser_state->aml; + pkg_length = + acpi_ps_get_next_package_length(parser_state); + pkg_end += pkg_length; + + if (parser_state->aml < pkg_end) { + + /* Non-empty list */ + + arg = acpi_ps_alloc_op(AML_INT_BYTELIST_OP); + if (!arg) { + return_PTR(NULL); + } + + /* Get the actual buffer length argument */ + + opcode = ACPI_GET8(parser_state->aml); + parser_state->aml++; + + switch (opcode) { + case AML_BYTE_OP: /* AML_BYTEDATA_ARG */ + buffer_length = + ACPI_GET8(parser_state->aml); + parser_state->aml += 1; + break; + + case AML_WORD_OP: /* AML_WORDDATA_ARG */ + buffer_length = + ACPI_GET16(parser_state->aml); + parser_state->aml += 2; + break; + + case AML_DWORD_OP: /* AML_DWORDATA_ARG */ + buffer_length = + ACPI_GET32(parser_state->aml); + parser_state->aml += 4; + break; + + default: + buffer_length = 0; + break; + } + + /* Fill in bytelist data */ + + arg->named.value.size = buffer_length; + arg->named.data = parser_state->aml; + } + + /* Skip to End of byte data */ + + parser_state->aml = pkg_end; + } else { + arg = acpi_ps_alloc_op(AML_INT_NAMEPATH_OP); + if (!arg) { + return_PTR(NULL); + } + + /* Get the Namestring argument */ + + arg->common.value.name = + acpi_ps_get_next_namestring(parser_state); + } + + /* Link the buffer/namestring to parent (CONNECTION_OP) */ + + acpi_ps_append_arg(field, arg); break; default: diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c index bed08de..9b31910 100644 --- a/drivers/acpi/acpica/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -638,7 +638,16 @@ const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = { /* 7E */ ACPI_OP("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, - AML_FLAGS_EXEC_0A_0T_1R) + AML_FLAGS_EXEC_0A_0T_1R), + +/* ACPI 5.0 opcodes */ + +/* 7F */ ACPI_OP("-ConnectField-", ARGP_CONNECTFIELD_OP, + ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 80 */ ACPI_OP("-ExtAccessField-", ARGP_CONNECTFIELD_OP, + ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0) /*! [End] no source code translation !*/ }; @@ -657,7 +666,7 @@ static const u8 acpi_gbl_short_op_index[256] = { /* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, /* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX, /* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x6A, 0x7D, -/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x38 */ 0x7F, 0x80, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, /* 0x40 */ _UNK, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, /* 0x48 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, /* 0x50 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index f1464c0..04311bb 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -74,6 +74,12 @@ union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn) ACPI_FUNCTION_ENTRY(); +/* + if (Op->Common.aml_opcode == AML_INT_CONNECTION_OP) + { + return (Op->Common.Value.Arg); + } +*/ /* Get the info structure for this opcode */ op_info = acpi_ps_get_opcode_info(op->common.aml_opcode); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index ed73f67..d088c90 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -957,6 +957,14 @@ acpi_status(*acpi_adr_space_handler) (u32 function, #define ACPI_DEFAULT_HANDLER NULL +/* Special Context data for generic_serial_bus/general_purpose_io (ACPI 5.0) */ + +struct acpi_connection_info { + u8 *connection; + u16 length; + u8 access_length; +}; + typedef acpi_status(*acpi_adr_space_setup) (acpi_handle region_handle, u32 function, -- cgit v0.10.2 From 2da120b6847f85c406f9afa13853e2755684389e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 14:14:32 +0800 Subject: ACPI 5.0: Support for GeneralPurposeIo and GenericSerialBus operation region Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index f895a24..f467d49 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -202,9 +202,10 @@ #define ACPI_RSDP_CHECKSUM_LENGTH 20 #define ACPI_RSDP_XCHECKSUM_LENGTH 36 -/* SMBus and IPMI bidirectional buffer size */ +/* SMBus, GSBus and IPMI bidirectional buffer size */ #define ACPI_SMBUS_BUFFER_SIZE 34 +#define ACPI_GSBUS_BUFFER_SIZE 34 #define ACPI_IPMI_BUFFER_SIZE 66 /* _sx_d and _sx_w control methods */ diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index adf7a71..c7db80e 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -466,13 +466,16 @@ typedef enum { * access_as keyword */ typedef enum { - AML_FIELD_ATTRIB_SMB_QUICK = 0x02, - AML_FIELD_ATTRIB_SMB_SEND_RCV = 0x04, - AML_FIELD_ATTRIB_SMB_BYTE = 0x06, - AML_FIELD_ATTRIB_SMB_WORD = 0x08, - AML_FIELD_ATTRIB_SMB_BLOCK = 0x0A, - AML_FIELD_ATTRIB_SMB_WORD_CALL = 0x0C, - AML_FIELD_ATTRIB_SMB_BLOCK_CALL = 0x0D + AML_FIELD_ATTRIB_QUICK = 0x02, + AML_FIELD_ATTRIB_SEND_RCV = 0x04, + AML_FIELD_ATTRIB_BYTE = 0x06, + AML_FIELD_ATTRIB_WORD = 0x08, + AML_FIELD_ATTRIB_BLOCK = 0x0A, + AML_FIELD_ATTRIB_MULTIBYTE = 0x0B, + AML_FIELD_ATTRIB_WORD_CALL = 0x0C, + AML_FIELD_ATTRIB_BLOCK_CALL = 0x0D, + AML_FIELD_ATTRIB_RAW_BYTES = 0x0E, + AML_FIELD_ATTRIB_RAW_PROCESS = 0x0F } AML_ACCESS_ATTRIBUTE; /* Bit fields in the AML method_flags byte */ diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index 0bde223..3aeec02 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -100,18 +100,25 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, (obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_SMBUS || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS + || obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_IPMI)) { /* - * This is an SMBus or IPMI read. We must create a buffer to hold + * This is an SMBus, GSBus or IPMI read. We must create a buffer to hold * the data and then directly access the region handler. * - * Note: Smbus protocol value is passed in upper 16-bits of Function + * Note: SMBus and GSBus protocol value is passed in upper 16-bits of Function */ if (obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_SMBUS) { length = ACPI_SMBUS_BUFFER_SIZE; function = ACPI_READ | (obj_desc->field.attribute << 16); + } else if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS) { + length = ACPI_GSBUS_BUFFER_SIZE; + function = + ACPI_READ | (obj_desc->field.attribute << 16); } else { /* IPMI */ length = ACPI_IPMI_BUFFER_SIZE; @@ -248,21 +255,23 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, (obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_SMBUS || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS + || obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_IPMI)) { /* - * This is an SMBus or IPMI write. We will bypass the entire field + * This is an SMBus, GSBus or IPMI write. We will bypass the entire field * mechanism and handoff the buffer directly to the handler. For * these address spaces, the buffer is bi-directional; on a write, * return data is returned in the same buffer. * * Source must be a buffer of sufficient size: - * ACPI_SMBUS_BUFFER_SIZE or ACPI_IPMI_BUFFER_SIZE. + * ACPI_SMBUS_BUFFER_SIZE, ACPI_GSBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. * - * Note: SMBus protocol type is passed in upper 16-bits of Function + * Note: SMBus and GSBus protocol type is passed in upper 16-bits of Function */ if (source_desc->common.type != ACPI_TYPE_BUFFER) { ACPI_ERROR((AE_INFO, - "SMBus or IPMI write requires Buffer, found type %s", + "SMBus/IPMI/GenericSerialBus write requires Buffer, found type %s", acpi_ut_get_object_type_name(source_desc))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); @@ -273,6 +282,11 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, length = ACPI_SMBUS_BUFFER_SIZE; function = ACPI_WRITE | (obj_desc->field.attribute << 16); + } else if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS) { + length = ACPI_GSBUS_BUFFER_SIZE; + function = + ACPI_WRITE | (obj_desc->field.attribute << 16); } else { /* IPMI */ length = ACPI_IPMI_BUFFER_SIZE; @@ -281,7 +295,7 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, if (source_desc->buffer.length < length) { ACPI_ERROR((AE_INFO, - "SMBus or IPMI write requires Buffer of length %u, found length %u", + "SMBus/IPMI/GenericSerialBus write requires Buffer of length %u, found length %u", length, source_desc->buffer.length)); return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index cc9c5df..5b76c11 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -131,10 +131,11 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, } /* - * Exit now for SMBus or IPMI address space, it has a non-linear + * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear * address space and the request cannot be directly validated */ if (space_id == ACPI_ADR_SPACE_SMBUS || + space_id == ACPI_ADR_SPACE_GSBUS || space_id == ACPI_ADR_SPACE_IPMI) { /* SMBus or IPMI has a non-linear address space */ diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index 8b087e2..6fd56d5 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -171,7 +171,9 @@ const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = { "SMBus", "SystemCMOS", "PCIBARTarget", - "IPMI" + "IPMI", + "GeneralPurposeIo", + "GenericSerialBus" }; char *acpi_ut_get_region_name(u8 space_id) diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index d088c90..d5dee7c 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -712,8 +712,10 @@ typedef u8 acpi_adr_space_type; #define ACPI_ADR_SPACE_CMOS (acpi_adr_space_type) 5 #define ACPI_ADR_SPACE_PCI_BAR_TARGET (acpi_adr_space_type) 6 #define ACPI_ADR_SPACE_IPMI (acpi_adr_space_type) 7 +#define ACPI_ADR_SPACE_GPIO (acpi_adr_space_type) 8 +#define ACPI_ADR_SPACE_GSBUS (acpi_adr_space_type) 9 -#define ACPI_NUM_PREDEFINED_REGIONS 8 +#define ACPI_NUM_PREDEFINED_REGIONS 10 /* * Special Address Spaces -- cgit v0.10.2 From e0fe0a8d4ed5474261d0ee1452f5d9ae77236958 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Wed, 16 Nov 2011 14:38:13 +0800 Subject: ACPI 5.0: Support for all new resource descriptors FixedDMA, GPIO descriptors, SerialBus descriptors Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 1bd4661..b0af3b8 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -36,7 +36,7 @@ acpi-y += psargs.o psparse.o psloop.o pstree.o pswalk.o \ psopcode.o psscope.o psutils.o psxface.o acpi-y += rsaddr.o rscreate.o rsinfo.o rsio.o rslist.o rsmisc.o rsxface.o \ - rscalc.o rsirq.o rsmemory.o rsutils.o + rscalc.o rsirq.o rsmemory.o rsutils.o rsserial.o acpi-$(ACPI_FUTURE_USAGE) += rsdump.o diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 52c4ebd..60aee56 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -955,7 +955,7 @@ struct acpi_port_info { #define ACPI_RESOURCE_NAME_END_DEPENDENT 0x38 #define ACPI_RESOURCE_NAME_IO 0x40 #define ACPI_RESOURCE_NAME_FIXED_IO 0x48 -#define ACPI_RESOURCE_NAME_RESERVED_S1 0x50 +#define ACPI_RESOURCE_NAME_FIXED_DMA 0x50 #define ACPI_RESOURCE_NAME_RESERVED_S2 0x58 #define ACPI_RESOURCE_NAME_RESERVED_S3 0x60 #define ACPI_RESOURCE_NAME_RESERVED_S4 0x68 @@ -977,7 +977,9 @@ struct acpi_port_info { #define ACPI_RESOURCE_NAME_EXTENDED_IRQ 0x89 #define ACPI_RESOURCE_NAME_ADDRESS64 0x8A #define ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 0x8B -#define ACPI_RESOURCE_NAME_LARGE_MAX 0x8B +#define ACPI_RESOURCE_NAME_GPIO 0x8C +#define ACPI_RESOURCE_NAME_SERIAL_BUS 0x8E +#define ACPI_RESOURCE_NAME_LARGE_MAX 0x8E /***************************************************************************** * diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index f08b55b..05721b0 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -73,28 +73,40 @@ typedef const struct acpi_rsconvert_info { /* Resource conversion opcodes */ -#define ACPI_RSC_INITGET 0 -#define ACPI_RSC_INITSET 1 -#define ACPI_RSC_FLAGINIT 2 -#define ACPI_RSC_1BITFLAG 3 -#define ACPI_RSC_2BITFLAG 4 -#define ACPI_RSC_COUNT 5 -#define ACPI_RSC_COUNT16 6 -#define ACPI_RSC_LENGTH 7 -#define ACPI_RSC_MOVE8 8 -#define ACPI_RSC_MOVE16 9 -#define ACPI_RSC_MOVE32 10 -#define ACPI_RSC_MOVE64 11 -#define ACPI_RSC_SET8 12 -#define ACPI_RSC_DATA8 13 -#define ACPI_RSC_ADDRESS 14 -#define ACPI_RSC_SOURCE 15 -#define ACPI_RSC_SOURCEX 16 -#define ACPI_RSC_BITMASK 17 -#define ACPI_RSC_BITMASK16 18 -#define ACPI_RSC_EXIT_NE 19 -#define ACPI_RSC_EXIT_LE 20 -#define ACPI_RSC_EXIT_EQ 21 +typedef enum { + ACPI_RSC_INITGET = 0, + ACPI_RSC_INITSET, + ACPI_RSC_FLAGINIT, + ACPI_RSC_1BITFLAG, + ACPI_RSC_2BITFLAG, + ACPI_RSC_3BITFLAG, + ACPI_RSC_ADDRESS, + ACPI_RSC_BITMASK, + ACPI_RSC_BITMASK16, + ACPI_RSC_COUNT, + ACPI_RSC_COUNT16, + ACPI_RSC_COUNT_GPIO_PIN, + ACPI_RSC_COUNT_GPIO_RES, + ACPI_RSC_COUNT_GPIO_VEN, + ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RSC_DATA8, + ACPI_RSC_EXIT_EQ, + ACPI_RSC_EXIT_LE, + ACPI_RSC_EXIT_NE, + ACPI_RSC_LENGTH, + ACPI_RSC_MOVE_GPIO_PIN, + ACPI_RSC_MOVE_GPIO_RES, + ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RSC_MOVE8, + ACPI_RSC_MOVE16, + ACPI_RSC_MOVE32, + ACPI_RSC_MOVE64, + ACPI_RSC_SET8, + ACPI_RSC_SOURCE, + ACPI_RSC_SOURCEX +} ACPI_RSCONVERT_OPCODES; /* Resource Conversion sub-opcodes */ @@ -106,6 +118,9 @@ typedef const struct acpi_rsconvert_info { #define ACPI_RS_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_resource,f) #define AML_OFFSET(f) (u8) ACPI_OFFSET (union aml_resource,f) +/* + * Individual entry for the resource dump tables + */ typedef const struct acpi_rsdump_info { u8 opcode; u8 offset; @@ -116,20 +131,25 @@ typedef const struct acpi_rsdump_info { /* Values for the Opcode field above */ -#define ACPI_RSD_TITLE 0 -#define ACPI_RSD_LITERAL 1 -#define ACPI_RSD_STRING 2 -#define ACPI_RSD_UINT8 3 -#define ACPI_RSD_UINT16 4 -#define ACPI_RSD_UINT32 5 -#define ACPI_RSD_UINT64 6 -#define ACPI_RSD_1BITFLAG 7 -#define ACPI_RSD_2BITFLAG 8 -#define ACPI_RSD_SHORTLIST 9 -#define ACPI_RSD_LONGLIST 10 -#define ACPI_RSD_DWORDLIST 11 -#define ACPI_RSD_ADDRESS 12 -#define ACPI_RSD_SOURCE 13 +typedef enum { + ACPI_RSD_TITLE = 0, + ACPI_RSD_1BITFLAG, + ACPI_RSD_2BITFLAG, + ACPI_RSD_3BITFLAG, + ACPI_RSD_ADDRESS, + ACPI_RSD_DWORDLIST, + ACPI_RSD_LITERAL, + ACPI_RSD_LONGLIST, + ACPI_RSD_SHORTLIST, + ACPI_RSD_SHORTLISTX, + ACPI_RSD_SOURCE, + ACPI_RSD_STRING, + ACPI_RSD_UINT8, + ACPI_RSD_UINT16, + ACPI_RSD_UINT32, + ACPI_RSD_UINT64, + ACPI_RSD_WORDLIST +} ACPI_RSDUMP_OPCODES; /* restore default alignment */ @@ -138,13 +158,18 @@ typedef const struct acpi_rsdump_info { /* Resource tables indexed by internal resource type */ extern const u8 acpi_gbl_aml_resource_sizes[]; +extern const u8 acpi_gbl_aml_resource_serial_bus_sizes[]; extern struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[]; /* Resource tables indexed by raw AML resource descriptor type */ extern const u8 acpi_gbl_resource_struct_sizes[]; +extern const u8 acpi_gbl_resource_struct_serial_bus_sizes[]; extern struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[]; +extern struct acpi_rsconvert_info + *acpi_gbl_convert_resource_serial_bus_dispatch[]; + struct acpi_vendor_walk_info { struct acpi_vendor_uuid *uuid; struct acpi_buffer *buffer; @@ -293,6 +318,11 @@ extern struct acpi_rsconvert_info acpi_rs_convert_address16[]; extern struct acpi_rsconvert_info acpi_rs_convert_ext_irq[]; extern struct acpi_rsconvert_info acpi_rs_convert_address64[]; extern struct acpi_rsconvert_info acpi_rs_convert_ext_address64[]; +extern struct acpi_rsconvert_info acpi_rs_convert_gpio[]; +extern struct acpi_rsconvert_info acpi_rs_convert_fixed_dma[]; +extern struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[]; +extern struct acpi_rsconvert_info acpi_rs_convert_spi_serial_bus[]; +extern struct acpi_rsconvert_info acpi_rs_convert_uart_serial_bus[]; /* These resources require separate get/set tables */ @@ -310,6 +340,7 @@ extern struct acpi_rsconvert_info acpi_rs_set_vendor[]; * rsinfo */ extern struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[]; +extern struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[]; /* * rsdump @@ -331,6 +362,12 @@ extern struct acpi_rsdump_info acpi_rs_dump_address64[]; extern struct acpi_rsdump_info acpi_rs_dump_ext_address64[]; extern struct acpi_rsdump_info acpi_rs_dump_ext_irq[]; extern struct acpi_rsdump_info acpi_rs_dump_generic_reg[]; +extern struct acpi_rsdump_info acpi_rs_dump_gpio[]; +extern struct acpi_rsdump_info acpi_rs_dump_fixed_dma[]; +extern struct acpi_rsdump_info acpi_rs_dump_common_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_i2c_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_spi_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_uart_serial_bus[]; #endif #endif /* __ACRESRC_H__ */ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 99c140d..ab95d45 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -45,6 +45,7 @@ #define _ACUTILS_H extern const u8 acpi_gbl_resource_aml_sizes[]; +extern const u8 acpi_gbl_resource_aml_serial_bus_sizes[]; /* Strings used by the disassembler and debugger resource dump routines */ diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index 59122cd..d4c3051 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -58,29 +58,48 @@ #define ACPI_RESTAG_TYPESPECIFICATTRIBUTES "_ATT" #define ACPI_RESTAG_BASEADDRESS "_BAS" #define ACPI_RESTAG_BUSMASTER "_BM_" /* Master(1), Slave(0) */ +#define ACPI_RESTAG_DEBOUNCETIME "_DBT" #define ACPI_RESTAG_DECODE "_DEC" +#define ACPI_RESTAG_DEVICEPOLARITY "_DPL" #define ACPI_RESTAG_DMA "_DMA" #define ACPI_RESTAG_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */ +#define ACPI_RESTAG_DRIVESTRENGTH "_DRS" +#define ACPI_RESTAG_ENDIANNESS "_END" +#define ACPI_RESTAG_FLOWCONTROL "_FLC" #define ACPI_RESTAG_GRANULARITY "_GRA" #define ACPI_RESTAG_INTERRUPT "_INT" #define ACPI_RESTAG_INTERRUPTLEVEL "_LL_" /* active_lo(1), active_hi(0) */ #define ACPI_RESTAG_INTERRUPTSHARE "_SHR" /* Shareable(1), no_share(0) */ #define ACPI_RESTAG_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */ +#define ACPI_RESTAG_IORESTRICTION "_IOR" #define ACPI_RESTAG_LENGTH "_LEN" +#define ACPI_RESTAG_LINE "_LIN" #define ACPI_RESTAG_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */ #define ACPI_RESTAG_MEMTYPE "_MEM" /* non_cache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */ #define ACPI_RESTAG_MAXADDR "_MAX" #define ACPI_RESTAG_MINADDR "_MIN" #define ACPI_RESTAG_MAXTYPE "_MAF" #define ACPI_RESTAG_MINTYPE "_MIF" +#define ACPI_RESTAG_MODE "_MOD" +#define ACPI_RESTAG_PARITY "_PAR" +#define ACPI_RESTAG_PHASE "_PHA" +#define ACPI_RESTAG_PIN "_PIN" +#define ACPI_RESTAG_PINCONFIG "_PPI" +#define ACPI_RESTAG_POLARITY "_POL" #define ACPI_RESTAG_REGISTERBITOFFSET "_RBO" #define ACPI_RESTAG_REGISTERBITWIDTH "_RBW" #define ACPI_RESTAG_RANGETYPE "_RNG" #define ACPI_RESTAG_READWRITETYPE "_RW_" /* read_only(0), Writeable (1) */ +#define ACPI_RESTAG_LENGTH_RX "_RXL" +#define ACPI_RESTAG_LENGTH_TX "_TXL" +#define ACPI_RESTAG_SLAVEMODE "_SLV" +#define ACPI_RESTAG_SPEED "_SPE" +#define ACPI_RESTAG_STOPBITS "_STB" #define ACPI_RESTAG_TRANSLATION "_TRA" #define ACPI_RESTAG_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */ #define ACPI_RESTAG_TYPE "_TTP" /* Translation(1), Static (0) */ #define ACPI_RESTAG_XFERTYPE "_SIZ" /* 8(0), 8_and16(1), 16(2) */ +#define ACPI_RESTAG_VENDORDATA "_VEN" /* Default sizes for "small" resource descriptors */ @@ -90,6 +109,7 @@ #define ASL_RDESC_END_DEPEND_SIZE 0x00 #define ASL_RDESC_IO_SIZE 0x07 #define ASL_RDESC_FIXED_IO_SIZE 0x03 +#define ASL_RDESC_FIXED_DMA_SIZE 0x05 #define ASL_RDESC_END_TAG_SIZE 0x01 struct asl_resource_node { @@ -164,6 +184,12 @@ struct aml_resource_end_tag { AML_RESOURCE_SMALL_HEADER_COMMON u8 checksum; }; +struct aml_resource_fixed_dma { + AML_RESOURCE_SMALL_HEADER_COMMON u16 request_lines; + u16 channels; + u8 width; +}; + /* * LARGE descriptors */ @@ -263,6 +289,110 @@ struct aml_resource_generic_register { u64 address; }; +/* Common descriptor for gpio_int and gpio_io (ACPI 5.0) */ + +struct aml_resource_gpio { + AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id; + u8 connection_type; + u16 flags; + u16 int_flags; + u8 pin_config; + u16 drive_strength; + u16 debounce_timeout; + u16 pin_table_offset; + u8 res_source_index; + u16 res_source_offset; + u16 vendor_offset; + u16 vendor_length; + /* + * Optional fields follow immediately: + * 1) PIN list (Words) + * 2) Resource Source String + * 3) Vendor Data bytes + */ +}; + +#define AML_RESOURCE_GPIO_REVISION 1 /* ACPI 5.0 */ + +/* Values for connection_type above */ + +#define AML_RESOURCE_GPIO_TYPE_INT 0 +#define AML_RESOURCE_GPIO_TYPE_IO 1 +#define AML_RESOURCE_MAX_GPIOTYPE 1 + +/* Common preamble for all serial descriptors (ACPI 5.0) */ + +#define AML_RESOURCE_SERIAL_COMMON \ + u8 revision_id; \ + u8 res_source_index; \ + u8 type; \ + u8 flags; \ + u16 type_specific_flags; \ + u8 type_revision_id; \ + u16 type_data_length; \ + +/* Values for the type field above */ + +#define AML_RESOURCE_I2C_SERIALBUSTYPE 1 +#define AML_RESOURCE_SPI_SERIALBUSTYPE 2 +#define AML_RESOURCE_UART_SERIALBUSTYPE 3 +#define AML_RESOURCE_MAX_SERIALBUSTYPE 3 +#define AML_RESOURCE_VENDOR_SERIALBUSTYPE 192 /* Vendor defined is 0xC0-0xFF (NOT SUPPORTED) */ + +struct aml_resource_common_serialbus { +AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_SERIAL_COMMON}; + +struct aml_resource_i2c_serialbus { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_SERIAL_COMMON u32 connection_speed; + u16 slave_address; + /* + * Optional fields follow immediately: + * 1) Vendor Data bytes + * 2) Resource Source String + */ +}; + +#define AML_RESOURCE_I2C_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_I2C_TYPE_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_I2C_MIN_DATA_LEN 6 + +struct aml_resource_spi_serialbus { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_SERIAL_COMMON u32 connection_speed; + u8 data_bit_length; + u8 clock_phase; + u8 clock_polarity; + u16 device_selection; + /* + * Optional fields follow immediately: + * 1) Vendor Data bytes + * 2) Resource Source String + */ +}; + +#define AML_RESOURCE_SPI_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_SPI_TYPE_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_SPI_MIN_DATA_LEN 9 + +struct aml_resource_uart_serialbus { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_SERIAL_COMMON u32 default_baud_rate; + u16 rx_fifo_size; + u16 tx_fifo_size; + u8 parity; + u8 lines_enabled; + /* + * Optional fields follow immediately: + * 1) Vendor Data bytes + * 2) Resource Source String + */ +}; + +#define AML_RESOURCE_UART_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_UART_TYPE_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_UART_MIN_DATA_LEN 10 + /* restore default alignment */ #pragma pack() @@ -284,6 +414,7 @@ union aml_resource { struct aml_resource_end_dependent end_dpf; struct aml_resource_io io; struct aml_resource_fixed_io fixed_io; + struct aml_resource_fixed_dma fixed_dma; struct aml_resource_vendor_small vendor_small; struct aml_resource_end_tag end_tag; @@ -299,6 +430,11 @@ union aml_resource { struct aml_resource_address64 address64; struct aml_resource_extended_address64 ext_address64; struct aml_resource_extended_irq extended_irq; + struct aml_resource_gpio gpio; + struct aml_resource_i2c_serialbus i2c_serial_bus; + struct aml_resource_spi_serialbus spi_serial_bus; + struct aml_resource_uart_serialbus uart_serial_bus; + struct aml_resource_common_serialbus common_serial_bus; /* Utility overlays */ diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 3a8a89e..3a29b4dd 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -313,6 +313,38 @@ acpi_rs_get_aml_length(struct acpi_resource * resource, acpi_size * size_needed) resource_source)); break; + case ACPI_RESOURCE_TYPE_GPIO: + + total_size = + (acpi_rs_length) (total_size + + (resource->data.gpio. + pin_table_length * 2) + + resource->data.gpio. + resource_source.string_length + + resource->data.gpio. + vendor_length); + + break; + + case ACPI_RESOURCE_TYPE_SERIAL_BUS: + + total_size = + acpi_gbl_aml_resource_serial_bus_sizes[resource-> + data. + common_serial_bus. + type]; + + total_size = (acpi_rs_length) (total_size + + resource->data. + i2c_serial_bus. + resource_source. + string_length + + resource->data. + i2c_serial_bus. + vendor_length); + + break; + default: break; } @@ -362,10 +394,11 @@ acpi_rs_get_list_length(u8 * aml_buffer, u32 extra_struct_bytes; u8 resource_index; u8 minimum_aml_resource_length; + union aml_resource *aml_resource; ACPI_FUNCTION_TRACE(rs_get_list_length); - *size_needed = 0; + *size_needed = ACPI_RS_SIZE_MIN; /* Minimum size is one end_tag */ end_aml = aml_buffer + aml_buffer_length; /* Walk the list of AML resource descriptors */ @@ -376,9 +409,15 @@ acpi_rs_get_list_length(u8 * aml_buffer, status = acpi_ut_validate_resource(aml_buffer, &resource_index); if (ACPI_FAILURE(status)) { + /* + * Exit on failure. Cannot continue because the descriptor length + * may be bogus also. + */ return_ACPI_STATUS(status); } + aml_resource = (void *)aml_buffer; + /* Get the resource length and base (minimum) AML size */ resource_length = acpi_ut_get_resource_length(aml_buffer); @@ -422,10 +461,8 @@ acpi_rs_get_list_length(u8 * aml_buffer, case ACPI_RESOURCE_NAME_END_TAG: /* - * End Tag: - * This is the normal exit, add size of end_tag + * End Tag: This is the normal exit */ - *size_needed += ACPI_RS_SIZE_MIN; return_ACPI_STATUS(AE_OK); case ACPI_RESOURCE_NAME_ADDRESS32: @@ -457,6 +494,33 @@ acpi_rs_get_list_length(u8 * aml_buffer, minimum_aml_resource_length); break; + case ACPI_RESOURCE_NAME_GPIO: + + /* Vendor data is optional */ + + if (aml_resource->gpio.vendor_length) { + extra_struct_bytes += + aml_resource->gpio.vendor_offset - + aml_resource->gpio.pin_table_offset + + aml_resource->gpio.vendor_length; + } else { + extra_struct_bytes += + aml_resource->large_header.resource_length + + sizeof(struct aml_resource_large_header) - + aml_resource->gpio.pin_table_offset; + } + break; + + case ACPI_RESOURCE_NAME_SERIAL_BUS: + + minimum_aml_resource_length = + acpi_gbl_resource_aml_serial_bus_sizes + [aml_resource->common_serial_bus.type]; + extra_struct_bytes += + aml_resource->common_serial_bus.resource_length - + minimum_aml_resource_length; + break; + default: break; } @@ -467,9 +531,18 @@ acpi_rs_get_list_length(u8 * aml_buffer, * Important: Round the size up for the appropriate alignment. This * is a requirement on IA64. */ - buffer_size = acpi_gbl_resource_struct_sizes[resource_index] + - extra_struct_bytes; - buffer_size = (u32) ACPI_ROUND_UP_TO_NATIVE_WORD(buffer_size); + if (acpi_ut_get_resource_type(aml_buffer) == + ACPI_RESOURCE_NAME_SERIAL_BUS) { + buffer_size = + acpi_gbl_resource_struct_serial_bus_sizes + [aml_resource->common_serial_bus.type] + + extra_struct_bytes; + } else { + buffer_size = + acpi_gbl_resource_struct_sizes[resource_index] + + extra_struct_bytes; + } + buffer_size = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(buffer_size); *size_needed += buffer_size; diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index 4ce6e11..f61285d 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -66,9 +66,10 @@ ACPI_MODULE_NAME("rscreate") * of device resources. * ******************************************************************************/ + acpi_status acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer, - struct acpi_buffer *output_buffer) + struct acpi_buffer * output_buffer) { acpi_status status; diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index 33db752..9969985 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -61,11 +61,13 @@ static void acpi_rs_out_integer64(char *title, u64 value); static void acpi_rs_out_title(char *title); -static void acpi_rs_dump_byte_list(u16 length, u8 * data); +static void acpi_rs_dump_byte_list(u16 length, u8 *data); -static void acpi_rs_dump_dword_list(u8 length, u32 * data); +static void acpi_rs_dump_word_list(u16 length, u16 *data); -static void acpi_rs_dump_short_byte_list(u8 length, u8 * data); +static void acpi_rs_dump_dword_list(u8 length, u32 *data); + +static void acpi_rs_dump_short_byte_list(u8 length, u8 *data); static void acpi_rs_dump_resource_source(struct acpi_resource_source *resource_source); @@ -309,6 +311,125 @@ struct acpi_rsdump_info acpi_rs_dump_generic_reg[6] = { {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(generic_reg.address), "Address", NULL} }; +struct acpi_rsdump_info acpi_rs_dump_gpio[16] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_gpio), "GPIO", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.revision_id), "RevisionId", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.connection_type), + "ConnectionType", acpi_gbl_ct_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(gpio.producer_consumer), + "ProducerConsumer", acpi_gbl_consume_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.pin_config), "PinConfig", + acpi_gbl_ppc_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.sharable), "Sharable", + acpi_gbl_shr_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.io_restriction), + "IoRestriction", acpi_gbl_ior_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(gpio.triggering), "Triggering", + acpi_gbl_he_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.polarity), "Polarity", + acpi_gbl_ll_decode}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.drive_strength), "DriveStrength", + NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.debounce_timeout), + "DebounceTimeout", NULL}, + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(gpio.resource_source), + "ResourceSource", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.pin_table_length), + "PinTableLength", NULL}, + {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET(gpio.pin_table), "PinTable", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.vendor_length), "VendorLength", + NULL}, + {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(gpio.vendor_data), "VendorData", + NULL}, +}; + +struct acpi_rsdump_info acpi_rs_dump_fixed_dma[4] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_dma), + "FixedDma", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_dma.request_lines), + "RequestLines", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_dma.channels), "Channels", + NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(fixed_dma.width), "TransferWidth", + acpi_gbl_dts_decode}, +}; + +#define ACPI_RS_DUMP_COMMON_SERIAL_BUS \ + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.revision_id), "RevisionId", NULL}, \ + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.type), "Type", acpi_gbl_sbt_decode}, \ + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.producer_consumer), "ProducerConsumer", acpi_gbl_consume_decode}, \ + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.slave_mode), "SlaveMode", acpi_gbl_sm_decode}, \ + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.type_revision_id), "TypeRevisionId", NULL}, \ + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (common_serial_bus.type_data_length), "TypeDataLength", NULL}, \ + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (common_serial_bus.resource_source), "ResourceSource", NULL}, \ + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (common_serial_bus.vendor_length), "VendorLength", NULL}, \ + {ACPI_RSD_SHORTLISTX,ACPI_RSD_OFFSET (common_serial_bus.vendor_data), "VendorData", NULL}, + +struct acpi_rsdump_info acpi_rs_dump_common_serial_bus[10] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_common_serial_bus), + "Common Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS +}; + +struct acpi_rsdump_info acpi_rs_dump_i2c_serial_bus[13] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_i2c_serial_bus), + "I2C Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_1BITFLAG, + ACPI_RSD_OFFSET(i2c_serial_bus. + access_mode), + "AccessMode", acpi_gbl_am_decode}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(i2c_serial_bus.connection_speed), + "ConnectionSpeed", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(i2c_serial_bus.slave_address), + "SlaveAddress", NULL}, +}; + +struct acpi_rsdump_info acpi_rs_dump_spi_serial_bus[17] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_spi_serial_bus), + "Spi Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_1BITFLAG, + ACPI_RSD_OFFSET(spi_serial_bus. + wire_mode), "WireMode", + acpi_gbl_wm_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(spi_serial_bus.device_polarity), + "DevicePolarity", acpi_gbl_dp_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.data_bit_length), + "DataBitLength", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.clock_phase), + "ClockPhase", acpi_gbl_cph_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.clock_polarity), + "ClockPolarity", acpi_gbl_cpo_decode}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(spi_serial_bus.device_selection), + "DeviceSelection", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(spi_serial_bus.connection_speed), + "ConnectionSpeed", NULL}, +}; + +struct acpi_rsdump_info acpi_rs_dump_uart_serial_bus[19] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_uart_serial_bus), + "Uart Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_2BITFLAG, + ACPI_RSD_OFFSET(uart_serial_bus. + flow_control), + "FlowControl", acpi_gbl_fc_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.stop_bits), + "StopBits", acpi_gbl_sb_decode}, + {ACPI_RSD_3BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.data_bits), + "DataBits", acpi_gbl_bpb_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.endian), "Endian", + acpi_gbl_ed_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(uart_serial_bus.parity), "Parity", + acpi_gbl_pt_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(uart_serial_bus.lines_enabled), + "LinesEnabled", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(uart_serial_bus.rx_fifo_size), + "RxFifoSize", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(uart_serial_bus.tx_fifo_size), + "TxFifoSize", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(uart_serial_bus.default_baud_rate), + "ConnectionSpeed", NULL}, +}; + /* * Tables used for common address descriptor flag fields */ @@ -413,7 +534,14 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) /* Data items, 8/16/32/64 bit */ case ACPI_RSD_UINT8: - acpi_rs_out_integer8(name, ACPI_GET8(target)); + if (table->pointer) { + acpi_rs_out_string(name, ACPI_CAST_PTR(char, + table-> + pointer + [*target])); + } else { + acpi_rs_out_integer8(name, ACPI_GET8(target)); + } break; case ACPI_RSD_UINT16: @@ -444,6 +572,13 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) 0x03])); break; + case ACPI_RSD_3BITFLAG: + acpi_rs_out_string(name, ACPI_CAST_PTR(char, + table-> + pointer[*target & + 0x07])); + break; + case ACPI_RSD_SHORTLIST: /* * Short byte list (single line output) for DMA and IRQ resources @@ -456,6 +591,20 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) } break; + case ACPI_RSD_SHORTLISTX: + /* + * Short byte list (single line output) for GPIO vendor data + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_out_title(name); + acpi_rs_dump_short_byte_list(*previous_target, + * + (ACPI_CAST_INDIRECT_PTR + (u8, target))); + } + break; + case ACPI_RSD_LONGLIST: /* * Long byte list for Vendor resource data @@ -480,6 +629,18 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) } break; + case ACPI_RSD_WORDLIST: + /* + * Word list for GPIO Pin Table + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_dump_word_list(*previous_target, + *(ACPI_CAST_INDIRECT_PTR + (u16, target))); + } + break; + case ACPI_RSD_ADDRESS: /* * Common flags for all Address resources @@ -627,14 +788,20 @@ void acpi_rs_dump_resource_list(struct acpi_resource *resource_list) /* Dump the resource descriptor */ - acpi_rs_dump_descriptor(&resource_list->data, - acpi_gbl_dump_resource_dispatch[type]); + if (type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + acpi_rs_dump_descriptor(&resource_list->data, + acpi_gbl_dump_serial_bus_dispatch + [resource_list->data. + common_serial_bus.type]); + } else { + acpi_rs_dump_descriptor(&resource_list->data, + acpi_gbl_dump_resource_dispatch + [type]); + } /* Point to the next resource structure */ - resource_list = - ACPI_ADD_PTR(struct acpi_resource, resource_list, - resource_list->length); + resource_list = ACPI_NEXT_RESOURCE(resource_list); /* Exit when END_TAG descriptor is reached */ @@ -768,4 +935,13 @@ static void acpi_rs_dump_dword_list(u8 length, u32 * data) } } +static void acpi_rs_dump_word_list(u16 length, u16 *data) +{ + u16 i; + + for (i = 0; i < length; i++) { + acpi_os_printf("%25s%2.2X : %4.4X\n", "Word", i, data[i]); + } +} + #endif diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c index f9ea608..e5489bf 100644 --- a/drivers/acpi/acpica/rsinfo.c +++ b/drivers/acpi/acpica/rsinfo.c @@ -76,7 +76,10 @@ struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[] = { acpi_rs_convert_address64, /* 0x0D, ACPI_RESOURCE_TYPE_ADDRESS64 */ acpi_rs_convert_ext_address64, /* 0x0E, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */ acpi_rs_convert_ext_irq, /* 0x0F, ACPI_RESOURCE_TYPE_EXTENDED_IRQ */ - acpi_rs_convert_generic_reg /* 0x10, ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + acpi_rs_convert_generic_reg, /* 0x10, ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + acpi_rs_convert_gpio, /* 0x11, ACPI_RESOURCE_TYPE_GPIO */ + acpi_rs_convert_fixed_dma, /* 0x12, ACPI_RESOURCE_TYPE_FIXED_DMA */ + NULL, /* 0x13, ACPI_RESOURCE_TYPE_SERIAL_BUS - Use subtype table below */ }; /* Dispatch tables for AML-to-resource (Get Resource) conversion functions */ @@ -94,7 +97,7 @@ struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[] = { acpi_rs_convert_end_dpf, /* 0x07, ACPI_RESOURCE_NAME_END_DEPENDENT */ acpi_rs_convert_io, /* 0x08, ACPI_RESOURCE_NAME_IO */ acpi_rs_convert_fixed_io, /* 0x09, ACPI_RESOURCE_NAME_FIXED_IO */ - NULL, /* 0x0A, Reserved */ + acpi_rs_convert_fixed_dma, /* 0x0A, ACPI_RESOURCE_NAME_FIXED_DMA */ NULL, /* 0x0B, Reserved */ NULL, /* 0x0C, Reserved */ NULL, /* 0x0D, Reserved */ @@ -114,7 +117,19 @@ struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[] = { acpi_rs_convert_address16, /* 0x08, ACPI_RESOURCE_NAME_ADDRESS16 */ acpi_rs_convert_ext_irq, /* 0x09, ACPI_RESOURCE_NAME_EXTENDED_IRQ */ acpi_rs_convert_address64, /* 0x0A, ACPI_RESOURCE_NAME_ADDRESS64 */ - acpi_rs_convert_ext_address64 /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 */ + acpi_rs_convert_ext_address64, /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 */ + acpi_rs_convert_gpio, /* 0x0C, ACPI_RESOURCE_NAME_GPIO */ + NULL, /* 0x0D, Reserved */ + NULL, /* 0x0E, ACPI_RESOURCE_NAME_SERIAL_BUS - Use subtype table below */ +}; + +/* Subtype table for serial_bus -- I2C, SPI, and UART */ + +struct acpi_rsconvert_info *acpi_gbl_convert_resource_serial_bus_dispatch[] = { + NULL, + acpi_rs_convert_i2c_serial_bus, + acpi_rs_convert_spi_serial_bus, + acpi_rs_convert_uart_serial_bus, }; #ifdef ACPI_FUTURE_USAGE @@ -140,6 +155,16 @@ struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[] = { acpi_rs_dump_ext_address64, /* ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */ acpi_rs_dump_ext_irq, /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */ acpi_rs_dump_generic_reg, /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + acpi_rs_dump_gpio, /* ACPI_RESOURCE_TYPE_GPIO */ + acpi_rs_dump_fixed_dma, /* ACPI_RESOURCE_TYPE_FIXED_DMA */ + NULL, /* ACPI_RESOURCE_TYPE_SERIAL_BUS */ +}; + +struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[] = { + NULL, + acpi_rs_dump_i2c_serial_bus, /* AML_RESOURCE_I2C_BUS_TYPE */ + acpi_rs_dump_spi_serial_bus, /* AML_RESOURCE_SPI_BUS_TYPE */ + acpi_rs_dump_uart_serial_bus, /* AML_RESOURCE_UART_BUS_TYPE */ }; #endif @@ -166,7 +191,10 @@ const u8 acpi_gbl_aml_resource_sizes[] = { sizeof(struct aml_resource_address64), /* ACPI_RESOURCE_TYPE_ADDRESS64 */ sizeof(struct aml_resource_extended_address64), /*ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */ sizeof(struct aml_resource_extended_irq), /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */ - sizeof(struct aml_resource_generic_register) /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + sizeof(struct aml_resource_generic_register), /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + sizeof(struct aml_resource_gpio), /* ACPI_RESOURCE_TYPE_GPIO */ + sizeof(struct aml_resource_fixed_dma), /* ACPI_RESOURCE_TYPE_FIXED_DMA */ + sizeof(struct aml_resource_common_serialbus), /* ACPI_RESOURCE_TYPE_SERIAL_BUS */ }; const u8 acpi_gbl_resource_struct_sizes[] = { @@ -182,7 +210,7 @@ const u8 acpi_gbl_resource_struct_sizes[] = { ACPI_RS_SIZE_MIN, ACPI_RS_SIZE(struct acpi_resource_io), ACPI_RS_SIZE(struct acpi_resource_fixed_io), - 0, + ACPI_RS_SIZE(struct acpi_resource_fixed_dma), 0, 0, 0, @@ -202,5 +230,21 @@ const u8 acpi_gbl_resource_struct_sizes[] = { ACPI_RS_SIZE(struct acpi_resource_address16), ACPI_RS_SIZE(struct acpi_resource_extended_irq), ACPI_RS_SIZE(struct acpi_resource_address64), - ACPI_RS_SIZE(struct acpi_resource_extended_address64) + ACPI_RS_SIZE(struct acpi_resource_extended_address64), + ACPI_RS_SIZE(struct acpi_resource_gpio), + ACPI_RS_SIZE(struct acpi_resource_common_serialbus) +}; + +const u8 acpi_gbl_aml_resource_serial_bus_sizes[] = { + 0, + sizeof(struct aml_resource_i2c_serialbus), + sizeof(struct aml_resource_spi_serialbus), + sizeof(struct aml_resource_uart_serialbus), +}; + +const u8 acpi_gbl_resource_struct_serial_bus_sizes[] = { + 0, + ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), + ACPI_RS_SIZE(struct acpi_resource_spi_serialbus), + ACPI_RS_SIZE(struct acpi_resource_uart_serialbus), }; diff --git a/drivers/acpi/acpica/rsirq.c b/drivers/acpi/acpica/rsirq.c index 50b8ad2..3ac9d4a 100644 --- a/drivers/acpi/acpica/rsirq.c +++ b/drivers/acpi/acpica/rsirq.c @@ -264,3 +264,34 @@ struct acpi_rsconvert_info acpi_rs_convert_dma[6] = { AML_OFFSET(dma.dma_channel_mask), ACPI_RS_OFFSET(data.dma.channel_count)} }; + +/******************************************************************************* + * + * acpi_rs_convert_fixed_dma + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_fixed_dma[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_DMA, + ACPI_RS_SIZE(struct acpi_resource_fixed_dma), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_dma)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_DMA, + sizeof(struct aml_resource_fixed_dma), + 0}, + + /* + * These fields are contiguous in both the source and destination: + * request_lines + * Channels + */ + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.fixed_dma.request_lines), + AML_OFFSET(fixed_dma.request_lines), + 2}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.fixed_dma.width), + AML_OFFSET(fixed_dma.width), + 1}, + +}; diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c index 1bfcef7..c446351 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -70,6 +70,8 @@ acpi_rs_convert_aml_to_resources(u8 * aml, struct acpi_resource **resource_ptr = ACPI_CAST_INDIRECT_PTR(struct acpi_resource, context); struct acpi_resource *resource; + union aml_resource *aml_resource; + struct acpi_rsconvert_info *conversion_table; acpi_status status; ACPI_FUNCTION_TRACE(rs_convert_aml_to_resources); @@ -84,14 +86,37 @@ acpi_rs_convert_aml_to_resources(u8 * aml, "Misaligned resource pointer %p", resource)); } + /* Get the appropriate conversion info table */ + + aml_resource = ACPI_CAST_PTR(union aml_resource, aml); + if (acpi_ut_get_resource_type(aml) == ACPI_RESOURCE_NAME_SERIAL_BUS) { + if (aml_resource->common_serial_bus.type > + AML_RESOURCE_MAX_SERIALBUSTYPE) { + conversion_table = NULL; + } else { + /* This is an I2C, SPI, or UART serial_bus descriptor */ + + conversion_table = + acpi_gbl_convert_resource_serial_bus_dispatch + [aml_resource->common_serial_bus.type]; + } + } else { + conversion_table = + acpi_gbl_get_resource_dispatch[resource_index]; + } + + if (!conversion_table) { + ACPI_ERROR((AE_INFO, + "Invalid/unsupported resource descriptor: Type 0x%2.2X", + resource_index)); + return (AE_AML_INVALID_RESOURCE_TYPE); + } + /* Convert the AML byte stream resource to a local resource struct */ status = - acpi_rs_convert_aml_to_resource(resource, - ACPI_CAST_PTR(union aml_resource, - aml), - acpi_gbl_get_resource_dispatch - [resource_index]); + acpi_rs_convert_aml_to_resource(resource, aml_resource, + conversion_table); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Could not convert AML resource (Type 0x%X)", @@ -106,7 +131,7 @@ acpi_rs_convert_aml_to_resources(u8 * aml, /* Point to the next structure in the output buffer */ - *resource_ptr = ACPI_ADD_PTR(void, resource, resource->length); + *resource_ptr = ACPI_NEXT_RESOURCE(resource); return_ACPI_STATUS(AE_OK); } @@ -135,6 +160,7 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, { u8 *aml = output_buffer; u8 *end_aml = output_buffer + aml_size_needed; + struct acpi_rsconvert_info *conversion_table; acpi_status status; ACPI_FUNCTION_TRACE(rs_convert_resources_to_aml); @@ -154,11 +180,34 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, /* Perform the conversion */ - status = acpi_rs_convert_resource_to_aml(resource, ACPI_CAST_PTR(union - aml_resource, - aml), - acpi_gbl_set_resource_dispatch - [resource->type]); + if (resource->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + if (resource->data.common_serial_bus.type > + AML_RESOURCE_MAX_SERIALBUSTYPE) { + conversion_table = NULL; + } else { + /* This is an I2C, SPI, or UART serial_bus descriptor */ + + conversion_table = + acpi_gbl_convert_resource_serial_bus_dispatch + [resource->data.common_serial_bus.type]; + } + } else { + conversion_table = + acpi_gbl_set_resource_dispatch[resource->type]; + } + + if (!conversion_table) { + ACPI_ERROR((AE_INFO, + "Invalid/unsupported resource descriptor: Type 0x%2.2X", + resource->type)); + return (AE_AML_INVALID_RESOURCE_TYPE); + } + + status = acpi_rs_convert_resource_to_aml(resource, + ACPI_CAST_PTR(union + aml_resource, + aml), + conversion_table); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Could not convert resource (type 0x%X) to AML", @@ -192,9 +241,7 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, /* Point to the next input resource descriptor */ - resource = - ACPI_ADD_PTR(struct acpi_resource, resource, - resource->length); + resource = ACPI_NEXT_RESOURCE(resource); } /* Completed buffer, but did not find an end_tag resource descriptor */ diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index 410264b..af3dc63 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -83,6 +83,10 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, ACPI_FUNCTION_TRACE(rs_convert_aml_to_resource); + if (!info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + if (((acpi_size) resource) & 0x3) { /* Each internal resource struct is expected to be 32-bit aligned */ @@ -101,7 +105,6 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, * table length (# of table entries) */ count = INIT_TABLE_LENGTH(info); - while (count) { /* * Source is the external AML byte stream buffer, @@ -145,6 +148,14 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, ((ACPI_GET8(source) >> info->value) & 0x03); break; + case ACPI_RSC_3BITFLAG: + /* + * Mask and shift the flag bits + */ + ACPI_SET8(destination) = (u8) + ((ACPI_GET8(source) >> info->value) & 0x07); + break; + case ACPI_RSC_COUNT: item_count = ACPI_GET8(source); @@ -163,6 +174,69 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, (info->value * (item_count - 1)); break; + case ACPI_RSC_COUNT_GPIO_PIN: + + target = ACPI_ADD_PTR(void, aml, info->value); + item_count = ACPI_GET16(target) - ACPI_GET16(source); + + resource->length = resource->length + item_count; + item_count = item_count / 2; + ACPI_SET16(destination) = item_count; + break; + + case ACPI_RSC_COUNT_GPIO_VEN: + + item_count = ACPI_GET8(source); + ACPI_SET8(destination) = (u8)item_count; + + resource->length = resource->length + + (info->value * item_count); + break; + + case ACPI_RSC_COUNT_GPIO_RES: + + /* + * Vendor data is optional (length/offset may both be zero) + * Examine vendor data length field first + */ + target = ACPI_ADD_PTR(void, aml, (info->value + 2)); + if (ACPI_GET16(target)) { + + /* Use vendor offset to get resource source length */ + + target = ACPI_ADD_PTR(void, aml, info->value); + item_count = + ACPI_GET16(target) - ACPI_GET16(source); + } else { + /* No vendor data to worry about */ + + item_count = aml->large_header.resource_length + + sizeof(struct aml_resource_large_header) - + ACPI_GET16(source); + } + + resource->length = resource->length + item_count; + ACPI_SET16(destination) = item_count; + break; + + case ACPI_RSC_COUNT_SERIAL_VEN: + + item_count = ACPI_GET16(source) - info->value; + + resource->length = resource->length + item_count; + ACPI_SET16(destination) = item_count; + break; + + case ACPI_RSC_COUNT_SERIAL_RES: + + item_count = (aml_resource_length + + sizeof(struct aml_resource_large_header)) + - ACPI_GET16(source) - info->value; + + resource->length = resource->length + item_count; + ACPI_SET16(destination) = item_count; + break; + case ACPI_RSC_LENGTH: resource->length = resource->length + info->value; @@ -183,6 +257,72 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, info->opcode); break; + case ACPI_RSC_MOVE_GPIO_PIN: + + /* Generate and set the PIN data pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count * 2)); + *(u16 **)destination = ACPI_CAST_PTR(u16, target); + + /* Copy the PIN data */ + + source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_GPIO_RES: + + /* Generate and set the resource_source string pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count)); + *(u8 **)destination = ACPI_CAST_PTR(u8, target); + + /* Copy the resource_source string */ + + source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_VEN: + + /* Generate and set the Vendor Data pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count)); + *(u8 **)destination = ACPI_CAST_PTR(u8, target); + + /* Copy the Vendor Data */ + + source = ACPI_ADD_PTR(void, aml, info->value); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_RES: + + /* Generate and set the resource_source string pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count)); + *(u8 **)destination = ACPI_CAST_PTR(u8, target); + + /* Copy the resource_source string */ + + source = + ACPI_ADD_PTR(void, aml, + (ACPI_GET16(source) + info->value)); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + case ACPI_RSC_SET8: ACPI_MEMSET(destination, info->aml_offset, info->value); @@ -219,13 +359,18 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, * Optional resource_source (Index and String). This is the more * complicated case used by the Interrupt() macro */ - target = - ACPI_ADD_PTR(char, resource, - info->aml_offset + (item_count * 4)); + target = ACPI_ADD_PTR(char, resource, + info->aml_offset + + (item_count * 4)); resource->length += acpi_rs_get_resource_source(aml_resource_length, - (acpi_rs_length) (((item_count - 1) * sizeof(u32)) + info->value), destination, aml, target); + (acpi_rs_length) + (((item_count - + 1) * sizeof(u32)) + + info->value), + destination, aml, + target); break; case ACPI_RSC_BITMASK: @@ -327,6 +472,7 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, { void *source = NULL; void *destination; + char *target; acpi_rsdesc_size aml_length = 0; u8 count; u16 temp16 = 0; @@ -334,6 +480,10 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, ACPI_FUNCTION_TRACE(rs_convert_resource_to_aml); + if (!info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + /* * First table entry must be ACPI_RSC_INITxxx and must contain the * table length (# of table entries) @@ -383,6 +533,14 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, ((ACPI_GET8(source) & 0x03) << info->value); break; + case ACPI_RSC_3BITFLAG: + /* + * Mask and shift the flag bits + */ + ACPI_SET8(destination) |= (u8) + ((ACPI_GET8(source) & 0x07) << info->value); + break; + case ACPI_RSC_COUNT: item_count = ACPI_GET8(source); @@ -400,6 +558,63 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, acpi_rs_set_resource_length(aml_length, aml); break; + case ACPI_RSC_COUNT_GPIO_PIN: + + item_count = ACPI_GET16(source); + ACPI_SET16(destination) = (u16)aml_length; + + aml_length = (u16)(aml_length + item_count * 2); + target = ACPI_ADD_PTR(void, aml, info->value); + ACPI_SET16(target) = (u16)aml_length; + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_GPIO_VEN: + + item_count = ACPI_GET16(source); + ACPI_SET16(destination) = (u16)item_count; + + aml_length = + (u16)(aml_length + (info->value * item_count)); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_GPIO_RES: + + /* Set resource source string length */ + + item_count = ACPI_GET16(source); + ACPI_SET16(destination) = (u16)aml_length; + + /* Compute offset for the Vendor Data */ + + aml_length = (u16)(aml_length + item_count); + target = ACPI_ADD_PTR(void, aml, info->value); + + /* Set vendor offset only if there is vendor data */ + + if (resource->data.gpio.vendor_length) { + ACPI_SET16(target) = (u16)aml_length; + } + + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_SERIAL_VEN: + + item_count = ACPI_GET16(source); + ACPI_SET16(destination) = item_count + info->value; + aml_length = (u16)(aml_length + item_count); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_SERIAL_RES: + + item_count = ACPI_GET16(source); + aml_length = (u16)(aml_length + item_count); + acpi_rs_set_resource_length(aml_length, aml); + break; + case ACPI_RSC_LENGTH: acpi_rs_set_resource_length(info->value, aml); @@ -417,6 +632,48 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, info->opcode); break; + case ACPI_RSC_MOVE_GPIO_PIN: + + destination = (char *)ACPI_ADD_PTR(void, aml, + ACPI_GET16 + (destination)); + source = *(u16 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_GPIO_RES: + + /* Used for both resource_source string and vendor_data */ + + destination = (char *)ACPI_ADD_PTR(void, aml, + ACPI_GET16 + (destination)); + source = *(u8 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_VEN: + + destination = (char *)ACPI_ADD_PTR(void, aml, + (aml_length - + item_count)); + source = *(u8 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_RES: + + destination = (char *)ACPI_ADD_PTR(void, aml, + (aml_length - + item_count)); + source = *(u8 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + case ACPI_RSC_ADDRESS: /* Set the Resource Type, General Flags, and Type-Specific Flags */ diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c new file mode 100644 index 0000000..d0b8e12 --- /dev/null +++ b/drivers/acpi/acpica/rsserial.c @@ -0,0 +1,441 @@ +/******************************************************************************* + * + * Module Name: rsserial - GPIO/serial_bus resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2011, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsserial") + +/******************************************************************************* + * + * acpi_rs_convert_gpio + * + ******************************************************************************/ +struct acpi_rsconvert_info acpi_rs_convert_gpio[17] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_GPIO, + ACPI_RS_SIZE(struct acpi_resource_gpio), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_gpio)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_GPIO, + sizeof(struct aml_resource_gpio), + 0}, + + /* + * These fields are contiguous in both the source and destination: + * revision_id + * connection_type + */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.revision_id), + AML_OFFSET(gpio.revision_id), + 2}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.producer_consumer), + AML_OFFSET(gpio.flags), + 0}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.sharable), + AML_OFFSET(gpio.int_flags), + 3}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.io_restriction), + AML_OFFSET(gpio.int_flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.triggering), + AML_OFFSET(gpio.int_flags), + 0}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.polarity), + AML_OFFSET(gpio.int_flags), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.pin_config), + AML_OFFSET(gpio.pin_config), + 1}, + + /* + * These fields are contiguous in both the source and destination: + * drive_strength + * debounce_timeout + */ + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.gpio.drive_strength), + AML_OFFSET(gpio.drive_strength), + 2}, + + /* Pin Table */ + + {ACPI_RSC_COUNT_GPIO_PIN, ACPI_RS_OFFSET(data.gpio.pin_table_length), + AML_OFFSET(gpio.pin_table_offset), + AML_OFFSET(gpio.res_source_offset)}, + + {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET(data.gpio.pin_table), + AML_OFFSET(gpio.pin_table_offset), + 0}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.resource_source.index), + AML_OFFSET(gpio.res_source_index), + 1}, + + {ACPI_RSC_COUNT_GPIO_RES, + ACPI_RS_OFFSET(data.gpio.resource_source.string_length), + AML_OFFSET(gpio.res_source_offset), + AML_OFFSET(gpio.vendor_offset)}, + + {ACPI_RSC_MOVE_GPIO_RES, + ACPI_RS_OFFSET(data.gpio.resource_source.string_ptr), + AML_OFFSET(gpio.res_source_offset), + 0}, + + /* Vendor Data */ + + {ACPI_RSC_COUNT_GPIO_VEN, ACPI_RS_OFFSET(data.gpio.vendor_length), + AML_OFFSET(gpio.vendor_length), + 1}, + + {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET(data.gpio.vendor_data), + AML_OFFSET(gpio.vendor_offset), + 0}, +}; + +/******************************************************************************* + * + * acpi_rs_convert_i2c_serial_bus + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[16] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, + ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS, + sizeof(struct aml_resource_i2c_serialbus), + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id), + AML_OFFSET(common_serial_bus.revision_id), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type), + AML_OFFSET(common_serial_bus.type), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode), + AML_OFFSET(common_serial_bus.flags), + 0}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer), + AML_OFFSET(common_serial_bus.flags), + 1}, + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id), + AML_OFFSET(common_serial_bus.type_revision_id), + 1}, + + {ACPI_RSC_MOVE16, + ACPI_RS_OFFSET(data.common_serial_bus.type_data_length), + AML_OFFSET(common_serial_bus.type_data_length), + 1}, + + /* Vendor data */ + + {ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_length), + AML_OFFSET(common_serial_bus.type_data_length), + AML_RESOURCE_I2C_MIN_DATA_LEN}, + + {ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_data), + 0, + sizeof(struct aml_resource_i2c_serialbus)}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index), + AML_OFFSET(common_serial_bus.res_source_index), + 1}, + + {ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + {ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + /* I2C bus type specific */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.i2c_serial_bus.access_mode), + AML_OFFSET(i2c_serial_bus.type_specific_flags), + 0}, + + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed), + AML_OFFSET(i2c_serial_bus.connection_speed), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.i2c_serial_bus.slave_address), + AML_OFFSET(i2c_serial_bus.slave_address), + 1}, +}; + +/******************************************************************************* + * + * acpi_rs_convert_spi_serial_bus + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_spi_serial_bus[20] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, + ACPI_RS_SIZE(struct acpi_resource_spi_serialbus), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_spi_serial_bus)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS, + sizeof(struct aml_resource_spi_serialbus), + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id), + AML_OFFSET(common_serial_bus.revision_id), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type), + AML_OFFSET(common_serial_bus.type), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode), + AML_OFFSET(common_serial_bus.flags), + 0}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer), + AML_OFFSET(common_serial_bus.flags), + 1}, + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id), + AML_OFFSET(common_serial_bus.type_revision_id), + 1}, + + {ACPI_RSC_MOVE16, + ACPI_RS_OFFSET(data.common_serial_bus.type_data_length), + AML_OFFSET(common_serial_bus.type_data_length), + 1}, + + /* Vendor data */ + + {ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_length), + AML_OFFSET(common_serial_bus.type_data_length), + AML_RESOURCE_SPI_MIN_DATA_LEN}, + + {ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_data), + 0, + sizeof(struct aml_resource_spi_serialbus)}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index), + AML_OFFSET(common_serial_bus.res_source_index), + 1}, + + {ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + {ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + /* Spi bus type specific */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.spi_serial_bus.wire_mode), + AML_OFFSET(spi_serial_bus.type_specific_flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.spi_serial_bus.device_polarity), + AML_OFFSET(spi_serial_bus.type_specific_flags), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.data_bit_length), + AML_OFFSET(spi_serial_bus.data_bit_length), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.clock_phase), + AML_OFFSET(spi_serial_bus.clock_phase), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.clock_polarity), + AML_OFFSET(spi_serial_bus.clock_polarity), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.spi_serial_bus.device_selection), + AML_OFFSET(spi_serial_bus.device_selection), + 1}, + + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.spi_serial_bus.connection_speed), + AML_OFFSET(spi_serial_bus.connection_speed), + 1}, +}; + +/******************************************************************************* + * + * acpi_rs_convert_uart_serial_bus + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_uart_serial_bus[22] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, + ACPI_RS_SIZE(struct acpi_resource_uart_serialbus), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_uart_serial_bus)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS, + sizeof(struct aml_resource_uart_serialbus), + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id), + AML_OFFSET(common_serial_bus.revision_id), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type), + AML_OFFSET(common_serial_bus.type), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode), + AML_OFFSET(common_serial_bus.flags), + 0}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer), + AML_OFFSET(common_serial_bus.flags), + 1}, + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id), + AML_OFFSET(common_serial_bus.type_revision_id), + 1}, + + {ACPI_RSC_MOVE16, + ACPI_RS_OFFSET(data.common_serial_bus.type_data_length), + AML_OFFSET(common_serial_bus.type_data_length), + 1}, + + /* Vendor data */ + + {ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_length), + AML_OFFSET(common_serial_bus.type_data_length), + AML_RESOURCE_UART_MIN_DATA_LEN}, + + {ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_data), + 0, + sizeof(struct aml_resource_uart_serialbus)}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index), + AML_OFFSET(common_serial_bus.res_source_index), + 1}, + + {ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + {ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + /* Uart bus type specific */ + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.flow_control), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 0}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.stop_bits), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 2}, + + {ACPI_RSC_3BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.data_bits), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 4}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.endian), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 7}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.uart_serial_bus.parity), + AML_OFFSET(uart_serial_bus.parity), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.uart_serial_bus.lines_enabled), + AML_OFFSET(uart_serial_bus.lines_enabled), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.uart_serial_bus.rx_fifo_size), + AML_OFFSET(uart_serial_bus.rx_fifo_size), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.uart_serial_bus.tx_fifo_size), + AML_OFFSET(uart_serial_bus.tx_fifo_size), + 1}, + + {ACPI_RSC_MOVE32, + ACPI_RS_OFFSET(data.uart_serial_bus.default_baud_rate), + AML_OFFSET(uart_serial_bus.default_baud_rate), + 1}, +}; diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 231811e..4409db8 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -144,6 +144,9 @@ acpi_rs_move_data(void *destination, void *source, u16 item_count, u8 move_type) * since there are no alignment or endian issues */ case ACPI_RSC_MOVE8: + case ACPI_RSC_MOVE_GPIO_RES: + case ACPI_RSC_MOVE_SERIAL_VEN: + case ACPI_RSC_MOVE_SERIAL_RES: ACPI_MEMCPY(destination, source, item_count); return; @@ -153,6 +156,7 @@ acpi_rs_move_data(void *destination, void *source, u16 item_count, u8 move_type) * misaligned memory transfers */ case ACPI_RSC_MOVE16: + case ACPI_RSC_MOVE_GPIO_PIN: ACPI_MOVE_16_TO_16(&ACPI_CAST_PTR(u16, destination)[i], &ACPI_CAST_PTR(u16, source)[i]); break; diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index 6ffd3a8..cd7fbbf 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -43,7 +43,7 @@ #include #include "accommon.h" -#include "amlresrc.h" +#include "acresrc.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utresrc") @@ -154,6 +154,138 @@ const char *acpi_gbl_typ_decode[] = { "TypeF" }; +const char *acpi_gbl_ppc_decode[] = { + "PullDefault", + "PullUp", + "PullDown", + "PullNone" +}; + +const char *acpi_gbl_ior_decode[] = { + "IoRestrictionNone", + "IoRestrictionInputOnly", + "IoRestrictionOutputOnly", + "IoRestrictionNoneAndPreserve" +}; + +const char *acpi_gbl_dts_decode[] = { + "Width8bit", + "Width16bit", + "Width32bit", + "Width64bit", + "Width128bit", + "Width256bit", +}; + +/* GPIO connection type */ + +const char *acpi_gbl_ct_decode[] = { + "Interrupt", + "I/O" +}; + +/* Serial bus type */ + +const char *acpi_gbl_sbt_decode[] = { + "/* UNKNOWN serial bus type */", + "I2C", + "SPI", + "UART" +}; + +/* I2C serial bus access mode */ + +const char *acpi_gbl_am_decode[] = { + "AddressingMode7Bit", + "AddressingMode10Bit" +}; + +/* I2C serial bus slave mode */ + +const char *acpi_gbl_sm_decode[] = { + "ControllerInitiated", + "DeviceInitiated" +}; + +/* SPI serial bus wire mode */ + +const char *acpi_gbl_wm_decode[] = { + "FourWireMode", + "ThreeWireMode" +}; + +/* SPI serial clock phase */ + +const char *acpi_gbl_cph_decode[] = { + "ClockPhaseFirst", + "ClockPhaseSecond" +}; + +/* SPI serial bus clock polarity */ + +const char *acpi_gbl_cpo_decode[] = { + "ClockPolarityLow", + "ClockPolarityHigh" +}; + +/* SPI serial bus device polarity */ + +const char *acpi_gbl_dp_decode[] = { + "PolarityLow", + "PolarityHigh" +}; + +/* UART serial bus endian */ + +const char *acpi_gbl_ed_decode[] = { + "LittleEndian", + "BigEndian" +}; + +/* UART serial bus bits per byte */ + +const char *acpi_gbl_bpb_decode[] = { + "DataBitsFive", + "DataBitsSix", + "DataBitsSeven", + "DataBitsEight", + "DataBitsNine", + "/* UNKNOWN Bits per byte */", + "/* UNKNOWN Bits per byte */", + "/* UNKNOWN Bits per byte */" +}; + +/* UART serial bus stop bits */ + +const char *acpi_gbl_sb_decode[] = { + "StopBitsNone", + "StopBitsOne", + "StopBitsOnePlusHalf", + "StopBitsTwo" +}; + +/* UART serial bus flow control */ + +const char *acpi_gbl_fc_decode[] = { + "FlowControlNone", + "FlowControlHardware", + "FlowControlXON", + "/* UNKNOWN flow control keyword */" +}; + +/* UART serial bus parity type */ + +const char *acpi_gbl_pt_decode[] = { + "ParityTypeNone", + "ParityTypeEven", + "ParityTypeOdd", + "ParityTypeMark", + "ParityTypeSpace", + "/* UNKNOWN parity keyword */", + "/* UNKNOWN parity keyword */", + "/* UNKNOWN parity keyword */" +}; + #endif /* @@ -173,7 +305,7 @@ const u8 acpi_gbl_resource_aml_sizes[] = { ACPI_AML_SIZE_SMALL(struct aml_resource_end_dependent), ACPI_AML_SIZE_SMALL(struct aml_resource_io), ACPI_AML_SIZE_SMALL(struct aml_resource_fixed_io), - 0, + ACPI_AML_SIZE_SMALL(struct aml_resource_fixed_dma), 0, 0, 0, @@ -193,7 +325,17 @@ const u8 acpi_gbl_resource_aml_sizes[] = { ACPI_AML_SIZE_LARGE(struct aml_resource_address16), ACPI_AML_SIZE_LARGE(struct aml_resource_extended_irq), ACPI_AML_SIZE_LARGE(struct aml_resource_address64), - ACPI_AML_SIZE_LARGE(struct aml_resource_extended_address64) + ACPI_AML_SIZE_LARGE(struct aml_resource_extended_address64), + ACPI_AML_SIZE_LARGE(struct aml_resource_gpio), + 0, + ACPI_AML_SIZE_LARGE(struct aml_resource_common_serialbus), +}; + +const u8 acpi_gbl_resource_aml_serial_bus_sizes[] = { + 0, + ACPI_AML_SIZE_LARGE(struct aml_resource_i2c_serialbus), + ACPI_AML_SIZE_LARGE(struct aml_resource_spi_serialbus), + ACPI_AML_SIZE_LARGE(struct aml_resource_uart_serialbus), }; /* @@ -209,35 +351,49 @@ static const u8 acpi_gbl_resource_types[] = { 0, 0, 0, - ACPI_SMALL_VARIABLE_LENGTH, - ACPI_FIXED_LENGTH, - ACPI_SMALL_VARIABLE_LENGTH, - ACPI_FIXED_LENGTH, - ACPI_FIXED_LENGTH, - ACPI_FIXED_LENGTH, - 0, + ACPI_SMALL_VARIABLE_LENGTH, /* 04 IRQ */ + ACPI_FIXED_LENGTH, /* 05 DMA */ + ACPI_SMALL_VARIABLE_LENGTH, /* 06 start_dependent_functions */ + ACPI_FIXED_LENGTH, /* 07 end_dependent_functions */ + ACPI_FIXED_LENGTH, /* 08 IO */ + ACPI_FIXED_LENGTH, /* 09 fixed_iO */ + ACPI_FIXED_LENGTH, /* 0_a fixed_dMA */ 0, 0, 0, - ACPI_VARIABLE_LENGTH, - ACPI_FIXED_LENGTH, + ACPI_VARIABLE_LENGTH, /* 0_e vendor_short */ + ACPI_FIXED_LENGTH, /* 0_f end_tag */ /* Large descriptors */ 0, - ACPI_FIXED_LENGTH, - ACPI_FIXED_LENGTH, + ACPI_FIXED_LENGTH, /* 01 Memory24 */ + ACPI_FIXED_LENGTH, /* 02 generic_register */ 0, - ACPI_VARIABLE_LENGTH, - ACPI_FIXED_LENGTH, - ACPI_FIXED_LENGTH, - ACPI_VARIABLE_LENGTH, - ACPI_VARIABLE_LENGTH, - ACPI_VARIABLE_LENGTH, - ACPI_VARIABLE_LENGTH, - ACPI_FIXED_LENGTH + ACPI_VARIABLE_LENGTH, /* 04 vendor_long */ + ACPI_FIXED_LENGTH, /* 05 Memory32 */ + ACPI_FIXED_LENGTH, /* 06 memory32_fixed */ + ACPI_VARIABLE_LENGTH, /* 07 Dword* address */ + ACPI_VARIABLE_LENGTH, /* 08 Word* address */ + ACPI_VARIABLE_LENGTH, /* 09 extended_iRQ */ + ACPI_VARIABLE_LENGTH, /* 0_a Qword* address */ + ACPI_FIXED_LENGTH, /* 0_b Extended* address */ + ACPI_VARIABLE_LENGTH, /* 0_c Gpio* */ + 0, + ACPI_VARIABLE_LENGTH /* 0_e *serial_bus */ }; +/* + * For the i_aSL compiler/disassembler, we don't want any error messages + * because the disassembler uses the resource validation code to determine + * if Buffer objects are actually Resource Templates. + */ +#ifdef ACPI_ASL_COMPILER +#define ACPI_RESOURCE_ERROR(plist) +#else +#define ACPI_RESOURCE_ERROR(plist) ACPI_ERROR(plist) +#endif + /******************************************************************************* * * FUNCTION: acpi_ut_walk_aml_resources @@ -265,6 +421,7 @@ acpi_ut_walk_aml_resources(u8 * aml, u8 resource_index; u32 length; u32 offset = 0; + u8 end_tag[2] = { 0x79, 0x00 }; ACPI_FUNCTION_TRACE(ut_walk_aml_resources); @@ -286,6 +443,10 @@ acpi_ut_walk_aml_resources(u8 * aml, status = acpi_ut_validate_resource(aml, &resource_index); if (ACPI_FAILURE(status)) { + /* + * Exit on failure. Cannot continue because the descriptor length + * may be bogus also. + */ return_ACPI_STATUS(status); } @@ -300,7 +461,7 @@ acpi_ut_walk_aml_resources(u8 * aml, user_function(aml, length, offset, resource_index, context); if (ACPI_FAILURE(status)) { - return (status); + return_ACPI_STATUS(status); } } @@ -333,7 +494,19 @@ acpi_ut_walk_aml_resources(u8 * aml, /* Did not find an end_tag descriptor */ - return (AE_AML_NO_RESOURCE_END_TAG); + if (user_function) { + + /* Insert an end_tag anyway. acpi_rs_get_list_length always leaves room */ + + (void)acpi_ut_validate_resource(end_tag, &resource_index); + status = + user_function(end_tag, 2, offset, resource_index, context); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); } /******************************************************************************* @@ -354,6 +527,7 @@ acpi_ut_walk_aml_resources(u8 * aml, acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) { + union aml_resource *aml_resource; u8 resource_type; u8 resource_index; acpi_rs_length resource_length; @@ -375,7 +549,7 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) /* Verify the large resource type (name) against the max */ if (resource_type > ACPI_RESOURCE_NAME_LARGE_MAX) { - return (AE_AML_INVALID_RESOURCE_TYPE); + goto invalid_resource; } /* @@ -392,15 +566,17 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) ((resource_type & ACPI_RESOURCE_NAME_SMALL_MASK) >> 3); } - /* Check validity of the resource type, zero indicates name is invalid */ - + /* + * Check validity of the resource type, via acpi_gbl_resource_types. Zero + * indicates an invalid resource. + */ if (!acpi_gbl_resource_types[resource_index]) { - return (AE_AML_INVALID_RESOURCE_TYPE); + goto invalid_resource; } /* - * 2) Validate the resource_length field. This ensures that the length - * is at least reasonable, and guarantees that it is non-zero. + * Validate the resource_length field. This ensures that the length + * is at least reasonable, and guarantees that it is non-zero. */ resource_length = acpi_ut_get_resource_length(aml); minimum_resource_length = acpi_gbl_resource_aml_sizes[resource_index]; @@ -413,7 +589,7 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) /* Fixed length resource, length must match exactly */ if (resource_length != minimum_resource_length) { - return (AE_AML_BAD_RESOURCE_LENGTH); + goto bad_resource_length; } break; @@ -422,7 +598,7 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) /* Variable length resource, length must be at least the minimum */ if (resource_length < minimum_resource_length) { - return (AE_AML_BAD_RESOURCE_LENGTH); + goto bad_resource_length; } break; @@ -432,7 +608,7 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) if ((resource_length > minimum_resource_length) || (resource_length < (minimum_resource_length - 1))) { - return (AE_AML_BAD_RESOURCE_LENGTH); + goto bad_resource_length; } break; @@ -440,7 +616,23 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) /* Shouldn't happen (because of validation earlier), but be sure */ - return (AE_AML_INVALID_RESOURCE_TYPE); + goto invalid_resource; + } + + aml_resource = ACPI_CAST_PTR(union aml_resource, aml); + if (resource_type == ACPI_RESOURCE_NAME_SERIAL_BUS) { + + /* Validate the bus_type field */ + + if ((aml_resource->common_serial_bus.type == 0) || + (aml_resource->common_serial_bus.type > + AML_RESOURCE_MAX_SERIALBUSTYPE)) { + ACPI_RESOURCE_ERROR((AE_INFO, + "Invalid/unsupported SerialBus resource descriptor: BusType 0x%2.2X", + aml_resource->common_serial_bus. + type)); + return (AE_AML_INVALID_RESOURCE_TYPE); + } } /* Optionally return the resource table index */ @@ -450,6 +642,22 @@ acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index) } return (AE_OK); + + invalid_resource: + + ACPI_RESOURCE_ERROR((AE_INFO, + "Invalid/unsupported resource descriptor: Type 0x%2.2X", + resource_type)); + return (AE_AML_INVALID_RESOURCE_TYPE); + + bad_resource_length: + + ACPI_RESOURCE_ERROR((AE_INFO, + "Invalid resource descriptor length: Type " + "0x%2.2X, Length 0x%4.4X, MinLength 0x%4.4X", + resource_type, resource_length, + minimum_resource_length)); + return (AE_AML_BAD_RESOURCE_LENGTH); } /******************************************************************************* diff --git a/include/acpi/acrestyp.h b/include/acpi/acrestyp.h index 0a66cc4..3506e39 100644 --- a/include/acpi/acrestyp.h +++ b/include/acpi/acrestyp.h @@ -61,11 +61,14 @@ typedef u32 acpi_rsdesc_size; /* Max Resource Descriptor size is (Length+3) = (6 #define ACPI_WRITE_COMBINING_MEMORY (u8) 0x02 #define ACPI_PREFETCHABLE_MEMORY (u8) 0x03 +/*! [Begin] no source code translation */ /* * IO Attributes - * The ISA IO ranges are: n000-n0_fFh, n400-n4_fFh, n800-n8_fFh, n_c00-n_cFFh. - * The non-ISA IO ranges are: n100-n3_fFh, n500-n7_fFh, n900-n_bFFh, n_cd0-n_fFFh. + * The ISA IO ranges are: n000-n0FFh, n400-n4FFh, n800-n8FFh, nC00-nCFFh. + * The non-ISA IO ranges are: n100-n3FFh, n500-n7FFh, n900-nBFFh, nCD0-nFFFh. */ +/*! [End] no source code translation !*/ + #define ACPI_NON_ISA_ONLY_RANGES (u8) 0x01 #define ACPI_ISA_ONLY_RANGES (u8) 0x02 #define ACPI_ENTIRE_RANGE (ACPI_NON_ISA_ONLY_RANGES | ACPI_ISA_ONLY_RANGES) @@ -81,16 +84,26 @@ typedef u32 acpi_rsdesc_size; /* Max Resource Descriptor size is (Length+3) = (6 #define ACPI_DECODE_16 (u8) 0x01 /* 16-bit IO address decode */ /* - * IRQ Attributes + * Interrupt attributes - used in multiple descriptors */ + +/* Triggering */ + #define ACPI_LEVEL_SENSITIVE (u8) 0x00 #define ACPI_EDGE_SENSITIVE (u8) 0x01 +/* Polarity */ + #define ACPI_ACTIVE_HIGH (u8) 0x00 #define ACPI_ACTIVE_LOW (u8) 0x01 +#define ACPI_ACTIVE_BOTH (u8) 0x02 + +/* Sharing */ #define ACPI_EXCLUSIVE (u8) 0x00 #define ACPI_SHARED (u8) 0x01 +#define ACPI_EXCLUSIVE_AND_WAKE (u8) 0x02 +#define ACPI_SHARED_AND_WAKE (u8) 0x03 /* * DMA Attributes @@ -127,6 +140,8 @@ typedef u32 acpi_rsdesc_size; /* Max Resource Descriptor size is (Length+3) = (6 #define ACPI_POS_DECODE (u8) 0x00 #define ACPI_SUB_DECODE (u8) 0x01 +/* Producer/Consumer */ + #define ACPI_PRODUCER (u8) 0x00 #define ACPI_CONSUMER (u8) 0x01 @@ -192,6 +207,21 @@ struct acpi_resource_fixed_io { u8 address_length; }; +struct acpi_resource_fixed_dma { + u16 request_lines; + u16 channels; + u8 width; +}; + +/* Values for Width field above */ + +#define ACPI_DMA_WIDTH8 0 +#define ACPI_DMA_WIDTH16 1 +#define ACPI_DMA_WIDTH32 2 +#define ACPI_DMA_WIDTH64 3 +#define ACPI_DMA_WIDTH128 4 +#define ACPI_DMA_WIDTH256 5 + struct acpi_resource_vendor { u16 byte_length; u8 byte_data[1]; @@ -329,6 +359,166 @@ struct acpi_resource_generic_register { u64 address; }; +struct acpi_resource_gpio { + u8 revision_id; + u8 connection_type; + u8 producer_consumer; /* For values, see Producer/Consumer above */ + u8 pin_config; + u8 sharable; /* For values, see Interrupt Attributes above */ + u8 io_restriction; + u8 triggering; /* For values, see Interrupt Attributes above */ + u8 polarity; /* For values, see Interrupt Attributes above */ + u16 drive_strength; + u16 debounce_timeout; + u16 pin_table_length; + u16 vendor_length; + struct acpi_resource_source resource_source; + u16 *pin_table; + u8 *vendor_data; +}; + +/* Values for GPIO connection_type field above */ + +#define ACPI_RESOURCE_GPIO_TYPE_INT 0 +#define ACPI_RESOURCE_GPIO_TYPE_IO 1 + +/* Values for pin_config field above */ + +#define ACPI_PIN_CONFIG_DEFAULT 0 +#define ACPI_PIN_CONFIG_PULLUP 1 +#define ACPI_PIN_CONFIG_PULLDOWN 2 +#define ACPI_PIN_CONFIG_NOPULL 3 + +/* Values for io_restriction field above */ + +#define ACPI_IO_RESTRICT_NONE 0 +#define ACPI_IO_RESTRICT_INPUT 1 +#define ACPI_IO_RESTRICT_OUTPUT 2 +#define ACPI_IO_RESTRICT_NONE_PRESERVE 3 + +/* Common structure for I2C, SPI, and UART serial descriptors */ + +#define ACPI_RESOURCE_SERIAL_COMMON \ + u8 revision_id; \ + u8 type; \ + u8 producer_consumer; /* For values, see Producer/Consumer above */\ + u8 slave_mode; \ + u8 type_revision_id; \ + u16 type_data_length; \ + u16 vendor_length; \ + struct acpi_resource_source resource_source; \ + u8 *vendor_data; + +struct acpi_resource_common_serialbus { +ACPI_RESOURCE_SERIAL_COMMON}; + +/* Values for the Type field above */ + +#define ACPI_RESOURCE_SERIAL_TYPE_I2C 1 +#define ACPI_RESOURCE_SERIAL_TYPE_SPI 2 +#define ACPI_RESOURCE_SERIAL_TYPE_UART 3 + +/* Values for slave_mode field above */ + +#define ACPI_CONTROLLER_INITIATED 0 +#define ACPI_DEVICE_INITIATED 1 + +struct acpi_resource_i2c_serialbus { + ACPI_RESOURCE_SERIAL_COMMON u8 access_mode; + u16 slave_address; + u32 connection_speed; +}; + +/* Values for access_mode field above */ + +#define ACPI_I2C_7BIT_MODE 0 +#define ACPI_I2C_10BIT_MODE 1 + +struct acpi_resource_spi_serialbus { + ACPI_RESOURCE_SERIAL_COMMON u8 wire_mode; + u8 device_polarity; + u8 data_bit_length; + u8 clock_phase; + u8 clock_polarity; + u16 device_selection; + u32 connection_speed; +}; + +/* Values for wire_mode field above */ + +#define ACPI_SPI_4WIRE_MODE 0 +#define ACPI_SPI_3WIRE_MODE 1 + +/* Values for device_polarity field above */ + +#define ACPI_SPI_ACTIVE_LOW 0 +#define ACPI_SPI_ACTIVE_HIGH 1 + +/* Values for clock_phase field above */ + +#define ACPI_SPI_FIRST_PHASE 0 +#define ACPI_SPI_SECOND_PHASE 1 + +/* Values for clock_polarity field above */ + +#define ACPI_SPI_START_LOW 0 +#define ACPI_SPI_START_HIGH 1 + +struct acpi_resource_uart_serialbus { + ACPI_RESOURCE_SERIAL_COMMON u8 endian; + u8 data_bits; + u8 stop_bits; + u8 flow_control; + u8 parity; + u8 lines_enabled; + u16 rx_fifo_size; + u16 tx_fifo_size; + u32 default_baud_rate; +}; + +/* Values for Endian field above */ + +#define ACPI_UART_LITTLE_ENDIAN 0 +#define ACPI_UART_BIG_ENDIAN 1 + +/* Values for data_bits field above */ + +#define ACPI_UART_5_DATA_BITS 0 +#define ACPI_UART_6_DATA_BITS 1 +#define ACPI_UART_7_DATA_BITS 2 +#define ACPI_UART_8_DATA_BITS 3 +#define ACPI_UART_9_DATA_BITS 4 + +/* Values for stop_bits field above */ + +#define ACPI_UART_NO_STOP_BITS 0 +#define ACPI_UART_1_STOP_BIT 1 +#define ACPI_UART_1P5_STOP_BITS 2 +#define ACPI_UART_2_STOP_BITS 3 + +/* Values for flow_control field above */ + +#define ACPI_UART_FLOW_CONTROL_NONE 0 +#define ACPI_UART_FLOW_CONTROL_HW 1 +#define ACPI_UART_FLOW_CONTROL_XON_XOFF 2 + +/* Values for Parity field above */ + +#define ACPI_UART_PARITY_NONE 0 +#define ACPI_UART_PARITY_EVEN 1 +#define ACPI_UART_PARITY_ODD 2 +#define ACPI_UART_PARITY_MARK 3 +#define ACPI_UART_PARITY_SPACE 4 + +/* Values for lines_enabled bitfield above */ + +#define ACPI_UART_CARRIER_DETECT (1<<2) +#define ACPI_UART_RING_INDICATOR (1<<3) +#define ACPI_UART_DATA_SET_READY (1<<4) +#define ACPI_UART_DATA_TERMINAL_READY (1<<5) +#define ACPI_UART_CLEAR_TO_SEND (1<<6) +#define ACPI_UART_REQUEST_TO_SEND (1<<7) + /* ACPI_RESOURCE_TYPEs */ #define ACPI_RESOURCE_TYPE_IRQ 0 @@ -348,7 +538,10 @@ struct acpi_resource_generic_register { #define ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 14 /* ACPI 3.0 */ #define ACPI_RESOURCE_TYPE_EXTENDED_IRQ 15 #define ACPI_RESOURCE_TYPE_GENERIC_REGISTER 16 -#define ACPI_RESOURCE_TYPE_MAX 16 +#define ACPI_RESOURCE_TYPE_GPIO 17 /* ACPI 5.0 */ +#define ACPI_RESOURCE_TYPE_FIXED_DMA 18 /* ACPI 5.0 */ +#define ACPI_RESOURCE_TYPE_SERIAL_BUS 19 /* ACPI 5.0 */ +#define ACPI_RESOURCE_TYPE_MAX 19 /* Master union for resource descriptors */ @@ -358,6 +551,7 @@ union acpi_resource_data { struct acpi_resource_start_dependent start_dpf; struct acpi_resource_io io; struct acpi_resource_fixed_io fixed_io; + struct acpi_resource_fixed_dma fixed_dma; struct acpi_resource_vendor vendor; struct acpi_resource_vendor_typed vendor_typed; struct acpi_resource_end_tag end_tag; @@ -370,6 +564,11 @@ union acpi_resource_data { struct acpi_resource_extended_address64 ext_address64; struct acpi_resource_extended_irq extended_irq; struct acpi_resource_generic_register generic_reg; + struct acpi_resource_gpio gpio; + struct acpi_resource_i2c_serialbus i2c_serial_bus; + struct acpi_resource_spi_serialbus spi_serial_bus; + struct acpi_resource_uart_serialbus uart_serial_bus; + struct acpi_resource_common_serialbus common_serial_bus; /* Common fields */ -- cgit v0.10.2 From a91cdde20a870bd773d605c764ed211539bf3020 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 14:46:57 +0800 Subject: ACPI 5.0: New interface, acpi_get_event_resources Executes _AEI and formats the result, similar to acpi_get_current_resources, etc. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index 05721b0..61b2e04 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -215,6 +215,10 @@ acpi_status acpi_rs_set_srs_method_data(struct acpi_namespace_node *node, struct acpi_buffer *ret_buffer); +acpi_status +acpi_rs_get_aei_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + /* * rscalc */ diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 4409db8..54a9a97 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -594,6 +594,56 @@ acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, /******************************************************************************* * + * FUNCTION: acpi_rs_get_aei_method_data + * + * PARAMETERS: Node - Device node + * ret_buffer - Pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _AEI value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_aei_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_get_aei_method_data); + + /* Parameters guaranteed valid by caller */ + + /* Execute the method, no parameters */ + + status = acpi_ut_evaluate_object(node, METHOD_NAME__AEI, + ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list(obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * * FUNCTION: acpi_rs_get_method_data * * PARAMETERS: Handle - Handle to the containing object diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index fe86b37..45a9e5e 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -307,6 +307,46 @@ acpi_set_current_resources(acpi_handle device_handle, ACPI_EXPORT_SYMBOL(acpi_set_current_resources) +/******************************************************************************* + * + * FUNCTION: acpi_get_event_resources + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are getting resources + * in_buffer - Pointer to a buffer containing the + * resources to be set for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the event resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is passed to the routine + * the buffer pointed to by the in_buffer variable. Uses the + * _AEI method. + * + ******************************************************************************/ +acpi_status +acpi_get_event_resources(acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_get_event_resources); + + /* Validate parameters then dispatch to internal routine */ + + status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_rs_get_aei_method_data(node, ret_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_event_resources) + /****************************************************************************** * * FUNCTION: acpi_resource_to_address64 diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index dd86610..726d937 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -358,6 +358,10 @@ acpi_get_possible_resources(acpi_handle device, struct acpi_buffer *ret_buffer); #endif acpi_status +acpi_get_event_resources(acpi_handle device_handle, + struct acpi_buffer *ret_buffer); + +acpi_status acpi_walk_resources(acpi_handle device, char *name, acpi_walk_resource_callback user_function, void *context); -- cgit v0.10.2 From 0e243178047c0219b3367dd44f81040826b7ea83 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 14:51:01 +0800 Subject: ACPI 5.0: New interface, acpi_buffer_to_resource This interface converts an AML buffer to an internal ACPI_RESOURCE. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index f61285d..06264e8 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -51,6 +51,70 @@ ACPI_MODULE_NAME("rscreate") /******************************************************************************* * + * FUNCTION: acpi_buffer_to_resource + * + * PARAMETERS: aml_buffer - Pointer to the resource byte stream + * aml_buffer_length - Length of the aml_buffer + * resource_ptr - Where the converted resource is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a raw AML buffer to a resource list + * + ******************************************************************************/ +acpi_status +acpi_buffer_to_resource(u8 *aml_buffer, + u16 aml_buffer_length, + struct acpi_resource **resource_ptr) +{ + acpi_status status; + acpi_size list_size_needed; + void *resource; + void *current_resource_ptr; + + /* + * Note: we allow AE_AML_NO_RESOURCE_END_TAG, since an end tag + * is not required here. + */ + + /* Get the required length for the converted resource */ + + status = acpi_rs_get_list_length(aml_buffer, aml_buffer_length, + &list_size_needed); + if (status == AE_AML_NO_RESOURCE_END_TAG) { + status = AE_OK; + } + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Allocate a buffer for the converted resource */ + + resource = ACPI_ALLOCATE_ZEROED(list_size_needed); + current_resource_ptr = resource; + if (!resource) { + return (AE_NO_MEMORY); + } + + /* Perform the AML-to-Resource conversion */ + + status = acpi_ut_walk_aml_resources(aml_buffer, aml_buffer_length, + acpi_rs_convert_aml_to_resources, + ¤t_resource_ptr); + if (status == AE_AML_NO_RESOURCE_END_TAG) { + status = AE_OK; + } + if (ACPI_FAILURE(status)) { + ACPI_FREE(resource); + } else { + *resource_ptr = resource; + } + + return (status); +} + +/******************************************************************************* + * * FUNCTION: acpi_rs_create_resource_list * * PARAMETERS: aml_buffer - Pointer to the resource byte stream diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 726d937..5d8df1d 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -376,6 +376,11 @@ acpi_status acpi_resource_to_address64(struct acpi_resource *resource, struct acpi_resource_address64 *out); +acpi_status +acpi_buffer_to_resource(u8 *aml_buffer, + u16 aml_buffer_length, + struct acpi_resource **resource_ptr); + /* * Hardware (ACPI device) interfaces */ -- cgit v0.10.2 From 7fce7a4b0db7d90db553faf568853d56cd908c39 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 16 Nov 2011 14:59:17 +0800 Subject: ACPI 5.0: Add new predefined names Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 60aee56..2ddd61d 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -319,7 +319,8 @@ struct acpi_name_info { /* * Used for ACPI_PTYPE1_FIXED, ACPI_PTYPE1_VAR, ACPI_PTYPE2, - * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT + * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT, + * ACPI_PTYPE2_FIX_VAR */ struct acpi_package_info { u8 type; diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index c445cca..ecac9cf 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -94,6 +94,14 @@ * ACPI_PTYPE2_REV_FIXED: Revision at start, each subpackage is Fixed-length * (Used for _ART, _FPS) * + * ACPI_PTYPE2_FIX_VAR: Each subpackage consists of some fixed-length elements + * followed by an optional element + * object type + * count + * object type + * count = 0 (optional) + * (Used for _DLM) + * *****************************************************************************/ enum acpi_return_package_types { @@ -105,7 +113,8 @@ enum acpi_return_package_types { ACPI_PTYPE2_PKG_COUNT = 6, ACPI_PTYPE2_FIXED = 7, ACPI_PTYPE2_MIN = 8, - ACPI_PTYPE2_REV_FIXED = 9 + ACPI_PTYPE2_REV_FIXED = 9, + ACPI_PTYPE2_FIX_VAR = 10 }; #ifdef ACPI_CREATE_PREDEFINED_TABLE @@ -154,6 +163,7 @@ static const union acpi_predefined_info predefined_names[] = {{"_AC8", 0, ACPI_RTYPE_INTEGER}}, {{"_AC9", 0, ACPI_RTYPE_INTEGER}}, {{"_ADR", 0, ACPI_RTYPE_INTEGER}}, + {{"_AEI", 0, ACPI_RTYPE_BUFFER}}, {{"_AL0", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, @@ -229,6 +239,13 @@ static const union acpi_predefined_info predefined_names[] = {{"_CID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints/Strs) */ {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0,0}, 0,0}}, + {{"_CLS", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (3 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0}, 0, 0}}, + + {{"_CPC", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints/Bufs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0, 0}, 0, + 0}}, + {{"_CRS", 0, ACPI_RTYPE_BUFFER}}, {{"_CRT", 0, ACPI_RTYPE_INTEGER}}, {{"_CSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(n), n-1 Int) */ @@ -237,12 +254,21 @@ static const union acpi_predefined_info predefined_names[] = {{"_CST", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(n), n Pkg (1 Buf/3 Int) */ {{{ACPI_PTYPE2_PKG_COUNT,ACPI_RTYPE_BUFFER, 1, ACPI_RTYPE_INTEGER}, 3,0}}, + {{"_CWS", 1, ACPI_RTYPE_INTEGER}}, {{"_DCK", 1, ACPI_RTYPE_INTEGER}}, {{"_DCS", 0, ACPI_RTYPE_INTEGER}}, {{"_DDC", 1, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER}}, {{"_DDN", 0, ACPI_RTYPE_STRING}}, + {{"_DEP", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, + {{"_DGS", 0, ACPI_RTYPE_INTEGER}}, {{"_DIS", 0, 0}}, + + {{"_DLM", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (1 Ref, 0/1 Optional Buf/Ref) */ + {{{ACPI_PTYPE2_FIX_VAR, ACPI_RTYPE_REFERENCE, 1, + ACPI_RTYPE_REFERENCE | ACPI_RTYPE_BUFFER}, 0, 0}}, + {{"_DMA", 0, ACPI_RTYPE_BUFFER}}, {{"_DOD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */ {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}}, @@ -262,6 +288,7 @@ static const union acpi_predefined_info predefined_names[] = {{"_EJ3", 1, 0}}, {{"_EJ4", 1, 0}}, {{"_EJD", 0, ACPI_RTYPE_STRING}}, + {{"_EVT", 1, 0}}, {{"_FDE", 0, ACPI_RTYPE_BUFFER}}, {{"_FDI", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (16 Int) */ {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16,0}, 0,0}}, @@ -281,14 +308,17 @@ static const union acpi_predefined_info predefined_names[] = {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0}, 0, 0}}, {{"_GAI", 0, ACPI_RTYPE_INTEGER}}, + {{"_GCP", 0, ACPI_RTYPE_INTEGER}}, {{"_GHL", 0, ACPI_RTYPE_INTEGER}}, {{"_GLK", 0, ACPI_RTYPE_INTEGER}}, {{"_GPD", 0, ACPI_RTYPE_INTEGER}}, {{"_GPE", 0, ACPI_RTYPE_INTEGER}}, /* _GPE method, not _GPE scope */ + {{"_GRT", 0, ACPI_RTYPE_BUFFER}}, {{"_GSB", 0, ACPI_RTYPE_INTEGER}}, {{"_GTF", 0, ACPI_RTYPE_BUFFER}}, {{"_GTM", 0, ACPI_RTYPE_BUFFER}}, {{"_GTS", 1, 0}}, + {{"_GWS", 1, ACPI_RTYPE_INTEGER}}, {{"_HID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, {{"_HOT", 0, ACPI_RTYPE_INTEGER}}, {{"_HPP", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */ @@ -303,6 +333,7 @@ static const union acpi_predefined_info predefined_names[] = {{"_HPX", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (var Ints) */ {{{ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5,0}, 0,0}}, + {{"_HRV", 0, ACPI_RTYPE_INTEGER}}, {{"_IFT", 0, ACPI_RTYPE_INTEGER}}, /* See IPMI spec */ {{"_INI", 0, 0}}, {{"_IRC", 0, 0}}, @@ -361,6 +392,9 @@ static const union acpi_predefined_info predefined_names[] = {{"_PR3", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, + {{"_PRE", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, + {{"_PRL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, @@ -391,6 +425,7 @@ static const union acpi_predefined_info predefined_names[] = {{"_PSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (5 Int) with count */ {{{ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER,0,0}, 0,0}}, + {{"_PSE", 1, 0}}, {{"_PSL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, @@ -457,6 +492,7 @@ static const union acpi_predefined_info predefined_names[] = {{"_SLI", 0, ACPI_RTYPE_BUFFER}}, {{"_SPD", 1, ACPI_RTYPE_INTEGER}}, {{"_SRS", 1, 0}}, + {{"_SRT", 1, ACPI_RTYPE_INTEGER}}, {{"_SRV", 0, ACPI_RTYPE_INTEGER}}, /* See IPMI spec */ {{"_SST", 1, 0}}, {{"_STA", 0, ACPI_RTYPE_INTEGER}}, @@ -464,6 +500,7 @@ static const union acpi_predefined_info predefined_names[] = {{"_STP", 2, ACPI_RTYPE_INTEGER}}, {{"_STR", 0, ACPI_RTYPE_BUFFER}}, {{"_STV", 2, ACPI_RTYPE_INTEGER}}, + {{"_SUB", 0, ACPI_RTYPE_STRING}}, {{"_SUN", 0, ACPI_RTYPE_INTEGER}}, {{"_SWS", 0, ACPI_RTYPE_INTEGER}}, {{"_TC1", 0, ACPI_RTYPE_INTEGER}}, diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index c845c80..db80366 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -620,6 +620,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, case ACPI_PTYPE2_FIXED: case ACPI_PTYPE2_MIN: case ACPI_PTYPE2_COUNT: + case ACPI_PTYPE2_FIX_VAR: /* * These types all return a single Package that consists of a @@ -759,6 +760,34 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, } break; + case ACPI_PTYPE2_FIX_VAR: + /* + * Each subpackage has a fixed number of elements and an + * optional element + */ + expected_count = + package->ret_info.count1 + package->ret_info.count2; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + status = + acpi_ns_check_package_elements(data, sub_elements, + package->ret_info. + object_type1, + package->ret_info. + count1, + package->ret_info. + object_type2, + sub_package->package. + count - + package->ret_info. + count1, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + case ACPI_PTYPE2_FIXED: /* Each sub-package has a fixed length */ diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index ac7b854..dd0a0bd 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -634,6 +634,7 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data, case ACPI_PTYPE2_FIXED: case ACPI_PTYPE2_MIN: case ACPI_PTYPE2_REV_FIXED: + case ACPI_PTYPE2_FIX_VAR: break; default: diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 024c4f2..3ee2b22 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -467,11 +467,12 @@ acpi_ns_repair_HID(struct acpi_predefined_data *data, } /* - * Copy and uppercase the string. From the ACPI specification: + * Copy and uppercase the string. From the ACPI 5.0 specification: * * A valid PNP ID must be of the form "AAA####" where A is an uppercase * letter and # is a hex digit. A valid ACPI ID must be of the form - * "ACPI####" where # is a hex digit. + * "NNNN####" where N is an uppercase letter or decimal digit, and + * # is a hex digit. */ for (dest = new_string->string.pointer; *source; dest++, source++) { *dest = (char)ACPI_TOUPPER(*source); -- cgit v0.10.2 From 25169b77f7c33b363db7d099d67491e977cdefc7 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 16 Nov 2011 17:03:54 +0800 Subject: ACPI 5.0: Allow _AEI method in walk resources _AEI contains a resource template, this change adds support for the walk resources function. Signed-off-by: Zhang Rui Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 45a9e5e..f6cceac 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -526,8 +526,9 @@ acpi_rs_match_vendor_resource(struct acpi_resource *resource, void *context) * * PARAMETERS: device_handle - Handle to the device object for the * device we are querying - * Name - Method name of the resources we want - * (METHOD_NAME__CRS or METHOD_NAME__PRS) + * Name - Method name of the resources we want. + * (METHOD_NAME__CRS, METHOD_NAME__PRS, or + * METHOD_NAME__AEI) * user_function - Called for each resource * Context - Passed to user_function * @@ -554,11 +555,12 @@ acpi_walk_resources(acpi_handle device_handle, if (!device_handle || !user_function || !name || (!ACPI_COMPARE_NAME(name, METHOD_NAME__CRS) && - !ACPI_COMPARE_NAME(name, METHOD_NAME__PRS))) { + !ACPI_COMPARE_NAME(name, METHOD_NAME__PRS) && + !ACPI_COMPARE_NAME(name, METHOD_NAME__AEI))) { return_ACPI_STATUS(AE_BAD_PARAMETER); } - /* Get the _CRS or _PRS resource list */ + /* Get the _CRS/_PRS/_AEI resource list */ buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; status = acpi_rs_get_method_data(device_handle, name, &buffer); diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index fc1575f..5b5af0d 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -58,6 +58,7 @@ #define METHOD_NAME__PRT "_PRT" #define METHOD_NAME__CRS "_CRS" #define METHOD_NAME__PRS "_PRS" +#define METHOD_NAME__AEI "_AEI" #define METHOD_NAME__PRW "_PRW" #define METHOD_NAME__SRS "_SRS" -- cgit v0.10.2 From 5dc46c3f13b2f538402a09083bdf67d47836fd81 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Tue, 29 Nov 2011 14:16:05 +0800 Subject: ACPICA: Clean up Makefile Make the various files in alphabetical order to simplify addition of new files. Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index b0af3b8..5f7d0cd 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -8,41 +8,150 @@ ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT # use acpi.o to put all files here into acpi.o modparam namespace obj-y += acpi.o -acpi-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \ - dsmethod.o dsobject.o dsutils.o dswload.o dswstate.o \ - dsinit.o dsargs.o dscontrol.o dswload2.o +acpi-y := \ + dsargs.o \ + dscontrol.o \ + dsfield.o \ + dsinit.o \ + dsmethod.o \ + dsmthdat.o \ + dsobject.o \ + dsopcode.o \ + dsutils.o \ + dswexec.o \ + dswload.o \ + dswload2.o \ + dswscope.o \ + dswstate.o -acpi-y += evevent.o evregion.o evsci.o evxfevnt.o \ - evmisc.o evrgnini.o evxface.o evxfregn.o \ - evgpe.o evgpeblk.o evgpeinit.o evgpeutil.o evxfgpe.o evglock.o +acpi-y += \ + evevent.o \ + evgpe.o \ + evgpeblk.o \ + evgpeinit.o \ + evgpeutil.o \ + evglock.o \ + evmisc.o \ + evregion.o \ + evrgnini.o \ + evsci.o \ + evxface.o \ + evxfevnt.o \ + evxfgpe.o \ + evxfregn.o -acpi-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ - exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\ - excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \ - exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o exdebug.o +acpi-y += \ + exconfig.o \ + exconvrt.o \ + excreate.o \ + exdebug.o \ + exdump.o \ + exfield.o \ + exfldio.o \ + exmutex.o \ + exnames.o \ + exoparg1.o \ + exoparg2.o \ + exoparg3.o \ + exoparg6.o \ + exprep.o \ + exmisc.o \ + exregion.o \ + exresnte.o \ + exresolv.o \ + exresop.o \ + exstore.o \ + exstoren.o \ + exstorob.o \ + exsystem.o \ + exutils.o -acpi-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o hwvalid.o hwpci.o +acpi-y += \ + hwacpi.o \ + hwgpe.o \ + hwpci.o \ + hwregs.o \ + hwsleep.o \ + hwvalid.o \ + hwxface.o acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o -acpi-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \ - nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \ - nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \ - nsparse.o nspredef.o nsrepair.o nsrepair2.o +acpi-y += \ + nsaccess.o \ + nsalloc.o \ + nsdump.o \ + nseval.o \ + nsinit.o \ + nsload.o \ + nsnames.o \ + nsobject.o \ + nsparse.o \ + nspredef.o \ + nsrepair.o \ + nsrepair2.o \ + nssearch.o \ + nsutils.o \ + nswalk.o \ + nsxfeval.o \ + nsxfname.o \ + nsxfobj.o acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o -acpi-y += psargs.o psparse.o psloop.o pstree.o pswalk.o \ - psopcode.o psscope.o psutils.o psxface.o +acpi-y += \ + psargs.o \ + psloop.o \ + psopcode.o \ + psparse.o \ + psscope.o \ + pstree.o \ + psutils.o \ + pswalk.o \ + psxface.o -acpi-y += rsaddr.o rscreate.o rsinfo.o rsio.o rslist.o rsmisc.o rsxface.o \ - rscalc.o rsirq.o rsmemory.o rsutils.o rsserial.o +acpi-y += \ + rsaddr.o \ + rscalc.o \ + rscreate.o \ + rsinfo.o \ + rsio.o \ + rsirq.o \ + rslist.o \ + rsmemory.o \ + rsmisc.o \ + rsserial.o \ + rsutils.o \ + rsxface.o acpi-$(ACPI_FUTURE_USAGE) += rsdump.o -acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o +acpi-y += \ + tbfadt.o \ + tbfind.o \ + tbinstal.o \ + tbutils.o \ + tbxface.o \ + tbxfroot.o -acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ - utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ - utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o \ - utosi.o utxferror.o utdecode.o utxfmutex.o +acpi-y += \ + utalloc.o \ + utcopy.o \ + utdebug.o \ + utdecode.o \ + utdelete.o \ + uteval.o \ + utglobal.o \ + utids.o \ + utinit.o \ + utlock.o \ + utmath.o \ + utmisc.o \ + utmutex.o \ + utobject.o \ + utosi.o \ + utresrc.o \ + utstate.o \ + utxface.o \ + utxferror.o \ + utxfmutex.o -- cgit v0.10.2 From 240c320c59aba93605bff761ceb698df3ee90761 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 28 Nov 2011 10:18:24 +0800 Subject: ACPICA: Update to version 20111123 This version contains full support for the ACPI 5.0 specification. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 5d8df1d..30f5bef 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20110623 +#define ACPI_CA_VERSION 0x20111123 #include "actypes.h" #include "actbl.h" -- cgit v0.10.2 From ecafe6faa294697305453b5a6af7760b94622787 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 12 Jan 2012 11:09:58 +0800 Subject: ACPICA: Update for larger ACPI 5 FADT size FADT is now larger than 256 bytes, so all FADT offsets must be changed from 8 bits to 16 bits. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 1e1a7de..ff1f7f0 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -63,14 +63,15 @@ static void acpi_tb_setup_fadt_registers(void); typedef struct acpi_fadt_info { char *name; - u8 address64; - u8 address32; - u8 length; + u16 address64; + u16 address32; + u16 length; u8 default_length; u8 type; } acpi_fadt_info; +#define ACPI_FADT_OPTIONAL 0 #define ACPI_FADT_REQUIRED 1 #define ACPI_FADT_SEPARATE_LENGTH 2 @@ -87,7 +88,7 @@ static struct acpi_fadt_info fadt_info_table[] = { ACPI_FADT_OFFSET(pm1b_event_block), ACPI_FADT_OFFSET(pm1_event_length), ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */ - 0}, + ACPI_FADT_OPTIONAL}, {"Pm1aControlBlock", ACPI_FADT_OFFSET(xpm1a_control_block), @@ -101,7 +102,7 @@ static struct acpi_fadt_info fadt_info_table[] = { ACPI_FADT_OFFSET(pm1b_control_block), ACPI_FADT_OFFSET(pm1_control_length), ACPI_PM1_REGISTER_WIDTH, - 0}, + ACPI_FADT_OPTIONAL}, {"Pm2ControlBlock", ACPI_FADT_OFFSET(xpm2_control_block), @@ -139,7 +140,7 @@ static struct acpi_fadt_info fadt_info_table[] = { typedef struct acpi_fadt_pm_info { struct acpi_generic_address *target; - u8 source; + u16 source; u8 register_num; } acpi_fadt_pm_info; -- cgit v0.10.2 From f654c0fefa8c16d439185b61442710fadc167e78 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 12 Jan 2012 13:10:32 +0800 Subject: ACPICA: Add support for region address conflict checking Allows drivers to determine if any memory or I/O addresses will conflict with addresses used by ACPI operation regions. Introduces a new interface, acpi_check_address_range. http://marc.info/?t=132251388700002&r=1&w=2 Reported-and-tested-by: Luca Tettamanti Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 5f7d0cd..0ca208b 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -135,6 +135,7 @@ acpi-y += \ tbxfroot.o acpi-y += \ + utaddress.o \ utalloc.o \ utcopy.o \ utdebug.o \ diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index f467d49..4925aa5 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -123,6 +123,10 @@ #define ACPI_MAX_SLEEP 2000 /* Two seconds */ +/* Address Range lists are per-space_id (Memory and I/O only) */ + +#define ACPI_ADDRESS_RANGE_MAX 2 + /****************************************************************************** * * ACPI Specification constants (Do not change unless the specification changes) diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 25912a9..dd5a3e3 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -306,6 +306,8 @@ ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present; ACPI_EXTERN u8 acpi_gbl_events_initialized; ACPI_EXTERN u8 acpi_gbl_osi_data; ACPI_EXTERN struct acpi_interface_info *acpi_gbl_supported_interfaces; +ACPI_EXTERN struct acpi_address_range + *acpi_gbl_address_range_list[ACPI_ADDRESS_RANGE_MAX]; #ifndef DEFINE_ACPI_GLOBALS diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2ddd61d..d3b6f29 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -630,6 +630,15 @@ union acpi_generic_state { typedef acpi_status(*ACPI_EXECUTE_OP) (struct acpi_walk_state * walk_state); +/* Address Range info block */ + +struct acpi_address_range { + struct acpi_address_range *next; + struct acpi_namespace_node *region_node; + acpi_physical_address start_address; + acpi_physical_address end_address; +}; + /***************************************************************************** * * Parser typedefs and structs diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index ab95d45..57df293 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -580,6 +580,24 @@ acpi_ut_create_list(char *list_name, #endif /* ACPI_DBG_TRACK_ALLOCATIONS */ /* + * utaddress - address range check + */ +acpi_status +acpi_ut_add_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + u32 length, struct acpi_namespace_node *region_node); + +void +acpi_ut_remove_address_range(acpi_adr_space_type space_id, + struct acpi_namespace_node *region_node); + +u32 +acpi_ut_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, u32 length, u8 warn); + +void acpi_ut_delete_address_lists(void); + +/* * utxferror - various error/warning output functions */ void ACPI_INTERNAL_VAR_XFACE diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index d69e4a5..19c2be9 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -250,6 +250,13 @@ acpi_ds_get_bank_field_arguments(union acpi_operand_object *obj_desc) status = acpi_ds_execute_arguments(node, node->parent, extra_desc->extra.aml_length, extra_desc->extra.aml_start); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ut_add_address_range(obj_desc->region.space_id, + obj_desc->region.address, + obj_desc->region.length, node); return_ACPI_STATUS(status); } @@ -391,25 +398,8 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) return_ACPI_STATUS(status); } - /* Validate the region address/length via the host OS */ - - status = acpi_os_validate_address(obj_desc->region.space_id, - obj_desc->region.address, - (acpi_size) obj_desc->region.length, - acpi_ut_get_node_name(node)); - - if (ACPI_FAILURE(status)) { - /* - * Invalid address/length. We will emit an error message and mark - * the region as invalid, so that it will cause an additional error if - * it is ever used. Then return AE_OK. - */ - ACPI_EXCEPTION((AE_INFO, status, - "During address validation of OpRegion [%4.4s]", - node->name.ascii)); - obj_desc->common.flags |= AOPOBJ_INVALID; - status = AE_OK; - } - + status = acpi_ut_add_address_range(obj_desc->region.space_id, + obj_desc->region.address, + obj_desc->region.length, node); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c new file mode 100644 index 0000000..67932ae --- /dev/null +++ b/drivers/acpi/acpica/utaddress.c @@ -0,0 +1,294 @@ +/****************************************************************************** + * + * Module Name: utaddress - op_region address range check + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2012, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utaddress") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_add_address_range + * + * PARAMETERS: space_id - Address space ID + * Address - op_region start address + * Length - op_region length + * region_node - op_region namespace node + * + * RETURN: Status + * + * DESCRIPTION: Add the Operation Region address range to the global list. + * The only supported Space IDs are Memory and I/O. Called when + * the op_region address/length operands are fully evaluated. + * + * MUTEX: Locks the namespace + * + * NOTE: Because this interface is only called when an op_region argument + * list is evaluated, there cannot be any duplicate region_nodes. + * Duplicate Address/Length values are allowed, however, so that multiple + * address conflicts can be detected. + * + ******************************************************************************/ +acpi_status +acpi_ut_add_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + u32 length, struct acpi_namespace_node *region_node) +{ + struct acpi_address_range *range_info; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_add_address_range); + + if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + return_ACPI_STATUS(AE_OK); + } + + /* Allocate/init a new info block, add it to the appropriate list */ + + range_info = ACPI_ALLOCATE(sizeof(struct acpi_address_range)); + if (!range_info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + range_info->start_address = address; + range_info->end_address = (address + length - 1); + range_info->region_node = region_node; + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + ACPI_FREE(range_info); + return_ACPI_STATUS(status); + } + + range_info->next = acpi_gbl_address_range_list[space_id]; + acpi_gbl_address_range_list[space_id] = range_info; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "\nAdded [%4.4s] address range: 0x%p-0x%p\n", + acpi_ut_get_node_name(range_info->region_node), + ACPI_CAST_PTR(void, address), + ACPI_CAST_PTR(void, range_info->end_address))); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_address_range + * + * PARAMETERS: space_id - Address space ID + * region_node - op_region namespace node + * + * RETURN: None + * + * DESCRIPTION: Remove the Operation Region from the global list. The only + * supported Space IDs are Memory and I/O. Called when an + * op_region is deleted. + * + * MUTEX: Assumes the namespace is locked + * + ******************************************************************************/ + +void +acpi_ut_remove_address_range(acpi_adr_space_type space_id, + struct acpi_namespace_node *region_node) +{ + struct acpi_address_range *range_info; + struct acpi_address_range *prev; + + ACPI_FUNCTION_TRACE(ut_remove_address_range); + + if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + return_VOID; + } + + /* Get the appropriate list head and check the list */ + + range_info = prev = acpi_gbl_address_range_list[space_id]; + while (range_info) { + if (range_info->region_node == region_node) { + if (range_info == prev) { /* Found at list head */ + acpi_gbl_address_range_list[space_id] = + range_info->next; + } else { + prev->next = range_info->next; + } + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "\nRemoved [%4.4s] address range: 0x%p-0x%p\n", + acpi_ut_get_node_name(range_info-> + region_node), + ACPI_CAST_PTR(void, + range_info-> + start_address), + ACPI_CAST_PTR(void, + range_info-> + end_address))); + + ACPI_FREE(range_info); + return_VOID; + } + + prev = range_info; + range_info = range_info->next; + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_check_address_range + * + * PARAMETERS: space_id - Address space ID + * Address - Start address + * Length - Length of address range + * Warn - TRUE if warning on overlap desired + * + * RETURN: Count of the number of conflicts detected. Zero is always + * returned for Space IDs other than Memory or I/O. + * + * DESCRIPTION: Check if the input address range overlaps any of the + * ASL operation region address ranges. The only supported + * Space IDs are Memory and I/O. + * + * MUTEX: Assumes the namespace is locked. + * + ******************************************************************************/ + +u32 +acpi_ut_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, u32 length, u8 warn) +{ + struct acpi_address_range *range_info; + acpi_physical_address end_address; + char *pathname; + u32 overlap_count = 0; + + ACPI_FUNCTION_TRACE(ut_check_address_range); + + if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + return_UINT32(0); + } + + range_info = acpi_gbl_address_range_list[space_id]; + end_address = address + length - 1; + + /* Check entire list for all possible conflicts */ + + while (range_info) { + /* + * Check if the requested Address/Length overlaps this address_range. + * Four cases to consider: + * + * 1) Input address/length is contained completely in the address range + * 2) Input address/length overlaps range at the range start + * 3) Input address/length overlaps range at the range end + * 4) Input address/length completely encompasses the range + */ + if ((address <= range_info->end_address) && + (end_address >= range_info->start_address)) { + + /* Found an address range overlap */ + + overlap_count++; + if (warn) { /* Optional warning message */ + pathname = + acpi_ns_get_external_pathname(range_info-> + region_node); + + ACPI_WARNING((AE_INFO, + "0x%p-0x%p %s conflicts with Region %s %d", + ACPI_CAST_PTR(void, address), + ACPI_CAST_PTR(void, end_address), + acpi_ut_get_region_name(space_id), + pathname, overlap_count)); + ACPI_FREE(pathname); + } + } + + range_info = range_info->next; + } + + return_UINT32(overlap_count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_address_lists + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Delete all global address range lists (called during + * subsystem shutdown). + * + ******************************************************************************/ + +void acpi_ut_delete_address_lists(void) +{ + struct acpi_address_range *next; + struct acpi_address_range *range_info; + int i; + + /* Delete all elements in all address range lists */ + + for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) { + next = acpi_gbl_address_range_list[i]; + + while (next) { + range_info = next; + next = range_info->next; + ACPI_FREE(range_info); + } + + acpi_gbl_address_range_list[i] = NULL; + } +} diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 31f5a78..93ec06b 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -215,11 +215,14 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "***** Region %p\n", object)); - /* Invalidate the region address/length via the host OS */ - - acpi_os_invalidate_address(object->region.space_id, - object->region.address, - (acpi_size) object->region.length); + /* + * Update address_range list. However, only permanent regions + * are installed in this list. (Not created within a method) + */ + if (!(object->region.node->flags & ANOBJ_TEMPORARY)) { + acpi_ut_remove_address_range(object->region.space_id, + object->region.node); + } second_desc = acpi_ns_get_secondary_object(object); if (second_desc) { diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index ffba0a3..3cf758b 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -264,6 +264,12 @@ acpi_status acpi_ut_init_globals(void) return_ACPI_STATUS(status); } + /* Address Range lists */ + + for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) { + acpi_gbl_address_range_list[i] = NULL; + } + /* Mutex locked flags */ for (i = 0; i < ACPI_NUM_MUTEX; i++) { diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index 191b682..cab61a0 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -92,6 +92,7 @@ static void acpi_ut_terminate(void) gpe_xrupt_info = next_gpe_xrupt_info; } + acpi_ut_delete_address_lists(); return_VOID; } diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 420ebfe..15cbdde 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -48,6 +48,7 @@ #include "acnamesp.h" #include "acdebug.h" #include "actables.h" +#include "acinterp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utxface") @@ -640,4 +641,41 @@ acpi_status acpi_install_interface_handler(acpi_interface_handler handler) } ACPI_EXPORT_SYMBOL(acpi_install_interface_handler) + +/***************************************************************************** + * + * FUNCTION: acpi_check_address_range + * + * PARAMETERS: space_id - Address space ID + * Address - Start address + * Length - Length + * Warn - TRUE if warning on overlap desired + * + * RETURN: Count of the number of conflicts detected. + * + * DESCRIPTION: Check if the input address range overlaps any of the + * ASL operation region address ranges. + * + ****************************************************************************/ +u32 +acpi_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + acpi_size length, u8 warn) +{ + u32 overlaps; + acpi_status status; + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (0); + } + + overlaps = acpi_ut_check_address_range(space_id, address, + (u32)length, warn); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (overlaps); +} + +ACPI_EXPORT_SYMBOL(acpi_check_address_range) #endif /* !ACPI_ASL_COMPILER */ diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f31c5c5..3e57fbd 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -83,19 +83,6 @@ static struct workqueue_struct *kacpi_notify_wq; struct workqueue_struct *kacpi_hotplug_wq; EXPORT_SYMBOL(kacpi_hotplug_wq); -struct acpi_res_list { - resource_size_t start; - resource_size_t end; - acpi_adr_space_type resource_type; /* IO port, System memory, ...*/ - char name[5]; /* only can have a length of 4 chars, make use of this - one instead of res->name, no need to kalloc then */ - struct list_head resource_list; - int count; -}; - -static LIST_HEAD(resource_list_head); -static DEFINE_SPINLOCK(acpi_res_lock); - /* * This list of permanent mappings is for memory that may be accessed from * interrupt context, where we can't do the ioremap(). @@ -1278,44 +1265,28 @@ __setup("acpi_enforce_resources=", acpi_enforce_resources_setup); * drivers */ int acpi_check_resource_conflict(const struct resource *res) { - struct acpi_res_list *res_list_elem; - int ioport = 0, clash = 0; + acpi_adr_space_type space_id; + acpi_size length; + u8 warn = 0; + int clash = 0; if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) return 0; if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM)) return 0; - ioport = res->flags & IORESOURCE_IO; - - spin_lock(&acpi_res_lock); - list_for_each_entry(res_list_elem, &resource_list_head, - resource_list) { - if (ioport && (res_list_elem->resource_type - != ACPI_ADR_SPACE_SYSTEM_IO)) - continue; - if (!ioport && (res_list_elem->resource_type - != ACPI_ADR_SPACE_SYSTEM_MEMORY)) - continue; + if (res->flags & IORESOURCE_IO) + space_id = ACPI_ADR_SPACE_SYSTEM_IO; + else + space_id = ACPI_ADR_SPACE_SYSTEM_MEMORY; - if (res->end < res_list_elem->start - || res_list_elem->end < res->start) - continue; - clash = 1; - break; - } - spin_unlock(&acpi_res_lock); + length = res->end - res->start + 1; + if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) + warn = 1; + clash = acpi_check_address_range(space_id, res->start, length, warn); if (clash) { if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) { - printk(KERN_WARNING "ACPI: resource %s %pR" - " conflicts with ACPI region %s " - "[%s 0x%zx-0x%zx]\n", - res->name, res, res_list_elem->name, - (res_list_elem->resource_type == - ACPI_ADR_SPACE_SYSTEM_IO) ? "io" : "mem", - (size_t) res_list_elem->start, - (size_t) res_list_elem->end); if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX) printk(KERN_NOTICE "ACPI: This conflict may" " cause random problems and system" @@ -1467,155 +1438,6 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) kmem_cache_free(cache, object); return (AE_OK); } - -static inline int acpi_res_list_add(struct acpi_res_list *res) -{ - struct acpi_res_list *res_list_elem; - - list_for_each_entry(res_list_elem, &resource_list_head, - resource_list) { - - if (res->resource_type == res_list_elem->resource_type && - res->start == res_list_elem->start && - res->end == res_list_elem->end) { - - /* - * The Region(addr,len) already exist in the list, - * just increase the count - */ - - res_list_elem->count++; - return 0; - } - } - - res->count = 1; - list_add(&res->resource_list, &resource_list_head); - return 1; -} - -static inline void acpi_res_list_del(struct acpi_res_list *res) -{ - struct acpi_res_list *res_list_elem; - - list_for_each_entry(res_list_elem, &resource_list_head, - resource_list) { - - if (res->resource_type == res_list_elem->resource_type && - res->start == res_list_elem->start && - res->end == res_list_elem->end) { - - /* - * If the res count is decreased to 0, - * remove and free it - */ - - if (--res_list_elem->count == 0) { - list_del(&res_list_elem->resource_list); - kfree(res_list_elem); - } - return; - } - } -} - -acpi_status -acpi_os_invalidate_address( - u8 space_id, - acpi_physical_address address, - acpi_size length) -{ - struct acpi_res_list res; - - switch (space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - /* Only interference checks against SystemIO and SystemMemory - are needed */ - res.start = address; - res.end = address + length - 1; - res.resource_type = space_id; - spin_lock(&acpi_res_lock); - acpi_res_list_del(&res); - spin_unlock(&acpi_res_lock); - break; - case ACPI_ADR_SPACE_PCI_CONFIG: - case ACPI_ADR_SPACE_EC: - case ACPI_ADR_SPACE_SMBUS: - case ACPI_ADR_SPACE_CMOS: - case ACPI_ADR_SPACE_PCI_BAR_TARGET: - case ACPI_ADR_SPACE_DATA_TABLE: - case ACPI_ADR_SPACE_FIXED_HARDWARE: - break; - } - return AE_OK; -} - -/****************************************************************************** - * - * FUNCTION: acpi_os_validate_address - * - * PARAMETERS: space_id - ACPI space ID - * address - Physical address - * length - Address length - * - * RETURN: AE_OK if address/length is valid for the space_id. Otherwise, - * should return AE_AML_ILLEGAL_ADDRESS. - * - * DESCRIPTION: Validate a system address via the host OS. Used to validate - * the addresses accessed by AML operation regions. - * - *****************************************************************************/ - -acpi_status -acpi_os_validate_address ( - u8 space_id, - acpi_physical_address address, - acpi_size length, - char *name) -{ - struct acpi_res_list *res; - int added; - if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) - return AE_OK; - - switch (space_id) { - case ACPI_ADR_SPACE_SYSTEM_IO: - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - /* Only interference checks against SystemIO and SystemMemory - are needed */ - res = kzalloc(sizeof(struct acpi_res_list), GFP_KERNEL); - if (!res) - return AE_OK; - /* ACPI names are fixed to 4 bytes, still better use strlcpy */ - strlcpy(res->name, name, 5); - res->start = address; - res->end = address + length - 1; - res->resource_type = space_id; - spin_lock(&acpi_res_lock); - added = acpi_res_list_add(res); - spin_unlock(&acpi_res_lock); - pr_debug("%s %s resource: start: 0x%llx, end: 0x%llx, " - "name: %s\n", added ? "Added" : "Already exist", - (space_id == ACPI_ADR_SPACE_SYSTEM_IO) - ? "SystemIO" : "System Memory", - (unsigned long long)res->start, - (unsigned long long)res->end, - res->name); - if (!added) - kfree(res); - break; - case ACPI_ADR_SPACE_PCI_CONFIG: - case ACPI_ADR_SPACE_EC: - case ACPI_ADR_SPACE_SMBUS: - case ACPI_ADR_SPACE_CMOS: - case ACPI_ADR_SPACE_PCI_BAR_TARGET: - case ACPI_ADR_SPACE_DATA_TABLE: - case ACPI_ADR_SPACE_FIXED_HARDWARE: - break; - } - return AE_OK; -} #endif acpi_status __init acpi_os_initialize(void) diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 83062ed..2fe8639 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -238,13 +238,6 @@ acpi_os_write_pci_configuration(struct acpi_pci_id *pci_id, /* * Miscellaneous */ -acpi_status -acpi_os_validate_address(u8 space_id, acpi_physical_address address, - acpi_size length, char *name); -acpi_status -acpi_os_invalidate_address(u8 space_id, acpi_physical_address address, - acpi_size length); - u64 acpi_os_get_timer(void); acpi_status acpi_os_signal(u32 function, void *info); diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 30f5bef..a4cfded 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -112,6 +112,11 @@ acpi_status acpi_install_interface(acpi_string interface_name); acpi_status acpi_remove_interface(acpi_string interface_name); +u32 +acpi_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + acpi_size length, u8 warn); + /* * ACPI Memory management */ -- cgit v0.10.2 From 77848130e53b06c22fe37a7b6acbb82bb3e9bfba Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 12 Jan 2012 13:27:23 +0800 Subject: ACPICA: Update all copyrights to 2012 Update all copyrights to 2012. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h index e0ba17f..a44bd42 100644 --- a/drivers/acpi/acpica/accommon.h +++ b/drivers/acpi/acpica/accommon.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index 4925aa5..1f30af6 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index eb0b1f8..deaa819 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h index 2d1b7ff..5935ba6 100644 --- a/drivers/acpi/acpica/acdispat.h +++ b/drivers/acpi/acpica/acdispat.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index f763b88..c53caa5 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index dd5a3e3..261fd5e 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index e7213be..677793e 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index 4ee9058..eb30863 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index d3b6f29..3f24068 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index b7491ee..ef338a9 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 79a598c..2c9e0f0 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 435dd2f..c065078 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index 9896c77..9440d05 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 5ea1e06..b725d78 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index ecac9cf..bbb34c9 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index 61b2e04..0347d09 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h index 1623b24..0404df6 100644 --- a/drivers/acpi/acpica/acstruct.h +++ b/drivers/acpi/acpica/acstruct.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 967f081..d5bec30 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 57df293..925ccf2 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index c7db80e..905280f 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -7,7 +7,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index d4c3051..7b2128f 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index 19c2be9..80eb190 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c index 26c49ff..effe4ca 100644 --- a/drivers/acpi/acpica/dscontrol.c +++ b/drivers/acpi/acpica/dscontrol.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index aa880d9..cd243cf 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c index a7718bf..9e5ac7f 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 5d79775..00f5dab 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index 905ce29..b40bd50 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index f42e17e..d7045ca 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index c627a28..e5eff75 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c index 2c477ce..1abcda3 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index fe40e4c..642f3c0 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 324acec..552aa3a 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c index 9763181..ae71477 100644 --- a/drivers/acpi/acpica/dswload2.c +++ b/drivers/acpi/acpica/dswload2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c index 76a661f..9e9490a 100644 --- a/drivers/acpi/acpica/dswscope.c +++ b/drivers/acpi/acpica/dswscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index a6c374e..c9c2ac1 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index 1f43be4..6729ebe 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c index af1e71a..5e5683c 100644 --- a/drivers/acpi/acpica/evglock.c +++ b/drivers/acpi/acpica/evglock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 65c79ad..9e88cb6 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index ca2c41a..be75339 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index ce9aa9f..adf7494 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 80a81d0..2507393 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index d0b3318..84966f4 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index dcdc7e4..1b0180a 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 55a5d35..819c17f 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c index 2ebd40e..26065c6 100644 --- a/drivers/acpi/acpica/evsci.c +++ b/drivers/acpi/acpica/evsci.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index f4f523b..61944e8 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 20516e5..1768bbe 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index f06a3ee..33388fd 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index aee887e..6019208c 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index efe41f7..c86d44e 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index 74162a1..e385436 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index c66562b..3f5bc99 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c index c7a2f1e..e211e9c 100644 --- a/drivers/acpi/acpica/exdebug.c +++ b/drivers/acpi/acpica/exdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index c7064f4..2a6ac0a 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index 3aeec02..dc092f5 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 5b76c11..149de45 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 703d88e..0a08933 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index be1c56e..60933e9 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c index 49ec049..fcc75fa 100644 --- a/drivers/acpi/acpica/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 236ead1..9ba8c73 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index 2571b4a..879e8a2 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 1b48d9d..71fcc65 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index f4a2787..0786b86 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 18b75fe..30157f5 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index f0d5e14..12d51df 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c index 55997e4..fa50e77 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index db502cd..6e335dc 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index e3bb00c..a67b1d9 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index c0c8842..c6cf843 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index a979017..b35bed5 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -7,7 +7,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c index dc665cc..65a45d8 100644 --- a/drivers/acpi/acpica/exstorob.c +++ b/drivers/acpi/acpica/exstorob.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c index df66e7b..191a129 100644 --- a/drivers/acpi/acpica/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index 3993aa5..eb6798b 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index fc380d3..d21ec5f 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index f610d88..1a6894a 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c index 050fd22..1455ddc 100644 --- a/drivers/acpi/acpica/hwpci.c +++ b/drivers/acpi/acpica/hwpci.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index cc70f3f..4ea4eeb5 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -7,7 +7,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index d52da30..3c4a922 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c index 50d21c4..d4973d9 100644 --- a/drivers/acpi/acpica/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index f8fbea9..6e5c43a 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index c2793a8..7bdbbcf 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index d93172f..61623f3 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index 1d0ef15..7c3d3ce 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index b683cc2..b7f2b3b 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index 2ed294b..30ea5bc 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index c1bd02b..f375cb8 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index fd7c638..9d84ec2 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index 5f7dc69..5cbf15f 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index d5fa520..b20e7c8 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c index 3bb8bf1..dd77a3c 100644 --- a/drivers/acpi/acpica/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index b3234fa..ec7ba2d 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index db80366..bbe46a4 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index dd0a0bd..9c35d20 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 3ee2b22..726bc8e 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index 28b0d7a..507043d 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index cb1b104..a535b7a 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 345f0c3..f69895a 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index e7f016d..71d15f6 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 83bf930..af401c9 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index 57e6d82..880a605 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 4037f90..5ac36ab 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index 01dd70d..9547ad8 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c index 9b31910..a0226fd 100644 --- a/drivers/acpi/acpica/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 9bb0cbd..2ff9c35 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c index a5faa13..c872aa4 100644 --- a/drivers/acpi/acpica/psscope.c +++ b/drivers/acpi/acpica/psscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index 04311bb..2b03cdb 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c index 7eda785..13bb131 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c index 3312d63..ab96cf4 100644 --- a/drivers/acpi/acpica/pswalk.c +++ b/drivers/acpi/acpica/pswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 8086805..9d98c5f 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c index 9e66f90..a030565 100644 --- a/drivers/acpi/acpica/rsaddr.c +++ b/drivers/acpi/acpica/rsaddr.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 3a29b4dd..3c6df4b 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index 06264e8..46d6eb3 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index 9969985..b4c5811 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c index e5489bf..a9fa515 100644 --- a/drivers/acpi/acpica/rsinfo.c +++ b/drivers/acpi/acpica/rsinfo.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsio.c b/drivers/acpi/acpica/rsio.c index 0c7efef..f6a0810 100644 --- a/drivers/acpi/acpica/rsio.c +++ b/drivers/acpi/acpica/rsio.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsirq.c b/drivers/acpi/acpica/rsirq.c index 3ac9d4a..e23a9ec 100644 --- a/drivers/acpi/acpica/rsirq.c +++ b/drivers/acpi/acpica/rsirq.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c index c446351..9be129f 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsmemory.c b/drivers/acpi/acpica/rsmemory.c index 7cc6d86..4fd611a 100644 --- a/drivers/acpi/acpica/rsmemory.c +++ b/drivers/acpi/acpica/rsmemory.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index af3dc63..8073b37 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index d0b8e12..9aa5e68 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 54a9a97..433a375 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index f6cceac..f58c098 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index ff1f7f0..c5d8704 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index a55cb2b..4903e36 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 62365f6..1aecf7b 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index da56a19..09ca39e 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index e7d13f5..abcc641 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index 7eb6c6c..4258f64 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c index 0a69735..9982d2ea 100644 --- a/drivers/acpi/acpica/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index aded299..3317c0a 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index a1f8d75..a0998a8 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index 6fd56d5..d42ede52 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 93ec06b..2a6c3e1 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 18f73c9..479f32b 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 3cf758b..4153584 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index b679ea6..c92eb1d 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index cab61a0..8359c0c 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c index f6bb75c..155fd78 100644 --- a/drivers/acpi/acpica/utlock.c +++ b/drivers/acpi/acpica/utlock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c index ce481da..2491a55 100644 --- a/drivers/acpi/acpica/utmath.c +++ b/drivers/acpi/acpica/utmath.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index c33a852..86f19db 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 302c74e..43174df 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 188340a..b112744 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index 1fb10cb..2360cf7 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index cd7fbbf..9d441ea 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c index 30c21e1..4267477 100644 --- a/drivers/acpi/acpica/utstate.c +++ b/drivers/acpi/acpica/utstate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 15cbdde..644e8c8 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c index 8d0245e..52b568a 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxfmutex.c b/drivers/acpi/acpica/utxfmutex.c index 1ea016b..1427d19 100644 --- a/drivers/acpi/acpica/utxfmutex.c +++ b/drivers/acpi/acpica/utxfmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2011, Intel Corp. + * Copyright (C) 2000 - 2012, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without -- cgit v0.10.2 From 5d1f86572685d577b76142b7c61453000e1a5e26 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 12 Jan 2012 13:29:41 +0800 Subject: ACPICA: Update to version 20120111 Version 20120111. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index a4cfded..dceeaf51 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20111123 +#define ACPI_CA_VERSION 0x20120111 #include "actypes.h" #include "actbl.h" -- cgit v0.10.2 From 04c25997c97e57d95d1f5a7a6e5fff2411dbd48b Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:40 +0800 Subject: ACPI, Add 64bit read/write support to atomicio on i386 There is no 64bit read/write support in ACPI atomicio because readq/writeq is used to implement 64bit read/write, but readq/writeq is not available on i386. This patch implement 64bit read/write support in atomicio via two readl/writel. Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/atomicio.c b/drivers/acpi/atomicio.c index cfc0cc1..1016f18 100644 --- a/drivers/acpi/atomicio.c +++ b/drivers/acpi/atomicio.c @@ -260,6 +260,21 @@ int acpi_post_unmap_gar(struct acpi_generic_address *reg) } EXPORT_SYMBOL_GPL(acpi_post_unmap_gar); +#ifdef readq +static inline u64 read64(const volatile void __iomem *addr) +{ + return readq(addr); +} +#else +static inline u64 read64(const volatile void __iomem *addr) +{ + u64 l, h; + l = readl(addr); + h = readl(addr+4); + return l | (h << 32); +} +#endif + /* * Can be used in atomic (including NMI) or process context. RCU read * lock can only be released after the IO memory area accessing. @@ -280,11 +295,9 @@ static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width) case 32: *val = readl(addr); break; -#ifdef readq case 64: - *val = readq(addr); + *val = read64(addr); break; -#endif default: return -EINVAL; } @@ -293,6 +306,19 @@ static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width) return 0; } +#ifdef writeq +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writeq(val, addr); +} +#else +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val>>32, addr+4); +} +#endif + static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width) { void __iomem *addr; @@ -309,11 +335,9 @@ static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width) case 32: writel(val, addr); break; -#ifdef writeq case 64: - writeq(val, addr); + write64(val, addr); break; -#endif default: return -EINVAL; } -- cgit v0.10.2 From a654e5ee4f2213844d23361eda4955fe9efaf35f Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:41 +0800 Subject: ACPI, APEI, GHES: Add PCIe AER recovery support aer_recover_queue() is called when recoverable PCIe AER errors are notified by firmware to do the recovery work. Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index b8e08cb..511b971 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include #include @@ -476,6 +478,27 @@ static void ghes_do_proc(const struct acpi_hest_generic_status *estatus) } #endif } +#ifdef CONFIG_ACPI_APEI_PCIEAER + else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, + CPER_SEC_PCIE)) { + struct cper_sec_pcie *pcie_err; + pcie_err = (struct cper_sec_pcie *)(gdata+1); + if (sev == GHES_SEV_RECOVERABLE && + sec_sev == GHES_SEV_RECOVERABLE && + pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && + pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { + unsigned int devfn; + int aer_severity; + devfn = PCI_DEVFN(pcie_err->device_id.device, + pcie_err->device_id.function); + aer_severity = cper_severity_to_aer(sev); + aer_recover_queue(pcie_err->device_id.segment, + pcie_err->device_id.bus, + devfn, aer_severity); + } + + } +#endif } } -- cgit v0.10.2 From 46b91e379f7180b482b789fbe615946d91e3a07f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 8 Dec 2011 11:25:42 +0800 Subject: ACPI, APEI, Print resource errors in conventional format Use the normal %pR-like format for MMIO and I/O port ranges. Signed-off-by: Bjorn Helgaas Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 6154036..3492896 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -460,9 +460,9 @@ int apei_resources_request(struct apei_resources *resources, desc); if (!r) { pr_err(APEI_PFX - "Can not request iomem region <%016llx-%016llx> for GARs.\n", + "Can not request [mem %#010llx-%#010llx] for %s registers\n", (unsigned long long)res->start, - (unsigned long long)res->end); + (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_iomem; } @@ -472,9 +472,9 @@ int apei_resources_request(struct apei_resources *resources, r = request_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX - "Can not request ioport region <%016llx-%016llx> for GARs.\n", + "Can not request [io %#06llx-%#06llx] for %s registers\n", (unsigned long long)res->start, - (unsigned long long)res->end); + (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_ioport; } diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 589b96c..d253902 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -209,9 +209,10 @@ static int __einj_error_trigger(u64 trigger_paddr) "APEI EINJ Trigger Table"); if (!r) { pr_err(EINJ_PFX - "Can not request iomem region <%016llx-%016llx> for Trigger table.\n", + "Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, - (unsigned long long)trigger_paddr+sizeof(*trigger_tab)); + (unsigned long long)trigger_paddr + + sizeof(*trigger_tab) - 1); goto out; } trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); @@ -232,9 +233,9 @@ static int __einj_error_trigger(u64 trigger_paddr) "APEI EINJ Trigger Table"); if (!r) { pr_err(EINJ_PFX -"Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n", - (unsigned long long)trigger_paddr+sizeof(*trigger_tab), - (unsigned long long)trigger_paddr + table_size); +"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", + (unsigned long long)trigger_paddr + sizeof(*trigger_tab), + (unsigned long long)trigger_paddr + table_size - 1); goto out_rel_header; } iounmap(trigger_tab); -- cgit v0.10.2 From ad6861547b52ad7c31eacc336b79ac91d7fded75 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:43 +0800 Subject: ACPI, APEI, Remove table not found message Because APEI tables are optional, these message may confuse users, for example, https://bugs.launchpad.net/ubuntu/+source/linux/+bug/599715 Reported-by: Bjorn Helgaas Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index d253902..43eeb2e 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -466,10 +466,9 @@ static int __init einj_init(void) status = acpi_get_table(ACPI_SIG_EINJ, 0, (struct acpi_table_header **)&einj_tab); - if (status == AE_NOT_FOUND) { - pr_info(EINJ_PFX "Table is not found!\n"); + if (status == AE_NOT_FOUND) return -ENODEV; - } else if (ACPI_FAILURE(status)) { + else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(EINJ_PFX "Failed to get table, %s\n", msg); return -EINVAL; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 631b947..8e8d786 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -1125,10 +1125,9 @@ static int __init erst_init(void) status = acpi_get_table(ACPI_SIG_ERST, 0, (struct acpi_table_header **)&erst_tab); - if (status == AE_NOT_FOUND) { - pr_info(ERST_PFX "Table is not found!\n"); + if (status == AE_NOT_FOUND) goto err; - } else if (ACPI_FAILURE(status)) { + else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(ERST_PFX "Failed to get table, %s\n", msg); rc = -EINVAL; diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index 05fee06..f709269 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -221,10 +221,9 @@ void __init acpi_hest_init(void) status = acpi_get_table(ACPI_SIG_HEST, 0, (struct acpi_table_header **)&hest_tab); - if (status == AE_NOT_FOUND) { - pr_info(HEST_PFX "Table not found.\n"); + if (status == AE_NOT_FOUND) goto err; - } else if (ACPI_FAILURE(status)) { + else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(HEST_PFX "Failed to get table, %s\n", msg); rc = -EINVAL; -- cgit v0.10.2 From 5ba82ab534a325d310fe02af1c149f1072792c7b Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:44 +0800 Subject: ACPI, APEI, GHES, Distinguish interleaved error report in kernel log In most cases, printk only guarantees messages from different printk calling will not be interleaved between each other. But, one APEI GHES hardware error report will involve multiple printk calling, normally each for one line. So it is possible that the hardware error report comes from different generic hardware error source will be interleaved. In this patch, a sequence number is prefixed to each line of error report. So that, even if they are interleaved, they still can be distinguished by the prefixed sequence number. Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 511b971..9dcb2d8 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -506,16 +506,22 @@ static void __ghes_print_estatus(const char *pfx, const struct acpi_hest_generic *generic, const struct acpi_hest_generic_status *estatus) { + static atomic_t seqno; + unsigned int curr_seqno; + char pfx_seq[64]; + if (pfx == NULL) { if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED) - pfx = KERN_WARNING HW_ERR; + pfx = KERN_WARNING; else - pfx = KERN_ERR HW_ERR; + pfx = KERN_ERR; } + curr_seqno = atomic_inc_return(&seqno); + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", - pfx, generic->header.source_id); - apei_estatus_print(pfx, estatus); + pfx_seq, generic->header.source_id); + apei_estatus_print(pfx_seq, estatus); } static int ghes_print_estatus(const char *pfx, @@ -798,7 +804,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (sev_global >= GHES_SEV_PANIC) { oops_begin(); - __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic, + __ghes_print_estatus(KERN_EMERG, ghes_global->generic, ghes_global->estatus); /* reboot to log the error! */ if (panic_timeout == 0) -- cgit v0.10.2 From 46d12f0bcb17b2de89a059114349d472b7eb1783 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:45 +0800 Subject: ACPI, APEI, Printk queued error record before panic Because printk is not safe inside NMI handler, the recoverable error records received in NMI handler will be queued to be printked in a delayed IRQ context via irq_work. If a fatal error occurs after the recoverable error and before the irq_work processed, we lost a error report. To solve the issue, the queued error records are printked in NMI handler if system will go panic. Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 9dcb2d8..aaf3609 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -740,26 +740,34 @@ static int ghes_notify_sci(struct notifier_block *this, return ret; } +static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) +{ + struct llist_node *next, *tail = NULL; + + while (llnode) { + next = llnode->next; + llnode->next = tail; + tail = llnode; + llnode = next; + } + + return tail; +} + static void ghes_proc_in_irq(struct irq_work *irq_work) { - struct llist_node *llnode, *next, *tail = NULL; + struct llist_node *llnode, *next; struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; struct acpi_hest_generic_status *estatus; u32 len, node_len; + llnode = llist_del_all(&ghes_estatus_llist); /* * Because the time order of estatus in list is reversed, * revert it back to proper order. */ - llnode = llist_del_all(&ghes_estatus_llist); - while (llnode) { - next = llnode->next; - llnode->next = tail; - tail = llnode; - llnode = next; - } - llnode = tail; + llnode = llist_nodes_reverse(llnode); while (llnode) { next = llnode->next; estatus_node = llist_entry(llnode, struct ghes_estatus_node, @@ -779,6 +787,32 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) } } +static void ghes_print_queued_estatus(void) +{ + struct llist_node *llnode; + struct ghes_estatus_node *estatus_node; + struct acpi_hest_generic *generic; + struct acpi_hest_generic_status *estatus; + u32 len, node_len; + + llnode = llist_del_all(&ghes_estatus_llist); + /* + * Because the time order of estatus in list is reversed, + * revert it back to proper order. + */ + llnode = llist_nodes_reverse(llnode); + while (llnode) { + estatus_node = llist_entry(llnode, struct ghes_estatus_node, + llnode); + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + len = apei_estatus_len(estatus); + node_len = GHES_ESTATUS_NODE_LEN(len); + generic = estatus_node->generic; + ghes_print_estatus(NULL, generic, estatus); + llnode = llnode->next; + } +} + static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { struct ghes *ghes, *ghes_global = NULL; @@ -804,6 +838,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (sev_global >= GHES_SEV_PANIC) { oops_begin(); + ghes_print_queued_estatus(); __ghes_print_estatus(KERN_EMERG, ghes_global->generic, ghes_global->estatus); /* reboot to log the error! */ -- cgit v0.10.2 From 76da3fb3575e39fb23b2c072997ccd1187a2ce9d Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:46 +0800 Subject: ACPI, Add RAM mapping support to ACPI atomic IO support On one of our testing machine, the following EINJ command lines: # echo 0x10000000 > param1 # echo 0xfffffffffffff000 > param2 # echo 0x8 > error_type # echo 1 > error_inject Will get: echo: write error: Input/output error The EIO comes from: rc = apei_exec_pre_map_gars(&trigger_ctx); The root cause is as follow. Normally, ACPI atomic IO support is used to access IO memory. But in EINJ of that machine, it is used to access RAM to trigger the injected error. And the ioremap() called by apei_exec_pre_map_gars() can not map the RAM. This patch add RAM mapping support to ACPI atomic IO support to satisfy EINJ requirement. Signed-off-by: Huang Ying Tested-by: Tony Luck Signed-off-by: Len Brown diff --git a/drivers/acpi/atomicio.c b/drivers/acpi/atomicio.c index 1016f18..d4a5b3d 100644 --- a/drivers/acpi/atomicio.c +++ b/drivers/acpi/atomicio.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #define ACPI_PFX "ACPI: " @@ -97,6 +99,37 @@ static void __iomem *__acpi_try_ioremap(phys_addr_t paddr, return NULL; } +#ifndef CONFIG_IA64 +#define should_use_kmap(pfn) page_is_ram(pfn) +#else +/* ioremap will take care of cache attributes */ +#define should_use_kmap(pfn) 0 +#endif + +static void __iomem *acpi_map(phys_addr_t pg_off, unsigned long pg_sz) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (should_use_kmap(pfn)) { + if (pg_sz > PAGE_SIZE) + return NULL; + return (void __iomem __force *)kmap(pfn_to_page(pfn)); + } else + return ioremap(pg_off, pg_sz); +} + +static void acpi_unmap(phys_addr_t pg_off, void __iomem *vaddr) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (page_is_ram(pfn)) + kunmap(pfn_to_page(pfn)); + else + iounmap(vaddr); +} + /* * Used to pre-map the specified IO memory area. First try to find * whether the area is already pre-mapped, if it is, increase the @@ -119,7 +152,7 @@ static void __iomem *acpi_pre_map(phys_addr_t paddr, pg_off = paddr & PAGE_MASK; pg_sz = ((paddr + size + PAGE_SIZE - 1) & PAGE_MASK) - pg_off; - vaddr = ioremap(pg_off, pg_sz); + vaddr = acpi_map(pg_off, pg_sz); if (!vaddr) return NULL; map = kmalloc(sizeof(*map), GFP_KERNEL); @@ -135,7 +168,7 @@ static void __iomem *acpi_pre_map(phys_addr_t paddr, vaddr = __acpi_try_ioremap(paddr, size); if (vaddr) { spin_unlock_irqrestore(&acpi_iomaps_lock, flags); - iounmap(map->vaddr); + acpi_unmap(pg_off, map->vaddr); kfree(map); return vaddr; } @@ -144,7 +177,7 @@ static void __iomem *acpi_pre_map(phys_addr_t paddr, return map->vaddr + (paddr - map->paddr); err_unmap: - iounmap(vaddr); + acpi_unmap(pg_off, vaddr); return NULL; } @@ -177,7 +210,7 @@ static void acpi_post_unmap(phys_addr_t paddr, unsigned long size) return; synchronize_rcu(); - iounmap(map->vaddr); + acpi_unmap(map->paddr, map->vaddr); kfree(map); } -- cgit v0.10.2 From fdea163d8c17ba08814142259a467ba3e899010d Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:47 +0800 Subject: ACPI, APEI, EINJ, Fix resource conflict on some machine Some APEI firmware implementation will access injected address specified in param1 to trigger the error when injecting memory error. This will cause resource conflict with RAM. On one of our testing machine, if injecting at memory address 0x10000000, the following error will be reported in dmesg: APEI: Can not request iomem region <0000000010000000-0000000010000008> for GARs. This patch removes the injecting memory address range from trigger table resources to avoid conflict. Signed-off-by: Huang Ying Tested-by: Tony Luck Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 3492896..f2c5062 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -421,6 +421,17 @@ static int apei_resources_merge(struct apei_resources *resources1, return 0; } +int apei_resources_add(struct apei_resources *resources, + unsigned long start, unsigned long size, + bool iomem) +{ + if (iomem) + return apei_res_add(&resources->iomem, start, size); + else + return apei_res_add(&resources->ioport, start, size); +} +EXPORT_SYMBOL_GPL(apei_resources_add); + /* * EINJ has two groups of GARs (EINJ table entry and trigger table * entry), so common resources are subtracted from the trigger table diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index f57050e..d778edd 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -95,6 +95,9 @@ static inline void apei_resources_init(struct apei_resources *resources) } void apei_resources_fini(struct apei_resources *resources); +int apei_resources_add(struct apei_resources *resources, + unsigned long start, unsigned long size, + bool iomem); int apei_resources_sub(struct apei_resources *resources1, struct apei_resources *resources2); int apei_resources_request(struct apei_resources *resources, diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 43eeb2e..4fdc8a3 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -195,7 +195,8 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) } /* Execute instructions in trigger error action table */ -static int __einj_error_trigger(u64 trigger_paddr) +static int __einj_error_trigger(u64 trigger_paddr, u32 type, + u64 param1, u64 param2) { struct acpi_einj_trigger *trigger_tab = NULL; struct apei_exec_context trigger_ctx; @@ -256,6 +257,25 @@ static int __einj_error_trigger(u64 trigger_paddr) rc = apei_resources_sub(&trigger_resources, &einj_resources); if (rc) goto out_fini; + /* + * Some firmware will access target address specified in + * param1 to trigger the error when injecting memory error. + * This will cause resource conflict with regular memory. So + * remove it from trigger table resources. + */ + if (param_extension && (type & 0x0038) && param2) { + struct apei_resources addr_resources; + apei_resources_init(&addr_resources); + rc = apei_resources_add(&addr_resources, + param1 & param2, + ~param2 + 1, true); + if (rc) + goto out_fini; + rc = apei_resources_sub(&trigger_resources, &addr_resources); + apei_resources_fini(&addr_resources); + if (rc) + goto out_fini; + } rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); if (rc) goto out_fini; @@ -325,7 +345,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) if (rc) return rc; trigger_paddr = apei_exec_ctx_get_output(&ctx); - rc = __einj_error_trigger(trigger_paddr); + rc = __einj_error_trigger(trigger_paddr, type, param1, param2); if (rc) return rc; rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); -- cgit v0.10.2 From b4e008dc53a31cb4bf6a12d9dbaf1d5c6070a838 Mon Sep 17 00:00:00 2001 From: "Xiao, Hui" Date: Thu, 8 Dec 2011 11:25:48 +0800 Subject: ACPI, APEI, EINJ, Refine the fix of resource conflict Current fix for resource conflict is to remove the address region from trigger resource, which is highly relies on valid user input. This patch is trying to avoid such potential issues by fetching the exact address region from trigger action table entry. Signed-off-by: Xiao, Hui Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 4fdc8a3..6e6512e 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -194,6 +194,26 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) return 0; } +static struct acpi_generic_address *einj_get_trigger_parameter_region( + struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) +{ + int i; + struct acpi_whea_header *entry; + + entry = (struct acpi_whea_header *) + ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); + for (i = 0; i < trigger_tab->entry_count; i++) { + if (entry->action == ACPI_EINJ_TRIGGER_ERROR && + entry->instruction == ACPI_EINJ_WRITE_REGISTER_VALUE && + entry->register_region.space_id == + ACPI_ADR_SPACE_SYSTEM_MEMORY && + (entry->register_region.address & param2) == (param1 & param2)) + return &entry->register_region; + entry++; + } + + return NULL; +} /* Execute instructions in trigger error action table */ static int __einj_error_trigger(u64 trigger_paddr, u32 type, u64 param1, u64 param2) @@ -205,6 +225,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, struct resource *r; u32 table_size; int rc = -EIO; + struct acpi_generic_address *trigger_param_region = NULL; r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), "APEI EINJ Trigger Table"); @@ -266,12 +287,17 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, if (param_extension && (type & 0x0038) && param2) { struct apei_resources addr_resources; apei_resources_init(&addr_resources); - rc = apei_resources_add(&addr_resources, - param1 & param2, - ~param2 + 1, true); - if (rc) - goto out_fini; - rc = apei_resources_sub(&trigger_resources, &addr_resources); + trigger_param_region = einj_get_trigger_parameter_region( + trigger_tab, param1, param2); + if (trigger_param_region) { + rc = apei_resources_add(&addr_resources, + trigger_param_region->address, + trigger_param_region->bit_width/8, true); + if (rc) + goto out_fini; + rc = apei_resources_sub(&trigger_resources, + &addr_resources); + } apei_resources_fini(&addr_resources); if (rc) goto out_fini; -- cgit v0.10.2 From b54ac6d2a25084667da781c7ca2cebef52a2bcdd Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:49 +0800 Subject: ACPI, Record ACPI NVS regions Some firmware will access memory in ACPI NVS region via APEI. That is, instructions in APEI ERST/EINJ table will read/write ACPI NVS region. The original resource conflict checking in APEI code will check memory/ioport accessed by APEI via general resource management mechanism. But ACPI NVS region is marked as busy already, so that the false resource conflict will prevent APEI ERST/EINJ to work. To fix this, this patch record ACPI NVS regions, so that we can avoid request resources for memory region inside it. Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 303a0e4..51c3b18 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -714,7 +714,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn) } #endif -#ifdef CONFIG_HIBERNATION +#ifdef CONFIG_ACPI /** * Mark ACPI NVS memory region, so that we can save/restore it during * hibernation and the subsequent resume. @@ -727,7 +727,7 @@ static int __init e820_mark_nvs_memory(void) struct e820entry *ei = &e820.map[i]; if (ei->type == E820_NVS) - suspend_nvs_register(ei->addr, ei->size); + acpi_nvs_register(ei->addr, ei->size); } return 0; diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index ecb26b4..c07f44f 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -20,11 +20,12 @@ obj-y += acpi.o \ # All the builtin files are in the "acpi." module_param namespace. acpi-y += osl.o utils.o reboot.o acpi-y += atomicio.o +acpi-y += nvs.o # sleep related files acpi-y += wakeup.o acpi-y += sleep.o -acpi-$(CONFIG_ACPI_SLEEP) += proc.o nvs.o +acpi-$(CONFIG_ACPI_SLEEP) += proc.o # diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 096787b..7a2035f 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -15,6 +15,56 @@ #include #include +/* ACPI NVS regions, APEI may use it */ + +struct nvs_region { + __u64 phys_start; + __u64 size; + struct list_head node; +}; + +static LIST_HEAD(nvs_region_list); + +#ifdef CONFIG_ACPI_SLEEP +static int suspend_nvs_register(unsigned long start, unsigned long size); +#else +static inline int suspend_nvs_register(unsigned long a, unsigned long b) +{ + return 0; +} +#endif + +int acpi_nvs_register(__u64 start, __u64 size) +{ + struct nvs_region *region; + + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->phys_start = start; + region->size = size; + list_add_tail(®ion->node, &nvs_region_list); + + return suspend_nvs_register(start, size); +} + +int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data), + void *data) +{ + int rc; + struct nvs_region *region; + + list_for_each_entry(region, &nvs_region_list, node) { + rc = func(region->phys_start, region->size, data); + if (rc) + return rc; + } + + return 0; +} + + +#ifdef CONFIG_ACPI_SLEEP /* * Platforms, like ACPI, may want us to save some memory used by them during * suspend and to restore the contents of this memory during the subsequent @@ -41,7 +91,7 @@ static LIST_HEAD(nvs_list); * things so that the data from page-aligned addresses in this region will * be copied into separate RAM pages. */ -int suspend_nvs_register(unsigned long start, unsigned long size) +static int suspend_nvs_register(unsigned long start, unsigned long size) { struct nvs_page *entry, *next; @@ -159,3 +209,4 @@ void suspend_nvs_restore(void) if (entry->data) memcpy(entry->kaddr, entry->data, entry->size); } +#endif diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6001b4da..26b7544 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -306,6 +306,11 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); extern void acpi_early_init(void); +extern int acpi_nvs_register(__u64 start, __u64 size); + +extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), + void *data); + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 @@ -348,15 +353,18 @@ static inline int acpi_table_parse(char *id, { return -1; } -#endif /* !CONFIG_ACPI */ -#ifdef CONFIG_ACPI_SLEEP -int suspend_nvs_register(unsigned long start, unsigned long size); -#else -static inline int suspend_nvs_register(unsigned long a, unsigned long b) +static inline int acpi_nvs_register(__u64 start, __u64 size) { return 0; } -#endif + +static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), + void *data) +{ + return 0; +} + +#endif /* !CONFIG_ACPI */ #endif /*_LINUX_ACPI_H*/ -- cgit v0.10.2 From 4134b8c8811f23aa8a281db50dcee64dda414736 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Thu, 8 Dec 2011 11:25:50 +0800 Subject: ACPI, APEI, Resolve false conflict between ACPI NVS and APEI Some firmware will access memory in ACPI NVS region via APEI. That is, instructions in APEI ERST/EINJ table will read/write ACPI NVS region. The original resource conflict checking in APEI code will check memory/ioport accessed by APEI via general resource management mech. But ACPI NVS region is marked as busy already, so that the false resource conflict will prevent APEI ERST/EINJ to work. To fix this, this patch excludes ACPI NVS regions when APEI components request resources. So that they will not conflict with ACPI NVS regions. Reported-and-tested-by: Pavel Ivanov Signed-off-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index f2c5062..4abb6c7 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -449,8 +449,19 @@ int apei_resources_sub(struct apei_resources *resources1, } EXPORT_SYMBOL_GPL(apei_resources_sub); +static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) +{ + struct apei_resources *resources = data; + return apei_res_add(&resources->iomem, start, size); +} + +static int apei_get_nvs_resources(struct apei_resources *resources) +{ + return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); +} + /* - * IO memory/port rersource management mechanism is used to check + * IO memory/port resource management mechanism is used to check * whether memory/port area used by GARs conflicts with normal memory * or IO memory/port of devices. */ @@ -459,12 +470,26 @@ int apei_resources_request(struct apei_resources *resources, { struct apei_res *res, *res_bak = NULL; struct resource *r; + struct apei_resources nvs_resources; int rc; rc = apei_resources_sub(resources, &apei_resources_all); if (rc) return rc; + /* + * Some firmware uses ACPI NVS region, that has been marked as + * busy, so exclude it from APEI resources to avoid false + * conflict. + */ + apei_resources_init(&nvs_resources); + rc = apei_get_nvs_resources(&nvs_resources); + if (rc) + goto res_fini; + rc = apei_resources_sub(resources, &nvs_resources); + if (rc) + goto res_fini; + rc = -EINVAL; list_for_each_entry(res, &resources->iomem, list) { r = request_mem_region(res->start, res->end - res->start, @@ -511,6 +536,8 @@ err_unmap_iomem: break; release_mem_region(res->start, res->end - res->start); } +res_fini: + apei_resources_fini(&nvs_resources); return rc; } EXPORT_SYMBOL_GPL(apei_resources_request); -- cgit v0.10.2 From 8df0eb7c9d96f9e82f233ee8b74e0f0c8471f868 Mon Sep 17 00:00:00 2001 From: Kurt Garloff Date: Tue, 17 Jan 2012 04:18:02 -0500 Subject: ACPI: Store SRAT table revision In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides 32bits for these. The new fields were reserved before. According to the ACPI spec, the OS must disregrard reserved fields. In order to know whether or not, we must know what version the SRAT table has. This patch stores the SRAT table revision for later consumption by arch specific __init functions. Signed-off-by: Kurt Garloff Signed-off-by: Len Brown diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 3b5c318..e56f3be 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -45,6 +45,8 @@ static int pxm_to_node_map[MAX_PXM_DOMAINS] static int node_to_pxm_map[MAX_NUMNODES] = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; +unsigned char acpi_srat_revision __initdata; + int pxm_to_node(int pxm) { if (pxm < 0) @@ -255,9 +257,13 @@ acpi_parse_memory_affinity(struct acpi_subtable_header * header, static int __init acpi_parse_srat(struct acpi_table_header *table) { + struct acpi_table_srat *srat; if (!table) return -EINVAL; + srat = (struct acpi_table_srat *)table; + acpi_srat_revision = srat->header.revision; + /* Real work done in acpi_table_parse_srat below. */ return 0; diff --git a/include/acpi/acpi_numa.h b/include/acpi/acpi_numa.h index 1739726..451823c 100644 --- a/include/acpi/acpi_numa.h +++ b/include/acpi/acpi_numa.h @@ -15,6 +15,7 @@ extern int pxm_to_node(int); extern int node_to_pxm(int); extern void __acpi_map_pxm_to_node(int, int); extern int acpi_map_pxm_to_node(int); +extern unsigned char acpi_srat_revision; #endif /* CONFIG_ACPI_NUMA */ #endif /* __ACP_NUMA_H */ -- cgit v0.10.2 From cd298f60a2451a16e0f077404bf69b62ec868733 Mon Sep 17 00:00:00 2001 From: Kurt Garloff Date: Tue, 17 Jan 2012 04:20:31 -0500 Subject: ACPI, x86: Use SRAT table rev to use 8bit or 32bit PXM fields (x86/x86-64) In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides 32bits for these. The new fields were reserved before. According to the ACPI spec, the OS must disregrard reserved fields. x86/x86-64 was rather inconsistent prior to this patch; it used 8 bits for the pxm field in cpu_affinity, but 32 bits in mem_affinity. This patch makes it consistent: Either use 8 bits consistently (SRAT rev 1 or lower) or 32 bits (SRAT rev 2 or higher). cc: x86@kernel.org Signed-off-by: Kurt Garloff Signed-off-by: Len Brown diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 81dbfde..7efd0c6 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -104,6 +104,8 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) return; pxm = pa->proximity_domain_lo; + if (acpi_srat_revision >= 2) + pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8; node = setup_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); @@ -155,6 +157,8 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) start = ma->base_address; end = start + ma->length; pxm = ma->proximity_domain; + if (acpi_srat_revision <= 1) + pxm &= 0xff; node = setup_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains.\n"); -- cgit v0.10.2 From 9f10f6a520deb3639fac78d81151a3ade88b4e7f Mon Sep 17 00:00:00 2001 From: Kurt Garloff Date: Tue, 17 Jan 2012 04:21:49 -0500 Subject: ACPI, ia64: Use SRAT table rev to use 8bit or 16/32bit PXM fields (ia64) In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides 32bits for these. The new fields were reserved before. According to the ACPI spec, the OS must disregrard reserved fields. ia64 did handle the PXM fields almost consistently, but depending on sgi's sn2 platform. This patch leaves the sn2 logic in, but does also use 16/32 bits for PXM if the SRAT has rev 2 or higher. The patch also adds __init to the two pxm accessor functions, as they access __initdata now and are called from an __init function only anyway. Note that the code only uses 16 bits for the PXM field in the processor proximity field; the patch does not address this as 16 bits are more than enough. Signed-off-by: Kurt Garloff Signed-off-by: Len Brown diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index bfb4d01..5207035 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -429,22 +429,24 @@ static u32 __devinitdata pxm_flag[PXM_FLAG_LEN]; static struct acpi_table_slit __initdata *slit_table; cpumask_t early_cpu_possible_map = CPU_MASK_NONE; -static int get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa) +static int __init +get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa) { int pxm; pxm = pa->proximity_domain_lo; - if (ia64_platform_is("sn2")) + if (ia64_platform_is("sn2") || acpi_srat_revision >= 2) pxm += pa->proximity_domain_hi[0] << 8; return pxm; } -static int get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma) +static int __init +get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma) { int pxm; pxm = ma->proximity_domain; - if (!ia64_platform_is("sn2")) + if (!ia64_platform_is("sn2") && acpi_srat_revision <= 1) pxm &= 0xff; return pxm; -- cgit v0.10.2 From bc9ffce27962c0c5fdc6adf74790ea0fcbe4a99c Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 7 Nov 2011 16:23:27 -0700 Subject: ACPI: Fix possible alignment issues with GAS 'address' references Generic Address Structures (GAS) may reside within ACPI tables which are byte aligned. This patch copies GAS 'address' references to a local variable, which will be naturally aligned, to be used going forward. ACPI Generic Address Structure (GAS) reference: ACPI Specification, Revision 4.0, Section 5.2.3.1, "Generic Address Structure" Signed-off-by: Myron Stowe Signed-off-by: Len Brown diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f31c5c5..2e285cd 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -166,17 +166,21 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported) return supported; } -static void __init acpi_request_region (struct acpi_generic_address *addr, +static void __init acpi_request_region (struct acpi_generic_address *gas, unsigned int length, char *desc) { - if (!addr->address || !length) + u64 addr; + + /* Handle possible alignment issues */ + memcpy(&addr, &gas->address, sizeof(addr)); + if (!addr || !length) return; /* Resources are never freed */ - if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_IO) - request_region(addr->address, length, desc); - else if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - request_mem_region(addr->address, length, desc); + if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) + request_region(addr, length, desc); + else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) + request_mem_region(addr, length, desc); } static int __init acpi_reserve_resources(void) @@ -427,35 +431,41 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) __acpi_unmap_table(virt, size); } -static int acpi_os_map_generic_address(struct acpi_generic_address *addr) +static int acpi_os_map_generic_address(struct acpi_generic_address *gas) { + u64 addr; void __iomem *virt; - if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return 0; - if (!addr->address || !addr->bit_width) + /* Handle possible alignment issues */ + memcpy(&addr, &gas->address, sizeof(addr)); + if (!addr || !gas->bit_width) return -EINVAL; - virt = acpi_os_map_memory(addr->address, addr->bit_width / 8); + virt = acpi_os_map_memory(addr, gas->bit_width / 8); if (!virt) return -EIO; return 0; } -static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) +static void acpi_os_unmap_generic_address(struct acpi_generic_address *gas) { + u64 addr; struct acpi_ioremap *map; - if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) return; - if (!addr->address || !addr->bit_width) + /* Handle possible alignment issues */ + memcpy(&addr, &gas->address, sizeof(addr)); + if (!addr || !gas->bit_width) return; mutex_lock(&acpi_ioremap_lock); - map = acpi_map_lookup(addr->address, addr->bit_width / 8); + map = acpi_map_lookup(addr, gas->bit_width / 8); if (!map) { mutex_unlock(&acpi_ioremap_lock); return; -- cgit v0.10.2 From 6f68c91c55ea3576d366797fa8d45e31c4aa79f8 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 7 Nov 2011 16:23:34 -0700 Subject: ACPI: Export interfaces for ioremapping/iounmapping ACPI registers Export remapping and unmapping interfaces - acpi_os_map_generic_address() and acpi_os_unmap_generic_address() - for ACPI generic registers that are backed by memory mapped I/O (MMIO). The acpi_os_map_generic_address() and acpi_os_unmap_generic_address() declarations may more properly belong in include/acpi/acpiosxf.h next to acpi_os_read_memory() but I believe that would require the ACPI CA making them an official part of the ACPI CA - OS interface. ACPI Generic Address Structure (GAS) reference (ACPI's fixed/generic hardware registers use the GAS format): ACPI Specification, Revision 4.0, Section 5.2.3.1, "Generic Address Structure" Signed-off-by: Myron Stowe Acked-by: Rafael J. Wysocki Signed-off-by: Len Brown diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 2e285cd..b11f267 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -431,7 +431,7 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) __acpi_unmap_table(virt, size); } -static int acpi_os_map_generic_address(struct acpi_generic_address *gas) +int acpi_os_map_generic_address(struct acpi_generic_address *gas) { u64 addr; void __iomem *virt; @@ -450,8 +450,9 @@ static int acpi_os_map_generic_address(struct acpi_generic_address *gas) return 0; } +EXPORT_SYMBOL(acpi_os_map_generic_address); -static void acpi_os_unmap_generic_address(struct acpi_generic_address *gas) +void acpi_os_unmap_generic_address(struct acpi_generic_address *gas) { u64 addr; struct acpi_ioremap *map; @@ -475,6 +476,7 @@ static void acpi_os_unmap_generic_address(struct acpi_generic_address *gas) acpi_os_map_cleanup(map); } +EXPORT_SYMBOL(acpi_os_unmap_generic_address); #ifdef ACPI_FUTURE_USAGE acpi_status diff --git a/include/linux/acpi_io.h b/include/linux/acpi_io.h index 4afd710..b0ffa21 100644 --- a/include/linux/acpi_io.h +++ b/include/linux/acpi_io.h @@ -12,4 +12,7 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size); +int acpi_os_map_generic_address(struct acpi_generic_address *addr); +void acpi_os_unmap_generic_address(struct acpi_generic_address *addr); + #endif -- cgit v0.10.2 From 700130b41f4ee54520ac2ef2f7f1d072789711a4 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 7 Nov 2011 16:23:41 -0700 Subject: ACPI APEI: Convert atomicio routines APEI needs memory access in interrupt context. The obvious choice is acpi_read(), but originally it couldn't be used in interrupt context because it makes temporary mappings with ioremap(). Therefore, we added drivers/acpi/atomicio.c, which provides: acpi_pre_map_gar() -- ioremap in process context acpi_atomic_read() -- memory access in interrupt context acpi_post_unmap_gar() -- iounmap Later we added acpi_os_map_generic_address() (2971852) and enhanced acpi_read() so it works in interrupt context as long as the address has been previously mapped (620242a). Now this sequence: acpi_os_map_generic_address() -- ioremap in process context acpi_read()/apei_read() -- now OK in interrupt context acpi_os_unmap_generic_address() is equivalent to what atomicio.c provides. This patch introduces apei_read() and apei_write(), which currently are functional equivalents of acpi_read() and acpi_write(). This is mainly proactive, to prevent APEI breakages if acpi_read() and acpi_write() are ever augmented to support the 'bit_offset' field of GAS, as APEI's __apei_exec_write_register() precludes splitting up functionality related to 'bit_offset' and APEI's 'mask' (see its APEI_EXEC_PRESERVE_REGISTER block). With apei_read() and apei_write() in place, usages of atomicio routines are converted to apei_read()/apei_write() and existing calls within osl.c and the CA, based on the re-factoring that was done in an earlier patch series - http://marc.info/?l=linux-acpi&m=128769263327206&w=2: acpi_pre_map_gar() --> acpi_os_map_generic_address() acpi_post_unmap_gar() --> acpi_os_unmap_generic_address() acpi_atomic_read() --> apei_read() acpi_atomic_write() --> apei_write() Note that acpi_read() and acpi_write() currently use 'bit_width' for accessing GARs which seems incorrect. 'bit_width' is the size of the register, while 'access_width' is the size of the access the processor must generate on the bus. The 'access_width' may be larger, for example, if the hardware only supports 32-bit or 64-bit reads. I wanted to minimize any possible impacts with this patch series so I did *not* change this behavior. Signed-off-by: Myron Stowe Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 4abb6c7..e45350c 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -34,13 +34,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "apei-internal.h" @@ -70,7 +70,7 @@ int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) { int rc; - rc = acpi_atomic_read(val, &entry->register_region); + rc = apei_read(val, &entry->register_region); if (rc) return rc; *val >>= entry->register_region.bit_offset; @@ -116,13 +116,13 @@ int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) val <<= entry->register_region.bit_offset; if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { u64 valr = 0; - rc = acpi_atomic_read(&valr, &entry->register_region); + rc = apei_read(&valr, &entry->register_region); if (rc) return rc; valr &= ~(entry->mask << entry->register_region.bit_offset); val |= valr; } - rc = acpi_atomic_write(val, &entry->register_region); + rc = apei_write(val, &entry->register_region); return rc; } @@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx, u8 ins = entry->instruction; if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) - return acpi_pre_map_gar(&entry->register_region); + return acpi_os_map_generic_address(&entry->register_region); return 0; } @@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx, u8 ins = entry->instruction; if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) - acpi_post_unmap_gar(&entry->register_region); + acpi_os_unmap_generic_address(&entry->register_region); return 0; } @@ -591,6 +591,96 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) return 0; } +/* read GAR in interrupt (including NMI) or process context */ +int apei_read(u64 *val, struct acpi_generic_address *reg) +{ + int rc; + u64 address; + u32 tmp, width = reg->bit_width; + acpi_status status; + + rc = apei_check_gar(reg, &address); + if (rc) + return rc; + + if (width == 64) + width = 32; /* Break into two 32-bit transfers */ + + *val = 0; + switch(reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + status = acpi_os_read_memory((acpi_physical_address) + address, &tmp, width); + if (ACPI_FAILURE(status)) + return -EIO; + *val = tmp; + + if (reg->bit_width == 64) { + /* Read the top 32 bits */ + status = acpi_os_read_memory((acpi_physical_address) + (address + 4), &tmp, 32); + if (ACPI_FAILURE(status)) + return -EIO; + *val |= ((u64)tmp << 32); + } + break; + case ACPI_ADR_SPACE_SYSTEM_IO: + status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_read); + +/* write GAR in interrupt (including NMI) or process context */ +int apei_write(u64 val, struct acpi_generic_address *reg) +{ + int rc; + u64 address; + u32 width = reg->bit_width; + acpi_status status; + + rc = apei_check_gar(reg, &address); + if (rc) + return rc; + + if (width == 64) + width = 32; /* Break into two 32-bit transfers */ + + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + status = acpi_os_write_memory((acpi_physical_address) + address, ACPI_LODWORD(val), + width); + if (ACPI_FAILURE(status)) + return -EIO; + + if (reg->bit_width == 64) { + status = acpi_os_write_memory((acpi_physical_address) + (address + 4), + ACPI_HIDWORD(val), 32); + if (ACPI_FAILURE(status)) + return -EIO; + } + break; + case ACPI_ADR_SPACE_SYSTEM_IO: + status = acpi_os_write_port(address, val, reg->bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_write); + static int collect_res_callback(struct apei_exec_context *ctx, struct acpi_whea_header *entry, void *data) diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index d778edd..cca240a 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -68,6 +68,9 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio /* IP has been set in instruction function */ #define APEI_EXEC_SET_IP 1 +int apei_read(u64 *val, struct acpi_generic_address *reg); +int apei_write(u64 val, struct acpi_generic_address *reg); + int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); int apei_exec_read_register(struct apei_exec_context *ctx, diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index aaf3609..b3207e1 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -301,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) if (!ghes) return ERR_PTR(-ENOMEM); ghes->generic = generic; - rc = acpi_pre_map_gar(&generic->error_status_address); + rc = acpi_os_map_generic_address(&generic->error_status_address); if (rc) goto err_free; error_block_length = generic->error_block_length; @@ -321,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) return ghes; err_unmap: - acpi_post_unmap_gar(&generic->error_status_address); + acpi_os_unmap_generic_address(&generic->error_status_address); err_free: kfree(ghes); return ERR_PTR(rc); @@ -330,7 +330,7 @@ err_free: static void ghes_fini(struct ghes *ghes) { kfree(ghes->estatus); - acpi_post_unmap_gar(&ghes->generic->error_status_address); + acpi_os_unmap_generic_address(&ghes->generic->error_status_address); } enum { @@ -401,7 +401,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) u32 len; int rc; - rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); + rc = apei_read(&buf_paddr, &g->error_status_address); if (rc) { if (!silent && printk_ratelimit()) pr_warning(FW_WARN GHES_PFX -- cgit v0.10.2 From 5ee71535440f034de1196b11f78cef81c4025c2b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 16 Jan 2012 11:57:18 -0800 Subject: x86/kconfig: Move the ZONE_DMA entry under a menu Move the ZONE_DMA kconfig symbol under a menu item instead of having it listed before everything else in "make {xconfig | gconfig | nconfig | menuconfig}". This drops the first line of the top-level kernel config menu (in 3.2) below and moves it under "Processor type and features". [*] DMA memory allocation support General setup ---> [*] Enable loadable module support ---> [*] Enable the block layer ---> Processor type and features ---> Power management and ACPI options ---> Bus options (PCI etc.) ---> Executable file formats / Emulations ---> Signed-off-by: Randy Dunlap Acked-by: David Rientjes Cc: Linus Torvalds Cc: linux-mm@kvack.org Link: http://lkml.kernel.org/r/4F14811E.6090107@xenotime.net Signed-off-by: Ingo Molnar Cc: David Rientjes diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5731eb7..db190fa 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -120,16 +120,6 @@ config HAVE_LATENCYTOP_SUPPORT config MMU def_bool y -config ZONE_DMA - bool "DMA memory allocation support" if EXPERT - default y - help - DMA memory allocation support allows devices with less than 32-bit - addressing to allocate within the first 16MB of address space. - Disable if no such devices will be used. - - If unsure, say Y. - config SBUS bool @@ -253,6 +243,16 @@ source "kernel/Kconfig.freezer" menu "Processor type and features" +config ZONE_DMA + bool "DMA memory allocation support" if EXPERT + default y + help + DMA memory allocation support allows devices with less than 32-bit + addressing to allocate within the first 16MB of address space. + Disable if no such devices will be used. + + If unsure, say Y. + source "kernel/time/Kconfig" config SMP -- cgit v0.10.2 From 088067f4f14d6ee5c6a196b015a560cbe7744224 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 17 Jan 2012 10:39:05 +0100 Subject: netfilter: ipset: autoload set type modules safely Jan Engelhardt noticed when userspace requests a set type unknown to the kernel, it can lead to a loop due to the unsafe type module loading. The issue is fixed in this patch. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 86137b5..0f8e5f2 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -77,35 +77,42 @@ find_set_type(const char *name, u8 family, u8 revision) } /* Unlock, try to load a set type module and lock again */ -static int -try_to_load_type(const char *name) +static bool +load_settype(const char *name) { nfnl_unlock(); pr_debug("try to load ip_set_%s\n", name); if (request_module("ip_set_%s", name) < 0) { pr_warning("Can't find ip_set type %s\n", name); nfnl_lock(); - return -IPSET_ERR_FIND_TYPE; + return false; } nfnl_lock(); - return -EAGAIN; + return true; } /* Find a set type and reference it */ +#define find_set_type_get(name, family, revision, found) \ + __find_set_type_get(name, family, revision, found, false) + static int -find_set_type_get(const char *name, u8 family, u8 revision, - struct ip_set_type **found) +__find_set_type_get(const char *name, u8 family, u8 revision, + struct ip_set_type **found, bool retry) { struct ip_set_type *type; int err; + if (retry && !load_settype(name)) + return -IPSET_ERR_FIND_TYPE; + rcu_read_lock(); *found = find_set_type(name, family, revision); if (*found) { err = !try_module_get((*found)->me) ? -EFAULT : 0; goto unlock; } - /* Make sure the type is loaded but we don't support the revision */ + /* Make sure the type is already loaded + * but we don't support the revision */ list_for_each_entry_rcu(type, &ip_set_type_list, list) if (STREQ(type->name, name)) { err = -IPSET_ERR_FIND_TYPE; @@ -113,7 +120,8 @@ find_set_type_get(const char *name, u8 family, u8 revision, } rcu_read_unlock(); - return try_to_load_type(name); + return retry ? -IPSET_ERR_FIND_TYPE : + __find_set_type_get(name, family, revision, found, true); unlock: rcu_read_unlock(); @@ -124,12 +132,19 @@ unlock: * If we succeeded, the supported minimal and maximum revisions are * filled out. */ +#define find_set_type_minmax(name, family, min, max) \ + __find_set_type_minmax(name, family, min, max, false) + static int -find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) +__find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max, + bool retry) { struct ip_set_type *type; bool found = false; + if (retry && !load_settype(name)) + return -IPSET_ERR_FIND_TYPE; + *min = 255; *max = 0; rcu_read_lock(); list_for_each_entry_rcu(type, &ip_set_type_list, list) @@ -145,7 +160,8 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) if (found) return 0; - return try_to_load_type(name); + return retry ? -IPSET_ERR_FIND_TYPE : + __find_set_type_minmax(name, family, min, max, true); } #define family_name(f) ((f) == AF_INET ? "inet" : \ -- cgit v0.10.2 From be94db9ddac0d6ea76f479b04fb3a43314654c9f Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Sat, 14 Jan 2012 16:42:13 +0100 Subject: netfilter: ipset: dumping error triggered removing references twice If there was a dumping error in the middle, the set-specific variable was not zeroed out and thus the 'done' function of the dumping wrongly tried to release the already released reference of the set. The already released reference was caught by __ip_set_put and triggered a kernel BUG message. Reported by Jean-Philippe Menil. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 0f8e5f2..32dbf0f 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1142,6 +1142,7 @@ release_refcount: if (ret || !cb->args[2]) { pr_debug("release set %s\n", ip_set_list[index]->name); ip_set_put_byindex(index); + cb->args[2] = 0; } out: if (nlh) { -- cgit v0.10.2 From 39a74fdedd1c1461d6fb6d330b5266886513c98f Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 10 Jan 2012 15:48:19 -0800 Subject: intel_idle: fix API misuse smp_call_function() only lets all other CPUs execute a specific function, while we expect all CPUs do in intel_idle. Without the fix, we could have one cpu which has auto_demotion enabled or has no broadcast timer setup. Usually we don't see impact because auto demotion just harms power and the intel_idle init is called in CPU 0, where boradcast timer delivers interrupt, but this still could be a problem. Cc: stable@vger.kernel.org Signed-off-by: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 5d2f8e1..af0b4a5 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -394,7 +394,7 @@ static int intel_idle_probe(void) if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; else { - smp_call_function(__setup_broadcast_timer, (void *)true, 1); + on_each_cpu(__setup_broadcast_timer, (void *)true, 1); register_cpu_notifier(&setup_broadcast_notifier); } @@ -471,7 +471,7 @@ static int intel_idle_cpuidle_driver_init(void) } if (auto_demotion_disable_flags) - smp_call_function(auto_demotion_disable, NULL, 1); + on_each_cpu(auto_demotion_disable, NULL, 1); return 0; } @@ -568,7 +568,7 @@ static void __exit intel_idle_exit(void) cpuidle_unregister_driver(&intel_idle_driver); if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { - smp_call_function(__setup_broadcast_timer, (void *)false, 1); + on_each_cpu(__setup_broadcast_timer, (void *)false, 1); unregister_cpu_notifier(&setup_broadcast_notifier); } -- cgit v0.10.2 From d640113fe80e45ebd4a5b420b220d3f6bf37f682 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Tue, 13 Dec 2011 09:36:03 +0800 Subject: ACPI: processor: fix acpi_get_cpuid for UP processor For UP processor, it is likely that no _MAT method or MADT table defined. So currently acpi_get_cpuid(...) always return -1 for UP processor. This is wrong. It should return valid value for CPU0. In the other hand, BIOS may define multiple CPU handles even for UP processor, for example Scope (_PR) { Processor (CPU0, 0x00, 0x00000410, 0x06) {} Processor (CPU1, 0x01, 0x00000410, 0x06) {} Processor (CPU2, 0x02, 0x00000410, 0x06) {} Processor (CPU3, 0x03, 0x00000410, 0x06) {} } We should only return valid value for CPU0's acpi handle. And return invalid value for others. http://marc.info/?t=132329819900003&r=1&w=2 Cc: stable@vger.kernel.org Reported-and-tested-by: wallak@free.fr Signed-off-by: Lin Ming Signed-off-by: Len Brown diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 3a0428e..c850de4 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -173,8 +173,30 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) apic_id = map_mat_entry(handle, type, acpi_id); if (apic_id == -1) apic_id = map_madt_entry(type, acpi_id); - if (apic_id == -1) - return apic_id; + if (apic_id == -1) { + /* + * On UP processor, there is no _MAT or MADT table. + * So above apic_id is always set to -1. + * + * BIOS may define multiple CPU handles even for UP processor. + * For example, + * + * Scope (_PR) + * { + * Processor (CPU0, 0x00, 0x00000410, 0x06) {} + * Processor (CPU1, 0x01, 0x00000410, 0x06) {} + * Processor (CPU2, 0x02, 0x00000410, 0x06) {} + * Processor (CPU3, 0x03, 0x00000410, 0x06) {} + * } + * + * Ignores apic_id and always return 0 for CPU0's handle. + * Return -1 for other CPU's handle. + */ + if (acpi_id == 0) + return acpi_id; + else + return apic_id; + } #ifdef CONFIG_SMP for_each_possible_cpu(i) { -- cgit v0.10.2 From 3333ea7804201918aa241641cf8493a62b8ad527 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 17 Nov 2011 23:37:00 +0100 Subject: ACPI processor: Fix error path, also remove sysdev link Signed-off-by: Thomas Renninger Signed-off-by: Len Brown diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 9d7bc9f..90719d1 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -539,6 +539,7 @@ err_thermal_unregister: thermal_cooling_device_unregister(pr->cdev); err_power_exit: acpi_processor_power_exit(pr, device); + sysfs_remove_link(&device->dev.kobj, "sysdev"); err_free_cpumask: free_cpumask_var(pr->throttling.shared_cpu_map); -- cgit v0.10.2 From a34e2a784f8d5b4d26fb3cc378e04296cdc7ad23 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Jan 2012 09:45:48 -0200 Subject: [media] DocBook/dvbproperty.xml: Fix the units for DTV_FREQUENCY The units for DTV_FREQUENCY are kHz for satellital delivery systems (DVB-S/DVB-S2/DVB-TURBO/ISDB-S). Fix it at the API spec. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index ffee1fb..0496641 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -163,14 +163,16 @@ get/set up to 64 properties. The actual meaning of each property is described on
<constant>DTV_FREQUENCY</constant> - Central frequency of the channel, in HZ. + Central frequency of the channel. Notes: - 1)For ISDB-T, the channels are usually transmitted with an offset of 143kHz. + 1)For satellital delivery systems, it is measured in kHz. + For the other ones, it is measured in Hz. + 2)For ISDB-T, the channels are usually transmitted with an offset of 143kHz. E.g. a valid frequncy could be 474143 kHz. The stepping is bound to the bandwidth of the channel which is 6MHz. - 2)As in ISDB-Tsb the channel consists of only one or three segments the + 3)As in ISDB-Tsb the channel consists of only one or three segments the frequency step is 429kHz, 3*429 respectively. As for ISDB-T the central frequency of the channel is expected.
-- cgit v0.10.2 From b5dad9fdd8e2c102e1a0f8490c55dbda198d412c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Jan 2012 09:49:28 -0200 Subject: [media] DocBook/dvbproperty.xml: Fix ISDB-T delivery system parameters The ISDB-T differs on its way to implement the hierarchical transmissions: instead of using a low-priority/high-priority FEC codes, it does that by using different layers, each layer with their groups of segments. So, those parameters don't make sense for ISDB-T. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 0496641..2de2bb3 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -740,11 +740,8 @@ typedef enum fe_hierarchy { DTV_MODULATION DTV_BANDWIDTH_HZ DTV_INVERSION - DTV_CODE_RATE_HP - DTV_CODE_RATE_LP DTV_GUARD_INTERVAL DTV_TRANSMISSION_MODE - DTV_HIERARCHY DTV_ISDBT_LAYER_ENABLED DTV_ISDBT_PARTIAL_RECEPTION DTV_ISDBT_SOUND_BROADCASTING -- cgit v0.10.2 From 7bb0f088f8dd1d60b8f5743471cc3db3f820df59 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Jan 2012 10:00:41 -0200 Subject: [media] DocBook/dvbproperty.xml: Remove DTV_MODULATION from ISDB-T On ISDB-T, each layer can have its own independent modulation, applied to the carriers that belong to the segments associated with them. So, there's no sense to define a global modulation parameter. Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 2de2bb3..c7a4ca5 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -737,7 +737,6 @@ typedef enum fe_hierarchy { DTV_TUNE DTV_CLEAR DTV_FREQUENCY - DTV_MODULATION DTV_BANDWIDTH_HZ DTV_INVERSION DTV_GUARD_INTERVAL -- cgit v0.10.2 From 5d2031f2395728c765cf2bd7c1ebb6d51c4fe076 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 13 Jan 2012 23:58:39 +0100 Subject: bcma: convert suspend/resume to pm_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .. and avoid doing the unnecessary PCI operations - the PCI layer will do them for us. Signed-off-by: Linus Torvalds Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index c1ca9e3..86dfeea 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -235,41 +235,32 @@ static void bcma_host_pci_remove(struct pci_dev *dev) } #ifdef CONFIG_PM -static int bcma_host_pci_suspend(struct pci_dev *dev, pm_message_t state) +static int bcma_host_pci_suspend(struct device *dev) { - struct bcma_bus *bus = pci_get_drvdata(dev); - - /* Host specific */ - pci_save_state(dev); - pci_disable_device(dev); - pci_set_power_state(dev, pci_choose_state(dev, state)); + struct pci_dev *pdev = to_pci_dev(dev); + struct bcma_bus *bus = pci_get_drvdata(pdev); bus->mapped_core = NULL; + return 0; } -static int bcma_host_pci_resume(struct pci_dev *dev) +static int bcma_host_pci_resume(struct device *dev) { - struct bcma_bus *bus = pci_get_drvdata(dev); - int err; + struct pci_dev *pdev = to_pci_dev(dev); + struct bcma_bus *bus = pci_get_drvdata(pdev); - /* Host specific */ - pci_set_power_state(dev, 0); - err = pci_enable_device(dev); - if (err) - return err; - pci_restore_state(dev); + return bcma_bus_resume(bus); +} - /* Bus specific */ - err = bcma_bus_resume(bus); - if (err) - return err; +static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend, + bcma_host_pci_resume); +#define BCMA_PM_OPS (&bcma_pm_ops) - return 0; -} #else /* CONFIG_PM */ -# define bcma_host_pci_suspend NULL -# define bcma_host_pci_resume NULL + +#define BCMA_PM_OPS NULL + #endif /* CONFIG_PM */ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = { @@ -287,8 +278,7 @@ static struct pci_driver bcma_pci_bridge_driver = { .id_table = bcma_pci_bridge_tbl, .probe = bcma_host_pci_probe, .remove = bcma_host_pci_remove, - .suspend = bcma_host_pci_suspend, - .resume = bcma_host_pci_resume, + .driver.pm = BCMA_PM_OPS, }; int __init bcma_host_pci_init(void) -- cgit v0.10.2 From 685a4ef0df23b97eea1842c2698da9a2e35fc976 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 13 Jan 2012 23:58:40 +0100 Subject: bcma: add stub for bcma_bus_suspend() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .. and connect it up with the pci host bcma driver. Now, the next step is to connect those bcma bus-level suspend/resume functions to the actual bcma device suspend resume functions. Signed-off-by: Linus Torvalds Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index fda56bd..0def898 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -19,6 +19,7 @@ int __init bcma_bus_early_register(struct bcma_bus *bus, struct bcma_device *core_cc, struct bcma_device *core_mips); #ifdef CONFIG_PM +int bcma_bus_suspend(struct bcma_bus *bus); int bcma_bus_resume(struct bcma_bus *bus); #endif diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index 86dfeea..f59244e 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -242,7 +242,7 @@ static int bcma_host_pci_suspend(struct device *dev) bus->mapped_core = NULL; - return 0; + return bcma_bus_suspend(bus); } static int bcma_host_pci_resume(struct device *dev) diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 10f92b3..b711d9d 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -241,6 +241,11 @@ int __init bcma_bus_early_register(struct bcma_bus *bus, } #ifdef CONFIG_PM +int bcma_bus_suspend(struct bcma_bus *bus) +{ + return 0; +} + int bcma_bus_resume(struct bcma_bus *bus) { struct bcma_device *core; -- cgit v0.10.2 From 7d5869e78f4c9d32f834dadefbb7dcb3c9d4d85f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 13 Jan 2012 23:58:41 +0100 Subject: bcma: connect the bcma bus suspend/resume to the bcma driver suspend/resume Now the low-level driver actually gets informed that it is getting suspended and resumed. Signed-off-by: Linus Torvalds Signed-off-by: John W. Linville diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index b711d9d..febbc0a 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -243,6 +243,16 @@ int __init bcma_bus_early_register(struct bcma_bus *bus, #ifdef CONFIG_PM int bcma_bus_suspend(struct bcma_bus *bus) { + struct bcma_device *core; + + list_for_each_entry(core, &bus->cores, list) { + struct device_driver *drv = core->dev.driver; + if (drv) { + struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); + if (adrv->suspend) + adrv->suspend(core); + } + } return 0; } @@ -257,6 +267,15 @@ int bcma_bus_resume(struct bcma_bus *bus) bcma_core_chipcommon_init(&bus->drv_cc); } + list_for_each_entry(core, &bus->cores, list) { + struct device_driver *drv = core->dev.driver; + if (drv) { + struct bcma_driver *adrv = container_of(drv, struct bcma_driver, drv); + if (adrv->resume) + adrv->resume(core); + } + } + return 0; } #endif diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index d106576..213130a 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1135,7 +1135,7 @@ static int brcms_pci_suspend(struct pci_dev *pdev) return pci_set_power_state(pdev, PCI_D3hot); } -static int brcms_suspend(struct bcma_device *pdev, pm_message_t state) +static int brcms_suspend(struct bcma_device *pdev) { struct brcms_info *wl; struct ieee80211_hw *hw; diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index f4b8346..83c209f 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -162,7 +162,7 @@ struct bcma_driver { int (*probe)(struct bcma_device *dev); void (*remove)(struct bcma_device *dev); - int (*suspend)(struct bcma_device *dev, pm_message_t state); + int (*suspend)(struct bcma_device *dev); int (*resume)(struct bcma_device *dev); void (*shutdown)(struct bcma_device *dev); -- cgit v0.10.2 From 7e9e7fa414e47fccc6127f23bc866cbcc7e27dcb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 13 Jan 2012 23:58:42 +0100 Subject: brcmsmac: remove PCI suspend/resume from bcma driver The brcmsmac driver isn't a PCI driver any more, it's a bcma one. The PCI device has been resumed by the PCI driver (the generic PCI layer, really), we should be resuming just our own driver state. Also add pr_debug() calls to show that we now actually get the suspend/resume events. Signed-off-by: Linus Torvalds Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 213130a..448ab9c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1128,13 +1128,6 @@ static int __devinit brcms_bcma_probe(struct bcma_device *pdev) return 0; } -static int brcms_pci_suspend(struct pci_dev *pdev) -{ - pci_save_state(pdev); - pci_disable_device(pdev); - return pci_set_power_state(pdev, PCI_D3hot); -} - static int brcms_suspend(struct bcma_device *pdev) { struct brcms_info *wl; @@ -1153,40 +1146,15 @@ static int brcms_suspend(struct bcma_device *pdev) wl->pub->hw_up = false; spin_unlock_bh(&wl->lock); - /* temporarily do suspend ourselves */ - return brcms_pci_suspend(pdev->bus->host_pci); -} - -static int brcms_pci_resume(struct pci_dev *pdev) -{ - int err = 0; - uint val; - - err = pci_set_power_state(pdev, PCI_D0); - if (err) - return err; - - pci_restore_state(pdev); - - err = pci_enable_device(pdev); - if (err) - return err; - - pci_set_master(pdev); - - pci_read_config_dword(pdev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + pr_debug("brcms_suspend ok\n"); return 0; } static int brcms_resume(struct bcma_device *pdev) { - /* - * just do pci resume for now until bcma supports it. - */ - return brcms_pci_resume(pdev->bus->host_pci); + pr_debug("brcms_resume ok\n"); + return 0; } static struct bcma_driver brcms_bcma_driver = { -- cgit v0.10.2 From 1886e5d2c694e7fb59434c717e704e7fd8475d2e Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 13 Jan 2012 08:06:32 +0000 Subject: ehea: make some functions and variables static Some functions and variables in ehea are only used in their own file, so they should be static. One particular function had a very generic name, print_error_data. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c index 05b7359..6bdd8e3 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c @@ -263,7 +263,7 @@ static void ehea_get_ethtool_stats(struct net_device *dev, data[i++] = atomic_read(&port->port_res[k].swqe_avail); } -const struct ethtool_ops ehea_ethtool_ops = { +static const struct ethtool_ops ehea_ethtool_ops = { .get_settings = ehea_get_settings, .get_drvinfo = ehea_get_drvinfo, .get_msglevel = ehea_get_msglevel, diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 3554414..5d5fb26 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -94,8 +94,8 @@ static int port_name_cnt; static LIST_HEAD(adapter_list); static unsigned long ehea_driver_flags; static DEFINE_MUTEX(dlpar_mem_lock); -struct ehea_fw_handle_array ehea_fw_handles; -struct ehea_bcmc_reg_array ehea_bcmc_regs; +static struct ehea_fw_handle_array ehea_fw_handles; +static struct ehea_bcmc_reg_array ehea_bcmc_regs; static int __devinit ehea_probe_adapter(struct platform_device *dev, @@ -133,7 +133,7 @@ void ehea_dump(void *adr, int len, char *msg) } } -void ehea_schedule_port_reset(struct ehea_port *port) +static void ehea_schedule_port_reset(struct ehea_port *port) { if (!test_bit(__EHEA_DISABLE_PORT_RESET, &port->flags)) schedule_work(&port->reset_task); @@ -1404,7 +1404,7 @@ out: return ret; } -int ehea_gen_smrs(struct ehea_port_res *pr) +static int ehea_gen_smrs(struct ehea_port_res *pr) { int ret; struct ehea_adapter *adapter = pr->port->adapter; @@ -1426,7 +1426,7 @@ out: return -EIO; } -int ehea_rem_smrs(struct ehea_port_res *pr) +static int ehea_rem_smrs(struct ehea_port_res *pr) { if ((ehea_rem_mr(&pr->send_mr)) || (ehea_rem_mr(&pr->recv_mr))) @@ -2190,7 +2190,7 @@ out: return err; } -int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp) +static int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp) { int ret = -EIO; u64 hret; @@ -2531,7 +2531,7 @@ static void ehea_flush_sq(struct ehea_port *port) } } -int ehea_stop_qps(struct net_device *dev) +static int ehea_stop_qps(struct net_device *dev) { struct ehea_port *port = netdev_priv(dev); struct ehea_adapter *adapter = port->adapter; @@ -2600,7 +2600,7 @@ out: return ret; } -void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res *pr) +static void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res *pr) { struct ehea_qp qp = *orig_qp; struct ehea_qp_init_attr *init_attr = &qp.init_attr; @@ -2633,7 +2633,7 @@ void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res *pr) } } -int ehea_restart_qps(struct net_device *dev) +static int ehea_restart_qps(struct net_device *dev) { struct ehea_port *port = netdev_priv(dev); struct ehea_adapter *adapter = port->adapter; @@ -2824,7 +2824,7 @@ static void ehea_tx_watchdog(struct net_device *dev) ehea_schedule_port_reset(port); } -int ehea_sense_adapter_attr(struct ehea_adapter *adapter) +static int ehea_sense_adapter_attr(struct ehea_adapter *adapter) { struct hcp_query_ehea *cb; u64 hret; @@ -2852,7 +2852,7 @@ out: return ret; } -int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo) +static int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo) { struct hcp_ehea_port_cb4 *cb4; u64 hret; @@ -2966,7 +2966,7 @@ static const struct net_device_ops ehea_netdev_ops = { .ndo_tx_timeout = ehea_tx_watchdog, }; -struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, +static struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, u32 logical_port_id, struct device_node *dn) { @@ -3237,7 +3237,7 @@ static ssize_t ehea_remove_port(struct device *dev, static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port); static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port); -int ehea_create_device_sysfs(struct platform_device *dev) +static int ehea_create_device_sysfs(struct platform_device *dev) { int ret = device_create_file(&dev->dev, &dev_attr_probe_port); if (ret) @@ -3248,7 +3248,7 @@ out: return ret; } -void ehea_remove_device_sysfs(struct platform_device *dev) +static void ehea_remove_device_sysfs(struct platform_device *dev) { device_remove_file(&dev->dev, &dev_attr_probe_port); device_remove_file(&dev->dev, &dev_attr_remove_port); @@ -3379,7 +3379,7 @@ static int __devexit ehea_remove(struct platform_device *dev) return 0; } -void ehea_crash_handler(void) +static void ehea_crash_handler(void) { int i; @@ -3491,7 +3491,7 @@ static ssize_t ehea_show_capabilities(struct device_driver *drv, static DRIVER_ATTR(capabilities, S_IRUSR | S_IRGRP | S_IROTH, ehea_show_capabilities, NULL); -int __init ehea_module_init(void) +static int __init ehea_module_init(void) { int ret; diff --git a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c index 95b9f4f..c25b05b 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c @@ -34,9 +34,7 @@ #include "ehea_phyp.h" #include "ehea_qmr.h" -struct ehea_bmap *ehea_bmap = NULL; - - +static struct ehea_bmap *ehea_bmap; static void *hw_qpageit_get_inc(struct hw_queue *queue) { @@ -212,7 +210,7 @@ out_nomem: return NULL; } -u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force) +static u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force) { u64 hret; u64 adapter_handle = cq->adapter->handle; @@ -337,7 +335,7 @@ struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq) return eqe; } -u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force) +static u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force) { u64 hret; unsigned long flags; @@ -381,7 +379,7 @@ int ehea_destroy_eq(struct ehea_eq *eq) /** * allocates memory for a queue and registers pages in phyp */ -int ehea_qp_alloc_register(struct ehea_qp *qp, struct hw_queue *hw_queue, +static int ehea_qp_alloc_register(struct ehea_qp *qp, struct hw_queue *hw_queue, int nr_pages, int wqe_size, int act_nr_sges, struct ehea_adapter *adapter, int h_call_q_selector) { @@ -516,7 +514,7 @@ out_freemem: return NULL; } -u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force) +static u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force) { u64 hret; struct ehea_qp_init_attr *qp_attr = &qp->init_attr; @@ -976,7 +974,7 @@ int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr, return 0; } -void print_error_data(u64 *data) +static void print_error_data(u64 *data) { int length; u64 type = EHEA_BMASK_GET(ERROR_DATA_TYPE, data[2]); -- cgit v0.10.2 From 795d9a2538b205d9329f34798ec96503a07b7919 Mon Sep 17 00:00:00 2001 From: Hiroaki SHIMODA Date: Sat, 14 Jan 2012 07:10:21 +0000 Subject: bql: Fix inconsistency between file mode and attr method. There is no store() method for inflight attribute in the tx-/byte_queue_limits sysfs directory. So remove S_IWUSR bit. Signed-off-by: Hiroaki SHIMODA Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index f3dbd4f..a1727cd 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -929,7 +929,7 @@ static ssize_t bql_show_inflight(struct netdev_queue *queue, } static struct netdev_queue_attribute bql_inflight_attribute = - __ATTR(inflight, S_IRUGO | S_IWUSR, bql_show_inflight, NULL); + __ATTR(inflight, S_IRUGO, bql_show_inflight, NULL); #define BQL_ATTR(NAME, FIELD) \ static ssize_t bql_show_ ## NAME(struct netdev_queue *queue, \ -- cgit v0.10.2 From 8d9d399f14ea65aeb50c7404e986bceede93bb99 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Sat, 14 Jan 2012 08:16:21 +0000 Subject: net: remove version.h includes in net/openvswitch/ remove version.h includes in net/openswitch/ as reported by make versioncheck. Signed-off-by: Devendra Naga Signed-off-by: David S. Miller diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 9a27251..d9d01cb 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 5b9f884..c73370c 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -25,7 +25,6 @@ #include #include #include -#include #include "flow.h" diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 8fc28b8..322b8d2 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -23,7 +23,6 @@ #include #include #include -#include #include "datapath.h" #include "vport-internal_dev.h" diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 7f0ef37..6c066ba 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -27,7 +27,6 @@ #include #include #include -#include #include "vport.h" #include "vport-internal_dev.h" -- cgit v0.10.2 From 5addc0de28f5e286f9d121112c4222250807b5a5 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Mon, 16 Jan 2012 14:00:12 -0500 Subject: brcmfmac: work-around gcc 4.7 build issue Alexandre Oliva says: "It's an issue brought about by GCC 4.7's partial-inlining, that ends up splitting the udelay function just at the wrong spot, in such a way that some sanity checks for constants fails, and we end up calling bad_udelay. This patch fixes the problem. Feel free to push it upstream if it makes sense to you." Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index f23b0c3..bf11850 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2475,7 +2475,7 @@ static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv) return err; } -static void brcmf_delay(u32 ms) +static __always_inline void brcmf_delay(u32 ms) { if (ms < 1000 / HZ) { cond_resched(); -- cgit v0.10.2 From d981e05908f85beb68ae0d935e59c5e399d2136e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 Jan 2012 10:33:31 +0300 Subject: mwl8k: fix condition in mwl8k_cmd_encryption_remove_key() The intent here was to check whether key->cipher was WEP40 or WEP104. We do a similar check correctly in several other places in this file. The current condition is always true. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 7becea3..d187ea2 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4066,7 +4066,7 @@ static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, goto done; if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || - WLAN_CIPHER_SUITE_WEP104) + key->cipher == WLAN_CIPHER_SUITE_WEP104) mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); -- cgit v0.10.2 From 544204733a20200951c271f81c74930ba5424638 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Tue, 17 Jan 2012 09:22:49 +0100 Subject: mac80211: Fix possible race between sta_unblock and network softirq All other code paths in sta_unblock synchronize with the network softirq by using local_bh_disable/enable. Do the same around ieee80211_sta_ps_deliver_wakeup. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 3c428d4..ff11f6b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -238,9 +238,11 @@ static void sta_unblock(struct work_struct *wk) if (sta->dead) return; - if (!test_sta_flag(sta, WLAN_STA_PS_STA)) + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { + local_bh_disable(); ieee80211_sta_ps_deliver_wakeup(sta); - else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { + local_bh_enable(); + } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { clear_sta_flag(sta, WLAN_STA_PS_DRIVER); local_bh_disable(); -- cgit v0.10.2 From 604c4ef1c453a1b2ea2cdf04d2b49afec421ebfa Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Tue, 17 Jan 2012 15:45:15 +0530 Subject: mwl8k: Fixing Sparse ENDIAN CHECK warning Fixing following sparse warning >drivers/net/wireless/mwl8k.c:2780:15: warning: incorrect type in assignment (different base types) >drivers/net/wireless/mwl8k.c:2780:15: expected restricted unsigned short [usertype] channel >drivers/net/wireless/mwl8k.c:2780:15: got unsigned short [unsigned] [usertype] hw_value Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index d187ea2..dd5aeaf 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2777,7 +2777,7 @@ static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw, else if (channel->band == IEEE80211_BAND_5GHZ) cmd->band = cpu_to_le16(0x4); - cmd->channel = channel->hw_value; + cmd->channel = cpu_to_le16(channel->hw_value); if (conf->channel_type == NL80211_CHAN_NO_HT || conf->channel_type == NL80211_CHAN_HT20) { -- cgit v0.10.2 From 319d3b9c97b5e3191e419bb95496bf08ec50f096 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Sun, 15 Jan 2012 22:04:39 +0000 Subject: net: move sock_update_memcg outside of CONFIG_INET Although only used currently for tcp sockets, this function is now used in common sock code (for sock_clone()) Commit 475f1b52645a29936b9df1d8fcd45f7e56bd4a9f moved the declaration of sock_update_clone() to inside sock.c, but this only fixes the problem when CONFIG_CGROUP_MEM_RES_CTLR_KMEM is also not defined. This patch here is verified to fix both problems, although reverting the previous one is not necessary. Signed-off-by: Glauber Costa CC: David S. Miller CC: Stephen Rothwell Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: David S. Miller diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 602207b..3dbff4d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -373,7 +373,6 @@ static void mem_cgroup_put(struct mem_cgroup *memcg); /* Writing them here to avoid exposing memcg's inner layout */ #ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM -#ifdef CONFIG_INET #include #include @@ -420,6 +419,7 @@ void sock_release_memcg(struct sock *sk) } } +#ifdef CONFIG_INET struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg) { if (!memcg || mem_cgroup_is_root(memcg)) -- cgit v0.10.2 From 27a429383b399fc0f68bea55c422d7179f3c8b05 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Jan 2012 04:35:50 +0000 Subject: bridge: BH already disabled in br_fdb_cleanup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit br_fdb_cleanup() is run from timer interrupt, BH already masked. Signed-off-by: Eric Dumazet CC: Stephen Hemminger CC: Å tefan Gula Acked-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index f963f6b..5ba0c84 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -146,7 +146,7 @@ void br_fdb_cleanup(unsigned long _data) unsigned long next_timer = jiffies + br->ageing_time; int i; - spin_lock_bh(&br->hash_lock); + spin_lock(&br->hash_lock); for (i = 0; i < BR_HASH_SIZE; i++) { struct net_bridge_fdb_entry *f; struct hlist_node *h, *n; @@ -162,7 +162,7 @@ void br_fdb_cleanup(unsigned long _data) next_timer = this_timer; } } - spin_unlock_bh(&br->hash_lock); + spin_unlock(&br->hash_lock); mod_timer(&br->gc_timer, round_jiffies_up(next_timer)); } -- cgit v0.10.2 From e52ac3398c3d772d372b9b62ab408fd5eec96840 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 16 Jan 2012 12:38:59 +0000 Subject: net: Use device model to get driver name in skb_gso_segment() ethtool operations generally require the caller to hold RTNL and are not safe to call in atomic context. The device model provides this information for most devices; we'll only lose it for some old ISA drivers. Signed-off-by: Ben Hutchings Acked-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index f494675..7e6b7dc 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1962,13 +1962,13 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { struct net_device *dev = skb->dev; - struct ethtool_drvinfo info = {}; + const char *driver = ""; - if (dev && dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) - dev->ethtool_ops->get_drvinfo(dev, &info); + if (dev && dev->dev.parent) + driver = dev_driver_string(dev->dev.parent); WARN(1, "%s: caps=(%pNF, %pNF) len=%d data_len=%d ip_summed=%d\n", - info.driver, dev ? &dev->features : NULL, + driver, dev ? &dev->features : NULL, skb->sk ? &skb->sk->sk_route_caps : NULL, skb->len, skb->data_len, skb->ip_summed); -- cgit v0.10.2 From 34aa6f1400810890636ba0b170effbfa71eacec7 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Mon, 16 Jan 2012 16:50:16 +0000 Subject: net: sh-eth: Fix build error by the value which is not defined ----- drivers/net/ethernet/renesas/sh_eth.c:1706: error: 'pdid' undeclared (first use in this function) drivers/net/ethernet/renesas/sh_eth.c:1706: error: (Each undeclared identifier is reported only once drivers/net/ethernet/renesas/sh_eth.c:1706: error: for each function it appears in.) make[5]: *** [drivers/net/ethernet/renesas/sh_eth.o] Error 1 ----- Signed-off-by: Nobuhiro Iwamatsu CC: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 6ece429..813d41c 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1703,7 +1703,7 @@ static int sh_mdio_init(struct net_device *ndev, int id, mdp->mii_bus->name = "sh_mii"; mdp->mii_bus->parent = &ndev->dev; snprintf(mdp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", - mdp->pdev->name, pdid); + mdp->pdev->name, id); /* PHY IRQ */ mdp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); -- cgit v0.10.2 From 2b2d465631db59c144f537a9bef47a883c55d670 Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Mon, 16 Jan 2012 18:34:24 +0000 Subject: net: kill duplicate included header For net part, remove duplicate included header. Signed-off-by: Shan Wei Signed-off-by: David S. Miller diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c index 5fc9eca..3d737fb 100644 --- a/net/caif/caif_usb.c +++ b/net/caif/caif_usb.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index e5e18cb..8a949f1 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -15,7 +15,6 @@ #include #include #include -#include #include static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index fe7f020..1252c30 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include -- cgit v0.10.2 From 747465ef7a082033e086dedc8189febfda43b015 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Jan 2012 19:27:39 +0000 Subject: net: fix some sparse errors make C=2 CF="-D__CHECK_ENDIAN__" M=net And fix flowi4_init_output() prototype for sport Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/flow.h b/include/net/flow.h index da1f064..9b58243 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -78,7 +78,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be32 sport) + __be16 dport, __be16 sport) { fl4->flowi4_oif = oif; fl4->flowi4_iif = 0; diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 6fd4460..99b2596 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -46,7 +46,7 @@ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, memcpy(hash, saddr, 16); for (i = 0; i < 4; i++) - secret[i] = net_secret[i] + daddr[i]; + secret[i] = net_secret[i] + (__force u32)daddr[i]; secret[4] = net_secret[4] + (((__force u16)sport << 16) + (__force u16)dport); for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 86f13c67..986a02b 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -136,7 +136,7 @@ static int addr_compare(const struct inetpeer_addr *a, for (i = 0; i < n; i++) { if (a->addr.a6[i] == b->addr.a6[i]) continue; - if (a->addr.a6[i] < b->addr.a6[i]) + if ((__force u32)a->addr.a6[i] < (__force u32)b->addr.a6[i]) return -1; return 1; } diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 7e4ec9f..6e412a6 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -141,7 +141,7 @@ __be32 ic_servaddr = NONE; /* Boot server IP address */ __be32 root_server_addr = NONE; /* Address of NFS server */ u8 root_server_path[256] = { 0, }; /* Path to mount as root */ -u32 ic_dev_xid; /* Device under configuration */ +__be32 ic_dev_xid; /* Device under configuration */ /* vendor class identifier */ static char vendor_class_identifier[253] __initdata; @@ -859,9 +859,9 @@ static int __init ic_bootp_string(char *dest, char *src, int len, int max) */ static void __init ic_do_bootp_ext(u8 *ext) { - u8 servers; - int i; - u16 mtu; + u8 servers; + int i; + __be16 mtu; #ifdef IPCONFIG_DEBUG u8 *c; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 43d4c3b..aea5a19 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -140,13 +140,14 @@ static void ping_v4_unhash(struct sock *sk) write_lock_bh(&ping_table.lock); hlist_nulls_del(&sk->sk_nulls_node); sock_put(sk); - isk->inet_num = isk->inet_sport = 0; + isk->inet_num = 0; + isk->inet_sport = 0; sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); write_unlock_bh(&ping_table.lock); } } -static struct sock *ping_v4_lookup(struct net *net, u32 saddr, u32 daddr, +static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr, u16 ident, int dif) { struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); @@ -154,15 +155,15 @@ static struct sock *ping_v4_lookup(struct net *net, u32 saddr, u32 daddr, struct inet_sock *isk; struct hlist_nulls_node *hnode; - pr_debug("try to find: num = %d, daddr = %ld, dif = %d\n", - (int)ident, (unsigned long)daddr, dif); + pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", + (int)ident, &daddr, dif); read_lock_bh(&ping_table.lock); ping_portaddr_for_each_entry(sk, hnode, hslot) { isk = inet_sk(sk); - pr_debug("found: %p: num = %d, daddr = %ld, dif = %d\n", sk, - (int)isk->inet_num, (unsigned long)isk->inet_rcv_saddr, + pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk, + (int)isk->inet_num, &isk->inet_rcv_saddr, sk->sk_bound_dev_if); pr_debug("iterate\n"); @@ -254,7 +255,7 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk, addr->sin_addr.s_addr, ntohs(addr->sin_port)); chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); - if (addr->sin_addr.s_addr == INADDR_ANY) + if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) chk_addr_ret = RTN_LOCAL; if ((sysctl_ip_nonlocal_bind == 0 && @@ -278,9 +279,9 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto out; } - pr_debug("after bind(): num = %d, daddr = %ld, dif = %d\n", + pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n", (int)isk->inet_num, - (unsigned long) isk->inet_rcv_saddr, + &isk->inet_rcv_saddr, (int)sk->sk_bound_dev_if); err = 0; @@ -407,7 +408,7 @@ out: struct pingfakehdr { struct icmphdr icmph; struct iovec *iov; - u32 wcheck; + __wsum wcheck; }; static int ping_getfrag(void *from, char * to, @@ -459,7 +460,7 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct rtable *rt = NULL; struct ip_options_data opt_copy; int free = 0; - u32 saddr, daddr, faddr; + __be32 saddr, daddr, faddr; u8 tos; int err; @@ -696,8 +697,8 @@ void ping_rcv(struct sk_buff *skb) struct net *net = dev_net(skb->dev); struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = icmp_hdr(skb); - u32 saddr = iph->saddr; - u32 daddr = iph->daddr; + __be32 saddr = iph->saddr; + __be32 daddr = iph->daddr; /* We assume the packet has already been checked by icmp_rcv */ diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index ae08aee..251e7cd 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -575,7 +575,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) } if (np->rxopt.bits.rxorigdstaddr) { struct sockaddr_in6 sin6; - u16 *ports = (u16 *) skb_transport_header(skb); + __be16 *ports = (__be16 *) skb_transport_header(skb); if (skb_transport_offset(skb) + 4 <= skb->len) { /* All current transport protocols have the port numbers in the -- cgit v0.10.2 From 11b2ec6b739ee90211dc6f6942e2ba3a141434a8 Mon Sep 17 00:00:00 2001 From: Yaniv Rosner Date: Tue, 17 Jan 2012 02:33:25 +0000 Subject: bnx2x: Fix Super-Isolate mode for BCM84833 The Super-Isolate mode comes to isolate the BCM84833 PHY from the outside world. Not doing it correctly, made link partner see the link before the driver was loaded. This patch also involves SPIROM version fixes since it is used to determine whether the common init of the PHY was already executed, and the common init of this PHY is partially responsible for setting the Super-Isolate mode. Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 4df9505..3b184c2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -9266,62 +9266,68 @@ static void bnx2x_8727_link_reset(struct bnx2x_phy *phy, /* BCM8481/BCM84823/BCM84833 PHY SECTION */ /******************************************************************/ static void bnx2x_save_848xx_spirom_version(struct bnx2x_phy *phy, - struct link_params *params) + struct bnx2x *bp, + u8 port) { u16 val, fw_ver1, fw_ver2, cnt; - u8 port; - struct bnx2x *bp = params->bp; - port = params->port; + if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD, 0x400f, &fw_ver1); + bnx2x_save_spirom_version(bp, port, + ((fw_ver1 & 0xf000)>>5) | (fw_ver1 & 0x7f), + phy->ver_addr); + } else { + /* For 32-bit registers in 848xx, access via MDIO2ARM i/f. */ + /* (1) set reg 0xc200_0014(SPI_BRIDGE_CTRL_2) to 0x03000000 */ + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0014); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81B, 0x0000); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81C, 0x0300); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x0009); + + for (cnt = 0; cnt < 100; cnt++) { + bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val); + if (val & 1) + break; + udelay(5); + } + if (cnt == 100) { + DP(NETIF_MSG_LINK, "Unable to read 848xx " + "phy fw version(1)\n"); + bnx2x_save_spirom_version(bp, port, 0, + phy->ver_addr); + return; + } - /* For the 32 bits registers in 848xx, access via MDIO2ARM interface.*/ - /* (1) set register 0xc200_0014(SPI_BRIDGE_CTRL_2) to 0x03000000 */ - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0014); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81B, 0x0000); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81C, 0x0300); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x0009); - for (cnt = 0; cnt < 100; cnt++) { - bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val); - if (val & 1) - break; - udelay(5); - } - if (cnt == 100) { - DP(NETIF_MSG_LINK, "Unable to read 848xx phy fw version(1)\n"); - bnx2x_save_spirom_version(bp, port, 0, - phy->ver_addr); - return; - } + /* 2) read register 0xc200_0000 (SPI_FW_STATUS) */ + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0000); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200); + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x000A); + for (cnt = 0; cnt < 100; cnt++) { + bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val); + if (val & 1) + break; + udelay(5); + } + if (cnt == 100) { + DP(NETIF_MSG_LINK, "Unable to read 848xx phy fw " + "version(2)\n"); + bnx2x_save_spirom_version(bp, port, 0, + phy->ver_addr); + return; + } + /* lower 16 bits of the register SPI_FW_STATUS */ + bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81B, &fw_ver1); + /* upper 16 bits of register SPI_FW_STATUS */ + bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81C, &fw_ver2); - /* 2) read register 0xc200_0000 (SPI_FW_STATUS) */ - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0000); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x000A); - for (cnt = 0; cnt < 100; cnt++) { - bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val); - if (val & 1) - break; - udelay(5); - } - if (cnt == 100) { - DP(NETIF_MSG_LINK, "Unable to read 848xx phy fw version(2)\n"); - bnx2x_save_spirom_version(bp, port, 0, + bnx2x_save_spirom_version(bp, port, (fw_ver2<<16) | fw_ver1, phy->ver_addr); - return; } - /* lower 16 bits of the register SPI_FW_STATUS */ - bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81B, &fw_ver1); - /* upper 16 bits of register SPI_FW_STATUS */ - bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA81C, &fw_ver2); - - bnx2x_save_spirom_version(bp, port, (fw_ver2<<16) | fw_ver1, - phy->ver_addr); } - static void bnx2x_848xx_set_led(struct bnx2x *bp, struct bnx2x_phy *phy) { @@ -9392,10 +9398,13 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, u16 tmp_req_line_speed; tmp_req_line_speed = phy->req_line_speed; - if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) + if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { if (phy->req_line_speed == SPEED_10000) phy->req_line_speed = SPEED_AUTO_NEG; - + } else { + /* Save spirom version */ + bnx2x_save_848xx_spirom_version(phy, bp, params->port); + } /* * This phy uses the NIG latch mechanism since link indication * arrives through its LED4 and not via its LASI signal, so we @@ -9539,9 +9548,6 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, MDIO_AN_REG_8481_10GBASE_T_AN_CTRL, 1); - /* Save spirom version */ - bnx2x_save_848xx_spirom_version(phy, params); - phy->req_line_speed = tmp_req_line_speed; return 0; @@ -9749,17 +9755,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, /* Wait for GPHY to come out of reset */ msleep(50); - if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { - /* Bring PHY out of super isolate mode */ - bnx2x_cl45_read(bp, phy, - MDIO_CTL_DEVAD, - MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val); - val &= ~MDIO_84833_SUPER_ISOLATE; - bnx2x_cl45_write(bp, phy, - MDIO_CTL_DEVAD, - MDIO_84833_TOP_CFG_XGPHY_STRAP1, val); - bnx2x_84833_pair_swap_cfg(phy, params, vars); - } else { + if (phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { /* * BCM84823 requires that XGXS links up first @ 10G for normal * behavior. @@ -9816,24 +9812,27 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Multi_phy config = 0x%x, Media control = 0x%x\n", params->multi_phy_config, val); - /* AutogrEEEn */ - if (params->feature_config_flags & - FEATURE_CONFIG_AUTOGREEEN_ENABLED) - cmd_args[0] = 0x2; - else - cmd_args[0] = 0x0; + if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + bnx2x_84833_pair_swap_cfg(phy, params, vars); - cmd_args[1] = 0x0; - cmd_args[2] = PHY84833_CONSTANT_LATENCY + 1; - cmd_args[3] = PHY84833_CONSTANT_LATENCY; - rc = bnx2x_84833_cmd_hdlr(phy, params, - PHY84833_CMD_SET_EEE_MODE, cmd_args); - if (rc != 0) - DP(NETIF_MSG_LINK, "Cfg AutogrEEEn failed.\n"); + /* AutogrEEEn */ + if (params->feature_config_flags & + FEATURE_CONFIG_AUTOGREEEN_ENABLED) + cmd_args[0] = 0x2; + else + cmd_args[0] = 0x0; + cmd_args[1] = 0x0; + cmd_args[2] = PHY84833_CONSTANT_LATENCY + 1; + cmd_args[3] = PHY84833_CONSTANT_LATENCY; + rc = bnx2x_84833_cmd_hdlr(phy, params, + PHY84833_CMD_SET_EEE_MODE, cmd_args); + if (rc != 0) + DP(NETIF_MSG_LINK, "Cfg AutogrEEEn failed.\n"); + } if (initialize) rc = bnx2x_848xx_cmn_config_init(phy, params, vars); else - bnx2x_save_848xx_spirom_version(phy, params); + bnx2x_save_848xx_spirom_version(phy, bp, params->port); /* 84833 PHY has a better feature and doesn't need to support this. */ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823) { cms_enable = REG_RD(bp, params->shmem_base + @@ -9851,6 +9850,16 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, MDIO_CTL_REG_84823_USER_CTRL_REG, val); } + if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + /* Bring PHY out of super isolate mode as the final step. */ + bnx2x_cl45_read(bp, phy, + MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val); + val &= ~MDIO_84833_SUPER_ISOLATE; + bnx2x_cl45_write(bp, phy, + MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_XGPHY_STRAP1, val); + } return rc; } @@ -9988,10 +9997,11 @@ static void bnx2x_848x3_link_reset(struct bnx2x_phy *phy, } else { bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD, - 0x400f, &val16); + MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val16); + val16 |= MDIO_84833_SUPER_ISOLATE; bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_CTRL, 0x800); + MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_XGPHY_STRAP1, val16); } } @@ -12333,55 +12343,69 @@ static int bnx2x_84833_common_init_phy(struct bnx2x *bp, u32 chip_id) { u8 reset_gpios; - struct bnx2x_phy phy; - u32 shmem_base, shmem2_base, cnt; - s8 port = 0; - u16 val; - reset_gpios = bnx2x_84833_get_reset_gpios(bp, shmem_base_path, chip_id); bnx2x_set_mult_gpio(bp, reset_gpios, MISC_REGISTERS_GPIO_OUTPUT_LOW); udelay(10); bnx2x_set_mult_gpio(bp, reset_gpios, MISC_REGISTERS_GPIO_OUTPUT_HIGH); DP(NETIF_MSG_LINK, "84833 reset pulse on pin values 0x%x\n", reset_gpios); - for (port = PORT_MAX - 1; port >= PORT_0; port--) { - /* This PHY is for E2 and E3. */ - shmem_base = shmem_base_path[port]; - shmem2_base = shmem2_base_path[port]; - /* Extract the ext phy address for the port */ - if (bnx2x_populate_phy(bp, phy_index, shmem_base, shmem2_base, - 0, &phy) != - 0) { - DP(NETIF_MSG_LINK, "populate_phy failed\n"); - return -EINVAL; - } + return 0; +} - /* Wait for FW completing its initialization. */ - for (cnt = 0; cnt < 1000; cnt++) { - bnx2x_cl45_read(bp, &phy, +static int bnx2x_84833_pre_init_phy(struct bnx2x *bp, + struct bnx2x_phy *phy) +{ + u16 val, cnt; + /* Wait for FW completing its initialization. */ + for (cnt = 0; cnt < 1500; cnt++) { + bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &val); - if (!(val & (1<<15))) - break; - msleep(1); - } - if (cnt >= 1000) - DP(NETIF_MSG_LINK, - "84833 Cmn reset timeout (%d)\n", port); - - /* Put the port in super isolate mode. */ - bnx2x_cl45_read(bp, &phy, - MDIO_CTL_DEVAD, - MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val); - val |= MDIO_84833_SUPER_ISOLATE; - bnx2x_cl45_write(bp, &phy, - MDIO_CTL_DEVAD, - MDIO_84833_TOP_CFG_XGPHY_STRAP1, val); + if (!(val & (1<<15))) + break; + msleep(1); + } + if (cnt >= 1500) { + DP(NETIF_MSG_LINK, "84833 reset timeout\n"); + return -EINVAL; } + /* Put the port in super isolate mode. */ + bnx2x_cl45_read(bp, phy, + MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val); + val |= MDIO_84833_SUPER_ISOLATE; + bnx2x_cl45_write(bp, phy, + MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_XGPHY_STRAP1, val); + + /* Save spirom version */ + bnx2x_save_848xx_spirom_version(phy, bp, PORT_0); return 0; } +int bnx2x_pre_init_phy(struct bnx2x *bp, + u32 shmem_base, + u32 shmem2_base, + u32 chip_id) +{ + int rc = 0; + struct bnx2x_phy phy; + bnx2x_set_mdio_clk(bp, chip_id, PORT_0); + if (bnx2x_populate_phy(bp, EXT_PHY1, shmem_base, shmem2_base, + PORT_0, &phy)) { + DP(NETIF_MSG_LINK, "populate_phy failed\n"); + return -EINVAL; + } + switch (phy.type) { + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833: + rc = bnx2x_84833_pre_init_phy(bp, &phy); + break; + default: + break; + } + return rc; +} static int bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[], u32 shmem2_base_path[], u8 phy_index, -- cgit v0.10.2 From 127302bb42257eef5d357722d670c4ac53327088 Mon Sep 17 00:00:00 2001 From: Yaniv Rosner Date: Tue, 17 Jan 2012 02:33:26 +0000 Subject: bnx2x: Fix PFC setting on BCM57840 This patch handles the second port of a path in a 4-port device of BCM57840. Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 3b184c2..cc80637 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -2502,7 +2502,7 @@ static void bnx2x_update_pfc_nig(struct link_params *params, struct bnx2x_nig_brb_pfc_port_params *nig_params) { u32 xcm_mask = 0, ppp_enable = 0, pause_enable = 0, llfc_out_en = 0; - u32 llfc_enable = 0, xcm0_out_en = 0, p0_hwpfc_enable = 0; + u32 llfc_enable = 0, xcm_out_en = 0, hwpfc_enable = 0; u32 pkt_priority_to_cos = 0; struct bnx2x *bp = params->bp; u8 port = params->port; @@ -2516,9 +2516,8 @@ static void bnx2x_update_pfc_nig(struct link_params *params, * MAC control frames (that are not pause packets) * will be forwarded to the XCM. */ - xcm_mask = REG_RD(bp, - port ? NIG_REG_LLH1_XCM_MASK : - NIG_REG_LLH0_XCM_MASK); + xcm_mask = REG_RD(bp, port ? NIG_REG_LLH1_XCM_MASK : + NIG_REG_LLH0_XCM_MASK); /* * nig params will override non PFC params, since it's possible to * do transition from PFC to SAFC @@ -2533,8 +2532,8 @@ static void bnx2x_update_pfc_nig(struct link_params *params, ppp_enable = 1; xcm_mask &= ~(port ? NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN : NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN); - xcm0_out_en = 0; - p0_hwpfc_enable = 1; + xcm_out_en = 0; + hwpfc_enable = 1; } else { if (nig_params) { llfc_out_en = nig_params->llfc_out_en; @@ -2545,7 +2544,7 @@ static void bnx2x_update_pfc_nig(struct link_params *params, xcm_mask |= (port ? NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN : NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN); - xcm0_out_en = 1; + xcm_out_en = 1; } if (CHIP_IS_E3(bp)) @@ -2564,13 +2563,16 @@ static void bnx2x_update_pfc_nig(struct link_params *params, REG_WR(bp, port ? NIG_REG_LLH1_XCM_MASK : NIG_REG_LLH0_XCM_MASK, xcm_mask); - REG_WR(bp, NIG_REG_LLFC_EGRESS_SRC_ENABLE_0, 0x7); + REG_WR(bp, port ? NIG_REG_LLFC_EGRESS_SRC_ENABLE_1 : + NIG_REG_LLFC_EGRESS_SRC_ENABLE_0, 0x7); /* output enable for RX_XCM # IF */ - REG_WR(bp, NIG_REG_XCM0_OUT_EN, xcm0_out_en); + REG_WR(bp, port ? NIG_REG_XCM1_OUT_EN : + NIG_REG_XCM0_OUT_EN, xcm_out_en); /* HW PFC TX enable */ - REG_WR(bp, NIG_REG_P0_HWPFC_ENABLE, p0_hwpfc_enable); + REG_WR(bp, port ? NIG_REG_P1_HWPFC_ENABLE : + NIG_REG_P0_HWPFC_ENABLE, hwpfc_enable); if (nig_params) { u8 i = 0; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index 44609de..dddbcf6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -2176,6 +2176,7 @@ * set to 0x345678021. This is a new register (with 2_) added in E3 B0 to * accommodate the 9 input clients to ETS arbiter. */ #define NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB 0x18684 +#define NIG_REG_P1_HWPFC_ENABLE 0x181d0 #define NIG_REG_P1_MAC_IN_EN 0x185c0 /* [RW 1] Output enable for TX MAC interface */ #define NIG_REG_P1_MAC_OUT_EN 0x185c4 -- cgit v0.10.2 From 75318327802235ecd7e90b0760cceb994bf975ca Mon Sep 17 00:00:00 2001 From: Yaniv Rosner Date: Tue, 17 Jan 2012 02:33:27 +0000 Subject: bnx2x: Remove 100Mb force speed for BCM84833 Remove unsupported speed of 100Mb force for BCM84833 due to hardware limitation. Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index a688b9d..f99c6e3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -365,13 +365,18 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) DP(NETIF_MSG_LINK, "cfg_idx = %x\n", cfg_idx); if (cmd->autoneg == AUTONEG_ENABLE) { + u32 an_supported_speed = bp->port.supported[cfg_idx]; + if (bp->link_params.phy[EXT_PHY1].type == + PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) + an_supported_speed |= (SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full); if (!(bp->port.supported[cfg_idx] & SUPPORTED_Autoneg)) { DP(NETIF_MSG_LINK, "Autoneg not supported\n"); return -EINVAL; } /* advertise the requested speed and duplex if supported */ - if (cmd->advertising & ~(bp->port.supported[cfg_idx])) { + if (cmd->advertising & ~an_supported_speed) { DP(NETIF_MSG_LINK, "Advertisement parameters " "are not supported\n"); return -EINVAL; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index cc80637..1d1a809 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -9454,13 +9454,10 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, an_1000_val); /* set 100 speed advertisement */ - if (((phy->req_line_speed == SPEED_AUTO_NEG) && + if ((phy->req_line_speed == SPEED_AUTO_NEG) && (phy->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL | - PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)) && - (phy->supported & - (SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full)))) { + PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF))) { an_10_100_val |= (1<<7); /* Enable autoneg and restart autoneg for legacy speeds */ autoneg_val |= (1<<9 | 1<<12); @@ -11528,6 +11525,19 @@ static int bnx2x_populate_ext_phy(struct bnx2x *bp, } phy->mdio_ctrl = bnx2x_get_emac_base(bp, mdc_mdio_access, port); + if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) && + (phy->ver_addr)) { + /* + * Remove 100Mb link supported for BCM84833 when phy fw + * version lower than or equal to 1.39 + */ + u32 raw_ver = REG_RD(bp, phy->ver_addr); + if (((raw_ver & 0x7F) <= 39) && + (((raw_ver & 0xF80) >> 7) <= 1)) + phy->supported &= ~(SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full); + } + /* * In case mdc/mdio_access of the external phy is different than the * mdc/mdio access of the XGXS, a HW lock must be taken in each access -- cgit v0.10.2 From 096b9527db77defb2501509f2517d2ab24300d9c Mon Sep 17 00:00:00 2001 From: Yaniv Rosner Date: Tue, 17 Jan 2012 02:33:28 +0000 Subject: bnx2x: Remove AutoGrEEEn for BCM84833 Disable the autoGrEEEn feature for BCM84833. Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 1d1a809..a6c48d6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -9814,12 +9814,8 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { bnx2x_84833_pair_swap_cfg(phy, params, vars); - /* AutogrEEEn */ - if (params->feature_config_flags & - FEATURE_CONFIG_AUTOGREEEN_ENABLED) - cmd_args[0] = 0x2; - else - cmd_args[0] = 0x0; + /* Keep AutogrEEEn disabled. */ + cmd_args[0] = 0x0; cmd_args[1] = 0x0; cmd_args[2] = PHY84833_CONSTANT_LATENCY + 1; cmd_args[3] = PHY84833_CONSTANT_LATENCY; -- cgit v0.10.2 From 6ab48a5c86ce778188c173818cb2f1644526e962 Mon Sep 17 00:00:00 2001 From: Yaniv Rosner Date: Tue, 17 Jan 2012 02:33:29 +0000 Subject: bnx2x: Disable AN KR work-around for BCM57810 Disable the work-around for the autoneg KR of the BCM57810 in case the Warpcore version is 0xD108 and above, which fixes this problem. Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index a6c48d6..2091e5d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -3763,7 +3763,15 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, /* Advertise pause */ bnx2x_ext_phy_set_pause(params, phy, vars); - vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY; + /* + * Set KR Autoneg Work-Around flag for Warpcore version older than D108 + */ + bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_UC_INFO_B1_VERSION, &val16); + if (val16 < 0xd108) { + DP(NETIF_MSG_LINK, "Enable AN KR work-around\n"); + vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY; + } bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL5_MISC7, &val16); -- cgit v0.10.2 From 3371bb3f7ed11b7b7473a202e2713bde50dc01c0 Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Tue, 17 Jan 2012 03:03:13 +0000 Subject: caif: Fix typo in Vendor/Product-ID for CAIF modems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix typo for the Vendor/Product Id for ST-Ericsson CAIF modems. Discovery is based on fixed USB vendor 0x04cc (ST-Ericsson), product-id 0x230f (NCM). Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c index 3d737fb..fd7cbf5 100644 --- a/net/caif/caif_usb.c +++ b/net/caif/caif_usb.c @@ -26,7 +26,7 @@ MODULE_LICENSE("GPL"); #define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */ #define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1) #define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */ -#define STE_USB_PID_CAIF 0x2306 /* Product id for CAIF Modems */ +#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */ struct cfusbl { struct cflayer layer; -- cgit v0.10.2 From 59f608d84f937cb5e2fd3a0b9f848f200d47ba85 Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Tue, 17 Jan 2012 03:03:14 +0000 Subject: caif: Remove bad WARN_ON in caif_dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove WARN_ON and bad handling of SKB without destructor callback in caif_flow_cb. SKB without destructor cannot be handled as an error case. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 61570ee..673728a 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -146,15 +146,17 @@ void caif_flow_cb(struct sk_buff *skb) spin_lock_bh(&caifd->flow_lock); send_xoff = caifd->xoff; caifd->xoff = 0; - if (!WARN_ON(caifd->xoff_skb_dtor == NULL)) { - WARN_ON(caifd->xoff_skb != skb); - dtor = caifd->xoff_skb_dtor; - caifd->xoff_skb = NULL; - caifd->xoff_skb_dtor = NULL; - } + dtor = caifd->xoff_skb_dtor; + + if (WARN_ON(caifd->xoff_skb != skb)) + skb = NULL; + + caifd->xoff_skb = NULL; + caifd->xoff_skb_dtor = NULL; + spin_unlock_bh(&caifd->flow_lock); - if (dtor) + if (dtor && skb) dtor(skb); if (send_xoff) -- cgit v0.10.2 From 986b2f2c21b308e21160a40359b81370f8fee6ec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 17 Jan 2012 16:28:59 +0000 Subject: ASoC: Wait for WM8993 FLL to stabilise Ensure the FLL is locked before we return from set_fll(). Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 2b40c93..7c7fd92 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -444,6 +444,12 @@ static int _wm8993_set_fll(struct snd_soc_codec *codec, int fll_id, int source, /* Enable the FLL */ snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA); + /* Both overestimates */ + if (Fref < 1000000) + msleep(3); + else + msleep(1); + dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout); wm8993->fll_fref = Fref; -- cgit v0.10.2 From 951880e634a79884236a575b896abf55c39ae0bf Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 17 Jan 2012 10:19:41 -0800 Subject: Revert "capabitlies: ns_capable can use the cap helpers rather than lsm call" This reverts commit d2a7009f0bb03fa22ad08dd25472efa0568126b9. J. R. Okajima explains: "After this commit, I am afraid access(2) on NFS may not work correctly. The scenario based upon my guess. - access(2) overrides the credentials. - calls inode_permission() -- ... -- generic_permission() -- ns_capable(). - while the old ns_capable() calls security_capable(current_cred()), the new ns_capable() calls has_ns_capability(current) -- security_capable(__task_cred(t)). current_cred() returns current->cred which is effective (overridden) credentials, but __task_cred(current) returns current->real_cred (the NFSD's credential). And the overridden credentials by access(2) lost." Requested-by: J. R. Okajima Acked-by: Eric Paris Signed-off-by: Linus Torvalds diff --git a/kernel/capability.c b/kernel/capability.c index 0fcf1c1..3f1adb6 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -384,7 +384,7 @@ bool ns_capable(struct user_namespace *ns, int cap) BUG(); } - if (has_ns_capability(current, ns, cap)) { + if (security_capable(current_cred(), ns, cap) == 0) { current->flags |= PF_SUPERPRIV; return true; } -- cgit v0.10.2 From 51dcb19aaf9448f6547f653b60a9f083845aad4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 17 Jan 2012 16:20:37 -0200 Subject: [media] dvb_frontend: Don't call get_frontend() if idle If the frontend is in idle state, don't call get_frontend. Calling get_frontend() when the device is not tuned may result in wrong parameters to be returned to the userspace. I was tempted to not call get_frontend() at all, except inside the dvb frontend thread, but this won't work for all cases. The ISDB-T specs (ABNT NBR 15601 and ARIB STD-B31) allow the broadcaster to dynamically change the channel specs at runtime. That means that an ISDB-T optimized application may want/need to monitor the TMCC tables, decoded at the frontends via get_frontend call. So, let's do the simpler change here. Eventually, the logic could be changed to work only if the device is tuned and has lock, but, even so, the lock is also standard-dependent. For ISDB-T, the right lock to wait is that the demod has TMCC lock. So, drivers may need to implement some logic to detect if the get_frontend info was retrieved or not. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index f5fa7aa..2998f38 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1744,6 +1744,7 @@ static int dvb_frontend_ioctl_properties(struct file *file, { struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; + struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int err = 0; @@ -1810,9 +1811,14 @@ static int dvb_frontend_ioctl_properties(struct file *file, /* * Fills the cache out struct with the cache contents, plus - * the data retrieved from get_frontend. + * the data retrieved from get_frontend, if the frontend + * is not idle. Otherwise, returns the cached content */ - dtv_get_frontend(fe, NULL); + if (fepriv->state != FESTATE_IDLE) { + err = dtv_get_frontend(fe, NULL); + if (err < 0) + goto out; + } for (i = 0; i < tvps->num; i++) { err = dtv_property_process_get(fe, c, tvp + i, file); if (err < 0) -- cgit v0.10.2 From 20c300b10c358daa507be335aec6aa3987ef425a Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Tue, 17 Jan 2012 12:54:01 +0400 Subject: tty: remove unused tty_driver->termios_locked This field is unused since 2.6.28 (commit fe6e29fdb1a7: "tty: simplify ktermios allocation", to be exact) Signed-off-by: Konstantin Khlebnikov Signed-off-by: Linus Torvalds diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index ecdaeb9..5cf6850 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -312,7 +312,6 @@ struct tty_driver { */ struct tty_struct **ttys; struct ktermios **termios; - struct ktermios **termios_locked; void *driver_state; /* -- cgit v0.10.2 From ba8438fb4e85bfe6cf8d3149fe63b85e49fdf217 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 12 Dec 2011 09:49:14 +1100 Subject: powerpc: fix compile error with 85xx/p1022_ds.c Current linux-next compiled with mpc85xx_defconfig causes this: arch/powerpc/platforms/85xx/p1022_ds.c:341:14: error: 'udbg_progress' undeclared here (not in a function) Add include to fix this. Signed-off-by: Michael Neuling Acked-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index bb3d84f..b0984ad 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -25,6 +25,7 @@ #include #include +#include #include #include "smp.h" -- cgit v0.10.2 From 0cf572dc00cd36250af9260377a0b5faac9b3284 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 10:55:14 +0100 Subject: arch/powerpc/sysdev/fsl_pci.c: add missing iounmap Add missing iounmap in error handling code, in a case where the function already preforms iounmap on some other execution path. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; statement S,S1; int ret; @@ e = \(ioremap\|ioremap_nocache\)(...) ... when != iounmap(e) if (<+...e...+>) S ... when any when != iounmap(e) *if (...) { ... when != iounmap(e) return ...; } ... when any iounmap(e); // Signed-off-by: Julia Lawall Signed-off-by: Kumar Gala diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 3b61e8c..30eb17e 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -205,12 +205,12 @@ static void __init setup_pci_atmu(struct pci_controller *hose, if (paddr_hi == paddr_lo) { pr_err("%s: No outbound window space\n", name); - return ; + goto out; } if (paddr_lo == 0) { pr_err("%s: No space for inbound window\n", name); - return ; + goto out; } /* setup PCSRBAR/PEXCSRBAR */ @@ -357,6 +357,7 @@ static void __init setup_pci_atmu(struct pci_controller *hose, (u64)hose->dma_window_size); } +out: iounmap(pci); } -- cgit v0.10.2 From 63ff07beaebef2a82da41cf04053ae88b579226d Mon Sep 17 00:00:00 2001 From: Yanmin Zhang Date: Tue, 10 Jan 2012 15:48:21 -0800 Subject: intel_idle: remove redundant local_irq_disable() call irq disabling happens earlier in process_32.c:cpu_idle. Basically, cpuidle_state->enter is called, cpu irq is disabled. cpuidle_state->enter would turn on irq when exiting. intel_idle doesn't follow this assumption. Although it doesn't cause real issue, it misleads developers. Remove the call to local_irq_disable() at entry. [akpm@linux-foundation.org: add comment] Signed-off-by: Mingming Zhang Signed-off-by: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index af0b4a5..920c61c 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -232,6 +232,7 @@ static int get_driver_data(int cstate) * @drv: cpuidle driver * @index: index of cpuidle state * + * Must be called under local_irq_disable(). */ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) @@ -247,8 +248,6 @@ static int intel_idle(struct cpuidle_device *dev, cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1; - local_irq_disable(); - /* * leave_mm() to avoid costly and often unnecessary wakeups * for flushing the user TLB's associated with the active mm. -- cgit v0.10.2 From 2e92c7ad8f269c2b5b7f2a4763675f55f00b75f5 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Thu, 15 Dec 2011 01:18:52 +0900 Subject: ACPI: kernel-parameters.txt : Add intel_idle.max_cstate Add missing intel_idle.max_cstate in kernel-parameters.txt Signed-off-by Masanari Iida Signed-off-by: Len Brown diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 81c287f..c6a56d8 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1035,6 +1035,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. By default, super page will be supported if Intel IOMMU has the capability. With this option, super page will not be supported. + + intel_idle.max_cstate= [KNL,HW,ACPI,X86] + 0 disables intel_idle and fall back on acpi_idle. + 1 to 6 specify maximum depth of C-state. + intremap= [X86-64, Intel-IOMMU] on enable Interrupt Remapping (default) off disable Interrupt Remapping -- cgit v0.10.2 From 95e3ec11491d0cbce9fcdf1cc17a527c114c7dcf Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 15 Dec 2011 13:03:14 +0000 Subject: intel_idle: Fix a cast to pointer from integer of different size warning in intel_idle Fix the following warning: drivers/idle/intel_idle.c: In function 'intel_idle_cpuidle_devices_init': drivers/idle/intel_idle.c:518:5: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] By making get_driver_data() return a long instead of an int. Signed-off-by: David Howells Signed-off-by: Len Brown diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 920c61c..18f9ad8 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -197,7 +197,7 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = { .enter = &intel_idle }, }; -static int get_driver_data(int cstate) +static long get_driver_data(int cstate) { int driver_data; switch (cstate) { -- cgit v0.10.2 From 5c2a9f06a9cd7194f884cdc88144866235dec07d Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Sun, 4 Dec 2011 22:17:29 +0100 Subject: intel idle: Make idle driver more robust kvm -cpu host passes the original cpuid info to the guest. Latest kvm version seem to return true for mwait_leaf cpuid function on recent Intel CPUs. But it does not return mwait C-states (mwait_substates), instead zero is returned. While real CPUs seem to always return non-zero values, the intel idle driver should not get active in kvm (mwait_substates == 0) case and bail out. Otherwise a Null pointer exception will happen later when the cpuidle subsystem tries to get active: [0.984807] BUG: unable to handle kernel NULL pointer dereference at (null) [0.984807] IP: [<(null)>] (null) ... [0.984807][] ? cpuidle_idle_call+0xb4/0x340 [0.984807][] ? __atomic_notifier_call_chain+0x4c/0x70 [0.984807][] ? cpu_idle+0x78/0xd0 Reference: https://bugzilla.novell.com/show_bug.cgi?id=726296 Cc: stable@vger.kernel.org Signed-off-by: Thomas Renninger CC: Bruno Friedmann Signed-off-by: Len Brown diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 18f9ad8..91aff18 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -347,7 +347,8 @@ static int intel_idle_probe(void) cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &mwait_substates); if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || - !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + !(ecx & CPUID5_ECX_INTERRUPT_BREAK) || + !mwait_substates) return -ENODEV; pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates); -- cgit v0.10.2 From 5e7590d40dc59d5c1889d9e70c9da1f1df3918c6 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 17 Jan 2012 17:35:22 +0100 Subject: ACPI processor: Remove unneeded cpuidle_unregister_driver call Since commit 46bcfad7a819bd17ac4e831b04405152d59784ab registering and unregistering cpuidle is done in processor_idle.c. Unregistering via: acpi_bus_unregister_driver(&acpi_processor_driver) -> acpi_processor_remove() -> acpi_processor_power_exit() Remove not needed cpuidle_unregister_driver() call from acpi_processor_exit Signed-off-by: Thomas Renninger CC: Deepthi Dharwar Signed-off-by: Len Brown diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 90719d1..3616ce5 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -828,8 +828,6 @@ static void __exit acpi_processor_exit(void) acpi_bus_unregister_driver(&acpi_processor_driver); - cpuidle_unregister_driver(&acpi_idle_driver); - return; } -- cgit v0.10.2 From ce79dac861e0d9a473d9923391bdbaad83c1c57f Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Tue, 17 Jan 2012 14:14:02 -0500 Subject: x86, opcode: ANDN and Group 17 in x86-opcode-map.txt The Intel documentation at http://software.intel.com/file/36945 shows the ANDN opcode and Group 17 with encoding f2 and f3 encoding respectively. The current version of x86-opcode-map.txt shows them with f3 and f4. Unless someone can point to documentation which shows the currently used encoding the following patch be applied. Signed-off-by: Ulrich Drepper Link: http://lkml.kernel.org/r/CAOPLpQdq5SuVo9=023CYhbFLAX9rONyjmYq7jJkqc5xwctW5eA@mail.gmail.com Signed-off-by: H. Peter Anvin diff --git a/arch/x86/lib/x86-opcode-map.txt b/arch/x86/lib/x86-opcode-map.txt index 5b83c51..4c8010d 100644 --- a/arch/x86/lib/x86-opcode-map.txt +++ b/arch/x86/lib/x86-opcode-map.txt @@ -729,8 +729,8 @@ de: VAESDEC Vdq,Hdq,Wdq (66),(v1) df: VAESDECLAST Vdq,Hdq,Wdq (66),(v1) f0: MOVBE Gy,My | MOVBE Gw,Mw (66) | CRC32 Gd,Eb (F2) f1: MOVBE My,Gy | MOVBE Mw,Gw (66) | CRC32 Gd,Ey (F2) -f3: ANDN Gy,By,Ey (v) -f4: Grp17 (1A) +f2: ANDN Gy,By,Ey (v) +f3: Grp17 (1A) f5: BZHI Gy,Ey,By (v) | PEXT Gy,By,Ey (F3),(v) | PDEP Gy,By,Ey (F2),(v) f6: MULX By,Gy,rDX,Ey (F2),(v) f7: BEXTR Gy,Ey,By (v) | SHLX Gy,Ey,By (66),(v) | SARX Gy,Ey,By (F3),(v) | SHRX Gy,Ey,By (F2),(v) -- cgit v0.10.2 From 54b466e44b1c7809144bbd8cd6be3f85877ca46f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Jan 2012 21:26:11 +0100 Subject: cfq-iosched: fix use-after-free of cfqq With the changes in life time management between the cfq IO contexts and the cfq queues, we now risk having cfqd->active_queue being freed when cfq_slice_expired() is being called. cfq_preempt_queue() caches this queue and uses it after calling said function, causing a use-after-free condition. This triggers the following oops, when cfqq_type() attempts to dereference it: BUG: unable to handle kernel paging request at ffff8800746c4f0c IP: [] cfqq_type+0xb/0x20 PGD 18d4063 PUD 1fe15067 PMD 1ffb9067 PTE 80000000746c4160 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC CPU 3 Modules linked in: Pid: 1, comm: init Not tainted 3.2.0-josef+ #367 Bochs Bochs RIP: 0010:[] [] cfqq_type+0xb/0x20 RSP: 0018:ffff880079c11778 EFLAGS: 00010046 RAX: 0000000000000000 RBX: ffff880076f3df08 RCX: 0000000000000000 RDX: 0000000000000006 RSI: ffff880074271888 RDI: ffff8800746c4f08 RBP: ffff880079c11778 R08: 0000000000000078 R09: 0000000000000001 R10: 09f911029d74e35b R11: 09f911029d74e35b R12: ffff880076f337f0 R13: ffff8800746c4f08 R14: ffff8800746c4f08 R15: 0000000000000002 FS: 00007f62fd44f700(0000) GS:ffff88007cd80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffff8800746c4f0c CR3: 0000000076c21000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process init (pid: 1, threadinfo ffff880079c10000, task ffff880079c0a040) Stack: ffff880079c117c8 ffffffff812683d8 ffff880079c117a8 ffffffff8125de43 ffff8800744fcf48 ffff880074b43e98 ffff8800770c8828 ffff880074b43e98 0000000000000003 0000000000000000 ffff880079c117f8 ffffffff81254149 Call Trace: [] cfq_insert_request+0x3f5/0x47c [] ? blk_recount_segments+0x20/0x31 [] __elv_add_request+0x1ca/0x200 [] blk_queue_bio+0x2ef/0x312 [] generic_make_request+0x9f/0xe0 [] submit_bio+0xbf/0xca [] submit_bh+0xdf/0xfe [] ext3_bread+0x50/0x99 [] dx_probe+0x38/0x291 [] ext3_dx_find_entry+0x58/0x219 [] ext3_find_entry+0xb0/0x406 [] ? cache_alloc_debugcheck_after.isra.46+0x14d/0x1a0 [] ? kmem_cache_alloc+0xef/0x191 [] ext3_lookup+0x39/0xe1 [] d_alloc_and_lookup+0x45/0x6c [] do_lookup+0x1e4/0x2f5 [] link_path_walk+0x1a4/0x6ef [] path_lookupat+0x59/0x5ea [] ? __strncpy_from_user+0x30/0x5a [] do_path_lookup+0x23/0x59 [] user_path_at_empty+0x53/0x99 [] ? remove_wait_queue+0x51/0x56 [] user_path_at+0x11/0x13 [] vfs_fstatat+0x3a/0x64 [] vfs_stat+0x1b/0x1d [] sys_newstat+0x1a/0x33 [] ? task_stopped_code+0x42/0x42 [] system_call_fastpath+0x16/0x1b Code: 89 e6 48 89 c7 e8 fa ca fe ff 85 c0 74 06 4c 89 2b 41 b6 01 5b 44 89 f0 41 5c 41 5d 41 5e 5d c3 55 48 89 e5 66 66 66 66 90 31 c0 <8b> 57 04 f6 c6 01 74 0b 83 e2 20 83 fa 01 19 c0 83 c0 02 5d c3 RIP [] cfqq_type+0xb/0x20 RSP CR2: ffff8800746c4f0c Get rid of the caching of cfqd->active_queue, and reorder the check so that it happens before we expire the active queue. Thanks to Tejun for pin pointing the error location. Reported-by: Chris Mason Tested-by: Chris Mason Signed-off-by: Jens Axboe diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 163263d..ee55019 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3117,18 +3117,17 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, */ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { - struct cfq_queue *old_cfqq = cfqd->active_queue; - cfq_log_cfqq(cfqd, cfqq, "preempt"); - cfq_slice_expired(cfqd, 1); /* * workload type is changed, don't save slice, otherwise preempt * doesn't happen */ - if (cfqq_type(old_cfqq) != cfqq_type(cfqq)) + if (cfqq_type(cfqd->active_queue) != cfqq_type(cfqq)) cfqq->cfqg->saved_workload_slice = 0; + cfq_slice_expired(cfqd, 1); + /* * Put the new queue at the front of the of the current list, * so we know that it will be selected next. -- cgit v0.10.2 From 36c92474498ad6cc3afb24b91a67a444d79978fe Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 17 Jan 2012 07:57:56 +0000 Subject: net: WARN if skb_checksum_help() is called on skb requiring segmentation skb_checksum_help() has never done anything useful with skbs that require segmentation. Setting skb->ip_summed = CHECKSUM_NONE makes them invalid and provokes a later WARNing in skb_gso_segment(). Passing such an skb to skb_checksum_help() indicates a bug, so we should warn about it immediately. Move the warning from skb_gso_segment() into a shared function, and add gso_type and gso_size to it. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 7e6b7dc..17db2f2 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1887,6 +1887,22 @@ void skb_set_dev(struct sk_buff *skb, struct net_device *dev) EXPORT_SYMBOL(skb_set_dev); #endif /* CONFIG_NET_NS */ +static void skb_warn_bad_offload(const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + const char *driver = ""; + + if (dev && dev->dev.parent) + driver = dev_driver_string(dev->dev.parent); + + WARN(1, "%s: caps=(%pNF, %pNF) len=%d data_len=%d gso_size=%d " + "gso_type=%d ip_summed=%d\n", + driver, dev ? &dev->features : NULL, + skb->sk ? &skb->sk->sk_route_caps : NULL, + skb->len, skb->data_len, skb_shinfo(skb)->gso_size, + skb_shinfo(skb)->gso_type, skb->ip_summed); +} + /* * Invalidate hardware checksum when packet is to be mangled, and * complete checksum manually on outgoing path. @@ -1900,8 +1916,8 @@ int skb_checksum_help(struct sk_buff *skb) goto out_set_summed; if (unlikely(skb_shinfo(skb)->gso_size)) { - /* Let GSO fix up the checksum. */ - goto out_set_summed; + skb_warn_bad_offload(skb); + return -EINVAL; } offset = skb_checksum_start_offset(skb); @@ -1961,16 +1977,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, __skb_pull(skb, skb->mac_len); if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { - struct net_device *dev = skb->dev; - const char *driver = ""; - - if (dev && dev->dev.parent) - driver = dev_driver_string(dev->dev.parent); - - WARN(1, "%s: caps=(%pNF, %pNF) len=%d data_len=%d ip_summed=%d\n", - driver, dev ? &dev->features : NULL, - skb->sk ? &skb->sk->sk_route_caps : NULL, - skb->len, skb->data_len, skb->ip_summed); + skb_warn_bad_offload(skb); if (skb_header_cloned(skb) && (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) -- cgit v0.10.2 From 65e9d2faab70d07b9a38ac6ed298f191d24541fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 17 Jan 2012 10:00:40 +0000 Subject: net: fix NULL-deref in WARN() in skb_gso_segment() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug was introduced in commit c8f44affb7244f2ac3e703cab13d55ede27621bb. Signed-off-by: MichaÅ‚ MirosÅ‚aw Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 17db2f2..115dee1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1889,6 +1889,7 @@ EXPORT_SYMBOL(skb_set_dev); static void skb_warn_bad_offload(const struct sk_buff *skb) { + static const netdev_features_t null_features = 0; struct net_device *dev = skb->dev; const char *driver = ""; @@ -1897,8 +1898,8 @@ static void skb_warn_bad_offload(const struct sk_buff *skb) WARN(1, "%s: caps=(%pNF, %pNF) len=%d data_len=%d gso_size=%d " "gso_type=%d ip_summed=%d\n", - driver, dev ? &dev->features : NULL, - skb->sk ? &skb->sk->sk_route_caps : NULL, + driver, dev ? &dev->features : &null_features, + skb->sk ? &skb->sk->sk_route_caps : &null_features, skb->len, skb->data_len, skb_shinfo(skb)->gso_size, skb_shinfo(skb)->gso_type, skb->ip_summed); } -- cgit v0.10.2 From 10ec1bb7e9eb462548f14dd53c73e927a3ddf31c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 Jan 2012 10:48:43 +0000 Subject: inetpeer: initialize ->redirect_genid in inet_getpeer() kmemcheck complains that ->redirect_genid doesn't get initialized. Presumably it should be set to zero. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 986a02b..bf4a9c4 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -447,6 +447,7 @@ relookup: p->rate_last = 0; p->pmtu_expires = 0; p->pmtu_orig = 0; + p->redirect_genid = 0; memset(&p->redirect_learned, 0, sizeof(p->redirect_learned)); -- cgit v0.10.2 From 8096b1ebb59b94b3bc6abb6b7d121419e83447ba Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:07 +0000 Subject: xfs: remove the if_ext_max field in struct xfs_ifork We spent a lot of effort to maintain this field, but it always equals to the fork size divided by the constant size of an extent. The prime use of it is to assert that the two stay in sync. Just divide the fork size by the extent size in the few places that we actually use it and remove the overhead of maintaining it. Also introduce a few helpers to consolidate the places where we actually care about the value. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index c1b55e5..d25eafd 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -271,10 +271,6 @@ xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff) dp = args->dp; mp = dp->i_mount; dp->i_d.di_forkoff = forkoff; - dp->i_df.if_ext_max = - XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); - dp->i_afp->if_ext_max = - XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); ifp = dp->i_afp; ASSERT(ifp->if_flags & XFS_IFINLINE); @@ -326,7 +322,6 @@ xfs_attr_fork_reset( ASSERT(ip->i_d.di_anextents == 0); ASSERT(ip->i_afp == NULL); - ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); } @@ -389,10 +384,6 @@ xfs_attr_shortform_remove(xfs_da_args_t *args) (args->op_flags & XFS_DA_OP_ADDNAME) || !(mp->m_flags & XFS_MOUNT_ATTR2) || dp->i_d.di_format == XFS_DINODE_FMT_BTREE); - dp->i_afp->if_ext_max = - XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); - dp->i_df.if_ext_max = - XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t); xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); } diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index d0ab788..7a888ca 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -249,7 +249,27 @@ xfs_bmbt_lookup_ge( } /* -* Update the record referred to by cur to the value given + * Check if the inode needs to be converted to btree format. + */ +static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork) +{ + return XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS && + XFS_IFORK_NEXTENTS(ip, whichfork) > + XFS_IFORK_MAXEXT(ip, whichfork); +} + +/* + * Check if the inode should be converted to extent format. + */ +static inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork) +{ + return XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE && + XFS_IFORK_NEXTENTS(ip, whichfork) <= + XFS_IFORK_MAXEXT(ip, whichfork); +} + +/* + * Update the record referred to by cur to the value given * by [off, bno, len, state]. * This either works (return 0) or gets an EFSCORRUPTED error. */ @@ -683,8 +703,8 @@ xfs_bmap_add_extent_delay_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - if (bma->ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && - bma->ip->i_d.di_nextents > bma->ip->i_df.if_ext_max) { + + if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) { error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, bma->firstblock, bma->flist, &bma->cur, 1, &tmp_rval, XFS_DATA_FORK); @@ -767,8 +787,8 @@ xfs_bmap_add_extent_delay_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - if (bma->ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && - bma->ip->i_d.di_nextents > bma->ip->i_df.if_ext_max) { + + if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) { error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, bma->firstblock, bma->flist, &bma->cur, 1, &tmp_rval, XFS_DATA_FORK); @@ -836,8 +856,8 @@ xfs_bmap_add_extent_delay_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - if (bma->ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && - bma->ip->i_d.di_nextents > bma->ip->i_df.if_ext_max) { + + if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) { error = xfs_bmap_extents_to_btree(bma->tp, bma->ip, bma->firstblock, bma->flist, &bma->cur, 1, &tmp_rval, XFS_DATA_FORK); @@ -884,8 +904,7 @@ xfs_bmap_add_extent_delay_real( } /* convert to a btree if necessary */ - if (XFS_IFORK_FORMAT(bma->ip, XFS_DATA_FORK) == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(bma->ip, XFS_DATA_FORK) > ifp->if_ext_max) { + if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) { int tmp_logflags; /* partial log flag return val */ ASSERT(bma->cur == NULL); @@ -1421,8 +1440,7 @@ xfs_bmap_add_extent_unwritten_real( } /* convert to a btree if necessary */ - if (XFS_IFORK_FORMAT(ip, XFS_DATA_FORK) == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) > ifp->if_ext_max) { + if (xfs_bmap_needs_btree(ip, XFS_DATA_FORK)) { int tmp_logflags; /* partial log flag return val */ ASSERT(cur == NULL); @@ -1812,8 +1830,7 @@ xfs_bmap_add_extent_hole_real( } /* convert to a btree if necessary */ - if (XFS_IFORK_FORMAT(bma->ip, whichfork) == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(bma->ip, whichfork) > ifp->if_ext_max) { + if (xfs_bmap_needs_btree(bma->ip, whichfork)) { int tmp_logflags; /* partial log flag return val */ ASSERT(bma->cur == NULL); @@ -3037,8 +3054,7 @@ xfs_bmap_extents_to_btree( ifp = XFS_IFORK_PTR(ip, whichfork); ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS); - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); + /* * Make space in the inode incore. */ @@ -3184,13 +3200,8 @@ xfs_bmap_forkoff_reset( ip->i_d.di_format != XFS_DINODE_FMT_BTREE) { uint dfl_forkoff = xfs_default_attroffset(ip) >> 3; - if (dfl_forkoff > ip->i_d.di_forkoff) { + if (dfl_forkoff > ip->i_d.di_forkoff) ip->i_d.di_forkoff = dfl_forkoff; - ip->i_df.if_ext_max = - XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t); - ip->i_afp->if_ext_max = - XFS_IFORK_ASIZE(ip) / sizeof(xfs_bmbt_rec_t); - } } } @@ -3430,8 +3441,6 @@ xfs_bmap_add_attrfork( int error; /* error return value */ ASSERT(XFS_IFORK_Q(ip) == 0); - ASSERT(ip->i_df.if_ext_max == - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); mp = ip->i_mount; ASSERT(!XFS_NOT_DQATTACHED(mp, ip)); @@ -3486,12 +3495,9 @@ xfs_bmap_add_attrfork( error = XFS_ERROR(EINVAL); goto error1; } - ip->i_df.if_ext_max = - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); + ASSERT(ip->i_afp == NULL); ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP); - ip->i_afp->if_ext_max = - XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); ip->i_afp->if_flags = XFS_IFEXTENTS; logflags = 0; xfs_bmap_init(&flist, &firstblock); @@ -3535,20 +3541,17 @@ xfs_bmap_add_attrfork( } else spin_unlock(&mp->m_sb_lock); } - if ((error = xfs_bmap_finish(&tp, &flist, &committed))) + + error = xfs_bmap_finish(&tp, &flist, &committed); + if (error) goto error2; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); - ASSERT(ip->i_df.if_ext_max == - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); - return error; + return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); error2: xfs_bmap_cancel(&flist); error1: xfs_iunlock(ip, XFS_ILOCK_EXCL); error0: xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); - ASSERT(ip->i_df.if_ext_max == - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); return error; } @@ -4379,8 +4382,6 @@ xfs_bmapi_read( XFS_STATS_INC(xs_blk_mapr); ifp = XFS_IFORK_PTR(ip, whichfork); - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); if (!(ifp->if_flags & XFS_IFEXTENTS)) { error = xfs_iread_extents(NULL, ip, whichfork); @@ -4871,8 +4872,6 @@ xfs_bmapi_write( return XFS_ERROR(EIO); ifp = XFS_IFORK_PTR(ip, whichfork); - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); XFS_STATS_INC(xs_blk_mapw); @@ -4981,8 +4980,7 @@ xfs_bmapi_write( /* * Transform from btree to extents, give it cur. */ - if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE && - XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max) { + if (xfs_bmap_wants_extents(ip, whichfork)) { int tmp_logflags = 0; ASSERT(bma.cur); @@ -4992,10 +4990,10 @@ xfs_bmapi_write( if (error) goto error0; } - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); + ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE || - XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max); + XFS_IFORK_NEXTENTS(ip, whichfork) > + XFS_IFORK_MAXEXT(ip, whichfork)); error = 0; error0: /* @@ -5095,8 +5093,7 @@ xfs_bunmapi( ASSERT(len > 0); ASSERT(nexts >= 0); - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); + if (!(ifp->if_flags & XFS_IFEXTENTS) && (error = xfs_iread_extents(tp, ip, whichfork))) return error; @@ -5322,7 +5319,8 @@ xfs_bunmapi( */ if (!wasdel && xfs_trans_get_block_res(tp) == 0 && XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(ip, whichfork) >= ifp->if_ext_max && + XFS_IFORK_NEXTENTS(ip, whichfork) >= /* Note the >= */ + XFS_IFORK_MAXEXT(ip, whichfork) && del.br_startoff > got.br_startoff && del.br_startoff + del.br_blockcount < got.br_startoff + got.br_blockcount) { @@ -5353,13 +5351,11 @@ nodelete: } } *done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0; - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); + /* * Convert to a btree if necessary. */ - if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max) { + if (xfs_bmap_needs_btree(ip, whichfork)) { ASSERT(cur == NULL); error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist, &cur, 0, &tmp_logflags, whichfork); @@ -5370,8 +5366,7 @@ nodelete: /* * transform from btree to extents, give it cur */ - else if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE && - XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max) { + else if (xfs_bmap_wants_extents(ip, whichfork)) { ASSERT(cur != NULL); error = xfs_bmap_btree_to_extents(tp, ip, cur, &tmp_logflags, whichfork); @@ -5382,8 +5377,6 @@ nodelete: /* * transform from extents to local? */ - ASSERT(ifp->if_ext_max == - XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); error = 0; error0: /* diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index 654dc6f..dd974a5 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -163,12 +163,14 @@ xfs_swap_extents_check_format( /* Check temp in extent form to max in target */ if (tip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) > ip->i_df.if_ext_max) + XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) > + XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)) return EINVAL; /* Check target in extent form to max in temp */ if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && - XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) > tip->i_df.if_ext_max) + XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) > + XFS_IFORK_MAXEXT(tip, XFS_DATA_FORK)) return EINVAL; /* @@ -180,18 +182,25 @@ xfs_swap_extents_check_format( * (a common defrag case) which will occur when the temp inode is in * extent format... */ - if (tip->i_d.di_format == XFS_DINODE_FMT_BTREE && - ((XFS_IFORK_BOFF(ip) && - tip->i_df.if_broot_bytes > XFS_IFORK_BOFF(ip)) || - XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) <= ip->i_df.if_ext_max)) - return EINVAL; + if (tip->i_d.di_format == XFS_DINODE_FMT_BTREE) { + if (XFS_IFORK_BOFF(ip) && + tip->i_df.if_broot_bytes > XFS_IFORK_BOFF(ip)) + return EINVAL; + if (XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK) <= + XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)) + return EINVAL; + } /* Reciprocal target->temp btree format checks */ - if (ip->i_d.di_format == XFS_DINODE_FMT_BTREE && - ((XFS_IFORK_BOFF(tip) && - ip->i_df.if_broot_bytes > XFS_IFORK_BOFF(tip)) || - XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) <= tip->i_df.if_ext_max)) - return EINVAL; + if (ip->i_d.di_format == XFS_DINODE_FMT_BTREE) { + if (XFS_IFORK_BOFF(tip) && + ip->i_df.if_broot_bytes > XFS_IFORK_BOFF(tip)) + return EINVAL; + + if (XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK) <= + XFS_IFORK_MAXEXT(tip, XFS_DATA_FORK)) + return EINVAL; + } return 0; } @@ -349,16 +358,6 @@ xfs_swap_extents( *tifp = *tempifp; /* struct copy */ /* - * Fix the in-memory data fork values that are dependent on the fork - * offset in the inode. We can't assume they remain the same as attr2 - * has dynamic fork offsets. - */ - ifp->if_ext_max = XFS_IFORK_SIZE(ip, XFS_DATA_FORK) / - (uint)sizeof(xfs_bmbt_rec_t); - tifp->if_ext_max = XFS_IFORK_SIZE(tip, XFS_DATA_FORK) / - (uint)sizeof(xfs_bmbt_rec_t); - - /* * Fix the on-disk inode values */ tmp = (__uint64_t)ip->i_d.di_nblocks; diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 3960a06..f180ce8 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -450,8 +450,6 @@ again: *ipp = ip; - ASSERT(ip->i_df.if_ext_max == - XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t)); /* * If we have a real type for an on-disk inode, we can set ops(&unlock) * now. If it's a new inode being created, xfs_ialloc will handle it. diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index ccd619a..96b29e3 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -299,11 +299,8 @@ xfs_iformat( { xfs_attr_shortform_t *atp; int size; - int error; + int error = 0; xfs_fsize_t di_size; - ip->i_df.if_ext_max = - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); - error = 0; if (unlikely(be32_to_cpu(dip->di_nextents) + be16_to_cpu(dip->di_anextents) > @@ -409,10 +406,10 @@ xfs_iformat( } if (!XFS_DFORK_Q(dip)) return 0; + ASSERT(ip->i_afp == NULL); ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP | KM_NOFS); - ip->i_afp->if_ext_max = - XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); + switch (dip->di_aformat) { case XFS_DINODE_FMT_LOCAL: atp = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip); @@ -604,10 +601,11 @@ xfs_iformat_btree( * or the number of extents is greater than the number of * blocks. */ - if (unlikely(XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max - || XFS_BMDR_SPACE_CALC(nrecs) > - XFS_DFORK_SIZE(dip, ip->i_mount, whichfork) - || XFS_IFORK_NEXTENTS(ip, whichfork) > ip->i_d.di_nblocks)) { + if (unlikely(XFS_IFORK_NEXTENTS(ip, whichfork) <= + XFS_IFORK_MAXEXT(ip, whichfork) || + XFS_BMDR_SPACE_CALC(nrecs) > + XFS_DFORK_SIZE(dip, ip->i_mount, whichfork) || + XFS_IFORK_NEXTENTS(ip, whichfork) > ip->i_d.di_nblocks)) { xfs_warn(ip->i_mount, "corrupt inode %Lu (btree).", (unsigned long long) ip->i_ino); XFS_CORRUPTION_ERROR("xfs_iformat_btree", XFS_ERRLEVEL_LOW, @@ -835,12 +833,6 @@ xfs_iread( * with the uninitialized part of it. */ ip->i_d.di_mode = 0; - /* - * Initialize the per-fork minima and maxima for a new - * inode here. xfs_iformat will do it for old inodes. - */ - ip->i_df.if_ext_max = - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); } /* @@ -1740,8 +1732,6 @@ xfs_ifree( ip->i_d.di_flags = 0; ip->i_d.di_dmevmask = 0; ip->i_d.di_forkoff = 0; /* mark the attr fork not in use */ - ip->i_df.if_ext_max = - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS; ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS; /* @@ -2408,7 +2398,7 @@ xfs_iflush( ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); ASSERT(!completion_done(&ip->i_flush)); ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE || - ip->i_d.di_nextents > ip->i_df.if_ext_max); + ip->i_d.di_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); iip = ip->i_itemp; mp = ip->i_mount; @@ -2524,7 +2514,7 @@ xfs_iflush_int( ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); ASSERT(!completion_done(&ip->i_flush)); ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE || - ip->i_d.di_nextents > ip->i_df.if_ext_max); + ip->i_d.di_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); iip = ip->i_itemp; mp = ip->i_mount; diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index c157472..47497e1 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -66,7 +66,6 @@ typedef struct xfs_ifork { struct xfs_btree_block *if_broot; /* file's incore btree root */ short if_broot_bytes; /* bytes allocated for root */ unsigned char if_flags; /* per-fork flags */ - unsigned char if_ext_max; /* max # of extent records */ union { xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */ xfs_ext_irec_t *if_ext_irec; /* irec map file exts */ @@ -206,7 +205,8 @@ typedef struct xfs_icdinode { ((w) == XFS_DATA_FORK ? \ ((ip)->i_d.di_nextents = (n)) : \ ((ip)->i_d.di_anextents = (n))) - +#define XFS_IFORK_MAXEXT(ip, w) \ + (XFS_IFORK_SIZE(ip, w) / sizeof(xfs_bmbt_rec_t)) #ifdef __KERNEL__ diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index cfd6c7f..2b6b4fc 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -79,8 +79,6 @@ xfs_inode_item_size( break; case XFS_DINODE_FMT_BTREE: - ASSERT(ip->i_df.if_ext_max == - XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); iip->ili_format.ilf_fields &= ~(XFS_ILOG_DDATA | XFS_ILOG_DEXT | XFS_ILOG_DEV | XFS_ILOG_UUID); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 297f9fa..81efa04 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1568,7 +1568,6 @@ DECLARE_EVENT_CLASS(xfs_swap_extent_class, __field(xfs_ino_t, ino) __field(int, format) __field(int, nex) - __field(int, max_nex) __field(int, broot_size) __field(int, fork_off) ), @@ -1578,18 +1577,16 @@ DECLARE_EVENT_CLASS(xfs_swap_extent_class, __entry->ino = ip->i_ino; __entry->format = ip->i_d.di_format; __entry->nex = ip->i_d.di_nextents; - __entry->max_nex = ip->i_df.if_ext_max; __entry->broot_size = ip->i_df.if_broot_bytes; __entry->fork_off = XFS_IFORK_BOFF(ip); ), TP_printk("dev %d:%d ino 0x%llx (%s), %s format, num_extents %d, " - "Max in-fork extents %d, broot size %d, fork offset %d", + "broot size %d, fork offset %d", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __print_symbolic(__entry->which, XFS_SWAPEXT_INODES), __print_symbolic(__entry->format, XFS_INODE_FORMAT_STR), __entry->nex, - __entry->max_nex, __entry->broot_size, __entry->fork_off) ) -- cgit v0.10.2 From 49e4c70e52a2bc2090e5a4e003e2888af21d6a2b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:08 +0000 Subject: xfs: make i_flags an unsigned long To be used for bit wakeup i_flags needs to be an unsigned long or we'll run into trouble on big endian systems. Because of the 1-byte i_update field right after it this actually causes a fairly large size increase on its own (4 or 8 bytes), but that increase will be more than offset by the next two patches. Signed-off-by: Christoph Hellwig Reviewed-by: Alex Elder Reviewed-by: Dave Chinner Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 47497e1..be8dc0c 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -242,7 +242,7 @@ typedef struct xfs_inode { wait_queue_head_t i_ipin_wait; /* inode pinning wait queue */ spinlock_t i_flags_lock; /* inode i_flags lock */ /* Miscellaneous state. */ - unsigned short i_flags; /* see defined flags below */ + unsigned long i_flags; /* see defined flags below */ unsigned char i_update_core; /* timestamps/size is dirty */ unsigned int i_delayed_blks; /* count of delay alloc blks */ -- cgit v0.10.2 From 474fce067521a40dbacc722e8ba119e81c2d31bf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:09 +0000 Subject: xfs: replace i_flock with a sleeping bitlock We almost never block on i_flock, the exception is synchronous inode flushing. Instead of bloating the inode with a 16/24-byte completion that we abuse as a semaphore just implement it as a bitlock that uses a bit waitqueue for the rare sleeping path. This primarily is a tradeoff between a much smaller inode and a faster non-blocking path vs faster wakeups, and we are much better off with the former. A small downside is that we will lose lockdep checking for i_flock, but given that it's always taken inside the ilock that should be acceptable. Note that for example the inode writeback locking is implemented in a very similar way. Signed-off-by: Christoph Hellwig Reviewed-by: Alex Elder Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index f180ce8..a7cf713 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -77,7 +77,7 @@ xfs_inode_alloc( ASSERT(atomic_read(&ip->i_pincount) == 0); ASSERT(!spin_is_locked(&ip->i_flags_lock)); - ASSERT(completion_done(&ip->i_flush)); + ASSERT(!xfs_isiflocked(ip)); ASSERT(ip->i_ino == 0); mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino); @@ -150,7 +150,7 @@ xfs_inode_free( /* asserts to verify all state is correct here */ ASSERT(atomic_read(&ip->i_pincount) == 0); ASSERT(!spin_is_locked(&ip->i_flags_lock)); - ASSERT(completion_done(&ip->i_flush)); + ASSERT(!xfs_isiflocked(ip)); /* * Because we use RCU freeing we need to ensure the inode always @@ -713,3 +713,19 @@ xfs_isilocked( return 0; } #endif + +void +__xfs_iflock( + struct xfs_inode *ip) +{ + wait_queue_head_t *wq = bit_waitqueue(&ip->i_flags, __XFS_IFLOCK_BIT); + DEFINE_WAIT_BIT(wait, &ip->i_flags, __XFS_IFLOCK_BIT); + + do { + prepare_to_wait_exclusive(wq, &wait.wait, TASK_UNINTERRUPTIBLE); + if (xfs_isiflocked(ip)) + io_schedule(); + } while (!xfs_iflock_nowait(ip)); + + finish_wait(wq, &wait.wait); +} diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 96b29e3..eeb60d3 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2396,7 +2396,7 @@ xfs_iflush( XFS_STATS_INC(xs_iflush_count); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); - ASSERT(!completion_done(&ip->i_flush)); + ASSERT(xfs_isiflocked(ip)); ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE || ip->i_d.di_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); @@ -2512,7 +2512,7 @@ xfs_iflush_int( #endif ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); - ASSERT(!completion_done(&ip->i_flush)); + ASSERT(xfs_isiflocked(ip)); ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE || ip->i_d.di_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index be8dc0c..960d2a8 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -237,7 +237,6 @@ typedef struct xfs_inode { struct xfs_inode_log_item *i_itemp; /* logging information */ mrlock_t i_lock; /* inode lock */ mrlock_t i_iolock; /* inode IO lock */ - struct completion i_flush; /* inode flush completion q */ atomic_t i_pincount; /* inode pin count */ wait_queue_head_t i_ipin_wait; /* inode pinning wait queue */ spinlock_t i_flags_lock; /* inode i_flags lock */ @@ -324,6 +323,19 @@ xfs_iflags_test_and_clear(xfs_inode_t *ip, unsigned short flags) return ret; } +static inline int +xfs_iflags_test_and_set(xfs_inode_t *ip, unsigned short flags) +{ + int ret; + + spin_lock(&ip->i_flags_lock); + ret = ip->i_flags & flags; + if (!ret) + ip->i_flags |= flags; + spin_unlock(&ip->i_flags_lock); + return ret; +} + /* * Project quota id helpers (previously projid was 16bit only * and using two 16bit values to hold new 32bit projid was chosen @@ -344,35 +356,17 @@ xfs_set_projid(struct xfs_inode *ip, } /* - * Manage the i_flush queue embedded in the inode. This completion - * queue synchronizes processes attempting to flush the in-core - * inode back to disk. - */ -static inline void xfs_iflock(xfs_inode_t *ip) -{ - wait_for_completion(&ip->i_flush); -} - -static inline int xfs_iflock_nowait(xfs_inode_t *ip) -{ - return try_wait_for_completion(&ip->i_flush); -} - -static inline void xfs_ifunlock(xfs_inode_t *ip) -{ - complete(&ip->i_flush); -} - -/* * In-core inode flags. */ -#define XFS_IRECLAIM 0x0001 /* started reclaiming this inode */ -#define XFS_ISTALE 0x0002 /* inode has been staled */ -#define XFS_IRECLAIMABLE 0x0004 /* inode can be reclaimed */ -#define XFS_INEW 0x0008 /* inode has just been allocated */ -#define XFS_IFILESTREAM 0x0010 /* inode is in a filestream directory */ -#define XFS_ITRUNCATED 0x0020 /* truncated down so flush-on-close */ -#define XFS_IDIRTY_RELEASE 0x0040 /* dirty release already seen */ +#define XFS_IRECLAIM (1 << 0) /* started reclaiming this inode */ +#define XFS_ISTALE (1 << 1) /* inode has been staled */ +#define XFS_IRECLAIMABLE (1 << 2) /* inode can be reclaimed */ +#define XFS_INEW (1 << 3) /* inode has just been allocated */ +#define XFS_IFILESTREAM (1 << 4) /* inode is in a filestream dir. */ +#define XFS_ITRUNCATED (1 << 5) /* truncated down so flush-on-close */ +#define XFS_IDIRTY_RELEASE (1 << 6) /* dirty release already seen */ +#define __XFS_IFLOCK_BIT 7 /* inode is being flushed right now */ +#define XFS_IFLOCK (1 << __XFS_IFLOCK_BIT) /* * Per-lifetime flags need to be reset when re-using a reclaimable inode during @@ -385,6 +379,34 @@ static inline void xfs_ifunlock(xfs_inode_t *ip) XFS_IFILESTREAM); /* + * Synchronize processes attempting to flush the in-core inode back to disk. + */ + +extern void __xfs_iflock(struct xfs_inode *ip); + +static inline int xfs_iflock_nowait(struct xfs_inode *ip) +{ + return !xfs_iflags_test_and_set(ip, XFS_IFLOCK); +} + +static inline void xfs_iflock(struct xfs_inode *ip) +{ + if (!xfs_iflock_nowait(ip)) + __xfs_iflock(ip); +} + +static inline void xfs_ifunlock(struct xfs_inode *ip) +{ + xfs_iflags_clear(ip, XFS_IFLOCK); + wake_up_bit(&ip->i_flags, __XFS_IFLOCK_BIT); +} + +static inline int xfs_isiflocked(struct xfs_inode *ip) +{ + return xfs_iflags_test(ip, XFS_IFLOCK); +} + +/* * Flags for inode locking. * Bit ranges: 1<<1 - 1<<16-1 -- iolock/ilock modes (bitfield) * 1<<16 - 1<<32-1 -- lockdep annotation (integers) diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 2b6b4fc..c8d4ce0 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -717,7 +717,7 @@ xfs_inode_item_pushbuf( * If a flush is not in progress anymore, chances are that the * inode was taken off the AIL. So, just get out. */ - if (completion_done(&ip->i_flush) || + if (!xfs_isiflocked(ip) || !(lip->li_flags & XFS_LI_IN_AIL)) { xfs_iunlock(ip, XFS_ILOCK_SHARED); return true; @@ -750,7 +750,7 @@ xfs_inode_item_push( struct xfs_inode *ip = iip->ili_inode; ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED)); - ASSERT(!completion_done(&ip->i_flush)); + ASSERT(xfs_isiflocked(ip)); /* * Since we were able to lock the inode's flush lock and diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 281961c..6851fa7 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -829,13 +829,6 @@ xfs_fs_inode_init_once( atomic_set(&ip->i_pincount, 0); spin_lock_init(&ip->i_flags_lock); init_waitqueue_head(&ip->i_ipin_wait); - /* - * Because we want to use a counting completion, complete - * the flush completion once to allow a single access to - * the flush completion without blocking. - */ - init_completion(&ip->i_flush); - complete(&ip->i_flush); mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER, "xfsino", ip->i_ino); diff --git a/fs/xfs/xfs_sync.c b/fs/xfs/xfs_sync.c index 72c01a1..40b75ee 100644 --- a/fs/xfs/xfs_sync.c +++ b/fs/xfs/xfs_sync.c @@ -707,14 +707,13 @@ xfs_reclaim_inode_grab( return 1; /* - * do some unlocked checks first to avoid unnecessary lock traffic. - * The first is a flush lock check, the second is a already in reclaim - * check. Only do these checks if we are not going to block on locks. + * If we are asked for non-blocking operation, do unlocked checks to + * see if the inode already is being flushed or in reclaim to avoid + * lock traffic. */ if ((flags & SYNC_TRYLOCK) && - (!ip->i_flush.done || __xfs_iflags_test(ip, XFS_IRECLAIM))) { + __xfs_iflags_test(ip, XFS_IFLOCK | XFS_IRECLAIM)) return 1; - } /* * The radix tree lock here protects a thread in xfs_iget from racing -- cgit v0.10.2 From f392e6319a4e9a028b0c8b48f000bb01d660ad53 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:10 +0000 Subject: xfs: replace i_pin_wait with a bit waitqueue Replace i_pin_wait, which is only used during synchronous inode flushing with a bit waitqueue. This trades off a much smaller inode against slightly slower wakeup performance, and saves 12 (32-bit) or 20 (64-bit) bytes in the XFS inode. Reviewed-by: Alex Elder Reviewed-by: Dave Chinner Signed-off-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index eeb60d3..6260336 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2037,7 +2037,7 @@ xfs_idestroy_fork( * once someone is waiting for it to be unpinned. */ static void -xfs_iunpin_nowait( +xfs_iunpin( struct xfs_inode *ip) { ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); @@ -2049,14 +2049,29 @@ xfs_iunpin_nowait( } +static void +__xfs_iunpin_wait( + struct xfs_inode *ip) +{ + wait_queue_head_t *wq = bit_waitqueue(&ip->i_flags, __XFS_IPINNED_BIT); + DEFINE_WAIT_BIT(wait, &ip->i_flags, __XFS_IPINNED_BIT); + + xfs_iunpin(ip); + + do { + prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE); + if (xfs_ipincount(ip)) + io_schedule(); + } while (xfs_ipincount(ip)); + finish_wait(wq, &wait.wait); +} + void xfs_iunpin_wait( struct xfs_inode *ip) { - if (xfs_ipincount(ip)) { - xfs_iunpin_nowait(ip); - wait_event(ip->i_ipin_wait, (xfs_ipincount(ip) == 0)); - } + if (xfs_ipincount(ip)) + __xfs_iunpin_wait(ip); } /* @@ -2415,7 +2430,7 @@ xfs_iflush( * out for us if they occur after the log force completes. */ if (!(flags & SYNC_WAIT) && xfs_ipincount(ip)) { - xfs_iunpin_nowait(ip); + xfs_iunpin(ip); xfs_ifunlock(ip); return EAGAIN; } diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 960d2a8..4acbe74 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -238,7 +238,6 @@ typedef struct xfs_inode { mrlock_t i_lock; /* inode lock */ mrlock_t i_iolock; /* inode IO lock */ atomic_t i_pincount; /* inode pin count */ - wait_queue_head_t i_ipin_wait; /* inode pinning wait queue */ spinlock_t i_flags_lock; /* inode i_flags lock */ /* Miscellaneous state. */ unsigned long i_flags; /* see defined flags below */ @@ -367,6 +366,8 @@ xfs_set_projid(struct xfs_inode *ip, #define XFS_IDIRTY_RELEASE (1 << 6) /* dirty release already seen */ #define __XFS_IFLOCK_BIT 7 /* inode is being flushed right now */ #define XFS_IFLOCK (1 << __XFS_IFLOCK_BIT) +#define __XFS_IPINNED_BIT 8 /* wakeup key for zero pin count */ +#define XFS_IPINNED (1 << __XFS_IPINNED_BIT) /* * Per-lifetime flags need to be reset when re-using a reclaimable inode during diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index c8d4ce0..91d71dc 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -555,7 +555,7 @@ xfs_inode_item_unpin( trace_xfs_inode_unpin(ip, _RET_IP_); ASSERT(atomic_read(&ip->i_pincount) > 0); if (atomic_dec_and_test(&ip->i_pincount)) - wake_up(&ip->i_ipin_wait); + wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT); } /* diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 6851fa7..ee5b695 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -828,7 +828,6 @@ xfs_fs_inode_init_once( /* xfs inode */ atomic_set(&ip->i_pincount, 0); spin_lock_init(&ip->i_flags_lock); - init_waitqueue_head(&ip->i_ipin_wait); mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER, "xfsino", ip->i_ino); -- cgit v0.10.2 From ce7ae151ddada3dbf67301464343c154903166b3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:11 +0000 Subject: xfs: remove the i_size field in struct xfs_inode There is no fundamental need to keep an in-memory inode size copy in the XFS inode. We already have the on-disk value in the dinode, and the separate in-memory copy that we need for regular files only in the XFS inode. Remove the xfs_inode i_size field and change the XFS_ISIZE macro to use the VFS inode i_size field for regular files. Switch code that was directly accessing the i_size field in the xfs_inode to XFS_ISIZE, or in cases where we are limited to regular files direct access of the VFS inode i_size field. This also allows dropping some fairly complicated code in the write path which dealt with keeping the xfs_inode i_size uptodate with the VFS i_size that is getting updated inside ->write_end. Note that we do not bother resetting the VFS i_size when truncating a file that gets freed to zero as there is no point in doing so because the VFS inode is no longer in use at this point. Just relax the assert in xfs_ifree to only check the on-disk size instead. Reviewed-by: Dave Chinner Signed-off-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 574d4ee..4d27ea1 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -111,7 +111,7 @@ xfs_ioend_new_eof( xfs_fsize_t bsize; bsize = ioend->io_offset + ioend->io_size; - isize = MAX(ip->i_size, ip->i_new_size); + isize = MAX(i_size_read(VFS_I(ip)), ip->i_new_size); isize = MIN(isize, bsize); return isize > ip->i_d.di_size ? isize : 0; } diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 7a888ca..188ef2f 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -3997,11 +3997,8 @@ xfs_bmap_one_block( xfs_bmbt_irec_t s; /* internal version of extent */ #ifndef DEBUG - if (whichfork == XFS_DATA_FORK) { - return S_ISREG(ip->i_d.di_mode) ? - (ip->i_size == ip->i_mount->m_sb.sb_blocksize) : - (ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize); - } + if (whichfork == XFS_DATA_FORK) + return XFS_ISIZE(ip) == ip->i_mount->m_sb.sb_blocksize; #endif /* !DEBUG */ if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1) return 0; @@ -4013,7 +4010,7 @@ xfs_bmap_one_block( xfs_bmbt_get_all(ep, &s); rval = s.br_startoff == 0 && s.br_blockcount == 1; if (rval && whichfork == XFS_DATA_FORK) - ASSERT(ip->i_size == ip->i_mount->m_sb.sb_blocksize); + ASSERT(XFS_ISIZE(ip) == ip->i_mount->m_sb.sb_blocksize); return rval; } @@ -5427,7 +5424,7 @@ xfs_getbmapx_fix_eof_hole( if (startblock == HOLESTARTBLOCK) { mp = ip->i_mount; out->bmv_block = -1; - fixlen = XFS_FSB_TO_BB(mp, XFS_B_TO_FSB(mp, ip->i_size)); + fixlen = XFS_FSB_TO_BB(mp, XFS_B_TO_FSB(mp, XFS_ISIZE(ip))); fixlen -= out->bmv_offset; if (prealloced && out->bmv_offset + out->bmv_length == end) { /* Came to hole at EOF. Trim it. */ @@ -5515,7 +5512,7 @@ xfs_getbmap( fixlen = XFS_MAXIOFFSET(mp); } else { prealloced = 0; - fixlen = ip->i_size; + fixlen = XFS_ISIZE(ip); } } @@ -5544,7 +5541,7 @@ xfs_getbmap( xfs_ilock(ip, XFS_IOLOCK_SHARED); if (whichfork == XFS_DATA_FORK && !(iflags & BMV_IF_DELALLOC)) { - if (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size) { + if (ip->i_delayed_blks || XFS_ISIZE(ip) > ip->i_d.di_size) { error = xfs_flush_pages(ip, 0, -1, 0, FI_REMAPF); if (error) goto out_unlock_iolock; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index f675f3d..86d5dc2 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -327,7 +327,7 @@ xfs_file_aio_read( mp->m_rtdev_targp : mp->m_ddev_targp; if ((iocb->ki_pos & target->bt_smask) || (size & target->bt_smask)) { - if (iocb->ki_pos == ip->i_size) + if (iocb->ki_pos == i_size_read(inode)) return 0; return -XFS_ERROR(EINVAL); } @@ -412,30 +412,6 @@ xfs_file_splice_read( return ret; } -STATIC void -xfs_aio_write_isize_update( - struct inode *inode, - loff_t *ppos, - ssize_t bytes_written) -{ - struct xfs_inode *ip = XFS_I(inode); - xfs_fsize_t isize = i_size_read(inode); - - if (bytes_written > 0) - XFS_STATS_ADD(xs_write_bytes, bytes_written); - - if (unlikely(bytes_written < 0 && bytes_written != -EFAULT && - *ppos > isize)) - *ppos = isize; - - if (*ppos > ip->i_size) { - xfs_rw_ilock(ip, XFS_ILOCK_EXCL); - if (*ppos > ip->i_size) - ip->i_size = *ppos; - xfs_rw_iunlock(ip, XFS_ILOCK_EXCL); - } -} - /* * If this was a direct or synchronous I/O that failed (such as ENOSPC) then * part of the I/O may have been written to disk before the error occurred. In @@ -451,8 +427,8 @@ xfs_aio_write_newsize_update( xfs_rw_ilock(ip, XFS_ILOCK_EXCL); if (new_size == ip->i_new_size) ip->i_new_size = 0; - if (ip->i_d.di_size > ip->i_size) - ip->i_d.di_size = ip->i_size; + if (ip->i_d.di_size > i_size_read(VFS_I(ip))) + ip->i_d.di_size = i_size_read(VFS_I(ip)); xfs_rw_iunlock(ip, XFS_ILOCK_EXCL); } } @@ -492,15 +468,16 @@ xfs_file_splice_write( new_size = *ppos + count; xfs_ilock(ip, XFS_ILOCK_EXCL); - if (new_size > ip->i_size) + if (new_size > i_size_read(inode)) ip->i_new_size = new_size; xfs_iunlock(ip, XFS_ILOCK_EXCL); trace_xfs_file_splice_write(ip, count, *ppos, ioflags); ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags); + if (ret > 0) + XFS_STATS_ADD(xs_write_bytes, ret); - xfs_aio_write_isize_update(inode, ppos, ret); xfs_aio_write_newsize_update(ip, new_size); xfs_iunlock(ip, XFS_IOLOCK_EXCL); return ret; @@ -728,14 +705,14 @@ restart: * values are still valid. */ if ((ip->i_new_size && *pos > ip->i_new_size) || - (!ip->i_new_size && *pos > ip->i_size)) { + (!ip->i_new_size && *pos > i_size_read(inode))) { if (*iolock == XFS_IOLOCK_SHARED) { xfs_rw_iunlock(ip, XFS_ILOCK_EXCL | *iolock); *iolock = XFS_IOLOCK_EXCL; xfs_rw_ilock(ip, XFS_ILOCK_EXCL | *iolock); goto restart; } - error = -xfs_zero_eof(ip, *pos, ip->i_size); + error = -xfs_zero_eof(ip, *pos, i_size_read(inode)); } /* @@ -744,7 +721,7 @@ restart: * ip->i_new_size if this IO ends beyond any other in-flight writes. */ new_size = *pos + *count; - if (new_size > ip->i_size) { + if (new_size > i_size_read(inode)) { if (new_size > ip->i_new_size) ip->i_new_size = new_size; *new_sizep = new_size; @@ -957,11 +934,11 @@ xfs_file_aio_write( ret = xfs_file_buffered_aio_write(iocb, iovp, nr_segs, pos, ocount, &new_size, &iolock); - xfs_aio_write_isize_update(inode, &iocb->ki_pos, ret); - if (ret <= 0) goto out_unlock; + XFS_STATS_ADD(xs_write_bytes, ret); + /* Handle various SYNC-type writes */ if ((file->f_flags & O_DSYNC) || IS_SYNC(inode)) { loff_t end = pos + ret - 1; diff --git a/fs/xfs/xfs_fs_subr.c b/fs/xfs/xfs_fs_subr.c index ed88ed1..652b875 100644 --- a/fs/xfs/xfs_fs_subr.c +++ b/fs/xfs/xfs_fs_subr.c @@ -90,7 +90,7 @@ xfs_wait_on_pages( if (mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK)) { return -filemap_fdatawait_range(mapping, first, - last == -1 ? ip->i_size - 1 : last); + last == -1 ? XFS_ISIZE(ip) - 1 : last); } return 0; } diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index a7cf713..3b5b78a 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -94,7 +94,6 @@ xfs_inode_alloc( ip->i_update_core = 0; ip->i_delayed_blks = 0; memset(&ip->i_d, 0, sizeof(xfs_icdinode_t)); - ip->i_size = 0; ip->i_new_size = 0; return ip; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 6260336..b210224 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -347,7 +347,6 @@ xfs_iformat( return XFS_ERROR(EFSCORRUPTED); } ip->i_d.di_size = 0; - ip->i_size = 0; ip->i_df.if_u2.if_rdev = xfs_dinode_get_rdev(dip); break; @@ -853,7 +852,6 @@ xfs_iread( } ip->i_delayed_blks = 0; - ip->i_size = ip->i_d.di_size; /* * Mark the buffer containing the inode as something to keep @@ -1043,7 +1041,6 @@ xfs_ialloc( } ip->i_d.di_size = 0; - ip->i_size = 0; ip->i_d.di_nextents = 0; ASSERT(ip->i_d.di_nblocks == 0); @@ -1198,7 +1195,7 @@ xfs_itruncate_extents( int done = 0; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL)); - ASSERT(new_size <= ip->i_size); + ASSERT(new_size <= XFS_ISIZE(ip)); ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); ASSERT(ip->i_itemp != NULL); ASSERT(ip->i_itemp->ili_lock_flags == 0); @@ -1712,8 +1709,7 @@ xfs_ifree( ASSERT(ip->i_d.di_nlink == 0); ASSERT(ip->i_d.di_nextents == 0); ASSERT(ip->i_d.di_anextents == 0); - ASSERT((ip->i_d.di_size == 0 && ip->i_size == 0) || - (!S_ISREG(ip->i_d.di_mode))); + ASSERT(ip->i_d.di_size == 0 || !S_ISREG(ip->i_d.di_mode)); ASSERT(ip->i_d.di_nblocks == 0); /* diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 4acbe74..cd99e43 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -246,16 +246,12 @@ typedef struct xfs_inode { xfs_icdinode_t i_d; /* most of ondisk inode */ - xfs_fsize_t i_size; /* in-memory size */ xfs_fsize_t i_new_size; /* size when write completes */ /* VFS inode */ struct inode i_vnode; /* embedded VFS inode */ } xfs_inode_t; -#define XFS_ISIZE(ip) S_ISREG((ip)->i_d.di_mode) ? \ - (ip)->i_size : (ip)->i_d.di_size; - /* Convert from vfs inode to xfs inode */ static inline struct xfs_inode *XFS_I(struct inode *inode) { @@ -269,6 +265,18 @@ static inline struct inode *VFS_I(struct xfs_inode *ip) } /* + * For regular files we only update the on-disk filesize when actually + * writing data back to disk. Until then only the copy in the VFS inode + * is uptodate. + */ +static inline xfs_fsize_t XFS_ISIZE(struct xfs_inode *ip) +{ + if (S_ISREG(ip->i_d.di_mode)) + return i_size_read(VFS_I(ip)); + return ip->i_d.di_size; +} + +/* * i_flags helper functions */ static inline void diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index a27a446..246c7d5 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -74,7 +74,7 @@ xfs_iomap_eof_align_last_fsb( else if (mp->m_dalign) align = mp->m_dalign; - if (align && ip->i_size >= XFS_FSB_TO_B(mp, align)) + if (align && XFS_ISIZE(ip) >= XFS_FSB_TO_B(mp, align)) new_last_fsb = roundup_64(*last_fsb, align); } @@ -154,7 +154,7 @@ xfs_iomap_write_direct( offset_fsb = XFS_B_TO_FSBT(mp, offset); last_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)(offset + count))); - if ((offset + count) > ip->i_size) { + if ((offset + count) > XFS_ISIZE(ip)) { error = xfs_iomap_eof_align_last_fsb(mp, ip, extsz, &last_fsb); if (error) goto error_out; @@ -211,7 +211,7 @@ xfs_iomap_write_direct( xfs_trans_ijoin(tp, ip, 0); bmapi_flag = 0; - if (offset < ip->i_size || extsz) + if (offset < XFS_ISIZE(ip) || extsz) bmapi_flag |= XFS_BMAPI_PREALLOC; /* @@ -286,7 +286,7 @@ xfs_iomap_eof_want_preallocate( int found_delalloc = 0; *prealloc = 0; - if ((offset + count) <= ip->i_size) + if (offset + count <= XFS_ISIZE(ip)) return 0; /* @@ -340,7 +340,7 @@ xfs_iomap_prealloc_size( * if we pass in alloc_blocks = 0. Hence the "+ 1" to * ensure we always pass in a non-zero value. */ - alloc_blocks = XFS_B_TO_FSB(mp, ip->i_size) + 1; + alloc_blocks = XFS_B_TO_FSB(mp, XFS_ISIZE(ip)) + 1; alloc_blocks = XFS_FILEOFF_MIN(MAXEXTLEN, rounddown_pow_of_two(alloc_blocks)); @@ -564,7 +564,7 @@ xfs_iomap_write_allocate( * back.... */ nimaps = 1; - end_fsb = XFS_B_TO_FSB(mp, ip->i_size); + end_fsb = XFS_B_TO_FSB(mp, XFS_ISIZE(ip)); error = xfs_bmap_last_offset(NULL, ip, &last_block, XFS_DATA_FORK); if (error) diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f02eaa2..ab30253 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -778,7 +778,7 @@ xfs_setattr_size( lock_flags |= XFS_IOLOCK_EXCL; xfs_ilock(ip, lock_flags); - oldsize = ip->i_size; + oldsize = inode->i_size; newsize = iattr->ia_size; /* @@ -897,7 +897,6 @@ xfs_setattr_size( * they get written to. */ ip->i_d.di_size = newsize; - ip->i_size = newsize; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); if (newsize <= oldsize) { diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 2737865..eafbcff 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -265,7 +265,6 @@ xfs_qm_scall_trunc_qfile( xfs_trans_ijoin(tp, ip, 0); ip->i_d.di_size = 0; - ip->i_size = 0; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 81efa04..2aabcc9 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1038,7 +1038,7 @@ DECLARE_EVENT_CLASS(xfs_simple_io_class, TP_fast_assign( __entry->dev = VFS_I(ip)->i_sb->s_dev; __entry->ino = ip->i_ino; - __entry->isize = ip->i_size; + __entry->isize = VFS_I(ip)->i_size; __entry->disize = ip->i_d.di_size; __entry->new_size = ip->i_new_size; __entry->offset = offset; diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 96ff034..0cf52da 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -175,7 +175,7 @@ xfs_free_eofblocks( * Figure out if there are any blocks beyond the end * of the file. If not, then there is nothing to do. */ - end_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)ip->i_size)); + end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_ISIZE(ip)); last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp)); if (last_fsb <= end_fsb) return 0; @@ -233,7 +233,7 @@ xfs_free_eofblocks( * may be full of holes (ie NULL files bug). */ error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, - ip->i_size); + XFS_ISIZE(ip)); if (error) { /* * If we get an error at this point we simply don't @@ -547,8 +547,8 @@ xfs_release( return 0; if ((S_ISREG(ip->i_d.di_mode) && - ((ip->i_size > 0) || (VN_CACHED(VFS_I(ip)) > 0 || - ip->i_delayed_blks > 0)) && + (VFS_I(ip)->i_size > 0 || + (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) && (ip->i_df.if_flags & XFS_IFEXTENTS)) && (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) { @@ -625,7 +625,7 @@ xfs_inactive( * only one with a reference to the inode. */ truncate = ((ip->i_d.di_nlink == 0) && - ((ip->i_d.di_size != 0) || (ip->i_size != 0) || + ((ip->i_d.di_size != 0) || XFS_ISIZE(ip) != 0 || (ip->i_d.di_nextents > 0) || (ip->i_delayed_blks > 0)) && S_ISREG(ip->i_d.di_mode)); @@ -639,12 +639,12 @@ xfs_inactive( if (ip->i_d.di_nlink != 0) { if ((S_ISREG(ip->i_d.di_mode) && - ((ip->i_size > 0) || (VN_CACHED(VFS_I(ip)) > 0 || - ip->i_delayed_blks > 0)) && - (ip->i_df.if_flags & XFS_IFEXTENTS) && - (!(ip->i_d.di_flags & + (VFS_I(ip)->i_size > 0 || + (VN_CACHED(VFS_I(ip)) > 0 || ip->i_delayed_blks > 0)) && + (ip->i_df.if_flags & XFS_IFEXTENTS) && + (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) || - (ip->i_delayed_blks != 0)))) { + ip->i_delayed_blks != 0))) { error = xfs_free_eofblocks(mp, ip, 0); if (error) return VN_INACTIVE_CACHE; @@ -678,7 +678,6 @@ xfs_inactive( xfs_trans_ijoin(tp, ip, 0); ip->i_d.di_size = 0; - ip->i_size = 0; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0); @@ -1974,11 +1973,11 @@ xfs_zero_remaining_bytes( * since nothing can read beyond eof. The space will * be zeroed when the file is extended anyway. */ - if (startoff >= ip->i_size) + if (startoff >= XFS_ISIZE(ip)) return 0; - if (endoff > ip->i_size) - endoff = ip->i_size; + if (endoff > XFS_ISIZE(ip)) + endoff = XFS_ISIZE(ip); bp = xfs_buf_get_uncached(XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp, @@ -2273,7 +2272,7 @@ xfs_change_file_space( bf->l_start += offset; break; case 2: /*SEEK_END*/ - bf->l_start += ip->i_size; + bf->l_start += XFS_ISIZE(ip); break; default: return XFS_ERROR(EINVAL); @@ -2290,7 +2289,7 @@ xfs_change_file_space( bf->l_whence = 0; startoffset = bf->l_start; - fsize = ip->i_size; + fsize = XFS_ISIZE(ip); /* * XFS_IOC_RESVSP and XFS_IOC_UNRESVSP will reserve or unreserve -- cgit v0.10.2 From 2813d682e8e6a278f94817429afd46b30875bb6e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:12 +0000 Subject: xfs: remove the i_new_size field in struct xfs_inode Now that we use the VFS i_size field throughout XFS there is no need for the i_new_size field any more given that the VFS i_size field gets updated in ->write_end before unlocking the page, and thus is always uptodate when writeback could see a page. Removing i_new_size also has the advantage that we will never have to trim back di_size during a failed buffered write, given that it never gets updated past i_size. Note that currently the generic direct I/O code only updates i_size after calling our end_io handler, which requires a small workaround to make sure di_size actually makes it to disk. I hope to fix this properly in the generic code. A downside is that we lose the support for parallel non-overlapping O_DIRECT appending writes that recently was added. I don't think keeping the complex and fragile i_new_size infrastructure for this is a good tradeoff - if we really care about parallel appending writers we should investigate turning the iolock into a range lock, which would also allow for parallel non-overlapping buffered writers. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 4d27ea1..74b9baf 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -111,8 +111,7 @@ xfs_ioend_new_eof( xfs_fsize_t bsize; bsize = ioend->io_offset + ioend->io_size; - isize = MAX(i_size_read(VFS_I(ip)), ip->i_new_size); - isize = MIN(isize, bsize); + isize = MIN(i_size_read(VFS_I(ip)), bsize); return isize > ip->i_d.di_size ? isize : 0; } @@ -126,11 +125,7 @@ static inline bool xfs_ioend_is_append(struct xfs_ioend *ioend) } /* - * Update on-disk file size now that data has been written to disk. The - * current in-memory file size is i_size. If a write is beyond eof i_new_size - * will be the intended file size until i_size is updated. If this write does - * not extend all the way to the valid file size then restrict this update to - * the end of the write. + * Update on-disk file size now that data has been written to disk. * * This function does not block as blocking on the inode lock in IO completion * can lead to IO completion order dependency deadlocks.. If it can't get the @@ -1279,6 +1274,15 @@ xfs_end_io_direct_write( struct xfs_ioend *ioend = iocb->private; /* + * While the generic direct I/O code updates the inode size, it does + * so only after the end_io handler is called, which means our + * end_io handler thinks the on-disk size is outside the in-core + * size. To prevent this just update it a little bit earlier here. + */ + if (offset + size > i_size_read(ioend->io_inode)) + i_size_write(ioend->io_inode, offset + size); + + /* * blockdev_direct_IO can return an error even after the I/O * completion handler was called. Thus we need to protect * against double-freeing. @@ -1340,12 +1344,11 @@ xfs_vm_write_failed( if (to > inode->i_size) { /* - * punch out the delalloc blocks we have already allocated. We - * don't call xfs_setattr() to do this as we may be in the - * middle of a multi-iovec write and so the vfs inode->i_size - * will not match the xfs ip->i_size and so it will zero too - * much. Hence we jus truncate the page cache to zero what is - * necessary and punch the delalloc blocks directly. + * Punch out the delalloc blocks we have already allocated. + * + * Don't bother with xfs_setattr given that nothing can have + * made it to disk yet as the page is still locked at this + * point. */ struct xfs_inode *ip = XFS_I(inode); xfs_fileoff_t start_fsb; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 86d5dc2..6323139 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -413,27 +413,6 @@ xfs_file_splice_read( } /* - * If this was a direct or synchronous I/O that failed (such as ENOSPC) then - * part of the I/O may have been written to disk before the error occurred. In - * this case the on-disk file size may have been adjusted beyond the in-memory - * file size and now needs to be truncated back. - */ -STATIC void -xfs_aio_write_newsize_update( - struct xfs_inode *ip, - xfs_fsize_t new_size) -{ - if (new_size == ip->i_new_size) { - xfs_rw_ilock(ip, XFS_ILOCK_EXCL); - if (new_size == ip->i_new_size) - ip->i_new_size = 0; - if (ip->i_d.di_size > i_size_read(VFS_I(ip))) - ip->i_d.di_size = i_size_read(VFS_I(ip)); - xfs_rw_iunlock(ip, XFS_ILOCK_EXCL); - } -} - -/* * xfs_file_splice_write() does not use xfs_rw_ilock() because * generic_file_splice_write() takes the i_mutex itself. This, in theory, * couuld cause lock inversions between the aio_write path and the splice path @@ -451,7 +430,6 @@ xfs_file_splice_write( { struct inode *inode = outfilp->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); - xfs_fsize_t new_size; int ioflags = 0; ssize_t ret; @@ -465,20 +443,12 @@ xfs_file_splice_write( xfs_ilock(ip, XFS_IOLOCK_EXCL); - new_size = *ppos + count; - - xfs_ilock(ip, XFS_ILOCK_EXCL); - if (new_size > i_size_read(inode)) - ip->i_new_size = new_size; - xfs_iunlock(ip, XFS_ILOCK_EXCL); - trace_xfs_file_splice_write(ip, count, *ppos, ioflags); ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags); if (ret > 0) XFS_STATS_ADD(xs_write_bytes, ret); - xfs_aio_write_newsize_update(ip, new_size); xfs_iunlock(ip, XFS_IOLOCK_EXCL); return ret; } @@ -673,16 +643,13 @@ xfs_file_aio_write_checks( struct file *file, loff_t *pos, size_t *count, - xfs_fsize_t *new_sizep, int *iolock) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); - xfs_fsize_t new_size; int error = 0; xfs_rw_ilock(ip, XFS_ILOCK_EXCL); - *new_sizep = 0; restart: error = generic_write_checks(file, pos, count, S_ISBLK(inode->i_mode)); if (error) { @@ -697,15 +664,13 @@ restart: /* * If the offset is beyond the size of the file, we need to zero any * blocks that fall between the existing EOF and the start of this - * write. There is no need to issue zeroing if another in-flght IO ends - * at or before this one If zeronig is needed and we are currently - * holding the iolock shared, we need to update it to exclusive which - * involves dropping all locks and relocking to maintain correct locking - * order. If we do this, restart the function to ensure all checks and - * values are still valid. + * write. If zeroing is needed and we are currently holding the + * iolock shared, we need to update it to exclusive which involves + * dropping all locks and relocking to maintain correct locking order. + * If we do this, restart the function to ensure all checks and values + * are still valid. */ - if ((ip->i_new_size && *pos > ip->i_new_size) || - (!ip->i_new_size && *pos > i_size_read(inode))) { + if (*pos > i_size_read(inode)) { if (*iolock == XFS_IOLOCK_SHARED) { xfs_rw_iunlock(ip, XFS_ILOCK_EXCL | *iolock); *iolock = XFS_IOLOCK_EXCL; @@ -714,19 +679,6 @@ restart: } error = -xfs_zero_eof(ip, *pos, i_size_read(inode)); } - - /* - * If this IO extends beyond EOF, we may need to update ip->i_new_size. - * We have already zeroed space beyond EOF (if necessary). Only update - * ip->i_new_size if this IO ends beyond any other in-flight writes. - */ - new_size = *pos + *count; - if (new_size > i_size_read(inode)) { - if (new_size > ip->i_new_size) - ip->i_new_size = new_size; - *new_sizep = new_size; - } - xfs_rw_iunlock(ip, XFS_ILOCK_EXCL); if (error) return error; @@ -772,7 +724,6 @@ xfs_file_dio_aio_write( unsigned long nr_segs, loff_t pos, size_t ocount, - xfs_fsize_t *new_size, int *iolock) { struct file *file = iocb->ki_filp; @@ -817,7 +768,7 @@ xfs_file_dio_aio_write( xfs_rw_ilock(ip, *iolock); } - ret = xfs_file_aio_write_checks(file, &pos, &count, new_size, iolock); + ret = xfs_file_aio_write_checks(file, &pos, &count, iolock); if (ret) return ret; @@ -855,7 +806,6 @@ xfs_file_buffered_aio_write( unsigned long nr_segs, loff_t pos, size_t ocount, - xfs_fsize_t *new_size, int *iolock) { struct file *file = iocb->ki_filp; @@ -869,7 +819,7 @@ xfs_file_buffered_aio_write( *iolock = XFS_IOLOCK_EXCL; xfs_rw_ilock(ip, *iolock); - ret = xfs_file_aio_write_checks(file, &pos, &count, new_size, iolock); + ret = xfs_file_aio_write_checks(file, &pos, &count, iolock); if (ret) return ret; @@ -909,7 +859,6 @@ xfs_file_aio_write( ssize_t ret; int iolock; size_t ocount = 0; - xfs_fsize_t new_size = 0; XFS_STATS_INC(xs_write_calls); @@ -929,10 +878,10 @@ xfs_file_aio_write( if (unlikely(file->f_flags & O_DIRECT)) ret = xfs_file_dio_aio_write(iocb, iovp, nr_segs, pos, - ocount, &new_size, &iolock); + ocount, &iolock); else ret = xfs_file_buffered_aio_write(iocb, iovp, nr_segs, pos, - ocount, &new_size, &iolock); + ocount, &iolock); if (ret <= 0) goto out_unlock; @@ -953,7 +902,6 @@ xfs_file_aio_write( } out_unlock: - xfs_aio_write_newsize_update(ip, new_size); xfs_rw_iunlock(ip, iolock); return ret; } diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 3b5b78a..8c3e463 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -94,7 +94,6 @@ xfs_inode_alloc( ip->i_update_core = 0; ip->i_delayed_blks = 0; memset(&ip->i_d, 0, sizeof(xfs_icdinode_t)); - ip->i_new_size = 0; return ip; } diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index cd99e43..2f27b74 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -246,8 +246,6 @@ typedef struct xfs_inode { xfs_icdinode_t i_d; /* most of ondisk inode */ - xfs_fsize_t i_new_size; /* size when write completes */ - /* VFS inode */ struct inode i_vnode; /* embedded VFS inode */ } xfs_inode_t; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 2aabcc9..6b6df58 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -891,7 +891,6 @@ DECLARE_EVENT_CLASS(xfs_file_class, __field(dev_t, dev) __field(xfs_ino_t, ino) __field(xfs_fsize_t, size) - __field(xfs_fsize_t, new_size) __field(loff_t, offset) __field(size_t, count) __field(int, flags) @@ -900,17 +899,15 @@ DECLARE_EVENT_CLASS(xfs_file_class, __entry->dev = VFS_I(ip)->i_sb->s_dev; __entry->ino = ip->i_ino; __entry->size = ip->i_d.di_size; - __entry->new_size = ip->i_new_size; __entry->offset = offset; __entry->count = count; __entry->flags = flags; ), - TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " + TP_printk("dev %d:%d ino 0x%llx size 0x%llx " "offset 0x%llx count 0x%zx ioflags %s", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->size, - __entry->new_size, __entry->offset, __entry->count, __print_flags(__entry->flags, "|", XFS_IO_FLAGS)) @@ -978,7 +975,6 @@ DECLARE_EVENT_CLASS(xfs_imap_class, __field(dev_t, dev) __field(xfs_ino_t, ino) __field(loff_t, size) - __field(loff_t, new_size) __field(loff_t, offset) __field(size_t, count) __field(int, type) @@ -990,7 +986,6 @@ DECLARE_EVENT_CLASS(xfs_imap_class, __entry->dev = VFS_I(ip)->i_sb->s_dev; __entry->ino = ip->i_ino; __entry->size = ip->i_d.di_size; - __entry->new_size = ip->i_new_size; __entry->offset = offset; __entry->count = count; __entry->type = type; @@ -998,13 +993,11 @@ DECLARE_EVENT_CLASS(xfs_imap_class, __entry->startblock = irec ? irec->br_startblock : 0; __entry->blockcount = irec ? irec->br_blockcount : 0; ), - TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " - "offset 0x%llx count %zd type %s " - "startoff 0x%llx startblock %lld blockcount 0x%llx", + TP_printk("dev %d:%d ino 0x%llx size 0x%llx offset 0x%llx count %zd " + "type %s startoff 0x%llx startblock %lld blockcount 0x%llx", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->size, - __entry->new_size, __entry->offset, __entry->count, __print_symbolic(__entry->type, XFS_IO_TYPES), @@ -1031,7 +1024,6 @@ DECLARE_EVENT_CLASS(xfs_simple_io_class, __field(xfs_ino_t, ino) __field(loff_t, isize) __field(loff_t, disize) - __field(loff_t, new_size) __field(loff_t, offset) __field(size_t, count) ), @@ -1040,17 +1032,15 @@ DECLARE_EVENT_CLASS(xfs_simple_io_class, __entry->ino = ip->i_ino; __entry->isize = VFS_I(ip)->i_size; __entry->disize = ip->i_d.di_size; - __entry->new_size = ip->i_new_size; __entry->offset = offset; __entry->count = count; ), - TP_printk("dev %d:%d ino 0x%llx isize 0x%llx disize 0x%llx new_size 0x%llx " + TP_printk("dev %d:%d ino 0x%llx isize 0x%llx disize 0x%llx " "offset 0x%llx count %zd", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->isize, __entry->disize, - __entry->new_size, __entry->offset, __entry->count) ); -- cgit v0.10.2 From 5bf1f26227a59b9634e95eb3c7c012b766e5e6a0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:13 +0000 Subject: xfs: always return with the iolock held from xfs_file_aio_write_checks While xfs_iunlock is fine with 0 lockflags the calling conventions are much cleaner if xfs_file_aio_write_checks never returns without the iolock held. Reviewed-by: Dave Chinner Signed-off-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 6323139..134ff2f 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -636,7 +636,9 @@ out_lock: /* * Common pre-write limit and setup checks. * - * Returns with iolock held according to @iolock. + * Called with the iolocked held either shared and exclusive according to + * @iolock, and returns with it held. Might upgrade the iolock to exclusive + * if called for a direct write beyond i_size. */ STATIC ssize_t xfs_file_aio_write_checks( @@ -653,8 +655,7 @@ xfs_file_aio_write_checks( restart: error = generic_write_checks(file, pos, count, S_ISBLK(inode->i_mode)); if (error) { - xfs_rw_iunlock(ip, XFS_ILOCK_EXCL | *iolock); - *iolock = 0; + xfs_rw_iunlock(ip, XFS_ILOCK_EXCL); return error; } -- cgit v0.10.2 From d060646436233912178e6b9e3a7f30a41214220f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Dec 2011 20:00:14 +0000 Subject: xfs: cleanup xfs_file_aio_write With all the size field updates out of the way xfs_file_aio_write can be further simplified by pushing all iolock handling into xfs_file_dio_aio_write and xfs_file_buffered_aio_write and using the generic generic_write_sync helper for synchronous writes. Reviewed-by: Dave Chinner Signed-off-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 134ff2f..7e5bc87 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -724,8 +724,7 @@ xfs_file_dio_aio_write( const struct iovec *iovp, unsigned long nr_segs, loff_t pos, - size_t ocount, - int *iolock) + size_t ocount) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -735,10 +734,10 @@ xfs_file_dio_aio_write( ssize_t ret = 0; size_t count = ocount; int unaligned_io = 0; + int iolock; struct xfs_buftarg *target = XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp; - *iolock = 0; if ((pos & target->bt_smask) || (count & target->bt_smask)) return -XFS_ERROR(EINVAL); @@ -753,31 +752,31 @@ xfs_file_dio_aio_write( * EOF zeroing cases and fill out the new inode size as appropriate. */ if (unaligned_io || mapping->nrpages) - *iolock = XFS_IOLOCK_EXCL; + iolock = XFS_IOLOCK_EXCL; else - *iolock = XFS_IOLOCK_SHARED; - xfs_rw_ilock(ip, *iolock); + iolock = XFS_IOLOCK_SHARED; + xfs_rw_ilock(ip, iolock); /* * Recheck if there are cached pages that need invalidate after we got * the iolock to protect against other threads adding new pages while * we were waiting for the iolock. */ - if (mapping->nrpages && *iolock == XFS_IOLOCK_SHARED) { - xfs_rw_iunlock(ip, *iolock); - *iolock = XFS_IOLOCK_EXCL; - xfs_rw_ilock(ip, *iolock); + if (mapping->nrpages && iolock == XFS_IOLOCK_SHARED) { + xfs_rw_iunlock(ip, iolock); + iolock = XFS_IOLOCK_EXCL; + xfs_rw_ilock(ip, iolock); } - ret = xfs_file_aio_write_checks(file, &pos, &count, iolock); + ret = xfs_file_aio_write_checks(file, &pos, &count, &iolock); if (ret) - return ret; + goto out; if (mapping->nrpages) { ret = -xfs_flushinval_pages(ip, (pos & PAGE_CACHE_MASK), -1, FI_REMAPF_LOCKED); if (ret) - return ret; + goto out; } /* @@ -786,15 +785,18 @@ xfs_file_dio_aio_write( */ if (unaligned_io) inode_dio_wait(inode); - else if (*iolock == XFS_IOLOCK_EXCL) { + else if (iolock == XFS_IOLOCK_EXCL) { xfs_rw_ilock_demote(ip, XFS_IOLOCK_EXCL); - *iolock = XFS_IOLOCK_SHARED; + iolock = XFS_IOLOCK_SHARED; } trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0); ret = generic_file_direct_write(iocb, iovp, &nr_segs, pos, &iocb->ki_pos, count, ocount); +out: + xfs_rw_iunlock(ip, iolock); + /* No fallback to buffered IO on errors for XFS. */ ASSERT(ret < 0 || ret == count); return ret; @@ -806,8 +808,7 @@ xfs_file_buffered_aio_write( const struct iovec *iovp, unsigned long nr_segs, loff_t pos, - size_t ocount, - int *iolock) + size_t ocount) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -815,14 +816,14 @@ xfs_file_buffered_aio_write( struct xfs_inode *ip = XFS_I(inode); ssize_t ret; int enospc = 0; + int iolock = XFS_IOLOCK_EXCL; size_t count = ocount; - *iolock = XFS_IOLOCK_EXCL; - xfs_rw_ilock(ip, *iolock); + xfs_rw_ilock(ip, iolock); - ret = xfs_file_aio_write_checks(file, &pos, &count, iolock); + ret = xfs_file_aio_write_checks(file, &pos, &count, &iolock); if (ret) - return ret; + goto out; /* We can write back this queue in page reclaim */ current->backing_dev_info = mapping->backing_dev_info; @@ -836,13 +837,15 @@ write_retry: * page locks and retry *once* */ if (ret == -ENOSPC && !enospc) { - ret = -xfs_flush_pages(ip, 0, -1, 0, FI_NONE); - if (ret) - return ret; enospc = 1; - goto write_retry; + ret = -xfs_flush_pages(ip, 0, -1, 0, FI_NONE); + if (!ret) + goto write_retry; } + current->backing_dev_info = NULL; +out: + xfs_rw_iunlock(ip, iolock); return ret; } @@ -858,7 +861,6 @@ xfs_file_aio_write( struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode); ssize_t ret; - int iolock; size_t ocount = 0; XFS_STATS_INC(xs_write_calls); @@ -878,32 +880,22 @@ xfs_file_aio_write( return -EIO; if (unlikely(file->f_flags & O_DIRECT)) - ret = xfs_file_dio_aio_write(iocb, iovp, nr_segs, pos, - ocount, &iolock); + ret = xfs_file_dio_aio_write(iocb, iovp, nr_segs, pos, ocount); else ret = xfs_file_buffered_aio_write(iocb, iovp, nr_segs, pos, - ocount, &iolock); + ocount); - if (ret <= 0) - goto out_unlock; - - XFS_STATS_ADD(xs_write_bytes, ret); + if (ret > 0) { + ssize_t err; - /* Handle various SYNC-type writes */ - if ((file->f_flags & O_DSYNC) || IS_SYNC(inode)) { - loff_t end = pos + ret - 1; - int error; + XFS_STATS_ADD(xs_write_bytes, ret); - xfs_rw_iunlock(ip, iolock); - error = xfs_file_fsync(file, pos, end, - (file->f_flags & __O_SYNC) ? 0 : 1); - xfs_rw_ilock(ip, iolock); - if (error) - ret = error; + /* Handle various SYNC-type writes */ + err = generic_write_sync(file, pos, ret); + if (err < 0) + ret = err; } -out_unlock: - xfs_rw_iunlock(ip, iolock); return ret; } -- cgit v0.10.2 From 5ef30ee53b187786e64bdc1f8109e39d17f2ce58 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:05 -0500 Subject: audit: make filetype matching consistent with other filters Every other filter that matches part of the inodes list collected by audit will match against any of the inodes on that list. The filetype matching however had a strange way of doing things. It allowed userspace to indicated if it should match on the first of the second name collected by the kernel. Name collection ordering seems like a kernel internal and making userspace rules get that right just seems like a bad idea. As it turns out the userspace audit writers had no idea it was doing this and thus never overloaded the value field. The kernel always checked the first name collected which for the tested rules was always correct. This patch just makes the filetype matching like the major, minor, inode, and LSM rules in that it will match against any of the names collected. It also changes the rule validation to reject the old unused rule types. Noone knew it was there. Noone used it. Why keep around the extra code? Signed-off-by: Eric Paris diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index f8277c8..d94dde8 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -385,7 +385,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) goto exit_free; break; case AUDIT_FILETYPE: - if ((f->val & ~S_IFMT) > S_IFMT) + if (f->val & ~S_IFMT) goto exit_free; break; case AUDIT_INODE: @@ -536,7 +536,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, goto exit_free; break; case AUDIT_FILETYPE: - if ((f->val & ~S_IFMT) > S_IFMT) + if (f->val & ~S_IFMT) goto exit_free; break; default: diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e7fe2b0..a09c503 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -305,21 +305,20 @@ static int audit_match_perm(struct audit_context *ctx, int mask) } } -static int audit_match_filetype(struct audit_context *ctx, int which) +static int audit_match_filetype(struct audit_context *ctx, int val) { - unsigned index = which & ~S_IFMT; - umode_t mode = which & S_IFMT; + int index; + umode_t mode = (umode_t)val; if (unlikely(!ctx)) return 0; - if (index >= ctx->name_count) - return 0; - if (ctx->names[index].ino == -1) - return 0; - if ((ctx->names[index].mode ^ mode) & S_IFMT) - return 0; - return 1; + for (index = 0; index < ctx->name_count; index++) { + if ((ctx->names[index].ino != -1) && + ((ctx->names[index].mode & S_IFMT) == mode)) + return 1; + } + return 0; } /* -- cgit v0.10.2 From 5195d8e217a78697152d64fc09a16e063a022465 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:05 -0500 Subject: audit: dynamically allocate audit_names when not enough space is in the names array This patch does 2 things. First it reduces the number of audit_names allocated in every audit context from 20 to 5. 5 should be enough for all 'normal' syscalls (rename being the worst). Some syscalls can still touch more the 5 inodes such as mount. When rpc filesystem is mounted it will create inodes and those can exceed 5. To handle that problem this patch will dynamically allocate audit_names if it needs more than 5. This should decrease the typicall memory usage while still supporting all the possible kernel operations. Signed-off-by: Eric Paris diff --git a/kernel/auditsc.c b/kernel/auditsc.c index a09c503..1a92d61 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -71,8 +71,9 @@ #include "audit.h" /* AUDIT_NAMES is the number of slots we reserve in the audit_context - * for saving names from getname(). */ -#define AUDIT_NAMES 20 + * for saving names from getname(). If we get more names we will allocate + * a name dynamically and also add those to the list anchored by names_list. */ +#define AUDIT_NAMES 5 /* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 @@ -101,9 +102,8 @@ struct audit_cap_data { * * Further, in fs/namei.c:path_lookup() we store the inode and device. */ struct audit_names { + struct list_head list; /* audit_context->names_list */ const char *name; - int name_len; /* number of name's characters to log */ - unsigned name_put; /* call __putname() for this name */ unsigned long ino; dev_t dev; umode_t mode; @@ -113,6 +113,14 @@ struct audit_names { u32 osid; struct audit_cap_data fcap; unsigned int fcap_ver; + int name_len; /* number of name's characters to log */ + bool name_put; /* call __putname() for this name */ + /* + * This was an allocated audit_names and not from the array of + * names allocated in the task audit context. Thus this name + * should be freed on syscall exit + */ + bool should_free; }; struct audit_aux_data { @@ -174,8 +182,17 @@ struct audit_context { long return_code;/* syscall return code */ u64 prio; int return_valid; /* return code is valid */ - int name_count; - struct audit_names names[AUDIT_NAMES]; + /* + * The names_list is the list of all audit_names collected during this + * syscall. The first AUDIT_NAMES entries in the names_list will + * actually be from the preallocated_names array for performance + * reasons. Except during allocation they should never be referenced + * through the preallocated_names array and should only be found/used + * by running the names_list. + */ + struct audit_names preallocated_names[AUDIT_NAMES]; + int name_count; /* total records in names_list */ + struct list_head names_list; /* anchor for struct audit_names->list */ char * filterkey; /* key for rule that triggered record */ struct path pwd; struct audit_context *previous; /* For nested syscalls */ @@ -307,17 +324,18 @@ static int audit_match_perm(struct audit_context *ctx, int mask) static int audit_match_filetype(struct audit_context *ctx, int val) { - int index; + struct audit_names *n; umode_t mode = (umode_t)val; if (unlikely(!ctx)) return 0; - for (index = 0; index < ctx->name_count; index++) { - if ((ctx->names[index].ino != -1) && - ((ctx->names[index].mode & S_IFMT) == mode)) + list_for_each_entry(n, &ctx->names_list, list) { + if ((n->ino != -1) && + ((n->mode & S_IFMT) == mode)) return 1; } + return 0; } @@ -456,13 +474,14 @@ static int audit_filter_rules(struct task_struct *tsk, bool task_creation) { const struct cred *cred; - int i, j, need_sid = 1; + int i, need_sid = 1; u32 sid; cred = rcu_dereference_check(tsk->cred, tsk == current || task_creation); for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; + struct audit_names *n; int result = 0; switch (f->type) { @@ -525,8 +544,8 @@ static int audit_filter_rules(struct task_struct *tsk, result = audit_comparator(MAJOR(name->dev), f->op, f->val); else if (ctx) { - for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_comparator(MAJOR(n->dev), f->op, f->val)) { ++result; break; } @@ -538,8 +557,8 @@ static int audit_filter_rules(struct task_struct *tsk, result = audit_comparator(MINOR(name->dev), f->op, f->val); else if (ctx) { - for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_comparator(MINOR(n->dev), f->op, f->val)) { ++result; break; } @@ -550,8 +569,8 @@ static int audit_filter_rules(struct task_struct *tsk, if (name) result = (name->ino == f->val); else if (ctx) { - for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(ctx->names[j].ino, f->op, f->val)) { + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_comparator(n->ino, f->op, f->val)) { ++result; break; } @@ -606,11 +625,10 @@ static int audit_filter_rules(struct task_struct *tsk, name->osid, f->type, f->op, f->lsm_rule, ctx); } else if (ctx) { - for (j = 0; j < ctx->name_count; j++) { - if (security_audit_rule_match( - ctx->names[j].osid, - f->type, f->op, - f->lsm_rule, ctx)) { + list_for_each_entry(n, &ctx->names_list, list) { + if (security_audit_rule_match(n->osid, f->type, + f->op, f->lsm_rule, + ctx)) { ++result; break; } @@ -721,40 +739,53 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, return AUDIT_BUILD_CONTEXT; } -/* At syscall exit time, this filter is called if any audit_names[] have been +/* + * Given an audit_name check the inode hash table to see if they match. + * Called holding the rcu read lock to protect the use of audit_inode_hash + */ +static int audit_filter_inode_name(struct task_struct *tsk, + struct audit_names *n, + struct audit_context *ctx) { + int word, bit; + int h = audit_hash_ino((u32)n->ino); + struct list_head *list = &audit_inode_hash[h]; + struct audit_entry *e; + enum audit_state state; + + word = AUDIT_WORD(ctx->major); + bit = AUDIT_BIT(ctx->major); + + if (list_empty(list)) + return 0; + + list_for_each_entry_rcu(e, list, list) { + if ((e->rule.mask[word] & bit) == bit && + audit_filter_rules(tsk, &e->rule, ctx, n, &state, false)) { + ctx->current_state = state; + return 1; + } + } + + return 0; +} + +/* At syscall exit time, this filter is called if any audit_names have been * collected during syscall processing. We only check rules in sublists at hash - * buckets applicable to the inode numbers in audit_names[]. + * buckets applicable to the inode numbers in audit_names. * Regarding audit_state, same rules apply as for audit_filter_syscall(). */ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx) { - int i; - struct audit_entry *e; - enum audit_state state; + struct audit_names *n; if (audit_pid && tsk->tgid == audit_pid) return; rcu_read_lock(); - for (i = 0; i < ctx->name_count; i++) { - int word = AUDIT_WORD(ctx->major); - int bit = AUDIT_BIT(ctx->major); - struct audit_names *n = &ctx->names[i]; - int h = audit_hash_ino((u32)n->ino); - struct list_head *list = &audit_inode_hash[h]; - if (list_empty(list)) - continue; - - list_for_each_entry_rcu(e, list, list) { - if ((e->rule.mask[word] & bit) == bit && - audit_filter_rules(tsk, &e->rule, ctx, n, - &state, false)) { - rcu_read_unlock(); - ctx->current_state = state; - return; - } - } + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_filter_inode_name(tsk, n, ctx)) + break; } rcu_read_unlock(); } @@ -798,7 +829,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, static inline void audit_free_names(struct audit_context *context) { - int i; + struct audit_names *n, *next; #if AUDIT_DEBUG == 2 if (context->put_count + context->ino_count != context->name_count) { @@ -809,10 +840,9 @@ static inline void audit_free_names(struct audit_context *context) context->serial, context->major, context->in_syscall, context->name_count, context->put_count, context->ino_count); - for (i = 0; i < context->name_count; i++) { + list_for_each_entry(n, &context->names_list, list) { printk(KERN_ERR "names[%d] = %p = %s\n", i, - context->names[i].name, - context->names[i].name ?: "(null)"); + n->name, n->name ?: "(null)"); } dump_stack(); return; @@ -823,9 +853,12 @@ static inline void audit_free_names(struct audit_context *context) context->ino_count = 0; #endif - for (i = 0; i < context->name_count; i++) { - if (context->names[i].name && context->names[i].name_put) - __putname(context->names[i].name); + list_for_each_entry_safe(n, next, &context->names_list, list) { + list_del(&n->list); + if (n->name && n->name_put) + __putname(n->name); + if (n->should_free) + kfree(n); } context->name_count = 0; path_put(&context->pwd); @@ -863,6 +896,7 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state) return NULL; audit_zero_context(context, state); INIT_LIST_HEAD(&context->killed_trees); + INIT_LIST_HEAD(&context->names_list); return context; } @@ -1323,6 +1357,68 @@ static void show_special(struct audit_context *context, int *call_panic) audit_log_end(ab); } +static void audit_log_name(struct audit_context *context, struct audit_names *n, + int record_num, int *call_panic) +{ + struct audit_buffer *ab; + ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); + if (!ab) + return; /* audit_panic has been called */ + + audit_log_format(ab, "item=%d", record_num); + + if (n->name) { + switch (n->name_len) { + case AUDIT_NAME_FULL: + /* log the full path */ + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, n->name); + break; + case 0: + /* name was specified as a relative path and the + * directory component is the cwd */ + audit_log_d_path(ab, "name=", &context->pwd); + break; + default: + /* log the name's directory component */ + audit_log_format(ab, " name="); + audit_log_n_untrustedstring(ab, n->name, + n->name_len); + } + } else + audit_log_format(ab, " name=(null)"); + + if (n->ino != (unsigned long)-1) { + audit_log_format(ab, " inode=%lu" + " dev=%02x:%02x mode=%#ho" + " ouid=%u ogid=%u rdev=%02x:%02x", + n->ino, + MAJOR(n->dev), + MINOR(n->dev), + n->mode, + n->uid, + n->gid, + MAJOR(n->rdev), + MINOR(n->rdev)); + } + if (n->osid != 0) { + char *ctx = NULL; + u32 len; + if (security_secid_to_secctx( + n->osid, &ctx, &len)) { + audit_log_format(ab, " osid=%u", n->osid); + *call_panic = 2; + } else { + audit_log_format(ab, " obj=%s", ctx); + security_release_secctx(ctx, len); + } + } + + audit_log_fcaps(ab, n); + + audit_log_end(ab); +} + static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { const struct cred *cred; @@ -1330,6 +1426,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts struct audit_buffer *ab; struct audit_aux_data *aux; const char *tty; + struct audit_names *n; /* tsk == current */ context->pid = tsk->pid; @@ -1469,66 +1566,10 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_end(ab); } } - for (i = 0; i < context->name_count; i++) { - struct audit_names *n = &context->names[i]; - - ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); - if (!ab) - continue; /* audit_panic has been called */ - - audit_log_format(ab, "item=%d", i); - - if (n->name) { - switch(n->name_len) { - case AUDIT_NAME_FULL: - /* log the full path */ - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, n->name); - break; - case 0: - /* name was specified as a relative path and the - * directory component is the cwd */ - audit_log_d_path(ab, "name=", &context->pwd); - break; - default: - /* log the name's directory component */ - audit_log_format(ab, " name="); - audit_log_n_untrustedstring(ab, n->name, - n->name_len); - } - } else - audit_log_format(ab, " name=(null)"); - - if (n->ino != (unsigned long)-1) { - audit_log_format(ab, " inode=%lu" - " dev=%02x:%02x mode=%#ho" - " ouid=%u ogid=%u rdev=%02x:%02x", - n->ino, - MAJOR(n->dev), - MINOR(n->dev), - n->mode, - n->uid, - n->gid, - MAJOR(n->rdev), - MINOR(n->rdev)); - } - if (n->osid != 0) { - char *ctx = NULL; - u32 len; - if (security_secid_to_secctx( - n->osid, &ctx, &len)) { - audit_log_format(ab, " osid=%u", n->osid); - call_panic = 2; - } else { - audit_log_format(ab, " obj=%s", ctx); - security_release_secctx(ctx, len); - } - } - - audit_log_fcaps(ab, n); - audit_log_end(ab); - } + i = 0; + list_for_each_entry(n, &context->names_list, list) + audit_log_name(context, n, i++, &call_panic); /* Send end of event record to help user space know we are finished */ ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE); @@ -1820,6 +1861,30 @@ retry: #endif } +static struct audit_names *audit_alloc_name(struct audit_context *context) +{ + struct audit_names *aname; + + if (context->name_count < AUDIT_NAMES) { + aname = &context->preallocated_names[context->name_count]; + memset(aname, 0, sizeof(*aname)); + } else { + aname = kzalloc(sizeof(*aname), GFP_NOFS); + if (!aname) + return NULL; + aname->should_free = true; + } + + aname->ino = (unsigned long)-1; + list_add_tail(&aname->list, &context->names_list); + + context->name_count++; +#if AUDIT_DEBUG + context->ino_count++; +#endif + return aname; +} + /** * audit_getname - add a name to the list * @name: name to add @@ -1830,6 +1895,7 @@ retry: void __audit_getname(const char *name) { struct audit_context *context = current->audit_context; + struct audit_names *n; if (IS_ERR(name) || !name) return; @@ -1842,13 +1908,15 @@ void __audit_getname(const char *name) #endif return; } - BUG_ON(context->name_count >= AUDIT_NAMES); - context->names[context->name_count].name = name; - context->names[context->name_count].name_len = AUDIT_NAME_FULL; - context->names[context->name_count].name_put = 1; - context->names[context->name_count].ino = (unsigned long)-1; - context->names[context->name_count].osid = 0; - ++context->name_count; + + n = audit_alloc_name(context); + if (!n) + return; + + n->name = name; + n->name_len = AUDIT_NAME_FULL; + n->name_put = true; + if (!context->pwd.dentry) get_fs_pwd(current->fs, &context->pwd); } @@ -1870,12 +1938,13 @@ void audit_putname(const char *name) printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n", __FILE__, __LINE__, context->serial, name); if (context->name_count) { + struct audit_names *n; int i; - for (i = 0; i < context->name_count; i++) + + list_for_each_entry(n, &context->names_list, list) printk(KERN_ERR "name[%d] = %p = %s\n", i, - context->names[i].name, - context->names[i].name ?: "(null)"); - } + n->name, n->name ?: "(null)"); + } #endif __putname(name); } @@ -1896,39 +1965,11 @@ void audit_putname(const char *name) #endif } -static int audit_inc_name_count(struct audit_context *context, - const struct inode *inode) -{ - if (context->name_count >= AUDIT_NAMES) { - if (inode) - printk(KERN_DEBUG "audit: name_count maxed, losing inode data: " - "dev=%02x:%02x, inode=%lu\n", - MAJOR(inode->i_sb->s_dev), - MINOR(inode->i_sb->s_dev), - inode->i_ino); - - else - printk(KERN_DEBUG "name_count maxed, losing inode data\n"); - return 1; - } - context->name_count++; -#if AUDIT_DEBUG - context->ino_count++; -#endif - return 0; -} - - static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry) { struct cpu_vfs_cap_data caps; int rc; - memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t)); - memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t)); - name->fcap.fE = 0; - name->fcap_ver = 0; - if (!dentry) return 0; @@ -1968,30 +2009,25 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent */ void __audit_inode(const char *name, const struct dentry *dentry) { - int idx; struct audit_context *context = current->audit_context; const struct inode *inode = dentry->d_inode; + struct audit_names *n; if (!context->in_syscall) return; - if (context->name_count - && context->names[context->name_count-1].name - && context->names[context->name_count-1].name == name) - idx = context->name_count - 1; - else if (context->name_count > 1 - && context->names[context->name_count-2].name - && context->names[context->name_count-2].name == name) - idx = context->name_count - 2; - else { - /* FIXME: how much do we care about inodes that have no - * associated name? */ - if (audit_inc_name_count(context, inode)) - return; - idx = context->name_count - 1; - context->names[idx].name = NULL; + + list_for_each_entry_reverse(n, &context->names_list, list) { + if (n->name && (n->name == name)) + goto out; } + + /* unable to find the name from a previous getname() */ + n = audit_alloc_name(context); + if (!n) + return; +out: handle_path(dentry); - audit_copy_inode(&context->names[idx], dentry, inode); + audit_copy_inode(n, dentry, inode); } /** @@ -2010,11 +2046,11 @@ void __audit_inode(const char *name, const struct dentry *dentry) void __audit_inode_child(const struct dentry *dentry, const struct inode *parent) { - int idx; struct audit_context *context = current->audit_context; const char *found_parent = NULL, *found_child = NULL; const struct inode *inode = dentry->d_inode; const char *dname = dentry->d_name.name; + struct audit_names *n; int dirlen = 0; if (!context->in_syscall) @@ -2024,9 +2060,7 @@ void __audit_inode_child(const struct dentry *dentry, handle_one(inode); /* parent is more likely, look for it first */ - for (idx = 0; idx < context->name_count; idx++) { - struct audit_names *n = &context->names[idx]; - + list_for_each_entry(n, &context->names_list, list) { if (!n->name) continue; @@ -2039,9 +2073,7 @@ void __audit_inode_child(const struct dentry *dentry, } /* no matching parent, look for matching child */ - for (idx = 0; idx < context->name_count; idx++) { - struct audit_names *n = &context->names[idx]; - + list_for_each_entry(n, &context->names_list, list) { if (!n->name) continue; @@ -2059,34 +2091,29 @@ void __audit_inode_child(const struct dentry *dentry, add_names: if (!found_parent) { - if (audit_inc_name_count(context, parent)) + n = audit_alloc_name(context); + if (!n) return; - idx = context->name_count - 1; - context->names[idx].name = NULL; - audit_copy_inode(&context->names[idx], NULL, parent); + audit_copy_inode(n, NULL, parent); } if (!found_child) { - if (audit_inc_name_count(context, inode)) + n = audit_alloc_name(context); + if (!n) return; - idx = context->name_count - 1; /* Re-use the name belonging to the slot for a matching parent * directory. All names for this context are relinquished in * audit_free_names() */ if (found_parent) { - context->names[idx].name = found_parent; - context->names[idx].name_len = AUDIT_NAME_FULL; + n->name = found_parent; + n->name_len = AUDIT_NAME_FULL; /* don't call __putname() */ - context->names[idx].name_put = 0; - } else { - context->names[idx].name = NULL; + n->name_put = false; } if (inode) - audit_copy_inode(&context->names[idx], NULL, inode); - else - context->names[idx].ino = (unsigned long)-1; + audit_copy_inode(n, NULL, inode); } } EXPORT_SYMBOL_GPL(__audit_inode_child); -- cgit v0.10.2 From 3035c51e8ac0512686ceb9f2bd1d13bdc6e4fb29 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:05 -0500 Subject: audit: drop the meaningless and format breaking word 'user' userspace audit messages look like so: type=USER msg=audit(1271170549.415:24710): user pid=14722 uid=0 auid=500 ses=1 subj=unconfined_u:unconfined_r:auditctl_t:s0-s0:c0.c1023 msg='' That third field just says 'user'. That's useless and doesn't follow the key=value pair we are trying to enforce. We already know it came from the user based on the record type. Kill that word. Die. Signed-off-by: Eric Paris diff --git a/kernel/audit.c b/kernel/audit.c index 2c1d6ab..00efe47 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -631,7 +631,7 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, } *ab = audit_log_start(NULL, GFP_KERNEL, msg_type); - audit_log_format(*ab, "user pid=%d uid=%u auid=%u ses=%u", + audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u", pid, uid, auid, ses); if (sid) { rc = security_secid_to_secctx(sid, &ctx, &len); -- cgit v0.10.2 From 16c174bd95cb07c9d0ad3fcd8c70f9cea7214c9d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:05 -0500 Subject: audit: check current inode and containing object when filtering on major and minor The audit system has the ability to filter on the major and minor number of the device containing the inode being operated upon. Lets say that /dev/sda1 has major,minor 8,1 and that we mount /dev/sda1 on /boot. Now lets say we add a watch with a filter on 8,1. If we proceed to open an inode inside /boot, such as /vboot/vmlinuz, we will match the major,minor filter. Lets instead assume that one were to use a tool like debugfs and were to open /dev/sda1 directly and to modify it's contents. We might hope that this would also be logged, but it isn't. The rules will check the major,minor of the device containing /dev/sda1. In other words the rule would match on the major/minor of the tmpfs mounted at /dev. I believe these rules should trigger on either device. The man page is devoid of useful information about the intended semantics. It only seems logical that if you want to know everything that happened on a major,minor that would include things that happened to the device itself... Signed-off-by: Eric Paris diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 1a92d61..7c49514 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -540,12 +540,14 @@ static int audit_filter_rules(struct task_struct *tsk, } break; case AUDIT_DEVMAJOR: - if (name) - result = audit_comparator(MAJOR(name->dev), - f->op, f->val); - else if (ctx) { + if (name) { + if (audit_comparator(MAJOR(name->dev), f->op, f->val) || + audit_comparator(MAJOR(name->rdev), f->op, f->val)) + ++result; + } else if (ctx) { list_for_each_entry(n, &ctx->names_list, list) { - if (audit_comparator(MAJOR(n->dev), f->op, f->val)) { + if (audit_comparator(MAJOR(n->dev), f->op, f->val) || + audit_comparator(MAJOR(n->rdev), f->op, f->val)) { ++result; break; } @@ -553,12 +555,14 @@ static int audit_filter_rules(struct task_struct *tsk, } break; case AUDIT_DEVMINOR: - if (name) - result = audit_comparator(MINOR(name->dev), - f->op, f->val); - else if (ctx) { + if (name) { + if (audit_comparator(MINOR(name->dev), f->op, f->val) || + audit_comparator(MINOR(name->rdev), f->op, f->val)) + ++result; + } else if (ctx) { list_for_each_entry(n, &ctx->names_list, list) { - if (audit_comparator(MINOR(n->dev), f->op, f->val)) { + if (audit_comparator(MINOR(n->dev), f->op, f->val) || + audit_comparator(MINOR(n->rdev), f->op, f->val)) { ++result; break; } -- cgit v0.10.2 From 85e7bac33b8d5edafc4e219c7dfdb3d48e0b4e31 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:05 -0500 Subject: seccomp: audit abnormal end to a process due to seccomp The audit system likes to collect information about processes that end abnormally (SIGSEGV) as this may me useful intrusion detection information. This patch adds audit support to collect information when seccomp forces a task to exit because of misbehavior in a similar way. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 426ab9f..6e1c533 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -430,6 +430,7 @@ extern void audit_putname(const char *name); extern void __audit_inode(const char *name, const struct dentry *dentry); extern void __audit_inode_child(const struct dentry *dentry, const struct inode *parent); +extern void __audit_seccomp(unsigned long syscall); extern void __audit_ptrace(struct task_struct *t); static inline int audit_dummy_context(void) @@ -453,6 +454,12 @@ static inline void audit_inode_child(const struct dentry *dentry, } void audit_core_dumps(long signr); +static inline void audit_seccomp(unsigned long syscall) +{ + if (unlikely(!audit_dummy_context())) + __audit_seccomp(syscall); +} + static inline void audit_ptrace(struct task_struct *t) { if (unlikely(!audit_dummy_context())) @@ -558,6 +565,7 @@ extern int audit_signals; #define audit_inode(n,d) do { (void)(d); } while (0) #define audit_inode_child(i,p) do { ; } while (0) #define audit_core_dumps(i) do { ; } while (0) +#define audit_seccomp(i) do { ; } while (0) #define auditsc_get_stamp(c,t,s) (0) #define audit_get_loginuid(t) (-1) #define audit_get_sessionid(t) (-1) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 7c49514..e9bcb93 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2529,6 +2529,25 @@ void __audit_mmap_fd(int fd, int flags) context->type = AUDIT_MMAP; } +static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr) +{ + uid_t auid, uid; + gid_t gid; + unsigned int sessionid; + + auid = audit_get_loginuid(current); + sessionid = audit_get_sessionid(current); + current_uid_gid(&uid, &gid); + + audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u", + auid, uid, gid, sessionid); + audit_log_task_context(ab); + audit_log_format(ab, " pid=%d comm=", current->pid); + audit_log_untrustedstring(ab, current->comm); + audit_log_format(ab, " reason="); + audit_log_string(ab, reason); + audit_log_format(ab, " sig=%ld", signr); +} /** * audit_core_dumps - record information about processes that end abnormally * @signr: signal value @@ -2539,10 +2558,6 @@ void __audit_mmap_fd(int fd, int flags) void audit_core_dumps(long signr) { struct audit_buffer *ab; - u32 sid; - uid_t auid = audit_get_loginuid(current), uid; - gid_t gid; - unsigned int sessionid = audit_get_sessionid(current); if (!audit_enabled) return; @@ -2551,24 +2566,17 @@ void audit_core_dumps(long signr) return; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); - current_uid_gid(&uid, &gid); - audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u", - auid, uid, gid, sessionid); - security_task_getsecid(current, &sid); - if (sid) { - char *ctx = NULL; - u32 len; + audit_log_abend(ab, "memory violation", signr); + audit_log_end(ab); +} - if (security_secid_to_secctx(sid, &ctx, &len)) - audit_log_format(ab, " ssid=%u", sid); - else { - audit_log_format(ab, " subj=%s", ctx); - security_release_secctx(ctx, len); - } - } - audit_log_format(ab, " pid=%d comm=", current->pid); - audit_log_untrustedstring(ab, current->comm); - audit_log_format(ab, " sig=%ld", signr); +void __audit_seccomp(unsigned long syscall) +{ + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); + audit_log_abend(ab, "seccomp", SIGKILL); + audit_log_format(ab, " syscall=%ld", syscall); audit_log_end(ab); } diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 57d4b13..e8d76c5 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -6,6 +6,7 @@ * This defines a simple but solid secure-computing mode. */ +#include #include #include #include @@ -54,6 +55,7 @@ void __secure_computing(int this_syscall) #ifdef SECCOMP_DEBUG dump_stack(); #endif + audit_seccomp(this_syscall); do_exit(SIGKILL); } -- cgit v0.10.2 From d7e7528bcd456f5c36ad4a202ccfb43c5aa98bc4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:06 -0500 Subject: Audit: push audit success and retcode into arch ptrace.h The audit system previously expected arches calling to audit_syscall_exit to supply as arguments if the syscall was a success and what the return code was. Audit also provides a helper AUDITSC_RESULT which was supposed to simplify things by converting from negative retcodes to an audit internal magic value stating success or failure. This helper was wrong and could indicate that a valid pointer returned to userspace was a failed syscall. The fix is to fix the layering foolishness. We now pass audit_syscall_exit a struct pt_reg and it in turns calls back into arch code to collect the return value and to determine if the syscall was a success or failure. We also define a generic is_syscall_success() macro which determines success/failure based on if the value is < -MAX_ERRNO. This works for arches like x86 which do not use a separate mechanism to indicate syscall failure. We make both the is_syscall_success() and regs_return_value() static inlines instead of macros. The reason is because the audit function must take a void* for the regs. (uml calls theirs struct uml_pt_regs instead of just struct pt_regs so audit_syscall_exit can't take a struct pt_regs). Since the audit function takes a void* we need to use static inlines to cast it back to the arch correct structure to dereference it. The other major change is that on some arches, like ia64, MIPS and ppc, we change regs_return_value() to give us the negative value on syscall failure. THE only other user of this macro, kretprobe_example.c, won't notice and it makes the value signed consistently for the audit functions across all archs. In arch/sh/kernel/ptrace_64.c I see that we were using regs[9] in the old audit code as the return value. But the ptrace_64.h code defined the macro regs_return_value() as regs[3]. I have no idea which one is correct, but this patch now uses the regs_return_value() function, so it now uses regs[3]. For powerpc we previously used regs->result but now use the regs_return_value() function which uses regs->gprs[3]. regs->gprs[3] is always positive so the regs_return_value(), much like ia64 makes it negative before calling the audit code when appropriate. Signed-off-by: Eric Paris Acked-by: H. Peter Anvin [for x86 portion] Acked-by: Tony Luck [for ia64] Acked-by: Richard Weinberger [for uml] Acked-by: David S. Miller [for sparc] Acked-by: Ralf Baechle [for mips] Acked-by: Benjamin Herrenschmidt [for ppc] diff --git a/arch/ia64/include/asm/ptrace.h b/arch/ia64/include/asm/ptrace.h index f5cb276..68c98f5 100644 --- a/arch/ia64/include/asm/ptrace.h +++ b/arch/ia64/include/asm/ptrace.h @@ -246,7 +246,18 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs) return regs->ar_bspstore; } -#define regs_return_value(regs) ((regs)->r8) +static inline int is_syscall_success(struct pt_regs *regs) +{ + return regs->r10 != -1; +} + +static inline long regs_return_value(struct pt_regs *regs) +{ + if (is_syscall_success(regs)) + return regs->r8; + else + return -regs->r8; +} /* Conserve space in histogram by encoding slot bits in address * bits 2 and 3 rather than bits 0 and 1. diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 8848f43..2c15408 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1268,14 +1268,7 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, { int step; - if (unlikely(current->audit_context)) { - int success = AUDITSC_RESULT(regs.r10); - long result = regs.r8; - - if (success != AUDITSC_SUCCESS) - result = -result; - audit_syscall_exit(success, result); - } + audit_syscall_exit(®s); step = test_thread_flag(TIF_SINGLESTEP); if (step || test_thread_flag(TIF_SYSCALL_TRACE)) diff --git a/arch/microblaze/include/asm/ptrace.h b/arch/microblaze/include/asm/ptrace.h index 816bee6..94e92c8 100644 --- a/arch/microblaze/include/asm/ptrace.h +++ b/arch/microblaze/include/asm/ptrace.h @@ -61,6 +61,11 @@ struct pt_regs { #define instruction_pointer(regs) ((regs)->pc) #define profile_pc(regs) instruction_pointer(regs) +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->r3; +} + #else /* __KERNEL__ */ /* pt_regs offsets used by gdbserver etc in ptrace syscalls */ diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index 043cb58..f564b1b 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -159,8 +159,7 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) { int step; - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->r3), regs->r3); + audit_syscall_exit(regs); step = test_thread_flag(TIF_SINGLESTEP); if (step || test_thread_flag(TIF_SYSCALL_TRACE)) diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h index de39b1f..7d40950 100644 --- a/arch/mips/include/asm/ptrace.h +++ b/arch/mips/include/asm/ptrace.h @@ -137,7 +137,19 @@ extern int ptrace_set_watch_regs(struct task_struct *child, */ #define user_mode(regs) (((regs)->cp0_status & KU_MASK) == KU_USER) -#define regs_return_value(_regs) ((_regs)->regs[2]) +static inline int is_syscall_success(struct pt_regs *regs) +{ + return !regs->regs[7]; +} + +static inline long regs_return_value(struct pt_regs *regs) +{ + if (is_syscall_success(regs)) + return regs->regs[2]; + else + return -regs->regs[2]; +} + #define instruction_pointer(regs) ((regs)->cp0_epc) #define profile_pc(regs) instruction_pointer(regs) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 4e6ea1f..ab0f196 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -572,9 +572,7 @@ out: */ asmlinkage void syscall_trace_leave(struct pt_regs *regs) { - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->regs[7]), - -regs->regs[2]); + audit_syscall_exit(regs); if (!(current->ptrace & PT_PTRACED)) return; diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 48223f9..78a2051 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -86,7 +86,18 @@ struct pt_regs { #define instruction_pointer(regs) ((regs)->nip) #define user_stack_pointer(regs) ((regs)->gpr[1]) #define kernel_stack_pointer(regs) ((regs)->gpr[1]) -#define regs_return_value(regs) ((regs)->gpr[3]) +static inline int is_syscall_success(struct pt_regs *regs) +{ + return !(regs->ccr & 0x10000000); +} + +static inline long regs_return_value(struct pt_regs *regs) +{ + if (is_syscall_success(regs)) + return regs->gpr[3]; + else + return -regs->gpr[3]; +} #ifdef CONFIG_SMP extern unsigned long profile_pc(struct pt_regs *regs); diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 5de73db..09d31c1 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1748,9 +1748,7 @@ void do_syscall_trace_leave(struct pt_regs *regs) { int step; - if (unlikely(current->audit_context)) - audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, - regs->result); + audit_syscall_exit(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->result); diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 56da355..aeb77f0 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -541,9 +541,13 @@ struct user_regs_struct #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN) #define user_stack_pointer(regs)((regs)->gprs[15]) -#define regs_return_value(regs)((regs)->gprs[2]) #define profile_pc(regs) instruction_pointer(regs) +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->gprs[2]; +} + int regs_query_register_offset(const char *name); const char *regs_query_register_name(unsigned int offset); unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 573bc29..f527586 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -751,9 +751,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) asmlinkage void do_syscall_trace_exit(struct pt_regs *regs) { - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), - regs->gprs[2]); + audit_syscall_exit(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->gprs[2]); diff --git a/arch/sh/include/asm/ptrace_32.h b/arch/sh/include/asm/ptrace_32.h index 6c2239c..2d3e906 100644 --- a/arch/sh/include/asm/ptrace_32.h +++ b/arch/sh/include/asm/ptrace_32.h @@ -76,7 +76,10 @@ struct pt_dspregs { #ifdef __KERNEL__ #define MAX_REG_OFFSET offsetof(struct pt_regs, tra) -#define regs_return_value(_regs) ((_regs)->regs[0]) +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->regs[0]; +} #endif /* __KERNEL__ */ diff --git a/arch/sh/include/asm/ptrace_64.h b/arch/sh/include/asm/ptrace_64.h index bf9be77..eb3fcce 100644 --- a/arch/sh/include/asm/ptrace_64.h +++ b/arch/sh/include/asm/ptrace_64.h @@ -13,7 +13,10 @@ struct pt_regs { #ifdef __KERNEL__ #define MAX_REG_OFFSET offsetof(struct pt_regs, tregs[7]) -#define regs_return_value(_regs) ((_regs)->regs[3]) +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->regs[3]; +} #endif /* __KERNEL__ */ diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 92b3c27..c0b5c17 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -530,9 +530,7 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) { int step; - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]), - regs->regs[0]); + audit_syscall_exit(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->regs[0]); diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index c8f9764..ba720d6 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -548,9 +548,7 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) { int step; - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->regs[9]), - regs->regs[9]); + audit_syscall_exit(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->regs[9]); diff --git a/arch/sparc/include/asm/ptrace.h b/arch/sparc/include/asm/ptrace.h index a0e1bcf..c00c3b5 100644 --- a/arch/sparc/include/asm/ptrace.h +++ b/arch/sparc/include/asm/ptrace.h @@ -207,7 +207,15 @@ do { current_thread_info()->syscall_noerror = 1; \ #define instruction_pointer(regs) ((regs)->tpc) #define instruction_pointer_set(regs, val) ((regs)->tpc = (val)) #define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP]) -#define regs_return_value(regs) ((regs)->u_regs[UREG_I0]) +static inline int is_syscall_success(struct pt_regs *regs) +{ + return !(regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY)); +} + +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->u_regs[UREG_I0]; +} #ifdef CONFIG_SMP extern unsigned long profile_pc(struct pt_regs *); #else diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 96ee50a..c73c8c5 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -1086,17 +1086,8 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) asmlinkage void syscall_trace_leave(struct pt_regs *regs) { -#ifdef CONFIG_AUDITSYSCALL - if (unlikely(current->audit_context)) { - unsigned long tstate = regs->tstate; - int result = AUDITSC_SUCCESS; + audit_syscall_exit(regs); - if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) - result = AUDITSC_FAILURE; - - audit_syscall_exit(result, regs->u_regs[UREG_I0]); - } -#endif if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->u_regs[UREG_G1]); diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index c9da32b..2ccf25c 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -175,8 +175,8 @@ void syscall_trace(struct uml_pt_regs *regs, int entryexit) UPT_SYSCALL_ARG2(regs), UPT_SYSCALL_ARG3(regs), UPT_SYSCALL_ARG4(regs)); - else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)), - UPT_SYSCALL_RET(regs)); + else + audit_syscall_exit(regs); } /* Fake a debug trap */ diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 3e27456..64ced0b 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -14,6 +14,7 @@ #include #include #include +#include /* Avoid __ASSEMBLER__'ifying just for this. */ #include @@ -208,12 +209,11 @@ sysexit_from_sys_call: TRACE_IRQS_ON sti movl %eax,%esi /* second arg, syscall return value */ - cmpl $0,%eax /* is it < 0? */ - setl %al /* 1 if so, 0 if not */ + cmpl $-MAX_ERRNO,%eax /* is it an error ? */ + setbe %al /* 1 if so, 0 if not */ movzbl %al,%edi /* zero-extend that into %edi */ - inc %edi /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */ - call audit_syscall_exit - movl RAX-ARGOFFSET(%rsp),%eax /* reload syscall return value */ + call __audit_syscall_exit + movq RAX-ARGOFFSET(%rsp),%rax /* reload syscall return value */ movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi cli TRACE_IRQS_OFF diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 22d0e21..a22facf 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -42,6 +42,7 @@ */ #include +#include #include #include #include @@ -466,11 +467,10 @@ sysexit_audit: TRACE_IRQS_ON ENABLE_INTERRUPTS(CLBR_ANY) movl %eax,%edx /* second arg, syscall return value */ - cmpl $0,%eax /* is it < 0? */ - setl %al /* 1 if so, 0 if not */ + cmpl $-MAX_ERRNO,%eax /* is it an error ? */ + setbe %al /* 1 if so, 0 if not */ movzbl %al,%eax /* zero-extend that */ - inc %eax /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */ - call audit_syscall_exit + call __audit_syscall_exit DISABLE_INTERRUPTS(CLBR_ANY) TRACE_IRQS_OFF movl TI_flags(%ebp), %ecx diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index a20e1cb..e51393d 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -55,6 +55,7 @@ #include #include #include +#include /* Avoid __ASSEMBLER__'ifying just for this. */ #include @@ -563,17 +564,16 @@ auditsys: jmp system_call_fastpath /* - * Return fast path for syscall audit. Call audit_syscall_exit() + * Return fast path for syscall audit. Call __audit_syscall_exit() * directly and then jump back to the fast path with TIF_SYSCALL_AUDIT * masked off. */ sysret_audit: movq RAX-ARGOFFSET(%rsp),%rsi /* second arg, syscall return value */ - cmpq $0,%rsi /* is it < 0? */ - setl %al /* 1 if so, 0 if not */ + cmpq $-MAX_ERRNO,%rsi /* is it < -MAX_ERRNO? */ + setbe %al /* 1 if so, 0 if not */ movzbl %al,%edi /* zero-extend that into %edi */ - inc %edi /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */ - call audit_syscall_exit + call __audit_syscall_exit movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi jmp sysret_check #endif /* CONFIG_AUDITSYSCALL */ diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 89a04c7..8b02187 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -1414,8 +1414,7 @@ void syscall_trace_leave(struct pt_regs *regs) { bool step; - if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax); + audit_syscall_exit(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->ax); diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c index 863f875..af17e1c 100644 --- a/arch/x86/kernel/vm86_32.c +++ b/arch/x86/kernel/vm86_32.c @@ -335,9 +335,9 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk if (info->flags & VM86_SCREEN_BITMAP) mark_screen_rdonly(tsk->mm); - /*call audit_syscall_exit since we do not exit via the normal paths */ + /*call __audit_syscall_exit since we do not exit via the normal paths */ if (unlikely(current->audit_context)) - audit_syscall_exit(AUDITSC_RESULT(0), 0); + __audit_syscall_exit(1, 0); __asm__ __volatile__( "movl %0,%%esp\n\t" diff --git a/arch/x86/um/shared/sysdep/ptrace.h b/arch/x86/um/shared/sysdep/ptrace.h index 711b162..5ef9344 100644 --- a/arch/x86/um/shared/sysdep/ptrace.h +++ b/arch/x86/um/shared/sysdep/ptrace.h @@ -3,3 +3,8 @@ #else #include "ptrace_64.h" #endif + +static inline long regs_return_value(struct uml_pt_regs *regs) +{ + return UPT_SYSCALL_RET(regs); +} diff --git a/include/linux/audit.h b/include/linux/audit.h index 6e1c533..3d65e4b 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -26,6 +26,7 @@ #include #include +#include /* The netlink messages for the audit system is divided into blocks: * 1000 - 1099 are for commanding the audit system @@ -408,10 +409,6 @@ struct audit_field { void *lsm_rule; }; -#define AUDITSC_INVALID 0 -#define AUDITSC_SUCCESS 1 -#define AUDITSC_FAILURE 2 -#define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS ) extern int __init audit_register_class(int class, unsigned *list); extern int audit_classify_syscall(int abi, unsigned syscall); extern int audit_classify_arch(int arch); @@ -424,7 +421,7 @@ extern void audit_free(struct task_struct *task); extern void audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); -extern void audit_syscall_exit(int failed, long return_code); +extern void __audit_syscall_exit(int ret_success, long ret_value); extern void __audit_getname(const char *name); extern void audit_putname(const char *name); extern void __audit_inode(const char *name, const struct dentry *dentry); @@ -438,6 +435,15 @@ static inline int audit_dummy_context(void) void *p = current->audit_context; return !p || *(int *)p; } +static inline void audit_syscall_exit(void *pt_regs) +{ + if (unlikely(current->audit_context)) { + int success = is_syscall_success(pt_regs); + int return_code = regs_return_value(pt_regs); + + __audit_syscall_exit(success, return_code); + } +} static inline void audit_getname(const char *name) { if (unlikely(!audit_dummy_context())) @@ -551,12 +557,12 @@ static inline void audit_mmap_fd(int fd, int flags) extern int audit_n_rules; extern int audit_signals; -#else +#else /* CONFIG_AUDITSYSCALL */ #define audit_finish_fork(t) #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) #define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0) -#define audit_syscall_exit(f,r) do { ; } while (0) +#define audit_syscall_exit(r) do { ; } while (0) #define audit_dummy_context() 1 #define audit_getname(n) do { ; } while (0) #define audit_putname(n) do { ; } while (0) @@ -587,7 +593,7 @@ extern int audit_signals; #define audit_ptrace(t) ((void)0) #define audit_n_rules 0 #define audit_signals 0 -#endif +#endif /* CONFIG_AUDITSYSCALL */ #ifdef CONFIG_AUDIT /* These are defined in audit.c */ diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 800f113..dd4cefa 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -112,6 +112,7 @@ #include /* For unlikely. */ #include /* For struct task_struct. */ +#include /* for IS_ERR_VALUE */ extern long arch_ptrace(struct task_struct *child, long request, @@ -265,6 +266,15 @@ static inline void ptrace_release_task(struct task_struct *task) #define force_successful_syscall_return() do { } while (0) #endif +#ifndef is_syscall_success +/* + * On most systems we can tell if a syscall is a success based on if the retval + * is an error value. On some systems like ia64 and powerpc they have different + * indicators of success/failure and must define their own. + */ +#define is_syscall_success(regs) (!IS_ERR_VALUE((unsigned long)(regs_return_value(regs)))) +#endif + /* * should define the following things inside #ifdef __KERNEL__. * diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e9bcb93..3d28538 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -70,6 +70,11 @@ #include "audit.h" +/* flags stating the success for a syscall */ +#define AUDITSC_INVALID 0 +#define AUDITSC_SUCCESS 1 +#define AUDITSC_FAILURE 2 + /* AUDIT_NAMES is the number of slots we reserve in the audit_context * for saving names from getname(). If we get more names we will allocate * a name dynamically and also add those to the list anchored by names_list. */ @@ -1724,8 +1729,7 @@ void audit_finish_fork(struct task_struct *child) /** * audit_syscall_exit - deallocate audit context after a system call - * @valid: success/failure flag - * @return_code: syscall return value + * @pt_regs: syscall registers * * Tear down after system call. If the audit context has been marked as * auditable (either because of the AUDIT_RECORD_CONTEXT state from @@ -1733,13 +1737,17 @@ void audit_finish_fork(struct task_struct *child) * message), then write out the syscall information. In call cases, * free the names stored from getname(). */ -void audit_syscall_exit(int valid, long return_code) +void __audit_syscall_exit(int success, long return_code) { struct task_struct *tsk = current; struct audit_context *context; - context = audit_get_context(tsk, valid, return_code); + if (success) + success = AUDITSC_SUCCESS; + else + success = AUDITSC_FAILURE; + context = audit_get_context(tsk, success, return_code); if (likely(!context)) return; -- cgit v0.10.2 From f031cd25568a390dc2c9c3a4015054183753449a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:06 -0500 Subject: audit: ia32entry.S sign extend error codes when calling 64 bit code In the ia32entry syscall exit audit fastpath we have assembly code which calls __audit_syscall_exit directly. This code was, however, zeroes the upper 32 bits of the return code. It then proceeded to call code which expects longs to be 64bits long. In order to handle code which expects longs to be 64bit we sign extend the return code if that code is an error. Thus the __audit_syscall_exit function can correctly handle using the values in snprintf("%ld"). This fixes the regression introduced in 5cbf1565f29eb57a86a. Old record: type=SYSCALL msg=audit(1306197182.256:281): arch=40000003 syscall=192 success=no exit=4294967283 New record: type=SYSCALL msg=audit(1306197182.256:281): arch=40000003 syscall=192 success=no exit=-13 Signed-off-by: Eric Paris Acked-by: H. Peter Anvin diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 64ced0b..025f0f0 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -210,7 +210,9 @@ sysexit_from_sys_call: sti movl %eax,%esi /* second arg, syscall return value */ cmpl $-MAX_ERRNO,%eax /* is it an error ? */ - setbe %al /* 1 if so, 0 if not */ + jbe 1f + movslq %eax, %rsi /* if error sign extend to 64 bits */ +1: setbe %al /* 1 if error, 0 if not */ movzbl %al,%edi /* zero-extend that into %edi */ call __audit_syscall_exit movq RAX-ARGOFFSET(%rsp),%rax /* reload syscall return value */ -- cgit v0.10.2 From b05d8447e7821695bc2fa3359431f7a664232743 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:06 -0500 Subject: audit: inline audit_syscall_entry to reduce burden on archs Every arch calls: if (unlikely(current->audit_context)) audit_syscall_entry() which requires knowledge about audit (the existance of audit_context) in the arch code. Just do it all in static inline in audit.h so that arch's can remain blissfully ignorant. Signed-off-by: Eric Paris diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 2c15408..dad9166 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1246,15 +1246,8 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, if (test_thread_flag(TIF_RESTORE_RSE)) ia64_sync_krbs(); - if (unlikely(current->audit_context)) { - long syscall; - int arch; - syscall = regs.r15; - arch = AUDIT_ARCH_IA64; - - audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3); - } + audit_syscall_entry(AUDIT_ARCH_IA64, regs.r15, arg0, arg1, arg2, arg3); return 0; } diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index f564b1b..6eb2aa9 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -147,10 +147,8 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) */ ret = -1L; - if (unlikely(current->audit_context)) - audit_syscall_entry(EM_MICROBLAZE, regs->r12, - regs->r5, regs->r6, - regs->r7, regs->r8); + audit_syscall_entry(EM_MICROBLAZE, regs->r12, regs->r5, regs->r6, + regs->r7, regs->r8); return ret ?: regs->r12; } diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index ab0f196..7786b60 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -560,10 +560,9 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs) } out: - if (unlikely(current->audit_context)) - audit_syscall_entry(audit_arch(), regs->regs[2], - regs->regs[4], regs->regs[5], - regs->regs[6], regs->regs[7]); + audit_syscall_entry(audit_arch(), regs->regs[2], + regs->regs[4], regs->regs[5], + regs->regs[6], regs->regs[7]); } /* diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 09d31c1..5b43325 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1724,22 +1724,20 @@ long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->gpr[0]); - if (unlikely(current->audit_context)) { #ifdef CONFIG_PPC64 - if (!is_32bit_task()) - audit_syscall_entry(AUDIT_ARCH_PPC64, - regs->gpr[0], - regs->gpr[3], regs->gpr[4], - regs->gpr[5], regs->gpr[6]); - else + if (!is_32bit_task()) + audit_syscall_entry(AUDIT_ARCH_PPC64, + regs->gpr[0], + regs->gpr[3], regs->gpr[4], + regs->gpr[5], regs->gpr[6]); + else #endif - audit_syscall_entry(AUDIT_ARCH_PPC, - regs->gpr[0], - regs->gpr[3] & 0xffffffff, - regs->gpr[4] & 0xffffffff, - regs->gpr[5] & 0xffffffff, - regs->gpr[6] & 0xffffffff); - } + audit_syscall_entry(AUDIT_ARCH_PPC, + regs->gpr[0], + regs->gpr[3] & 0xffffffff, + regs->gpr[4] & 0xffffffff, + regs->gpr[5] & 0xffffffff, + regs->gpr[6] & 0xffffffff); return ret ?: regs->gpr[0]; } diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index f527586..9d82ed4 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -740,12 +740,11 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->gprs[2]); - if (unlikely(current->audit_context)) - audit_syscall_entry(is_compat_task() ? - AUDIT_ARCH_S390 : AUDIT_ARCH_S390X, - regs->gprs[2], regs->orig_gpr2, - regs->gprs[3], regs->gprs[4], - regs->gprs[5]); + audit_syscall_entry(is_compat_task() ? + AUDIT_ARCH_S390 : AUDIT_ARCH_S390X, + regs->gprs[2], regs->orig_gpr2, + regs->gprs[3], regs->gprs[4], + regs->gprs[5]); return ret ?: regs->gprs[2]; } diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index c0b5c17..a3e6515 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -518,10 +518,9 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->regs[0]); - if (unlikely(current->audit_context)) - audit_syscall_entry(audit_arch(), regs->regs[3], - regs->regs[4], regs->regs[5], - regs->regs[6], regs->regs[7]); + audit_syscall_entry(audit_arch(), regs->regs[3], + regs->regs[4], regs->regs[5], + regs->regs[6], regs->regs[7]); return ret ?: regs->regs[0]; } diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index ba720d6..3d0080b 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -536,10 +536,9 @@ asmlinkage long long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->regs[9]); - if (unlikely(current->audit_context)) - audit_syscall_entry(audit_arch(), regs->regs[1], - regs->regs[2], regs->regs[3], - regs->regs[4], regs->regs[5]); + audit_syscall_entry(audit_arch(), regs->regs[1], + regs->regs[2], regs->regs[3], + regs->regs[4], regs->regs[5]); return ret ?: regs->regs[9]; } diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index c73c8c5..9388844 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -1071,15 +1071,14 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->u_regs[UREG_G1]); - if (unlikely(current->audit_context) && !ret) - audit_syscall_entry((test_thread_flag(TIF_32BIT) ? - AUDIT_ARCH_SPARC : - AUDIT_ARCH_SPARC64), - regs->u_regs[UREG_G1], - regs->u_regs[UREG_I0], - regs->u_regs[UREG_I1], - regs->u_regs[UREG_I2], - regs->u_regs[UREG_I3]); + audit_syscall_entry((test_thread_flag(TIF_32BIT) ? + AUDIT_ARCH_SPARC : + AUDIT_ARCH_SPARC64), + regs->u_regs[UREG_G1], + regs->u_regs[UREG_I0], + regs->u_regs[UREG_I1], + regs->u_regs[UREG_I2], + regs->u_regs[UREG_I3]); return ret; } diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 2ccf25c..06b1903 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -167,17 +167,15 @@ void syscall_trace(struct uml_pt_regs *regs, int entryexit) int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit; int tracesysgood; - if (unlikely(current->audit_context)) { - if (!entryexit) - audit_syscall_entry(HOST_AUDIT_ARCH, - UPT_SYSCALL_NR(regs), - UPT_SYSCALL_ARG1(regs), - UPT_SYSCALL_ARG2(regs), - UPT_SYSCALL_ARG3(regs), - UPT_SYSCALL_ARG4(regs)); - else - audit_syscall_exit(regs); - } + if (!entryexit) + audit_syscall_entry(HOST_AUDIT_ARCH, + UPT_SYSCALL_NR(regs), + UPT_SYSCALL_ARG1(regs), + UPT_SYSCALL_ARG2(regs), + UPT_SYSCALL_ARG3(regs), + UPT_SYSCALL_ARG4(regs)); + else + audit_syscall_exit(regs); /* Fake a debug trap */ if (is_singlestep) diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 025f0f0..cecfd9a 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -192,7 +192,7 @@ sysexit_from_sys_call: movl %ebx,%edx /* 3rd arg: 1st syscall arg */ movl %eax,%esi /* 2nd arg: syscall number */ movl $AUDIT_ARCH_I386,%edi /* 1st arg: audit arch */ - call audit_syscall_entry + call __audit_syscall_entry movl RAX-ARGOFFSET(%rsp),%eax /* reload syscall number */ cmpq $(IA32_NR_syscalls-1),%rax ja ia32_badsys diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index a22facf..1ccd742 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -456,7 +456,7 @@ sysenter_audit: movl %ebx,%ecx /* 3rd arg: 1st syscall arg */ movl %eax,%edx /* 2nd arg: syscall number */ movl $AUDIT_ARCH_I386,%eax /* 1st arg: audit arch */ - call audit_syscall_entry + call __audit_syscall_entry pushl_cfi %ebx movl PT_EAX(%esp),%eax /* reload syscall number */ jmp sysenter_do_call diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index e51393d..1ca66b6 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -549,7 +549,7 @@ badsys: #ifdef CONFIG_AUDITSYSCALL /* * Fast path for syscall audit without full syscall trace. - * We just call audit_syscall_entry() directly, and then + * We just call __audit_syscall_entry() directly, and then * jump back to the normal fast path. */ auditsys: @@ -559,7 +559,7 @@ auditsys: movq %rdi,%rdx /* 3rd arg: 1st syscall arg */ movq %rax,%rsi /* 2nd arg: syscall number */ movl $AUDIT_ARCH_X86_64,%edi /* 1st arg: audit arch */ - call audit_syscall_entry + call __audit_syscall_entry LOAD_ARGS 0 /* reload call-clobbered registers */ jmp system_call_fastpath diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 8b02187..5026738 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -1392,20 +1392,18 @@ long syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->orig_ax); - if (unlikely(current->audit_context)) { - if (IS_IA32) - audit_syscall_entry(AUDIT_ARCH_I386, - regs->orig_ax, - regs->bx, regs->cx, - regs->dx, regs->si); + if (IS_IA32) + audit_syscall_entry(AUDIT_ARCH_I386, + regs->orig_ax, + regs->bx, regs->cx, + regs->dx, regs->si); #ifdef CONFIG_X86_64 - else - audit_syscall_entry(AUDIT_ARCH_X86_64, - regs->orig_ax, - regs->di, regs->si, - regs->dx, regs->r10); + else + audit_syscall_entry(AUDIT_ARCH_X86_64, + regs->orig_ax, + regs->di, regs->si, + regs->dx, regs->r10); #endif - } return ret ?: regs->orig_ax; } diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index a0d042a..2dff698 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -334,8 +334,7 @@ void do_syscall_trace_enter(struct pt_regs *regs) do_syscall_trace(); #if 0 - if (unlikely(current->audit_context)) - audit_syscall_entry(current, AUDIT_ARCH_XTENSA..); + audit_syscall_entry(current, AUDIT_ARCH_XTENSA..); #endif } diff --git a/include/linux/audit.h b/include/linux/audit.h index 3d65e4b..f56ce26 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -418,9 +418,9 @@ extern int audit_classify_arch(int arch); extern void audit_finish_fork(struct task_struct *child); extern int audit_alloc(struct task_struct *task); extern void audit_free(struct task_struct *task); -extern void audit_syscall_entry(int arch, - int major, unsigned long a0, unsigned long a1, - unsigned long a2, unsigned long a3); +extern void __audit_syscall_entry(int arch, + int major, unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3); extern void __audit_syscall_exit(int ret_success, long ret_value); extern void __audit_getname(const char *name); extern void audit_putname(const char *name); @@ -435,6 +435,13 @@ static inline int audit_dummy_context(void) void *p = current->audit_context; return !p || *(int *)p; } +static inline void audit_syscall_entry(int arch, int major, unsigned long a0, + unsigned long a1, unsigned long a2, + unsigned long a3) +{ + if (unlikely(!audit_dummy_context())) + __audit_syscall_entry(arch, major, a0, a1, a2, a3); +} static inline void audit_syscall_exit(void *pt_regs) { if (unlikely(current->audit_context)) { diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 3d28538..b408100 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1632,7 +1632,7 @@ void audit_free(struct task_struct *tsk) * will only be written if another part of the kernel requests that it * be written). */ -void audit_syscall_entry(int arch, int major, +void __audit_syscall_entry(int arch, int major, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4) { -- cgit v0.10.2 From 997f5b6444f4608692ec807fb802fd9767c80e76 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:06 -0500 Subject: audit: remove AUDIT_SETUP_CONTEXT as it isn't used Audit contexts have 3 states. Disabled, which doesn't collect anything, build, which collects info but might not emit it, and record, which collects and emits. There is a 4th state, setup, which isn't used. Get rid of it. Signed-off-by: Eric Paris diff --git a/kernel/audit.h b/kernel/audit.h index 91e7071..8167668 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -36,12 +36,8 @@ enum audit_state { AUDIT_DISABLED, /* Do not create per-task audit_context. * No syscall-specific audit records can * be generated. */ - AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context, - * but don't necessarily fill it in at - * syscall entry time (i.e., filter - * instead). */ AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context, - * and always fill it in at syscall + * and fill it in at syscall * entry time. This makes a full * syscall record available if some * other part of the kernel decides it -- cgit v0.10.2 From 56179a6ec65a56e0279a58e35cb450d38f061b94 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:06 -0500 Subject: audit: drop some potentially inadvisable likely notations The audit code makes heavy use of likely() and unlikely() macros, but they don't always make sense. Drop any that seem questionable and let the computer do it's thing. Signed-off-by: Eric Paris diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b408100..d7382c2 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -805,7 +805,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, { struct audit_context *context = tsk->audit_context; - if (likely(!context)) + if (!context) return NULL; context->return_valid = return_valid; @@ -928,7 +928,7 @@ int audit_alloc(struct task_struct *tsk) return 0; /* Return if not auditing. */ state = audit_filter_task(tsk, &key); - if (likely(state == AUDIT_DISABLED)) + if (state == AUDIT_DISABLED) return 0; if (!(context = audit_alloc_context(state))) { @@ -1599,7 +1599,7 @@ void audit_free(struct task_struct *tsk) struct audit_context *context; context = audit_get_context(tsk, 0, 0); - if (likely(!context)) + if (!context) return; /* Check for system calls that do not go through the exit @@ -1640,7 +1640,7 @@ void __audit_syscall_entry(int arch, int major, struct audit_context *context = tsk->audit_context; enum audit_state state; - if (unlikely(!context)) + if (!context) return; /* @@ -1697,7 +1697,7 @@ void __audit_syscall_entry(int arch, int major, context->prio = 0; state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); } - if (likely(state == AUDIT_DISABLED)) + if (state == AUDIT_DISABLED) return; context->serial = 0; @@ -1748,7 +1748,7 @@ void __audit_syscall_exit(int success, long return_code) success = AUDITSC_FAILURE; context = audit_get_context(tsk, success, return_code); - if (likely(!context)) + if (!context) return; if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT) -- cgit v0.10.2 From 07c49417877f8658a6aa0ad9b4e21e4fd4df11b6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: inline checks for not needing to collect aux records A number of audit hooks make function calls before they determine that auxilary records do not need to be collected. Do those checks as static inlines since the most common case is going to be that records are not needed and we can skip the function call overhead. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index f56ce26..cf16faf 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -489,9 +489,9 @@ extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern void audit_log_task_context(struct audit_buffer *ab); extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode); -extern int audit_bprm(struct linux_binprm *bprm); -extern void audit_socketcall(int nargs, unsigned long *args); -extern int audit_sockaddr(int len, void *addr); +extern int __audit_bprm(struct linux_binprm *bprm); +extern void __audit_socketcall(int nargs, unsigned long *args); +extern int __audit_sockaddr(int len, void *addr); extern void __audit_fd_pair(int fd1, int fd2); extern int audit_set_macxattr(const char *name); extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr); @@ -519,6 +519,23 @@ static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid if (unlikely(!audit_dummy_context())) __audit_ipc_set_perm(qbytes, uid, gid, mode); } +static inline int audit_bprm(struct linux_binprm *bprm) +{ + if (unlikely(!audit_dummy_context())) + return __audit_bprm(bprm); + return 0; +} +static inline void audit_socketcall(int nargs, unsigned long *args) +{ + if (unlikely(!audit_dummy_context())) + __audit_socketcall(nargs, args); +} +static inline int audit_sockaddr(int len, void *addr) +{ + if (unlikely(!audit_dummy_context())) + return __audit_sockaddr(len, addr); + return 0; +} static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) { if (unlikely(!audit_dummy_context())) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d7382c2..e1062f6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2309,14 +2309,11 @@ void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mo context->ipc.has_perm = 1; } -int audit_bprm(struct linux_binprm *bprm) +int __audit_bprm(struct linux_binprm *bprm) { struct audit_aux_data_execve *ax; struct audit_context *context = current->audit_context; - if (likely(!audit_enabled || !context || context->dummy)) - return 0; - ax = kmalloc(sizeof(*ax), GFP_KERNEL); if (!ax) return -ENOMEM; @@ -2337,13 +2334,10 @@ int audit_bprm(struct linux_binprm *bprm) * @args: args array * */ -void audit_socketcall(int nargs, unsigned long *args) +void __audit_socketcall(int nargs, unsigned long *args) { struct audit_context *context = current->audit_context; - if (likely(!context || context->dummy)) - return; - context->type = AUDIT_SOCKETCALL; context->socketcall.nargs = nargs; memcpy(context->socketcall.args, args, nargs * sizeof(unsigned long)); @@ -2369,13 +2363,10 @@ void __audit_fd_pair(int fd1, int fd2) * * Returns 0 for success or NULL context or < 0 on error. */ -int audit_sockaddr(int len, void *a) +int __audit_sockaddr(int len, void *a) { struct audit_context *context = current->audit_context; - if (likely(!context || context->dummy)) - return 0; - if (!context->sockaddr) { void *p = kmalloc(sizeof(struct sockaddr_storage), GFP_KERNEL); if (!p) -- cgit v0.10.2 From 38cdce53daa0408a61fe6d86fe48f31515c9b840 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: drop audit_set_macxattr as it doesn't do anything unused. deleted. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index cf16faf..4f1efe3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -493,7 +493,6 @@ extern int __audit_bprm(struct linux_binprm *bprm); extern void __audit_socketcall(int nargs, unsigned long *args); extern int __audit_sockaddr(int len, void *addr); extern void __audit_fd_pair(int fd1, int fd2); -extern int audit_set_macxattr(const char *name); extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr); extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout); extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification); @@ -606,7 +605,6 @@ extern int audit_signals; #define audit_socketcall(n,a) ((void)0) #define audit_fd_pair(n,a) ((void)0) #define audit_sockaddr(len, addr) ({ 0; }) -#define audit_set_macxattr(n) do { ; } while (0) #define audit_mq_open(o,m,a) ((void)0) #define audit_mq_sendrecv(d,l,p,t) ((void)0) #define audit_mq_notify(d,n) ((void)0) -- cgit v0.10.2 From a4ff8dba7d8ce5ceb43fb27df66292251cc73bdc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: inline audit_free to simplify the look of generic code make the conditional a static inline instead of doing it in generic code. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 4f1efe3..8eb8bda 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -417,7 +417,7 @@ extern int audit_classify_arch(int arch); /* Public API */ extern void audit_finish_fork(struct task_struct *child); extern int audit_alloc(struct task_struct *task); -extern void audit_free(struct task_struct *task); +extern void __audit_free(struct task_struct *task); extern void __audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); @@ -435,6 +435,11 @@ static inline int audit_dummy_context(void) void *p = current->audit_context; return !p || *(int *)p; } +static inline void audit_free(struct task_struct *task) +{ + if (unlikely(task->audit_context)) + __audit_free(task); +} static inline void audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e1062f6..7aaeb38 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1594,7 +1594,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts * * Called from copy_process and do_exit */ -void audit_free(struct task_struct *tsk) +void __audit_free(struct task_struct *tsk) { struct audit_context *context; diff --git a/kernel/exit.c b/kernel/exit.c index 94ed6e2..88dcbbc 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -964,8 +964,7 @@ NORET_TYPE void do_exit(long code) acct_collect(code, group_dead); if (group_dead) tty_audit_exit(); - if (unlikely(tsk->audit_context)) - audit_free(tsk); + audit_free(tsk); tsk->exit_code = code; taskstats_exit(tsk, group_dead); -- cgit v0.10.2 From 7ff68e53ece8c175d2951bb8a30b3cce8f9c5579 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: reject entry,always rules We deprecated entry,always rules a long time ago. Reject those rules as invalid. Signed-off-by: Eric Paris diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index d94dde8..903caa2 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -235,13 +235,15 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) switch(listnr) { default: goto exit_err; - case AUDIT_FILTER_USER: - case AUDIT_FILTER_TYPE: #ifdef CONFIG_AUDITSYSCALL case AUDIT_FILTER_ENTRY: + if (rule->action == AUDIT_ALWAYS) + goto exit_err; case AUDIT_FILTER_EXIT: case AUDIT_FILTER_TASK: #endif + case AUDIT_FILTER_USER: + case AUDIT_FILTER_TYPE: ; } if (unlikely(rule->action == AUDIT_POSSIBLE)) { -- cgit v0.10.2 From 6422e78de6880c66a82af512d9bd0c85eb62e661 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: remove audit_finish_fork as it can't be called Audit entry,always rules are not allowed and are automatically changed in exit,always rules in userspace. The kernel refuses to load such rules. Thus a task in the middle of a syscall (and thus in audit_finish_fork()) can only be in one of two states: AUDIT_BUILD_CONTEXT or AUDIT_DISABLED. Since the current task cannot be in AUDIT_RECORD_CONTEXT we aren't every going to actually use the code in audit_finish_fork() since it will return without doing anything. Thus drop the code. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 8eb8bda..67b66c3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -415,7 +415,6 @@ extern int audit_classify_arch(int arch); #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ /* Public API */ -extern void audit_finish_fork(struct task_struct *child); extern int audit_alloc(struct task_struct *task); extern void __audit_free(struct task_struct *task); extern void __audit_syscall_entry(int arch, @@ -586,7 +585,6 @@ static inline void audit_mmap_fd(int fd, int flags) extern int audit_n_rules; extern int audit_signals; #else /* CONFIG_AUDITSYSCALL */ -#define audit_finish_fork(t) #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) #define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 7aaeb38..4d8920f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1707,26 +1707,6 @@ void __audit_syscall_entry(int arch, int major, context->ppid = 0; } -void audit_finish_fork(struct task_struct *child) -{ - struct audit_context *ctx = current->audit_context; - struct audit_context *p = child->audit_context; - if (!p || !ctx) - return; - if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT) - return; - p->arch = ctx->arch; - p->major = ctx->major; - memcpy(p->argv, ctx->argv, sizeof(ctx->argv)); - p->ctime = ctx->ctime; - p->dummy = ctx->dummy; - p->in_syscall = ctx->in_syscall; - p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL); - p->ppid = current->pid; - p->prio = ctx->prio; - p->current_state = ctx->current_state; -} - /** * audit_syscall_exit - deallocate audit context after a system call * @pt_regs: syscall registers diff --git a/kernel/fork.c b/kernel/fork.c index 443f512..c1e5c21 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1525,8 +1525,6 @@ long do_fork(unsigned long clone_flags, init_completion(&vfork); } - audit_finish_fork(p); - /* * We set PF_STARTING at creation in case tracing wants to * use this to distinguish a fully live task from one that -- cgit v0.10.2 From efaffd6e4417860c67576ac760dd6e8bbd15f006 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: allow matching on obj_uid Allow syscall exit filter matching based on the uid of the owner of an inode used in a syscall. aka: auditctl -a always,exit -S open -F obj_uid=0 -F perm=wa Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 67b66c3..55cb3da 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -223,6 +223,7 @@ #define AUDIT_PERM 106 #define AUDIT_DIR 107 #define AUDIT_FILETYPE 108 +#define AUDIT_OBJ_UID 109 #define AUDIT_ARG0 200 #define AUDIT_ARG1 (AUDIT_ARG0+1) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 903caa2..13e9974 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -461,6 +461,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: + case AUDIT_OBJ_UID: break; case AUDIT_ARCH: entry->rule.arch_f = f; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4d8920f..5cf3ecc 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -586,6 +586,18 @@ static int audit_filter_rules(struct task_struct *tsk, } } break; + case AUDIT_OBJ_UID: + if (name) { + result = audit_comparator(name->uid, f->op, f->val); + } else if (ctx) { + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_comparator(n->uid, f->op, f->val)) { + ++result; + break; + } + } + } + break; case AUDIT_WATCH: if (name) result = audit_watch_compare(rule->watch, name->ino, name->dev); -- cgit v0.10.2 From 54d3218b31aee5bc9c859ae60fbde933d922448b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:07 -0500 Subject: audit: allow audit matching on inode gid Much like the ability to filter audit on the uid of an inode collected, we should be able to filter on the gid of the inode. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 55cb3da..e36aa37 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -224,6 +224,7 @@ #define AUDIT_DIR 107 #define AUDIT_FILETYPE 108 #define AUDIT_OBJ_UID 109 +#define AUDIT_OBJ_GID 110 #define AUDIT_ARG0 200 #define AUDIT_ARG1 (AUDIT_ARG0+1) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 13e9974..f10605c 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -462,6 +462,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_ARG2: case AUDIT_ARG3: case AUDIT_OBJ_UID: + case AUDIT_OBJ_GID: break; case AUDIT_ARCH: entry->rule.arch_f = f; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 5cf3ecc..87b375f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -598,6 +598,18 @@ static int audit_filter_rules(struct task_struct *tsk, } } break; + case AUDIT_OBJ_GID: + if (name) { + result = audit_comparator(name->gid, f->op, f->val); + } else if (ctx) { + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_comparator(n->gid, f->op, f->val)) { + ++result; + break; + } + } + } + break; case AUDIT_WATCH: if (name) result = audit_watch_compare(rule->watch, name->ino, name->dev); -- cgit v0.10.2 From 0a300be6d5be8f66cd96609334710c268d0bfdce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:08 -0500 Subject: audit: remove task argument to audit_set_loginuid The function always deals with current. Don't expose an option pretending one can use it for something. You can't. Signed-off-by: Eric Paris diff --git a/fs/proc/base.c b/fs/proc/base.c index 8173dfd..e3cbebb 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1228,7 +1228,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf, goto out_free_page; } - length = audit_set_loginuid(current, loginuid); + length = audit_set_loginuid(loginuid); if (likely(length == 0)) length = count; diff --git a/include/linux/audit.h b/include/linux/audit.h index e36aa37..7cbd6fe 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -489,7 +489,7 @@ static inline void audit_ptrace(struct task_struct *t) extern unsigned int audit_serial(void); extern int auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); -extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); +extern int audit_set_loginuid(uid_t loginuid); #define audit_get_loginuid(t) ((t)->loginuid) #define audit_get_sessionid(t) ((t)->sessionid) extern void audit_log_task_context(struct audit_buffer *ab); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 87b375f..9d6dd7d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2163,16 +2163,16 @@ int auditsc_get_stamp(struct audit_context *ctx, static atomic_t session_id = ATOMIC_INIT(0); /** - * audit_set_loginuid - set a task's audit_context loginuid - * @task: task whose audit context is being modified + * audit_set_loginuid - set current task's audit_context loginuid * @loginuid: loginuid value * * Returns 0. * * Called (set) from fs/proc/base.c::proc_loginuid_write(). */ -int audit_set_loginuid(struct task_struct *task, uid_t loginuid) +int audit_set_loginuid(uid_t loginuid) { + struct task_struct *task = current; unsigned int sessionid = atomic_inc_return(&session_id); struct audit_context *context = task->audit_context; -- cgit v0.10.2 From 633b45454503489209b0d9a45f9e3cd1b852c614 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:08 -0500 Subject: audit: only allow tasks to set their loginuid if it is -1 At the moment we allow tasks to set their loginuid if they have CAP_AUDIT_CONTROL. In reality we want tasks to set the loginuid when they log in and it be impossible to ever reset. We had to make it mutable even after it was once set (with the CAP) because on update and admin might have to restart sshd. Now sshd would get his loginuid and the next user which logged in using ssh would not be able to set his loginuid. Systemd has changed how userspace works and allowed us to make the kernel work the way it should. With systemd users (even admins) are not supposed to restart services directly. The system will restart the service for them. Thus since systemd is going to loginuid==-1, sshd would get -1, and sshd would be allowed to set a new loginuid without special permissions. If an admin in this system were to manually start an sshd he is inserting himself into the system chain of trust and thus, logically, it's his loginuid that should be used! Since we have old systems I make this a Kconfig option. Signed-off-by: Eric Paris diff --git a/fs/proc/base.c b/fs/proc/base.c index e3cbebb..482df23 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1197,9 +1197,6 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf, ssize_t length; uid_t loginuid; - if (!capable(CAP_AUDIT_CONTROL)) - return -EPERM; - rcu_read_lock(); if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) { rcu_read_unlock(); diff --git a/init/Kconfig b/init/Kconfig index a075765..5ad8b77 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -372,6 +372,20 @@ config AUDIT_TREE depends on AUDITSYSCALL select FSNOTIFY +config AUDIT_LOGINUID_IMMUTABLE + bool "Make audit loginuid immutable" + depends on AUDIT + help + The config option toggles if a task setting it's loginuid requires + CAP_SYS_AUDITCONTROL or if that task should require no special permissions + but should instead only allow setting its loginuid if it was never + previously set. On systems which use systemd or a similar central + process to restart login services this should be set to true. On older + systems in which an admin would typically have to directly stop and + start processes this should be set to false. Setting this to true allows + one to drop potentially dangerous capabilites from the login tasks, + but may not be backwards compatible with older init systems. + source "kernel/irq/Kconfig" menu "RCU Subsystem" diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9d6dd7d..bd084a1 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2173,9 +2173,18 @@ static atomic_t session_id = ATOMIC_INIT(0); int audit_set_loginuid(uid_t loginuid) { struct task_struct *task = current; - unsigned int sessionid = atomic_inc_return(&session_id); struct audit_context *context = task->audit_context; + unsigned int sessionid; + +#ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE + if (task->loginuid != -1) + return -EPERM; +#else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */ + if (!capable(CAP_AUDIT_CONTROL)) + return -EPERM; +#endif /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */ + sessionid = atomic_inc_return(&session_id); if (context && context->in_syscall) { struct audit_buffer *ab; -- cgit v0.10.2 From 4043cde8ecf7f7d880eb1133c201a3d392fd68c3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:08 -0500 Subject: audit: do not call audit_getname on error Just a code cleanup really. We don't need to make a function call just for it to return on error. This also makes the VFS function even easier to follow and removes a conditional on a hot path. Signed-off-by: Eric Paris diff --git a/fs/namei.c b/fs/namei.c index c283a1e..208c6aa 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -140,21 +140,19 @@ static int do_getname(const char __user *filename, char *page) static char *getname_flags(const char __user *filename, int flags, int *empty) { - char *tmp, *result; - - result = ERR_PTR(-ENOMEM); - tmp = __getname(); - if (tmp) { - int retval = do_getname(filename, tmp); - - result = tmp; - if (retval < 0) { - if (retval == -ENOENT && empty) - *empty = 1; - if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { - __putname(tmp); - result = ERR_PTR(retval); - } + char *result = __getname(); + int retval; + + if (!result) + return ERR_PTR(-ENOMEM); + + retval = do_getname(filename, result); + if (retval < 0) { + if (retval == -ENOENT && empty) + *empty = 1; + if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { + __putname(result); + return ERR_PTR(retval); } } audit_getname(result); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index bd084a1..9161e70 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1913,9 +1913,6 @@ void __audit_getname(const char *name) struct audit_context *context = current->audit_context; struct audit_names *n; - if (IS_ERR(name) || !name) - return; - if (!context->in_syscall) { #if AUDIT_DEBUG == 2 printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n", -- cgit v0.10.2 From 29ef73b7a823b77a7cd0bdd7d7cded3fb6c2587b Mon Sep 17 00:00:00 2001 From: Nathaniel Husted Date: Tue, 3 Jan 2012 14:23:09 -0500 Subject: Kernel: Audit Support For The ARM Platform This patch provides functionality to audit system call events on the ARM platform. The implementation was based off the structure of the MIPS platform and information in this (http://lists.fedoraproject.org/pipermail/arm/2009-October/000382.html) mailing list thread. The required audit_syscall_exit and audit_syscall_entry checks were added to ptrace using the standard registers for system call values (r0 through r3). A thread information flag was added for auditing (TIF_SYSCALL_AUDIT) and a meta-flag was added (_TIF_SYSCALL_WORK) to simplify modifications to the syscall entry/exit. Now, if either the TRACE flag is set or the AUDIT flag is set, the syscall_trace function will be executed. The prober changes were made to Kconfig to allow CONFIG_AUDITSYSCALL to be enabled. Due to platform availability limitations, this patch was only tested on the Android platform running the modified "android-goldfish-2.6.29" kernel. A test compile was performed using Code Sourcery's cross-compilation toolset and the current linux-3.0 stable kernel. The changes compile without error. I'm hoping, due to the simple modifications, the patch is "obviously correct". Signed-off-by: Nathaniel Husted Signed-off-by: Eric Paris diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h index feec867..f82ec22 100644 --- a/arch/arm/include/asm/kprobes.h +++ b/arch/arm/include/asm/kprobes.h @@ -24,7 +24,6 @@ #define MAX_INSN_SIZE 2 #define MAX_STACK_SIZE 64 /* 32 would probably be OK */ -#define regs_return_value(regs) ((regs)->ARM_r0) #define flush_insn_slot(p) do { } while (0) #define kretprobe_blacklist_size 0 diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 96187ff..451808b 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -189,6 +189,11 @@ static inline int valid_user_regs(struct pt_regs *regs) return 0; } +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->ARM_r0; +} + #define instruction_pointer(regs) (regs)->ARM_pc #ifdef CONFIG_SMP diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 0f30c3a..d4c24d4 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -129,6 +129,7 @@ extern void vfp_flush_hwstate(struct thread_info *); /* * thread information flags: * TIF_SYSCALL_TRACE - syscall trace active + * TIF_SYSCAL_AUDIT - syscall auditing active * TIF_SIGPENDING - signal pending * TIF_NEED_RESCHED - rescheduling necessary * TIF_NOTIFY_RESUME - callback before returning to user @@ -139,6 +140,7 @@ extern void vfp_flush_hwstate(struct thread_info *); #define TIF_NEED_RESCHED 1 #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ #define TIF_SYSCALL_TRACE 8 +#define TIF_SYSCALL_AUDIT 9 #define TIF_POLLING_NRFLAG 16 #define TIF_USING_IWMMXT 17 #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ @@ -149,11 +151,15 @@ extern void vfp_flush_hwstate(struct thread_info *); #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) +#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) #define _TIF_SECCOMP (1 << TIF_SECCOMP) +/* Checks for any syscall work in entry-common.S */ +#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT) + /* * Change these and you break ASM code in entry-common.S */ diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index b2a27b6..520889c 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -87,7 +87,7 @@ ENTRY(ret_from_fork) get_thread_info tsk ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing mov why, #1 - tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? + tst r1, #_TIF_SYSCALL_WORK @ are we tracing syscalls? beq ret_slow_syscall mov r1, sp mov r0, #1 @ trace exit [IP = 1] @@ -443,7 +443,7 @@ ENTRY(vector_swi) 1: #endif - tst r10, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? + tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls? bne __sys_trace cmp scno, #NR_syscalls @ check upper syscall limit diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 483727a..e1d5e19 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -906,11 +906,6 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) { unsigned long ip; - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return scno; - if (!(current->ptrace & PT_PTRACED)) - return scno; - /* * Save IP. IP is used to denote syscall entry/exit: * IP = 0 -> entry, = 1 -> exit @@ -918,6 +913,17 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) ip = regs->ARM_ip; regs->ARM_ip = why; + if (!ip) + audit_syscall_exit(regs); + else + audit_syscall_entry(AUDIT_ARCH_ARMEB, scno, regs->ARM_r0, + regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); + + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return scno; + if (!(current->ptrace & PT_PTRACED)) + return scno; + current_thread_info()->syscall = scno; /* the 0x80 provides a way for the tracing parent to distinguish diff --git a/init/Kconfig b/init/Kconfig index 5ad8b77..fe25ffb 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -355,7 +355,7 @@ config AUDIT config AUDITSYSCALL bool "Enable system-call auditing support" - depends on AUDIT && (X86 || PPC || S390 || IA64 || UML || SPARC64 || SUPERH) + depends on AUDIT && (X86 || PPC || S390 || IA64 || UML || SPARC64 || SUPERH || ARM) default y if SECURITY_SELINUX help Enable low-overhead system-call auditing infrastructure that -- cgit v0.10.2 From 02d86a568c6d2d335256864451ac8ce781bc5652 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:08 -0500 Subject: audit: allow interfield comparison in audit rules We wish to be able to audit when a uid=500 task accesses a file which is uid=0. Or vice versa. This patch introduces a new audit filter type AUDIT_FIELD_COMPARE which takes as an 'enum' which indicates which fields should be compared. At this point we only define the task->uid vs inode->uid, but other comparisons can be added. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 7cbd6fe..838e05f 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -182,7 +182,10 @@ * AUDIT_UNUSED_BITS is updated if need be. */ #define AUDIT_UNUSED_BITS 0x07FFFC00 +/* AUDIT_FIELD_COMPARE rule list */ +#define AUDIT_COMPARE_UID_TO_OBJ_UID 1 +#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_UID_TO_OBJ_UID /* Rule fields */ /* These are useful when checking the * task structure at task creation time @@ -225,6 +228,7 @@ #define AUDIT_FILETYPE 108 #define AUDIT_OBJ_UID 109 #define AUDIT_OBJ_GID 110 +#define AUDIT_FIELD_COMPARE 111 #define AUDIT_ARG0 200 #define AUDIT_ARG1 (AUDIT_ARG0+1) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index f10605c..a6c3f1a 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -526,7 +526,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, goto exit_free; break; case AUDIT_FILTERKEY: - err = -EINVAL; if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN) goto exit_free; str = audit_unpack_string(&bufp, &remain, f->val); @@ -543,6 +542,10 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, if (f->val & ~S_IFMT) goto exit_free; break; + case AUDIT_FIELD_COMPARE: + if (f->val > AUDIT_MAX_FIELD_COMPARE) + goto exit_free; + break; default: goto exit_free; } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9161e70..8fb2c8e 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -463,6 +463,32 @@ static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree) return 0; } +static int audit_field_compare(struct task_struct *tsk, + const struct cred *cred, + struct audit_field *f, + struct audit_context *ctx, + struct audit_names *name) +{ + struct audit_names *n; + + switch (f->val) { + case AUDIT_COMPARE_UID_TO_OBJ_UID: + if (name) { + return audit_comparator(cred->uid, f->op, name->uid); + } else if (ctx) { + list_for_each_entry(n, &ctx->names_list, list) { + if (audit_comparator(cred->uid, f->op, n->uid)) + return 1; + } + } + break; + default: + WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); + return 0; + } + return 0; +} + /* Determine if any context name data matches a rule's watch data */ /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. @@ -693,8 +719,10 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_FILETYPE: result = audit_match_filetype(ctx, f->val); break; + case AUDIT_FIELD_COMPARE: + result = audit_field_compare(tsk, cred, f, ctx, name); + break; } - if (!result) return 0; } -- cgit v0.10.2 From b34b039324bf081554ee8678f9b8c5d937e5206c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:08 -0500 Subject: audit: complex interfield comparison helper Rather than code the same loop over and over implement a helper function which uses some pointer magic to make it generic enough to be used numerous places as we implement more audit interfield comparisons Signed-off-by: Eric Paris diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 8fb2c8e..b12cc32 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -463,25 +463,53 @@ static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree) return 0; } +static int audit_compare_id(uid_t uid1, + struct audit_names *name, + unsigned long name_offset, + struct audit_field *f, + struct audit_context *ctx) +{ + struct audit_names *n; + unsigned long addr; + uid_t uid2; + int rc; + + if (name) { + addr = (unsigned long)name; + addr += name_offset; + + uid2 = *(uid_t *)addr; + rc = audit_comparator(uid1, f->op, uid2); + if (rc) + return rc; + } + + if (ctx) { + list_for_each_entry(n, &ctx->names_list, list) { + addr = (unsigned long)n; + addr += name_offset; + + uid2 = *(uid_t *)addr; + + rc = audit_comparator(uid1, f->op, uid2); + if (rc) + return rc; + } + } + return 0; +} + static int audit_field_compare(struct task_struct *tsk, const struct cred *cred, struct audit_field *f, struct audit_context *ctx, struct audit_names *name) { - struct audit_names *n; - switch (f->val) { case AUDIT_COMPARE_UID_TO_OBJ_UID: - if (name) { - return audit_comparator(cred->uid, f->op, name->uid); - } else if (ctx) { - list_for_each_entry(n, &ctx->names_list, list) { - if (audit_comparator(cred->uid, f->op, n->uid)) - return 1; - } - } - break; + return audit_compare_id(cred->uid, + name, offsetof(struct audit_names, uid), + f, ctx); default: WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); return 0; -- cgit v0.10.2 From c9fe685f7a17a0ee8bf3fbe51e40b1c8b8e65896 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 3 Jan 2012 14:23:08 -0500 Subject: audit: allow interfield comparison between gid and ogid Allow audit rules to compare the gid of the running task to the gid of the inode in question. Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 838e05f..fffbc217 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -184,8 +184,9 @@ /* AUDIT_FIELD_COMPARE rule list */ #define AUDIT_COMPARE_UID_TO_OBJ_UID 1 +#define AUDIT_COMPARE_GID_TO_OBJ_GID 2 -#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_UID_TO_OBJ_UID +#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_GID_TO_OBJ_GID /* Rule fields */ /* These are useful when checking the * task structure at task creation time diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b12cc32..861c7b9 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -474,6 +474,8 @@ static int audit_compare_id(uid_t uid1, uid_t uid2; int rc; + BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); + if (name) { addr = (unsigned long)name; addr += name_offset; @@ -510,6 +512,10 @@ static int audit_field_compare(struct task_struct *tsk, return audit_compare_id(cred->uid, name, offsetof(struct audit_names, uid), f, ctx); + case AUDIT_COMPARE_GID_TO_OBJ_GID: + return audit_compare_id(cred->gid, + name, offsetof(struct audit_names, gid), + f, ctx); default: WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); return 0; -- cgit v0.10.2 From 4a6633ed08af5ba67790b4d1adcdeb8ceb55677e Mon Sep 17 00:00:00 2001 From: Peter Moody Date: Tue, 13 Dec 2011 16:17:51 -0800 Subject: audit: implement all object interfield comparisons This completes the matrix of interfield comparisons between uid/gid information for the current task and the uid/gid information for inodes. aka I can audit based on differences between the euid of the process and the uid of fs objects. Signed-off-by: Peter Moody Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index fffbc217..67113cb 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -185,8 +185,16 @@ /* AUDIT_FIELD_COMPARE rule list */ #define AUDIT_COMPARE_UID_TO_OBJ_UID 1 #define AUDIT_COMPARE_GID_TO_OBJ_GID 2 +#define AUDIT_COMPARE_EUID_TO_OBJ_UID 3 +#define AUDIT_COMPARE_EGID_TO_OBJ_GID 4 +#define AUDIT_COMPARE_AUID_TO_OBJ_UID 5 +#define AUDIT_COMPARE_SUID_TO_OBJ_UID 6 +#define AUDIT_COMPARE_SGID_TO_OBJ_GID 7 +#define AUDIT_COMPARE_FSUID_TO_OBJ_UID 8 +#define AUDIT_COMPARE_FSGID_TO_OBJ_GID 9 + +#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_FSGID_TO_OBJ_GID -#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_GID_TO_OBJ_GID /* Rule fields */ /* These are useful when checking the * task structure at task creation time diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 861c7b9..b8cee46 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -508,6 +508,7 @@ static int audit_field_compare(struct task_struct *tsk, struct audit_names *name) { switch (f->val) { + /* process to file object comparisons */ case AUDIT_COMPARE_UID_TO_OBJ_UID: return audit_compare_id(cred->uid, name, offsetof(struct audit_names, uid), @@ -516,6 +517,34 @@ static int audit_field_compare(struct task_struct *tsk, return audit_compare_id(cred->gid, name, offsetof(struct audit_names, gid), f, ctx); + case AUDIT_COMPARE_EUID_TO_OBJ_UID: + return audit_compare_id(cred->euid, + name, offsetof(struct audit_names, uid), + f, ctx); + case AUDIT_COMPARE_EGID_TO_OBJ_GID: + return audit_compare_id(cred->egid, + name, offsetof(struct audit_names, gid), + f, ctx); + case AUDIT_COMPARE_AUID_TO_OBJ_UID: + return audit_compare_id(tsk->loginuid, + name, offsetof(struct audit_names, uid), + f, ctx); + case AUDIT_COMPARE_SUID_TO_OBJ_UID: + return audit_compare_id(cred->suid, + name, offsetof(struct audit_names, uid), + f, ctx); + case AUDIT_COMPARE_SGID_TO_OBJ_GID: + return audit_compare_id(cred->sgid, + name, offsetof(struct audit_names, gid), + f, ctx); + case AUDIT_COMPARE_FSUID_TO_OBJ_UID: + return audit_compare_id(cred->fsuid, + name, offsetof(struct audit_names, uid), + f, ctx); + case AUDIT_COMPARE_FSGID_TO_OBJ_GID: + return audit_compare_id(cred->fsgid, + name, offsetof(struct audit_names, gid), + f, ctx); default: WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); return 0; -- cgit v0.10.2 From 10d68360871657204885371cdf2594412675d2f9 Mon Sep 17 00:00:00 2001 From: Peter Moody Date: Wed, 4 Jan 2012 15:24:31 -0500 Subject: audit: comparison on interprocess fields This allows audit to specify rules in which we compare two fields of a process. Such as is the running process uid != to the running process euid? Signed-off-by: Peter Moody Signed-off-by: Eric Paris diff --git a/include/linux/audit.h b/include/linux/audit.h index 67113cb..9ff7a2c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -193,7 +193,29 @@ #define AUDIT_COMPARE_FSUID_TO_OBJ_UID 8 #define AUDIT_COMPARE_FSGID_TO_OBJ_GID 9 -#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_FSGID_TO_OBJ_GID +#define AUDIT_COMPARE_UID_TO_AUID 10 +#define AUDIT_COMPARE_UID_TO_EUID 11 +#define AUDIT_COMPARE_UID_TO_FSUID 12 +#define AUDIT_COMPARE_UID_TO_SUID 13 + +#define AUDIT_COMPARE_AUID_TO_FSUID 14 +#define AUDIT_COMPARE_AUID_TO_SUID 15 +#define AUDIT_COMPARE_AUID_TO_EUID 16 + +#define AUDIT_COMPARE_EUID_TO_SUID 17 +#define AUDIT_COMPARE_EUID_TO_FSUID 18 + +#define AUDIT_COMPARE_SUID_TO_FSUID 19 + +#define AUDIT_COMPARE_GID_TO_EGID 20 +#define AUDIT_COMPARE_GID_TO_FSGID 21 +#define AUDIT_COMPARE_GID_TO_SGID 22 + +#define AUDIT_COMPARE_EGID_TO_FSGID 23 +#define AUDIT_COMPARE_EGID_TO_SGID 24 +#define AUDIT_COMPARE_SGID_TO_FSGID 25 + +#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_SGID_TO_FSGID /* Rule fields */ /* These are useful when checking the diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b8cee46..593237e 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -545,6 +545,45 @@ static int audit_field_compare(struct task_struct *tsk, return audit_compare_id(cred->fsgid, name, offsetof(struct audit_names, gid), f, ctx); + /* uid comparisons */ + case AUDIT_COMPARE_UID_TO_AUID: + return audit_comparator(cred->uid, f->op, tsk->loginuid); + case AUDIT_COMPARE_UID_TO_EUID: + return audit_comparator(cred->uid, f->op, cred->euid); + case AUDIT_COMPARE_UID_TO_SUID: + return audit_comparator(cred->uid, f->op, cred->suid); + case AUDIT_COMPARE_UID_TO_FSUID: + return audit_comparator(cred->uid, f->op, cred->fsuid); + /* auid comparisons */ + case AUDIT_COMPARE_AUID_TO_EUID: + return audit_comparator(tsk->loginuid, f->op, cred->euid); + case AUDIT_COMPARE_AUID_TO_SUID: + return audit_comparator(tsk->loginuid, f->op, cred->suid); + case AUDIT_COMPARE_AUID_TO_FSUID: + return audit_comparator(tsk->loginuid, f->op, cred->fsuid); + /* euid comparisons */ + case AUDIT_COMPARE_EUID_TO_SUID: + return audit_comparator(cred->euid, f->op, cred->suid); + case AUDIT_COMPARE_EUID_TO_FSUID: + return audit_comparator(cred->euid, f->op, cred->fsuid); + /* suid comparisons */ + case AUDIT_COMPARE_SUID_TO_FSUID: + return audit_comparator(cred->suid, f->op, cred->fsuid); + /* gid comparisons */ + case AUDIT_COMPARE_GID_TO_EGID: + return audit_comparator(cred->gid, f->op, cred->egid); + case AUDIT_COMPARE_GID_TO_SGID: + return audit_comparator(cred->gid, f->op, cred->sgid); + case AUDIT_COMPARE_GID_TO_FSGID: + return audit_comparator(cred->gid, f->op, cred->fsgid); + /* egid comparisons */ + case AUDIT_COMPARE_EGID_TO_SGID: + return audit_comparator(cred->egid, f->op, cred->sgid); + case AUDIT_COMPARE_EGID_TO_FSGID: + return audit_comparator(cred->egid, f->op, cred->fsgid); + /* sgid comparison */ + case AUDIT_COMPARE_SGID_TO_FSGID: + return audit_comparator(cred->sgid, f->op, cred->fsgid); default: WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); return 0; -- cgit v0.10.2 From 5afb8a3f96573f7ea018abb768f5b6ebe1a6c1a4 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Tue, 20 Dec 2011 18:39:41 -0500 Subject: audit: fix signedness bug in audit_log_execve_info() In the loop, a size_t "len" is used to hold the return value of audit_log_single_execve_arg(), which returns -1 on error. In that case the error handling (len <= 0) will be bypassed since "len" is unsigned, and the loop continues with (p += len) being wrapped. Change the type of "len" to signed int to fix the error handling. size_t len; ... for (...) { len = audit_log_single_execve_arg(...); if (len <= 0) break; p += len; } Signed-off-by: Xi Wang Signed-off-by: Eric Paris diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 593237e..86584ec 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1362,8 +1362,8 @@ static void audit_log_execve_info(struct audit_context *context, struct audit_buffer **ab, struct audit_aux_data_execve *axi) { - int i; - size_t len, len_sent = 0; + int i, len; + size_t len_sent = 0; const char __user *p; char *buf; -- cgit v0.10.2 From 41fdc3054e23e3229edea27053522fe052d02ec2 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 7 Jan 2012 10:41:04 -0800 Subject: audit: treat s_id as an untrusted string The use of s_id should go through the untrusted string path, just to be extra careful. Signed-off-by: Kees Cook Acked-by: Mimi Zohar Signed-off-by: Eric Paris diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c index c5c5a72..2ad942f 100644 --- a/security/integrity/ima/ima_audit.c +++ b/security/integrity/ima/ima_audit.c @@ -56,9 +56,11 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, audit_log_format(ab, " name="); audit_log_untrustedstring(ab, fname); } - if (inode) - audit_log_format(ab, " dev=%s ino=%lu", - inode->i_sb->s_id, inode->i_ino); + if (inode) { + audit_log_format(ab, " dev="); + audit_log_untrustedstring(ab, inode->i_sb->s_id); + audit_log_format(ab, " ino=%lu", inode->i_ino); + } audit_log_format(ab, " res=%d", !result ? 0 : 1); audit_log_end(ab); } diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 7bd6f13..fc41b7c 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -235,10 +235,11 @@ static void dump_common_audit_data(struct audit_buffer *ab, audit_log_d_path(ab, "path=", &a->u.path); inode = a->u.path.dentry->d_inode; - if (inode) - audit_log_format(ab, " dev=%s ino=%lu", - inode->i_sb->s_id, - inode->i_ino); + if (inode) { + audit_log_format(ab, " dev="); + audit_log_untrustedstring(ab, inode->i_sb->s_id); + audit_log_format(ab, " ino=%lu", inode->i_ino); + } break; } case LSM_AUDIT_DATA_DENTRY: { @@ -248,10 +249,11 @@ static void dump_common_audit_data(struct audit_buffer *ab, audit_log_untrustedstring(ab, a->u.dentry->d_name.name); inode = a->u.dentry->d_inode; - if (inode) - audit_log_format(ab, " dev=%s ino=%lu", - inode->i_sb->s_id, - inode->i_ino); + if (inode) { + audit_log_format(ab, " dev="); + audit_log_untrustedstring(ab, inode->i_sb->s_id); + audit_log_format(ab, " ino=%lu", inode->i_ino); + } break; } case LSM_AUDIT_DATA_INODE: { @@ -266,8 +268,9 @@ static void dump_common_audit_data(struct audit_buffer *ab, dentry->d_name.name); dput(dentry); } - audit_log_format(ab, " dev=%s ino=%lu", inode->i_sb->s_id, - inode->i_ino); + audit_log_format(ab, " dev="); + audit_log_untrustedstring(ab, inode->i_sb->s_id); + audit_log_format(ab, " ino=%lu", inode->i_ino); break; } case LSM_AUDIT_DATA_TASK: -- cgit v0.10.2 From c158a35c8a681cf68d36f22f058f9f5466386c71 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 6 Jan 2012 14:07:10 -0800 Subject: audit: no leading space in audit_log_d_path prefix audit_log_d_path() injects an additional space before the prefix, which serves no purpose and doesn't mix well with other audit_log*() functions that do not sneak extra characters into the log. Signed-off-by: Kees Cook Signed-off-by: Eric Paris diff --git a/kernel/audit.c b/kernel/audit.c index 00efe47..705c25a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1423,7 +1423,7 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, char *p, *pathname; if (prefix) - audit_log_format(ab, " %s", prefix); + audit_log_format(ab, "%s", prefix); /* We will allow 11 spaces for ' (deleted)' to be appended */ pathname = kmalloc(PATH_MAX+11, ab->gfp_mask); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 86584ec..caaea6e 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1171,7 +1171,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk while (vma) { if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { - audit_log_d_path(ab, "exe=", + audit_log_d_path(ab, " exe=", &vma->vm_file->f_path); break; } @@ -1540,7 +1540,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, case 0: /* name was specified as a relative path and the * directory component is the cwd */ - audit_log_d_path(ab, "name=", &context->pwd); + audit_log_d_path(ab, " name=", &context->pwd); break; default: /* log the name's directory component */ @@ -1725,7 +1725,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (context->pwd.dentry && context->pwd.mnt) { ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); if (ab) { - audit_log_d_path(ab, "cwd=", &context->pwd); + audit_log_d_path(ab, " cwd=", &context->pwd); audit_log_end(ab); } } diff --git a/security/lsm_audit.c b/security/lsm_audit.c index fc41b7c..293b8c4 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -232,7 +232,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, case LSM_AUDIT_DATA_PATH: { struct inode *inode; - audit_log_d_path(ab, "path=", &a->u.path); + audit_log_d_path(ab, " path=", &a->u.path); inode = a->u.path.dentry->d_inode; if (inode) { @@ -318,7 +318,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, .dentry = u->dentry, .mnt = u->mnt }; - audit_log_d_path(ab, "path=", &path); + audit_log_d_path(ab, " path=", &path); break; } if (!u->addr) -- cgit v0.10.2 From e1616300a20c80396109c1cf013ba9a36055a3da Mon Sep 17 00:00:00 2001 From: Kazuya Mio Date: Thu, 1 Dec 2011 16:51:07 +0900 Subject: wake up s_wait_unfrozen when ->freeze_fs fails dd slept infinitely when fsfeeze failed because of EIO. To fix this problem, if ->freeze_fs fails, freeze_super() wakes up the tasks waiting for the filesystem to become unfrozen. When s_frozen isn't SB_UNFROZEN in __generic_file_aio_write(), the function sleeps until FITHAW ioctl wakes up s_wait_unfrozen. However, if ->freeze_fs fails, s_frozen is set to SB_UNFROZEN and then freeze_super() returns an error number. In this case, FITHAW ioctl returns EINVAL because s_frozen is already SB_UNFROZEN. There is no way to wake up s_wait_unfrozen, so __generic_file_aio_write() sleeps infinitely. Signed-off-by: Kazuya Mio Signed-off-by: Al Viro diff --git a/fs/super.c b/fs/super.c index de41e1e..6015c02 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1186,6 +1186,8 @@ int freeze_super(struct super_block *sb) printk(KERN_ERR "VFS:Filesystem freeze failed\n"); sb->s_frozen = SB_UNFROZEN; + smp_wmb(); + wake_up(&sb->s_wait_unfrozen); deactivate_locked_super(sb); return ret; } -- cgit v0.10.2 From 424a5334a5235c2fbb80090b18a065eeceb51d64 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 12 Jan 2012 12:41:36 +0100 Subject: vfs: remove printk from set_nlink() Don't log a message for set_nlink(0). Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro diff --git a/fs/inode.c b/fs/inode.c index 4fa4f09..fb10d86 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -322,9 +322,6 @@ EXPORT_SYMBOL(clear_nlink); void set_nlink(struct inode *inode, unsigned int nlink) { if (!nlink) { - printk_ratelimited(KERN_INFO - "set_nlink() clearing i_nlink on %s inode %li\n", - inode->i_sb->s_type->name, inode->i_ino); clear_nlink(inode); } else { /* Yes, some filesystems do change nlink from zero to one */ -- cgit v0.10.2 From 2727b1753934e154931d6b3bdf20c9b2398457a2 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 8 Dec 2011 15:43:53 +0100 Subject: I2C: OMAP: correct SYSC register offset for OMAP4 Correct OMAP_I2C_SYSC_REG offset in omap4 register map. Offset 0x20 is reserved and OMAP_I2C_SYSC_REG has 0x10 as offset. Signed-off-by: Alexander Aring [khilman@ti.com: minor changelog edits] Cc: stable@vger.kernel.org Signed-off-by: Kevin Hilman diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index fa23faa..257c1a5 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -235,7 +235,7 @@ static const u8 reg_map_ip_v2[] = { [OMAP_I2C_BUF_REG] = 0x94, [OMAP_I2C_CNT_REG] = 0x98, [OMAP_I2C_DATA_REG] = 0x9c, - [OMAP_I2C_SYSC_REG] = 0x20, + [OMAP_I2C_SYSC_REG] = 0x10, [OMAP_I2C_CON_REG] = 0xa4, [OMAP_I2C_OA_REG] = 0xa8, [OMAP_I2C_SA_REG] = 0xac, -- cgit v0.10.2 From 78e1cf42ee3efd2c3e8af0b97cf0a0a570cc0c1f Mon Sep 17 00:00:00 2001 From: Jan Weitzel Date: Wed, 7 Dec 2011 11:50:16 -0800 Subject: I2C: OMAP: NACK without STP On OMAP4 OMAP_I2C_STAT_NACK is causing a timeout on the next access. The isr cleans all flags in OMAP_I2C_CON_REG by setting OMAP_I2C_CON_STP OMAP_I2C_CON_STP is also set in omap_i2c_xfer_msg on the last message. According to the TI TSR the sequence for OMAP_I2C_STAT_NACK and OMAP_I2C_STAT_AL are nearly the same. Removing the OMAP_I2C_CON_STP part in the isr fix the problem. Tested on OMAP4430 and OMAP3530 (here NACK was not a problem) Fixes also booting on 2430sdp. Signed-off-by: Jan Weitzel Acked-by: Tony Lindgren Signed-off-by: Kevin Hilman diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 257c1a5..e0733b7 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -830,11 +830,9 @@ complete: ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); - if (stat & OMAP_I2C_STAT_NACK) { + if (stat & OMAP_I2C_STAT_NACK) err |= OMAP_I2C_STAT_NACK; - omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, - OMAP_I2C_CON_STP); - } + if (stat & OMAP_I2C_STAT_AL) { dev_err(dev->dev, "Arbitration lost\n"); err |= OMAP_I2C_STAT_AL; -- cgit v0.10.2 From 6145197be6cc0583fa1a2f4ec1079d366137061e Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Thu, 22 Dec 2011 15:56:36 +0100 Subject: i2c: OMAP: Add DT support for i2c controller Add initial DT support to retrieve the frequency using a DT attribute instead of the pdata pointer if of_node exist. Add documentation for omap i2c controller binding. Based on original patches from Manju and Grant. Signed-off-by: Benoit Cousson Cc: Ben Dooks Reviewed-by: Rob Herring Acked-by: Grant Likely Signed-off-by: Kevin Hilman diff --git a/Documentation/devicetree/bindings/i2c/omap-i2c.txt b/Documentation/devicetree/bindings/i2c/omap-i2c.txt new file mode 100644 index 0000000..56564aa --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/omap-i2c.txt @@ -0,0 +1,30 @@ +I2C for OMAP platforms + +Required properties : +- compatible : Must be "ti,omap3-i2c" or "ti,omap4-i2c" +- ti,hwmods : Must be "i2c", n being the instance number (1-based) +- #address-cells = <1>; +- #size-cells = <0>; + +Recommended properties : +- clock-frequency : Desired I2C bus clock frequency in Hz. Otherwise + the default 100 kHz frequency will be used. + +Optional properties: +- Child nodes conforming to i2c bus binding + +Note: Current implementation will fetch base address, irq and dma +from omap hwmod data base during device registration. +Future plan is to migrate hwmod data base contents into device tree +blob so that, all the required data will be used from device tree dts +file. + +Examples : + +i2c1: i2c@0 { + compatible = "ti,omap3-i2c"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "i2c1"; + clock-frequency = <400000>; +}; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index e0733b7..f713eac 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -37,6 +37,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -182,7 +185,9 @@ struct omap_i2c_dev { u32 latency; /* maximum mpu wkup latency */ void (*set_mpu_wkup_lat)(struct device *dev, long latency); - u32 speed; /* Speed of bus in Khz */ + u32 speed; /* Speed of bus in kHz */ + u32 dtrev; /* extra revision from DT */ + u32 flags; u16 cmd_err; u8 *buf; u8 *regs; @@ -266,11 +271,7 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) static void omap_i2c_unidle(struct omap_i2c_dev *dev) { - struct omap_i2c_bus_platform_data *pdata; - - pdata = dev->dev->platform_data; - - if (pdata->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) { + if (dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) { omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate); omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate); @@ -291,13 +292,10 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev) static void omap_i2c_idle(struct omap_i2c_dev *dev) { - struct omap_i2c_bus_platform_data *pdata; u16 iv; - pdata = dev->dev->platform_data; - dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); - if (pdata->rev == OMAP_I2C_IP_VERSION_2) + if (dev->dtrev == OMAP_I2C_IP_VERSION_2) omap_i2c_write_reg(dev, OMAP_I2C_IP_V2_IRQENABLE_CLR, 1); else omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); @@ -320,9 +318,6 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) unsigned long timeout; unsigned long internal_clk = 0; struct clk *fclk; - struct omap_i2c_bus_platform_data *pdata; - - pdata = dev->dev->platform_data; if (dev->rev >= OMAP_I2C_OMAP1_REV_2) { /* Disable I2C controller before soft reset */ @@ -373,7 +368,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) } omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); - if (pdata->flags & OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK) { + if (dev->flags & OMAP_I2C_FLAG_ALWAYS_ARMXOR_CLK) { /* * The I2C functional clock is the armxor_ck, so there's * no need to get "armxor_ck" separately. Now, if OMAP2420 @@ -397,7 +392,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) psc = fclk_rate / 12000000; } - if (!(pdata->flags & OMAP_I2C_FLAG_SIMPLE_CLOCK)) { + if (!(dev->flags & OMAP_I2C_FLAG_SIMPLE_CLOCK)) { /* * HSI2C controller internal clk rate should be 19.2 Mhz for @@ -406,7 +401,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) * The filter is iclk (fclk for HS) period. */ if (dev->speed > 400 || - pdata->flags & OMAP_I2C_FLAG_FORCE_19200_INT_CLK) + dev->flags & OMAP_I2C_FLAG_FORCE_19200_INT_CLK) internal_clk = 19200; else if (dev->speed > 100) internal_clk = 9600; @@ -475,7 +470,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) dev->errata = 0; - if (pdata->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207) + if (dev->flags & OMAP_I2C_FLAG_APPLY_ERRATA_I207) dev->errata |= I2C_OMAP_ERRATA_I207; /* Enable interrupts */ @@ -484,7 +479,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) OMAP_I2C_IE_AL) | ((dev->fifo_size) ? (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0); omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); - if (pdata->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) { + if (dev->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) { dev->pscstate = psc; dev->scllstate = scll; dev->sclhstate = sclh; @@ -804,9 +799,6 @@ omap_i2c_isr(int this_irq, void *dev_id) u16 bits; u16 stat, w; int err, count = 0; - struct omap_i2c_bus_platform_data *pdata; - - pdata = dev->dev->platform_data; if (pm_runtime_suspended(dev->dev)) return IRQ_NONE; @@ -873,7 +865,7 @@ complete: * Data reg in 2430, omap3 and * omap4 is 8 bit wide */ - if (pdata->flags & + if (dev->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) { if (dev->buf_len) { *dev->buf++ = w >> 8; @@ -916,7 +908,7 @@ complete: * Data reg in 2430, omap3 and * omap4 is 8 bit wide */ - if (pdata->flags & + if (dev->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) { if (dev->buf_len) { w |= *dev->buf++ << 8; @@ -963,6 +955,32 @@ static const struct i2c_algorithm omap_i2c_algo = { .functionality = omap_i2c_func, }; +#ifdef CONFIG_OF +static struct omap_i2c_bus_platform_data omap3_pdata = { + .rev = OMAP_I2C_IP_VERSION_1, + .flags = OMAP_I2C_FLAG_APPLY_ERRATA_I207 | + OMAP_I2C_FLAG_RESET_REGS_POSTIDLE | + OMAP_I2C_FLAG_BUS_SHIFT_2, +}; + +static struct omap_i2c_bus_platform_data omap4_pdata = { + .rev = OMAP_I2C_IP_VERSION_2, +}; + +static const struct of_device_id omap_i2c_of_match[] = { + { + .compatible = "ti,omap4-i2c", + .data = &omap4_pdata, + }, + { + .compatible = "ti,omap3-i2c", + .data = &omap3_pdata, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_i2c_of_match); +#endif + static int __devinit omap_i2c_probe(struct platform_device *pdev) { @@ -970,9 +988,10 @@ omap_i2c_probe(struct platform_device *pdev) struct i2c_adapter *adap; struct resource *mem, *irq, *ioarea; struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; + const struct of_device_id *match; irq_handler_t isr; int r; - u32 speed = 0; /* NOTE: driver uses the static register mapping */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -999,15 +1018,24 @@ omap_i2c_probe(struct platform_device *pdev) goto err_release_region; } - if (pdata != NULL) { - speed = pdata->clkrate; + match = of_match_device(omap_i2c_of_match, &pdev->dev); + if (match) { + u32 freq = 100000; /* default to 100000 Hz */ + + pdata = match->data; + dev->dtrev = pdata->rev; + dev->flags = pdata->flags; + + of_property_read_u32(node, "clock-frequency", &freq); + /* convert DT freq value in Hz into kHz for speed */ + dev->speed = freq / 1000; + } else if (pdata != NULL) { + dev->speed = pdata->clkrate; + dev->flags = pdata->flags; dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat; - } else { - speed = 100; /* Default speed */ - dev->set_mpu_wkup_lat = NULL; + dev->dtrev = pdata->rev; } - dev->speed = speed; dev->dev = &pdev->dev; dev->irq = irq->start; dev->base = ioremap(mem->start, resource_size(mem)); @@ -1018,9 +1046,9 @@ omap_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); - dev->reg_shift = (pdata->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3; + dev->reg_shift = (dev->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3; - if (pdata->rev == OMAP_I2C_IP_VERSION_2) + if (dev->dtrev == OMAP_I2C_IP_VERSION_2) dev->regs = (u8 *)reg_map_ip_v2; else dev->regs = (u8 *)reg_map_ip_v1; @@ -1033,7 +1061,7 @@ omap_i2c_probe(struct platform_device *pdev) if (dev->rev <= OMAP_I2C_REV_ON_3430) dev->errata |= I2C_OMAP3_1P153; - if (!(pdata->flags & OMAP_I2C_FLAG_NO_FIFO)) { + if (!(dev->flags & OMAP_I2C_FLAG_NO_FIFO)) { u16 s; /* Set up the fifo size - Get total size */ @@ -1056,7 +1084,7 @@ omap_i2c_probe(struct platform_device *pdev) /* calculate wakeup latency constraint for MPU */ if (dev->set_mpu_wkup_lat != NULL) dev->latency = (1000000 * dev->fifo_size) / - (1000 * speed / 8); + (1000 * dev->speed / 8); } /* reset ASAP, clearing any IRQs */ @@ -1072,7 +1100,7 @@ omap_i2c_probe(struct platform_device *pdev) } dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id, - pdata->rev, dev->rev >> 4, dev->rev & 0xf, dev->speed); + dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed); pm_runtime_put(dev->dev); @@ -1083,6 +1111,7 @@ omap_i2c_probe(struct platform_device *pdev) strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); adap->algo = &omap_i2c_algo; adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; /* i2c device drivers may be active on return from add_adapter() */ adap->nr = pdev->id; @@ -1092,6 +1121,8 @@ omap_i2c_probe(struct platform_device *pdev) goto err_free_irq; } + of_i2c_register_devices(adap); + return 0; err_free_irq: @@ -1164,6 +1195,7 @@ static struct platform_driver omap_i2c_driver = { .name = "omap_i2c", .owner = THIS_MODULE, .pm = OMAP_I2C_PM_OPS, + .of_match_table = of_match_ptr(omap_i2c_of_match), }, }; -- cgit v0.10.2 From e268337dfe26dfc7efd422a804dbb27977a3cccc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 17 Jan 2012 15:21:19 -0800 Subject: proc: clean up and fix /proc//mem handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jüri Aedla reported that the /proc//mem handling really isn't very robust, and it also doesn't match the permission checking of any of the other related files. This changes it to do the permission checks at open time, and instead of tracking the process, it tracks the VM at the time of the open. That simplifies the code a lot, but does mean that if you hold the file descriptor open over an execve(), you'll continue to read from the _old_ VM. That is different from our previous behavior, but much simpler. If somebody actually finds a load where this matters, we'll need to revert this commit. I suspect that nobody will ever notice - because the process mapping addresses will also have changed as part of the execve. So you cannot actually usefully access the fd across a VM change simply because all the offsets for IO would have changed too. Reported-by: Jüri Aedla Cc: Al Viro Signed-off-by: Linus Torvalds diff --git a/fs/proc/base.c b/fs/proc/base.c index 5485a53..662ddf2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -198,65 +198,7 @@ static int proc_root_link(struct dentry *dentry, struct path *path) return result; } -static struct mm_struct *__check_mem_permission(struct task_struct *task) -{ - struct mm_struct *mm; - - mm = get_task_mm(task); - if (!mm) - return ERR_PTR(-EINVAL); - - /* - * A task can always look at itself, in case it chooses - * to use system calls instead of load instructions. - */ - if (task == current) - return mm; - - /* - * If current is actively ptrace'ing, and would also be - * permitted to freshly attach with ptrace now, permit it. - */ - if (task_is_stopped_or_traced(task)) { - int match; - rcu_read_lock(); - match = (ptrace_parent(task) == current); - rcu_read_unlock(); - if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) - return mm; - } - - /* - * No one else is allowed. - */ - mmput(mm); - return ERR_PTR(-EPERM); -} - -/* - * If current may access user memory in @task return a reference to the - * corresponding mm, otherwise ERR_PTR. - */ -static struct mm_struct *check_mem_permission(struct task_struct *task) -{ - struct mm_struct *mm; - int err; - - /* - * Avoid racing if task exec's as we might get a new mm but validate - * against old credentials. - */ - err = mutex_lock_killable(&task->signal->cred_guard_mutex); - if (err) - return ERR_PTR(err); - - mm = __check_mem_permission(task); - mutex_unlock(&task->signal->cred_guard_mutex); - - return mm; -} - -struct mm_struct *mm_for_maps(struct task_struct *task) +static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) { struct mm_struct *mm; int err; @@ -267,7 +209,7 @@ struct mm_struct *mm_for_maps(struct task_struct *task) mm = get_task_mm(task); if (mm && mm != current->mm && - !ptrace_may_access(task, PTRACE_MODE_READ)) { + !ptrace_may_access(task, mode)) { mmput(mm); mm = ERR_PTR(-EACCES); } @@ -276,6 +218,11 @@ struct mm_struct *mm_for_maps(struct task_struct *task) return mm; } +struct mm_struct *mm_for_maps(struct task_struct *task) +{ + return mm_access(task, PTRACE_MODE_READ); +} + static int proc_pid_cmdline(struct task_struct *task, char * buffer) { int res = 0; @@ -752,38 +699,39 @@ static const struct file_operations proc_single_file_operations = { static int mem_open(struct inode* inode, struct file* file) { - file->private_data = (void*)((long)current->self_exec_id); + struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); + struct mm_struct *mm; + + if (!task) + return -ESRCH; + + mm = mm_access(task, PTRACE_MODE_ATTACH); + put_task_struct(task); + + if (IS_ERR(mm)) + return PTR_ERR(mm); + /* OK to pass negative loff_t, we can catch out-of-range */ file->f_mode |= FMODE_UNSIGNED_OFFSET; + file->private_data = mm; + return 0; } static ssize_t mem_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { - struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); + int ret; char *page; unsigned long src = *ppos; - int ret = -ESRCH; - struct mm_struct *mm; + struct mm_struct *mm = file->private_data; - if (!task) - goto out_no_task; + if (!mm) + return 0; - ret = -ENOMEM; page = (char *)__get_free_page(GFP_TEMPORARY); if (!page) - goto out; - - mm = check_mem_permission(task); - ret = PTR_ERR(mm); - if (IS_ERR(mm)) - goto out_free; - - ret = -EIO; - - if (file->private_data != (void*)((long)current->self_exec_id)) - goto out_put; + return -ENOMEM; ret = 0; @@ -810,13 +758,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, } *ppos = src; -out_put: - mmput(mm); -out_free: free_page((unsigned long) page); -out: - put_task_struct(task); -out_no_task: return ret; } @@ -825,27 +767,15 @@ static ssize_t mem_write(struct file * file, const char __user *buf, { int copied; char *page; - struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); unsigned long dst = *ppos; - struct mm_struct *mm; + struct mm_struct *mm = file->private_data; - copied = -ESRCH; - if (!task) - goto out_no_task; + if (!mm) + return 0; - copied = -ENOMEM; page = (char *)__get_free_page(GFP_TEMPORARY); if (!page) - goto out_task; - - mm = check_mem_permission(task); - copied = PTR_ERR(mm); - if (IS_ERR(mm)) - goto out_free; - - copied = -EIO; - if (file->private_data != (void *)((long)current->self_exec_id)) - goto out_mm; + return -ENOMEM; copied = 0; while (count > 0) { @@ -869,13 +799,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf, } *ppos = dst; -out_mm: - mmput(mm); -out_free: free_page((unsigned long) page); -out_task: - put_task_struct(task); -out_no_task: return copied; } @@ -895,11 +819,20 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) return file->f_pos; } +static int mem_release(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = file->private_data; + + mmput(mm); + return 0; +} + static const struct file_operations proc_mem_operations = { .llseek = mem_lseek, .read = mem_read, .write = mem_write, .open = mem_open, + .release = mem_release, }; static ssize_t environ_read(struct file *file, char __user *buf, -- cgit v0.10.2 From 07e8a51ff68353e01d795cceafbac9f54c49132b Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 12 Jan 2012 15:38:02 +0800 Subject: i2c-eg20t: use i2c_add_numbered_adapter to get a fixed bus number For EG20T and ML7213 IOH, the i2c controller numbers are fixed, using fixed bus number will make it much easier for platform code to use i2c_register_board_info() to register i2c devices. Signed-off-by: Feng Tang Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 3ef3557..8dc3f75 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -918,7 +918,9 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, pch_adap->dev.parent = &pdev->dev; pch_i2c_init(&adap_info->pch_data[i]); - ret = i2c_add_adapter(pch_adap); + + pch_adap->nr = i; + ret = i2c_add_numbered_adapter(pch_adap); if (ret) { pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i); goto err_add_adapter; -- cgit v0.10.2 From ff35e8b18984ad2a82cbd259fc07f0be4b34b1aa Mon Sep 17 00:00:00 2001 From: Toshiharu Okada Date: Mon, 26 Sep 2011 16:16:23 +0900 Subject: i2c-eg20t: modified the setting of transfer rate. This patch modified the setting value of I2C Bus Transfer Rate Setting Counter regisrer. Signed-off-by: Toshiharu Okada Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 8dc3f75..357a66c 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -243,7 +243,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap) if (pch_clk > PCH_MAX_CLK) pch_clk = 62500; - pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / pch_i2c_speed * 8; + pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8); /* Set transfer speed in I2CBC */ iowrite32(pch_i2cbc, p + PCH_I2CBC); -- cgit v0.10.2 From c3f4661f55f86b541d23c66dbb53ce7b0a34e777 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 28 Oct 2011 09:40:10 +0900 Subject: i2c-eg20t: Support new device LAPIS Semiconductor ML7831 IOH ML7831 is companion chip for Intel Atom E6xx series. Signed-off-by: Tomoya MORINAGA Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index cbe7a2f..3101dd5 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -682,19 +682,19 @@ config I2C_XILINX will be called xilinx_i2c. config I2C_EG20T - tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223)" + tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" depends on PCI help This driver is for PCH(Platform controller Hub) I2C of EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. This driver can access PCH I2C bus device. - This driver also can be used for OKI SEMICONDUCTOR IOH(Input/ - Output Hub), ML7213 and ML7223. - ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is - for MP(Media Phone) use. - ML7213/ML7223 is companion chip for Intel Atom E6xx series. - ML7213/ML7223 is completely compatible for Intel EG20T PCH. + This driver also can be used for LAPIS Semiconductor IOH(Input/ + Output Hub), ML7213, ML7223 and ML7831. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is + for MP(Media Phone) use and ML7831 IOH is for general purpose use. + ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH. comment "External I2C/SMBus adapter drivers" diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 357a66c..c72d7f9 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -137,6 +137,7 @@ Set the number of I2C instance max Intel EG20T PCH : 1ch OKI SEMICONDUCTOR ML7213 IOH : 2ch +OKI SEMICONDUCTOR ML7831 IOH : 1ch */ #define PCH_I2C_MAX_DEV 2 @@ -184,11 +185,13 @@ static DEFINE_MUTEX(pch_mutex); #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_I2C 0x802D #define PCI_DEVICE_ID_ML7223_I2C 0x8010 +#define PCI_DEVICE_ID_ML7831_I2C 0x8817 static DEFINE_PCI_DEVICE_TABLE(pch_pcidev_id) = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_I2C), 1, }, {0,} }; -- cgit v0.10.2 From 8956dc102ca26357850830f1d26132719c1ce6ee Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 28 Oct 2011 09:40:11 +0900 Subject: i2c-eg20t: Change-company-name-OKI-SEMICONDUCTOR to LAPIS Semiconductor On October 1 in 2011, OKI SEMICONDUCTOR Co., Ltd. changed the company name in to LAPIS Semiconductor Co., Ltd. Signed-off-by: Tomoya MORINAGA Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index c72d7f9..ca88776 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. * * 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 @@ -136,8 +136,8 @@ /* Set the number of I2C instance max Intel EG20T PCH : 1ch -OKI SEMICONDUCTOR ML7213 IOH : 2ch -OKI SEMICONDUCTOR ML7831 IOH : 1ch +LAPIS Semiconductor ML7213 IOH : 2ch +LAPIS Semiconductor ML7831 IOH : 1ch */ #define PCH_I2C_MAX_DEV 2 @@ -181,7 +181,7 @@ static int pch_clk = 50000; /* specifies I2C clock speed in KHz */ static wait_queue_head_t pch_event; static DEFINE_MUTEX(pch_mutex); -/* Definition for ML7213 by OKI SEMICONDUCTOR */ +/* Definition for ML7213 by LAPIS Semiconductor */ #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_I2C 0x802D #define PCI_DEVICE_ID_ML7223_I2C 0x8010 @@ -1063,8 +1063,8 @@ static void __exit pch_pci_exit(void) } module_exit(pch_pci_exit); -MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH I2C Driver"); +MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semico ML7213/ML7223/ML7831 IOH I2C"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Tomoya MORINAGA. "); +MODULE_AUTHOR("Tomoya MORINAGA. "); module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); module_param(pch_clk, int, (S_IRUSR | S_IWUSR)); -- cgit v0.10.2 From 89879a7eb81f69e6f63bdb2a442fb765c46482c0 Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 18 Jan 2012 10:40:44 +1100 Subject: security: update MAINTAINERS file with new git repo Update MAINTAINERS file with new git repo: git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git Signed-off-by: James Morris diff --git a/MAINTAINERS b/MAINTAINERS index 2a90101..ece8935 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5846,7 +5846,7 @@ F: drivers/mmc/host/sdhci-spear.c SECURITY SUBSYSTEM M: James Morris L: linux-security-module@vger.kernel.org (suggested Cc:) -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git W: http://security.wiki.kernel.org/ S: Supported F: security/ -- cgit v0.10.2 From 67175b855bfd6ed95ffeff95532173c07de6432d Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 17 Jan 2012 21:14:05 +0000 Subject: Fix compile breakage with kref.h This set of build failures just started appearing on parisc: In file included from drivers/input/serio/serio_raw.c:12: include/linux/kref.h: In function 'kref_get': include/linux/kref.h:40: error: 'TAINT_WARN' undeclared (first use in this function) include/linux/kref.h:40: error: (Each undeclared identifier is reported only once include/linux/kref.h:40: error: for each function it appears in.) include/linux/kref.h: In function 'kref_sub': include/linux/kref.h:65: error: 'TAINT_WARN' undeclared (first use in this function) It happens because TAINT_WARN is defined in kernel.h and this particular compile doesn't seem to include it (no idea why it's just manifesting .. probably some #include file untangling exposed it). Fix by adding #include to linux/kref.h Signed-off-by: James Bottomley Acked-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/include/linux/kref.h b/include/linux/kref.h index abc0120..9c07dce 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -17,6 +17,7 @@ #include #include +#include struct kref { atomic_t refcount; -- cgit v0.10.2 From 25add8cf99c9ec8b8dc0acd8b9241e963fc0d29c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 15 Jan 2012 11:05:59 +0900 Subject: TOMOYO: Accept \000 as a valid character. TOMOYO 2.5 in Linux 3.2 and later handles Unix domain socket's address. Thus, tomoyo_correct_word2() needs to accept \000 as a valid character, or TOMOYO 2.5 cannot handle Unix domain's abstract socket address. Reported-by: Steven Allen Signed-off-by: Tetsuo Handa CC: stable@vger.kernel.org [3.2+] Signed-off-by: James Morris diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 4a9b4b2..867558c 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -492,13 +492,13 @@ static bool tomoyo_correct_word2(const char *string, size_t len) if (d < '0' || d > '7' || e < '0' || e > '7') break; c = tomoyo_make_byte(c, d, e); - if (tomoyo_invalid(c)) - continue; /* pattern is not \000 */ + if (c <= ' ' || c >= 127) + continue; } goto out; } else if (in_repetition && c == '/') { goto out; - } else if (tomoyo_invalid(c)) { + } else if (c <= ' ' || c >= 127) { goto out; } } -- cgit v0.10.2 From efde8b6e16f11e7d1681c68d86c7fd51053cada7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 17 Jan 2012 20:39:40 +0000 Subject: KEYS: Add missing smp_rmb() primitives to the keyring search code Add missing smp_rmb() primitives to the keyring search code. When keyring payloads are appended to without replacement (thus using up spare slots in the key pointer array), an smp_wmb() is issued between the pointer assignment and the increment of the key count (nkeys). There should be corresponding read barriers between the read of nkeys and dereferences of keys[n] when n is dependent on the value of nkeys. Signed-off-by: David Howells Reviewed-by: Paul E. McKenney Signed-off-by: James Morris diff --git a/security/keys/gc.c b/security/keys/gc.c index bf4d8da..a42b455 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -145,7 +145,9 @@ static void key_gc_keyring(struct key *keyring, time_t limit) if (!klist) goto unlock_dont_gc; - for (loop = klist->nkeys - 1; loop >= 0; loop--) { + loop = klist->nkeys; + smp_rmb(); + for (loop--; loop >= 0; loop--) { key = klist->keys[loop]; if (test_bit(KEY_FLAG_DEAD, &key->flags) || (key->expiry > 0 && key->expiry <= limit)) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 37a7f3b..d605f75 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -319,7 +319,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, struct key *keyring, *key; key_ref_t key_ref; long err; - int sp, kix; + int sp, nkeys, kix; keyring = key_ref_to_ptr(keyring_ref); possessed = is_key_possessed(keyring_ref); @@ -380,7 +380,9 @@ descend: goto not_this_keyring; /* iterate through the keys in this keyring first */ - for (kix = 0; kix < keylist->nkeys; kix++) { + nkeys = keylist->nkeys; + smp_rmb(); + for (kix = 0; kix < nkeys; kix++) { key = keylist->keys[kix]; kflags = key->flags; @@ -421,7 +423,9 @@ descend: /* search through the keyrings nested in this one */ kix = 0; ascend: - for (; kix < keylist->nkeys; kix++) { + nkeys = keylist->nkeys; + smp_rmb(); + for (; kix < nkeys; kix++) { key = keylist->keys[kix]; if (key->type != &key_type_keyring) continue; @@ -515,7 +519,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, struct keyring_list *klist; unsigned long possessed; struct key *keyring, *key; - int loop; + int nkeys, loop; keyring = key_ref_to_ptr(keyring_ref); possessed = is_key_possessed(keyring_ref); @@ -524,7 +528,9 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, klist = rcu_dereference(keyring->payload.subscriptions); if (klist) { - for (loop = 0; loop < klist->nkeys; loop++) { + nkeys = klist->nkeys; + smp_rmb(); + for (loop = 0; loop < nkeys ; loop++) { key = klist->keys[loop]; if (key->type == ktype && @@ -622,7 +628,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B) struct keyring_list *keylist; struct key *subtree, *key; - int sp, kix, ret; + int sp, nkeys, kix, ret; rcu_read_lock(); @@ -645,7 +651,9 @@ descend: ascend: /* iterate through the remaining keys in this keyring */ - for (; kix < keylist->nkeys; kix++) { + nkeys = keylist->nkeys; + smp_rmb(); + for (; kix < nkeys; kix++) { key = keylist->keys[kix]; if (key == A) -- cgit v0.10.2 From ee0b31a25a010116f44fca6c96f4516d417793dd Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 17 Jan 2012 20:39:51 +0000 Subject: keys: fix trusted/encrypted keys sparse rcu_assign_pointer messages Define rcu_assign_keypointer(), which uses the key payload.rcudata instead of payload.data, to resolve the CONFIG_SPARSE_RCU_POINTER message: "incompatible types in comparison expression (different address spaces)" Replace the rcu_assign_pointer() calls in encrypted/trusted keys with rcu_assign_keypointer(). Signed-off-by: Mimi Zohar Signed-off-by: David Howells Signed-off-by: James Morris diff --git a/include/linux/key.h b/include/linux/key.h index 183a6af..bfc014c 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -293,6 +293,9 @@ static inline bool key_is_instantiated(const struct key *key) (rcu_dereference_protected((KEY)->payload.rcudata, \ rwsem_is_locked(&((struct key *)(KEY))->sem))) +#define rcu_assign_keypointer(KEY, PAYLOAD) \ + (rcu_assign_pointer((KEY)->payload.rcudata, PAYLOAD)) + #ifdef CONFIG_SYSCTL extern ctl_table key_sysctls[]; #endif diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 41144f7..d91efb6 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -810,7 +810,7 @@ static int encrypted_instantiate(struct key *key, const void *data, goto out; } - rcu_assign_pointer(key->payload.data, epayload); + rcu_assign_keypointer(key, epayload); out: kfree(datablob); return ret; @@ -874,7 +874,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) memcpy(new_epayload->payload_data, epayload->payload_data, epayload->payload_datalen); - rcu_assign_pointer(key->payload.data, new_epayload); + rcu_assign_keypointer(key, new_epayload); call_rcu(&epayload->rcu, encrypted_rcu_free); out: kfree(buf); diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c index df87272..8c16c3e 100644 --- a/security/keys/encrypted-keys/masterkey_trusted.c +++ b/security/keys/encrypted-keys/masterkey_trusted.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include "encrypted.h" /* * request_trusted_key - request the trusted key diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 0ed5fdf..2d5d041 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -993,7 +993,7 @@ out: kfree(datablob); kfree(options); if (!ret) - rcu_assign_pointer(key->payload.data, payload); + rcu_assign_keypointer(key, payload); else kfree(payload); return ret; @@ -1067,7 +1067,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen) goto out; } } - rcu_assign_pointer(key->payload.data, new_p); + rcu_assign_keypointer(key, new_p); call_rcu(&p->rcu, trusted_rcu_free); out: kfree(datablob); -- cgit v0.10.2 From 6ac6172a935d1faf7ef259802267657bc0007a62 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 17 Jan 2012 20:40:02 +0000 Subject: encrypted-keys: fix rcu and sparse messages Enabling CONFIG_PROVE_RCU and CONFIG_SPARSE_RCU_POINTER resulted in "suspicious rcu_dereference_check() usage!" and "incompatible types in comparison expression (different address spaces)" messages. Access the masterkey directly when holding the rwsem. Changelog v1: - Use either rcu_read_lock()/rcu_derefence_key()/rcu_read_unlock() or remove the unnecessary rcu_derefence() - David Howells Reported-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar Signed-off-by: David Howells Signed-off-by: James Morris diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index d91efb6..2d1bb8a 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, u8 **master_key, goto error; down_read(&ukey->sem); - upayload = rcu_dereference(ukey->payload.data); + upayload = ukey->payload.data; *master_key = upayload->data; *master_keylen = upayload->datalen; error: diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c index 8c16c3e..013f7e5 100644 --- a/security/keys/encrypted-keys/masterkey_trusted.c +++ b/security/keys/encrypted-keys/masterkey_trusted.c @@ -39,7 +39,7 @@ struct key *request_trusted_key(const char *trusted_desc, goto error; down_read(&tkey->sem); - tpayload = rcu_dereference(tkey->payload.data); + tpayload = tkey->payload.data; *master_key = tpayload->key; *master_keylen = tpayload->key_len; error: -- cgit v0.10.2 From 5e8898e97a5db4125d944070922164d1d09a2689 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jan 2012 17:12:03 +0200 Subject: lib: digital signature config option name change It was reported that DIGSIG is confusing name for digital signature module. It was suggested to rename DIGSIG to SIGNATURE. Requested-by: Linus Torvalds Suggested-by: Pavel Machek Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris diff --git a/include/linux/digsig.h b/include/linux/digsig.h index efae755..b01558b 100644 --- a/include/linux/digsig.h +++ b/include/linux/digsig.h @@ -46,7 +46,7 @@ struct signature_hdr { char mpi[0]; } __packed; -#if defined(CONFIG_DIGSIG) || defined(CONFIG_DIGSIG_MODULE) +#if defined(CONFIG_SIGNATURE) || defined(CONFIG_SIGNATURE_MODULE) int digsig_verify(struct key *keyring, const char *sig, int siglen, const char *digest, int digestlen); @@ -59,6 +59,6 @@ static inline int digsig_verify(struct key *keyring, const char *sig, return -EOPNOTSUPP; } -#endif /* CONFIG_DIGSIG */ +#endif /* CONFIG_SIGNATURE */ #endif /* _DIGSIG_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 201e1b3..854735d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -302,7 +302,7 @@ config MPILIB_EXTRA This code in unnecessary for RSA digital signature verification, and can be compiled if needed. -config DIGSIG +config SIGNATURE tristate "In-kernel signature checker" depends on KEYS select MPILIB diff --git a/lib/Makefile b/lib/Makefile index dace162..d71aae1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -119,7 +119,7 @@ obj-$(CONFIG_CORDIC) += cordic.o obj-$(CONFIG_DQL) += dynamic_queue_limits.o obj-$(CONFIG_MPILIB) += mpi/ -obj-$(CONFIG_DIGSIG) += digsig.o +obj-$(CONFIG_SIGNATURE) += digsig.o hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index d384ea9..ff60bf7 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -7,7 +7,7 @@ config INTEGRITY_DIGSIG boolean "Digital signature verification using multiple keyrings" depends on INTEGRITY && KEYS default n - select DIGSIG + select SIGNATURE help This option enables digital signature verification support using multiple keyrings. It defines separate keyrings for each -- cgit v0.10.2 From be440ec77325d02ad5e43676eb8d868426d35cd5 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jan 2012 17:12:04 +0200 Subject: lib: digital signature dependency fix Randy Dunlap reported build break: ERROR: "crypto_alloc_shash" [lib/digsig.ko] undefined! ERROR: "crypto_shash_final" [lib/digsig.ko] undefined! ERROR: "crypto_shash_update" [lib/digsig.ko] undefined! ERROR: "crypto_destroy_tfm" [lib/digsig.ko] undefined! Added CRYPTO dependency and selected SHA1 algorithm. Reported-by: Randy Dunlap Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris diff --git a/lib/Kconfig b/lib/Kconfig index 854735d..dbaf19e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -304,7 +304,8 @@ config MPILIB_EXTRA config SIGNATURE tristate "In-kernel signature checker" - depends on KEYS + depends on KEYS && CRYPTO + select CRYPTO_SHA1 select MPILIB help Digital signature verification. Currently only RSA is supported. -- cgit v0.10.2 From 68adcad570a2ce0496918ddd9f8c83138d9bcc93 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jan 2012 17:12:05 +0200 Subject: lib: MPILIB Kconfig description update It was reported that description of the MPILIB_EXTRA is confusing. Indeed it was copy-paste typo. It is fixed here. Reported-by: Valdis Kletnieks Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris diff --git a/lib/Kconfig b/lib/Kconfig index dbaf19e..1cbc1fe 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -296,11 +296,9 @@ config MPILIB_EXTRA bool "Multiprecision maths library - additional sources" depends on MPILIB help - Multiprecision maths library from GnuPG. - It is used to implement RSA digital signature verification, - which is used by IMA/EVM digital signature extension. - This code in unnecessary for RSA digital signature verification, - and can be compiled if needed. + Additional sources of multiprecision maths library from GnuPG. + This code is unnecessary for RSA digital signature verification, + but can be compiled if needed. config SIGNATURE tristate "In-kernel signature checker" -- cgit v0.10.2 From 2e5f094b9dbf9463ab93f86351cd1a8dc88942cc Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jan 2012 17:12:06 +0200 Subject: lib: Removed MPILIB, MPILIB_EXTRA, and SIGNATURE prompts As modules are expected to select MPILIB, MPILIB_EXTRA, and SIGNATURE, removed Kconfig prompts. Requested-by: Linus Torvalds Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris diff --git a/lib/Kconfig b/lib/Kconfig index 1cbc1fe..169eb7c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -286,14 +286,14 @@ config CORDIC calculations are in fixed point. Module will be called cordic. config MPILIB - tristate "Multiprecision maths library" + tristate help Multiprecision maths library from GnuPG. It is used to implement RSA digital signature verification, which is used by IMA/EVM digital signature extension. config MPILIB_EXTRA - bool "Multiprecision maths library - additional sources" + bool depends on MPILIB help Additional sources of multiprecision maths library from GnuPG. @@ -301,7 +301,7 @@ config MPILIB_EXTRA but can be compiled if needed. config SIGNATURE - tristate "In-kernel signature checker" + tristate depends on KEYS && CRYPTO select CRYPTO_SHA1 select MPILIB -- cgit v0.10.2 From f1be242c95257b199d8b679bc952ca33487c9af6 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jan 2012 17:12:07 +0200 Subject: integrity: digital signature config option name change Similar to SIGNATURE, rename INTEGRITY_DIGSIG to INTEGRITY_SIGNATURE. Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index ff60bf7..5bd1cc1 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -3,7 +3,7 @@ config INTEGRITY def_bool y depends on IMA || EVM -config INTEGRITY_DIGSIG +config INTEGRITY_SIGNATURE boolean "Digital signature verification using multiple keyrings" depends on INTEGRITY && KEYS default n diff --git a/security/integrity/Makefile b/security/integrity/Makefile index bece056..d43799c 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_INTEGRITY) += integrity.o -obj-$(CONFIG_INTEGRITY_DIGSIG) += digsig.o +obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o integrity-y := iint.o diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 4da6ba8..7a25ece 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -51,7 +51,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode); #define INTEGRITY_KEYRING_IMA 2 #define INTEGRITY_KEYRING_MAX 3 -#ifdef CONFIG_INTEGRITY_DIGSIG +#ifdef CONFIG_INTEGRITY_SIGNATURE int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen); @@ -65,7 +65,7 @@ static inline int integrity_digsig_verify(const unsigned int id, return -EOPNOTSUPP; } -#endif /* CONFIG_INTEGRITY_DIGSIG */ +#endif /* CONFIG_INTEGRITY_SIGNATURE */ /* set during initialization */ extern int iint_initialized; -- cgit v0.10.2 From 68f30fbee19cc67849b9fa8e153ede70758afe81 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 17 Jan 2012 15:35:37 -0800 Subject: x86, tsc: Fix SMI induced variation in quick_pit_calibrate() pit_expect_msb() returns success wrongly in the below SMI scenario: a. pit_verify_msb() has not yet seen the MSB transition. b. we are close to the MSB transition though and got a SMI immediately after returning from pit_verify_msb() which didn't see the MSB transition. PIT MSB transition has happened somewhere during SMI execution. c. returned from SMI and we noted down the 'tsc', saw the pit MSB change now and exited the loop to calculate 'deltatsc'. Instead of noting the TSC at the MSB transition, we are way off because of the SMI. And as the SMI happened between the pit_verify_msb() and before the 'tsc' is recorded in the for loop, 'delattsc' (d1/d2 in quick_pit_calibrate()) will be small and quick_pit_calibrate() will not notice this error. Depending on whether SMI disturbance happens while computing d1 or d2, we will see the TSC calibrated value smaller or bigger than the expected value. As a result, in a cluster we were seeing a variation of approximately +/- 20MHz in the calibrated values, resulting in NTP failures. [ As far as the SMI source is concerned, this is a periodic SMI that gets disabled after ACPI is enabled by the OS. But the TSC calibration happens before the ACPI is enabled. ] To address this, change pit_expect_msb() so that - the 'tsc' is the TSC in between the two reads that read the MSB change from the PIT (same as before) - the 'delta' is the difference in TSC from *before* the MSB changed to *after* the MSB changed. Now the delta is twice as big as before (it covers four PIT accesses, roughly 4us) and quick_pit_calibrate() will loop a bit longer to get the calibrated value with in the 500ppm precision. As the delta (d1/d2) covers four PIT accesses, actual calibrated result might be closer to 250ppm precision. As the loop now takes longer to stabilize, double MAX_QUICK_PIT_MS to 50. SMI disturbance will showup as much larger delta's and the loop will take longer than usual for the result to be with in the accepted precision. Or will fallback to slow PIT calibration if it takes more than 50msec. Also while we are at this, remove the calibration correction that aims to get the result to the middle of the error bars. We really don't know which direction to correct into, so remove it. Reported-and-tested-by: Suresh Siddha Signed-off-by: Linus Torvalds Signed-off-by: Suresh Siddha Link: http://lkml.kernel.org/r/1326843337.5291.4.camel@sbsiddha-mobl2 Signed-off-by: H. Peter Anvin diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 2c9cf0f..f546946 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -290,14 +290,15 @@ static inline int pit_verify_msb(unsigned char val) static inline int pit_expect_msb(unsigned char val, u64 *tscp, unsigned long *deltap) { int count; - u64 tsc = 0; + u64 tsc = 0, prev_tsc = 0; for (count = 0; count < 50000; count++) { if (!pit_verify_msb(val)) break; + prev_tsc = tsc; tsc = get_cycles(); } - *deltap = get_cycles() - tsc; + *deltap = get_cycles() - prev_tsc; *tscp = tsc; /* @@ -311,9 +312,9 @@ static inline int pit_expect_msb(unsigned char val, u64 *tscp, unsigned long *de * How many MSB values do we want to see? We aim for * a maximum error rate of 500ppm (in practice the * real error is much smaller), but refuse to spend - * more than 25ms on it. + * more than 50ms on it. */ -#define MAX_QUICK_PIT_MS 25 +#define MAX_QUICK_PIT_MS 50 #define MAX_QUICK_PIT_ITERATIONS (MAX_QUICK_PIT_MS * PIT_TICK_RATE / 1000 / 256) static unsigned long quick_pit_calibrate(void) @@ -383,15 +384,12 @@ success: * * As a result, we can depend on there not being * any odd delays anywhere, and the TSC reads are - * reliable (within the error). We also adjust the - * delta to the middle of the error bars, just - * because it looks nicer. + * reliable (within the error). * * kHz = ticks / time-in-seconds / 1000; * kHz = (t2 - t1) / (I * 256 / PIT_TICK_RATE) / 1000 * kHz = ((t2 - t1) * PIT_TICK_RATE) / (I * 256 * 1000) */ - delta += (long)(d2 - d1)/2; delta *= PIT_TICK_RATE; do_div(delta, i*256*1000); printk("Fast TSC calibration using PIT\n"); -- cgit v0.10.2 From 265fe02b6fade1704f01c4e96f99ac051b7fab9d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 11 Jan 2012 11:03:41 -0700 Subject: gpio/it8761e: Restrict it8761e gpio driver to x86. This driver does an unconditional read of io space during module init which causes a bad dereference on ARM. It looks to me like this is an x86 only drivers, so restrict it to only compile on x86. Signed-off-by: Grant Likely Cc: Denis Turischev diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 37c4bd1..d0c4118 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -87,6 +87,7 @@ config GPIO_GENERIC_PLATFORM config GPIO_IT8761E tristate "IT8761E GPIO support" + depends on X86 # unconditional access to IO space. help Say yes here to support GPIO functionality of IT8761E super I/O chip. -- cgit v0.10.2 From 53e5ab946a1773f589b97df6ae1cc72b52656a7d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 10 Jan 2012 05:46:57 -0300 Subject: [media] s5p-fimc: Fix incorrect control ID assignment This patch fixes the mismatch between control IDs (CID) and controls for hflip, vflip and rotate. Signed-off-by: Sachin Kamat Acked-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index f5cbb8a..81bcbb9 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -848,11 +848,11 @@ int fimc_ctrls_create(struct fimc_ctx *ctx) v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); + V4L2_CID_ROTATE, 0, 270, 90, 0); ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + V4L2_CID_HFLIP, 0, 1, 1, 0); ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, - V4L2_CID_ROTATE, 0, 270, 90, 0); + V4L2_CID_VFLIP, 0, 1, 1, 0); if (variant->has_alpha) ctx->ctrl_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, -- cgit v0.10.2 From b1aa60892ecc1ba03185e7dfed99cc9202ef03b7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 17:49:28 -0300 Subject: [media] drivers/media/video/s5p-fimc/fimc-capture.c: adjust double test Rewrite a duplicated test to test the correct value The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E; @@ ( * E || ... || E | * E && ... && E ) // Signed-off-by: Julia Lawall Acked-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 510cfab..a9e9653 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -693,7 +693,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, mf->code = 0; continue; } - if (mf->width != tfmt->width || mf->width != tfmt->width) { + if (mf->width != tfmt->width || mf->height != tfmt->height) { u32 fcc = ffmt->fourcc; tfmt->width = mf->width; tfmt->height = mf->height; @@ -702,7 +702,8 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, NULL, &fcc, FIMC_SD_PAD_SOURCE); if (ffmt && ffmt->mbus_code) mf->code = ffmt->mbus_code; - if (mf->width != tfmt->width || mf->width != tfmt->width) + if (mf->width != tfmt->width || + mf->height != tfmt->height) continue; tfmt->code = mf->code; } @@ -710,7 +711,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); if (mf->code == tfmt->code && - mf->width == tfmt->width && mf->width == tfmt->width) + mf->width == tfmt->width && mf->height == tfmt->height) break; } -- cgit v0.10.2 From bb869368f36abecc427ffa94193439a05c0aac8d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 17:49:30 -0300 Subject: [media] drivers/media/video/s5p-mfc/s5p_mfc.c: adjust double test Rewrite a duplicated test to test the correct value The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E; @@ ( * E || ... || E | * E && ... && E ) // Signed-off-by: Julia Lawall Acked-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index e43e128..90cc058 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -475,7 +475,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, ctx->mv_size = 0; } ctx->dpb_count = s5p_mfc_get_dpb_count(); - if (ctx->img_width == 0 || ctx->img_width == 0) + if (ctx->img_width == 0 || ctx->img_height == 0) ctx->state = MFCINST_ERROR; else ctx->state = MFCINST_HEAD_PARSED; -- cgit v0.10.2 From 0da0a7833824b51488b81db002bb47ad8688f9bb Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Tue, 27 Dec 2011 10:07:24 -0300 Subject: [media] s5p-mfc: Fix volatile controls setup Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index 844a4d7..c25ec02 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -165,7 +165,7 @@ static struct mfc_control controls[] = { .maximum = 32, .step = 1, .default_value = 1, - .flags = V4L2_CTRL_FLAG_VOLATILE, + .is_volatile = 1, }, }; -- cgit v0.10.2 From 7f6cce69e2667fdf0ee978641ee4325ea05ce3f8 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Mon, 2 Jan 2012 09:19:25 -0300 Subject: [media] s5p-g2d: fixed a bug in controls setting function Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index c40b0dd..febaa67 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -184,6 +184,7 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl) ctx->rop = ROP4_INVERT; else ctx->rop = ROP4_COPY; + break; default: v4l2_err(&ctx->dev->v4l2_dev, "unknown control\n"); return -EINVAL; -- cgit v0.10.2 From 719c174ee9005e44b61bc8ab295f05851a989e21 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 13 Jan 2012 05:12:38 -0300 Subject: [media] s5p-jpeg: adapt to recent videobuf2 changes queue_setup callback has been extended with struct v4l2_format *fmt parameter in 2d86401c2c commit. This patch adds this parameter to s5p-jpeg driver. Signed-off-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index f841a3e..1105a87 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -989,9 +989,10 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { * ============================================================================ */ -static int s5p_jpeg_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) +static int s5p_jpeg_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); struct s5p_jpeg_q_data *q_data = NULL; -- cgit v0.10.2 From bad321f1bb8f45adc348a3913632387891dd6ac6 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Tue, 17 Jan 2012 05:49:14 -0300 Subject: [media] DVB-CORE: remove superfluous DTV_CMDs This small patch removes superfluous DTV_CMDs from dvb_frontend.c which were added in the initially when ISBD-T support was added. They were there unnoticed even though compilers should have warning about those duplicates. Finally they did and now we can remove them. Thanks to Dan Carpenter for pointing that out. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 2998f38..fbbe545 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1012,25 +1012,6 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0), _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0), - _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 0, 0), - _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 0, 0), - _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0), - _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0), - _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0), - _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0), - _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0), -- cgit v0.10.2 From 68dc8bc5806a583b547c9543c4a864ec93b423dc Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Tue, 17 Jan 2012 08:41:20 -0300 Subject: [media] : add MODULE_FIRMWARE to dib0700 The dib0700 needs a binary firmware file. This patch added the MODULE_FIRMWARE-macro. Signed-off-by: Christoph Anton Mitterer Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 156cbfc..2d259ca 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -854,6 +854,7 @@ static void __exit dib0700_module_exit(void) module_init (dib0700_module_init); module_exit (dib0700_module_exit); +MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw"); MODULE_AUTHOR("Patrick Boettcher "); MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge"); MODULE_VERSION("1.0"); -- cgit v0.10.2 From 9b6ba57bd45da30173f37d5099805d687440e5c0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 10 Jan 2012 14:11:22 -0300 Subject: [media] DVB: dib0700, move Nova-TD Stick to a separate set To properly support the three LEDs which are on the stick, we need a special handling in the ->frontend_attach function. Thus let's have a separate ->frontend_attach instead of ifs in the common one. The hadnling itself will be added in further patches. Signed-off-by: Jiri Slaby Acked-by: Michael Krufky Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 81ef4b4..3c6ee54 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -3892,7 +3892,58 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 6, + .num_device_descs = 1, + .devices = { + { "Hauppauge Nova-TD Stick (52009)", + { &dib0700_usb_id_table[35], NULL }, + { NULL }, + }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_TYPE_RC5 | + RC_TYPE_RC6 | + RC_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach0, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach1, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + }}, + .size_of_priv = sizeof(struct dib0700_adapter_state), + } + }, + + .num_device_descs = 5, .devices = { { "DiBcom STK7070PD reference design", { &dib0700_usb_id_table[17], NULL }, @@ -3902,10 +3953,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[18], NULL }, { NULL }, }, - { "Hauppauge Nova-TD Stick (52009)", - { &dib0700_usb_id_table[35], NULL }, - { NULL }, - }, { "Hauppauge Nova-TD-500 (84xxx)", { &dib0700_usb_id_table[36], NULL }, { NULL }, -- cgit v0.10.2 From 52fd5b2ea72863c53c8ab2e839983004ce33e559 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 10 Jan 2012 14:11:23 -0300 Subject: [media] DVB: dib0700, separate stk7070pd initialization The start is common for both stk7070pd and novatd specific routine. This is just a preparation for the next patch. Signed-off-by: Jiri Slaby Acked-by: Michael Krufky Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 3c6ee54..e5c2bd2 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -3066,19 +3066,25 @@ static struct dib7000p_config stk7070pd_dib7000p_config[2] = { } }; -static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) +static void stk7070pd_init(struct dvb_usb_device *dev) { - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO6, GPIO_OUT, 1); msleep(10); - dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + dib0700_set_gpio(dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO10, GPIO_OUT, 0); - dib0700_ctrl_clock(adap->dev, 72, 1); + dib0700_ctrl_clock(dev, 72, 1); msleep(10); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + dib0700_set_gpio(dev, GPIO10, GPIO_OUT, 1); +} + +static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) +{ + stk7070pd_init(adap->dev); + msleep(10); dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); -- cgit v0.10.2 From 2b05b881ec9b04cc308f0880ea604843bcb2265a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 10 Jan 2012 14:11:24 -0300 Subject: [media] DVB: dib0700, add corrected Nova-TD frontend_attach This means cut & paste from the former f. attach. But while at it write to the right GPIO to turn on the right LED. Also turn the other two off jsut for sure. Signed-off-by: Jiri Slaby Acked-by: Michael Krufky Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index e5c2bd2..3ab45ae 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -3105,6 +3105,38 @@ static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } +/** + * novatd_frontend_attach - Nova-TD specific attach + * + * Nova-TD has GPIO0, 1 and 2 for LEDs. So do not fiddle with them except for + * information purposes. + */ +static int novatd_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *dev = adap->dev; + + if (adap->id == 0) { + stk7070pd_init(dev); + + /* turn the power LED on, the other two off (just in case) */ + dib0700_set_gpio(dev, GPIO0, GPIO_OUT, 0); + dib0700_set_gpio(dev, GPIO1, GPIO_OUT, 0); + dib0700_set_gpio(dev, GPIO2, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&dev->i2c_adap, 2, 18, + stk7070pd_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + } + + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &dev->i2c_adap, + adap->id == 0 ? 0x80 : 0x82, + &stk7070pd_dib7000p_config[adap->id]); + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + /* S5H1411 */ static struct s5h1411_config pinnacle_801e_config = { .output_mode = S5H1411_PARALLEL_OUTPUT, @@ -3876,7 +3908,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, - .frontend_attach = stk7070pd_frontend_attach0, + .frontend_attach = novatd_frontend_attach, .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), @@ -3889,7 +3921,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, - .frontend_attach = stk7070pd_frontend_attach1, + .frontend_attach = novatd_frontend_attach, .tuner_attach = dib7070p_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x03), -- cgit v0.10.2 From d43272a4e898a1e43e5ac351ab625b7a40b39e88 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 10 Jan 2012 14:11:25 -0300 Subject: [media] DVB: dib0700, add support for Nova-TD LEDs Add an override of read_status to intercept lock status. This allows us to switch LEDs appropriately on and off with signal un/locked. The second phase is to override sleep to properly turn off both. This is a hackish way to achieve that. Thanks to Mike Krufky for his help. Signed-off-by: Jiri Slaby Acked-by: Michael Krufky Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index 9bd6d51..7de125c 100644 --- a/drivers/media/dvb/dvb-usb/dib0700.h +++ b/drivers/media/dvb/dvb-usb/dib0700.h @@ -48,6 +48,8 @@ struct dib0700_state { u8 disable_streaming_master_mode; u32 fw_version; u32 nb_packet_buffer_size; + int (*read_status)(struct dvb_frontend *, fe_status_t *); + int (*sleep)(struct dvb_frontend* fe); u8 buf[255]; }; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 3ab45ae..f9e966a 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -3105,6 +3105,35 @@ static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } +static int novatd_read_status_override(struct dvb_frontend *fe, + fe_status_t *stat) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *dev = adap->dev; + struct dib0700_state *state = dev->priv; + int ret; + + ret = state->read_status(fe, stat); + + if (!ret) + dib0700_set_gpio(dev, adap->id == 0 ? GPIO1 : GPIO0, GPIO_OUT, + !!(*stat & FE_HAS_LOCK)); + + return ret; +} + +static int novatd_sleep_override(struct dvb_frontend* fe) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *dev = adap->dev; + struct dib0700_state *state = dev->priv; + + /* turn off LED */ + dib0700_set_gpio(dev, adap->id == 0 ? GPIO1 : GPIO0, GPIO_OUT, 0); + + return state->sleep(fe); +} + /** * novatd_frontend_attach - Nova-TD specific attach * @@ -3114,6 +3143,7 @@ static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) static int novatd_frontend_attach(struct dvb_usb_adapter *adap) { struct dvb_usb_device *dev = adap->dev; + struct dib0700_state *st = dev->priv; if (adap->id == 0) { stk7070pd_init(dev); @@ -3134,7 +3164,16 @@ static int novatd_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &dev->i2c_adap, adap->id == 0 ? 0x80 : 0x82, &stk7070pd_dib7000p_config[adap->id]); - return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; + + if (adap->fe_adap[0].fe == NULL) + return -ENODEV; + + st->read_status = adap->fe_adap[0].fe->ops.read_status; + adap->fe_adap[0].fe->ops.read_status = novatd_read_status_override; + st->sleep = adap->fe_adap[0].fe->ops.sleep; + adap->fe_adap[0].fe->ops.sleep = novatd_sleep_override; + + return 0; } /* S5H1411 */ -- cgit v0.10.2 From f08aacf84f0a2c36cfd20d19459b036b20d2e1fb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Jan 2012 12:27:15 -0300 Subject: [media] v4l2-ctrls: make control names consistent Several control names used inconsistent capitalization or were inconsistent in other ways. I also corrected a spelling mistake and fixed four strings that were too long (>31 characters). Harmless, but the string is cut off when it is returned with QUERYCTRL. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index da1f4c2..cccd42b 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -465,8 +465,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1"; case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2"; - case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Minimum Number of Capture Buffers"; - case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Minimum Number of Output Buffers"; + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers"; + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers"; case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; /* MPEG controls */ @@ -506,25 +506,25 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV"; case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface"; case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable"; - case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "The Number of Intra Refresh MBs"; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs"; case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable"; case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control"; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode"; - case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "The Max Number of Reference Picture"; + case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "Max Number of Reference Pics"; case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B frame QP Value"; + case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: return "H263 Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: return "H263 Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: return "H264 I-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B frame QP Value"; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: return "H264 Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: return "H264 Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: return "H264 8x8 Transform Enable"; case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: return "H264 CPB Buffer Size"; - case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entorpy Mode"; - case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I Period"; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entropy Mode"; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I-Frame Period"; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: return "H264 Level"; case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: return "H264 Loop Filter Alpha Offset"; case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: return "H264 Loop Filter Beta Offset"; @@ -535,16 +535,16 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: return "Aspect Ratio VUI Enable"; case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: return "VUI Aspect Ratio IDC"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P frame QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B frame QP Value"; + case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; + case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: return "MPEG4 Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: return "MPEG4 Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return "MPEG4 Level"; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return "MPEG4 Profile"; case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: return "Quarter Pixel Search Enable"; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "The Maximum Bytes Per Slice"; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "The Number of MB in a Slice"; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "The Slice Partitioning Method"; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "Maximum Bytes in a Slice"; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice"; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method"; case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size"; /* CAMERA controls */ @@ -580,7 +580,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled"; + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled"; case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain"; case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold"; case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time"; @@ -588,24 +588,24 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled"; case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation"; case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency"; - case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings"; + case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-Emphasis"; case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level"; case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; /* Flash controls */ - case V4L2_CID_FLASH_CLASS: return "Flash controls"; - case V4L2_CID_FLASH_LED_MODE: return "LED mode"; - case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe source"; + case V4L2_CID_FLASH_CLASS: return "Flash Controls"; + case V4L2_CID_FLASH_LED_MODE: return "LED Mode"; + case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source"; case V4L2_CID_FLASH_STROBE: return "Strobe"; - case V4L2_CID_FLASH_STROBE_STOP: return "Stop strobe"; - case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe status"; - case V4L2_CID_FLASH_TIMEOUT: return "Strobe timeout"; - case V4L2_CID_FLASH_INTENSITY: return "Intensity, flash mode"; - case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, torch mode"; - case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, indicator"; + case V4L2_CID_FLASH_STROBE_STOP: return "Stop Strobe"; + case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe Status"; + case V4L2_CID_FLASH_TIMEOUT: return "Strobe Timeout"; + case V4L2_CID_FLASH_INTENSITY: return "Intensity, Flash Mode"; + case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, Torch Mode"; + case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator"; case V4L2_CID_FLASH_FAULT: return "Faults"; case V4L2_CID_FLASH_CHARGE: return "Charge"; - case V4L2_CID_FLASH_READY: return "Ready to strobe"; + case V4L2_CID_FLASH_READY: return "Ready to Strobe"; default: return NULL; -- cgit v0.10.2 From 3a9888f913595515893fa46aca70422aba8a3799 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 Jan 2012 03:28:51 -0300 Subject: [media] ds3000: using logical && instead of bitwise & The intent here was to test if the FE_HAS_LOCK was set. The current test is equivalent to "if (status) { ..." Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index 9387770..af65d01 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1195,7 +1195,7 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) for (i = 0; i < 30 ; i++) { ds3000_read_status(fe, &status); - if (status && FE_HAS_LOCK) + if (status & FE_HAS_LOCK) break; msleep(10); -- cgit v0.10.2 From 2bd43341217b6e8b75e382243328f458ac67fcbe Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Jan 2012 04:52:41 -0300 Subject: [media] s5p-mfc: Remove linux/version.h include from s5p_mfc.c Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 90cc058..83fe461 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include -- cgit v0.10.2 From a2e2baad60065614c48fee6f55479d6809b0ce83 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 17 Jan 2012 04:55:25 -0300 Subject: [media] s5p-fimc: Remove linux/version.h include from fimc-mdevice.c Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 615c862..8ea4ee1 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include -- cgit v0.10.2 From 36be126cb0ebe3000a65c1049f339a3e882a9a47 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 8 Jan 2012 20:18:19 -0300 Subject: [media] as3645a: Fix compilation by including slab.h The as3645a driver calls kzalloc(). Include slab.h. Reported-by: Fredrik Lingvall Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c index ec859a5..f241702 100644 --- a/drivers/media/video/as3645a.c +++ b/drivers/media/video/as3645a.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From 966f1212e1ac5fe3ddf04479d21488ddb36a2608 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 16 Jan 2012 13:23:23 +0800 Subject: [libata] enable ata port async suspend This saves devices suspend/resume time. Tested system suspend/resume with SATA IDE/AHCI mode 3 times. Below is the time took for devices suspend/resume. SATA mode vanilla-kernel patched-kernel --------- --------------------- --------------------- IDE suspend: 0.744 suspend: 0.432 (0.716, 0.768, 0.748) (0.440, 0.428, 0.428) resume: 5.084 resume: 2.209 (5.100, 5.064, 5.088) (2.168, 2.232, 2.228) AHCI: suspend: 0.725 suspend: 0.449 (0.740, 0.708, 0.728) (0.456, 0.448, 0.444) resume: 2.556 resume: 1.896 (2.604, 2.492, 2.572) (1.932, 1.872, 1.884) Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 9a7f0ea..74aaee3 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -291,6 +291,7 @@ int ata_tport_add(struct device *parent, goto tport_err; } + device_enable_async_suspend(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); -- cgit v0.10.2 From 93272b132a72450dfc16f13d32223fe47aaf5061 Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Tue, 20 Dec 2011 14:50:27 +0800 Subject: [libata] sata_fsl: fix the controller operating mode Configure the FSL SATA controller to the preferred, enterprise mode. Signed-off-by: Yutaka Ando Signed-off-by: Jerry Huang CC: Jeff Garzik Signed-off-by: Jeff Garzik diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 5a2c95b..0120b0d 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -140,6 +140,7 @@ enum { */ HCONTROL_ONLINE_PHY_RST = (1 << 31), HCONTROL_FORCE_OFFLINE = (1 << 30), + HCONTROL_LEGACY = (1 << 28), HCONTROL_PARITY_PROT_MOD = (1 << 14), HCONTROL_DPATH_PARITY = (1 << 12), HCONTROL_SNOOP_ENABLE = (1 << 10), @@ -1223,6 +1224,10 @@ static int sata_fsl_init_controller(struct ata_host *host) * part of the port_start() callback */ + /* sata controller to operate in enterprise mode */ + temp = ioread32(hcr_base + HCONTROL); + iowrite32(temp & ~HCONTROL_LEGACY, hcr_base + HCONTROL); + /* ack. any pending IRQs for this controller/port */ temp = ioread32(hcr_base + HSTATUS); if (temp & 0x3F) @@ -1421,6 +1426,12 @@ static int sata_fsl_resume(struct platform_device *op) /* Recovery the CHBA register in host controller cmd register set */ iowrite32(pp->cmdslot_paddr & 0xffffffff, hcr_base + CHBA); + iowrite32((ioread32(hcr_base + HCONTROL) + | HCONTROL_ONLINE_PHY_RST + | HCONTROL_SNOOP_ENABLE + | HCONTROL_PMP_ATTACHED), + hcr_base + HCONTROL); + ata_host_resume(host); return 0; } -- cgit v0.10.2 From 81b0287d341535ac722de891b19f7c49212ac91c Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 4 Jan 2012 14:06:51 +0800 Subject: [libata] pata_bf54x: Support sg list in bmdma transfer. BF54x on-chip ATAPI controller allows maximum 0x1fffe bytes to be transfered in one ATAPI transfer. So, set the max sg_tablesize to 4. Signed-off-by: Sonic Zhang Signed-off-by: Jeff Garzik diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index d6a4677..1e65842 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -251,6 +251,8 @@ static const u32 udma_tenvmin = 20; static const u32 udma_tackmin = 20; static const u32 udma_tssmin = 50; +#define BFIN_MAX_SG_SEGMENTS 4 + /** * * Function: num_clocks_min @@ -829,79 +831,61 @@ static void bfin_set_devctl(struct ata_port *ap, u8 ctl) static void bfin_bmdma_setup(struct ata_queued_cmd *qc) { - unsigned short config = WDSIZE_16; + struct ata_port *ap = qc->ap; + struct dma_desc_array *dma_desc_cpu = (struct dma_desc_array *)ap->bmdma_prd; + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + unsigned short config = DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_16 | DMAEN; struct scatterlist *sg; unsigned int si; + unsigned int channel; + unsigned int dir; + unsigned int size = 0; dev_dbg(qc->ap->dev, "in atapi dma setup\n"); /* Program the ATA_CTRL register with dir */ if (qc->tf.flags & ATA_TFLAG_WRITE) { - /* fill the ATAPI DMA controller */ - set_dma_config(CH_ATAPI_TX, config); - set_dma_x_modify(CH_ATAPI_TX, 2); - for_each_sg(qc->sg, sg, qc->n_elem, si) { - set_dma_start_addr(CH_ATAPI_TX, sg_dma_address(sg)); - set_dma_x_count(CH_ATAPI_TX, sg_dma_len(sg) >> 1); - } + channel = CH_ATAPI_TX; + dir = DMA_TO_DEVICE; } else { + channel = CH_ATAPI_RX; + dir = DMA_FROM_DEVICE; config |= WNR; - /* fill the ATAPI DMA controller */ - set_dma_config(CH_ATAPI_RX, config); - set_dma_x_modify(CH_ATAPI_RX, 2); - for_each_sg(qc->sg, sg, qc->n_elem, si) { - set_dma_start_addr(CH_ATAPI_RX, sg_dma_address(sg)); - set_dma_x_count(CH_ATAPI_RX, sg_dma_len(sg) >> 1); - } } -} -/** - * bfin_bmdma_start - Start an IDE DMA transaction - * @qc: Info associated with this ATA transaction. - * - * Note: Original code is ata_bmdma_start(). - */ + dma_map_sg(ap->dev, qc->sg, qc->n_elem, dir); -static void bfin_bmdma_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; - struct scatterlist *sg; - unsigned int si; + /* fill the ATAPI DMA controller */ + for_each_sg(qc->sg, sg, qc->n_elem, si) { + dma_desc_cpu[si].start_addr = sg_dma_address(sg); + dma_desc_cpu[si].cfg = config; + dma_desc_cpu[si].x_count = sg_dma_len(sg) >> 1; + dma_desc_cpu[si].x_modify = 2; + size += sg_dma_len(sg); + } - dev_dbg(qc->ap->dev, "in atapi dma start\n"); - if (!(ap->udma_mask || ap->mwdma_mask)) - return; + /* Set the last descriptor to stop mode */ + dma_desc_cpu[qc->n_elem - 1].cfg &= ~(DMAFLOW | NDSIZE); - /* start ATAPI DMA controller*/ - if (qc->tf.flags & ATA_TFLAG_WRITE) { - /* - * On blackfin arch, uncacheable memory is not - * allocated with flag GFP_DMA. DMA buffer from - * common kenel code should be flushed if WB - * data cache is enabled. Otherwise, this loop - * is an empty loop and optimized out. - */ - for_each_sg(qc->sg, sg, qc->n_elem, si) { - flush_dcache_range(sg_dma_address(sg), - sg_dma_address(sg) + sg_dma_len(sg)); - } - enable_dma(CH_ATAPI_TX); - dev_dbg(qc->ap->dev, "enable udma write\n"); + flush_dcache_range((unsigned int)dma_desc_cpu, + (unsigned int)dma_desc_cpu + + qc->n_elem * sizeof(struct dma_desc_array)); - /* Send ATA DMA write command */ - bfin_exec_command(ap, &qc->tf); + /* Enable ATA DMA operation*/ + set_dma_curr_desc_addr(channel, (unsigned long *)ap->bmdma_prd_dma); + set_dma_x_count(channel, 0); + set_dma_x_modify(channel, 0); + set_dma_config(channel, config); + + SSYNC(); + + /* Send ATA DMA command */ + bfin_exec_command(ap, &qc->tf); + if (qc->tf.flags & ATA_TFLAG_WRITE) { /* set ATA DMA write direction */ ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR)); } else { - enable_dma(CH_ATAPI_RX); - dev_dbg(qc->ap->dev, "enable udma read\n"); - - /* Send ATA DMA read command */ - bfin_exec_command(ap, &qc->tf); - /* set ATA DMA read direction */ ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR)); @@ -913,12 +897,28 @@ static void bfin_bmdma_start(struct ata_queued_cmd *qc) /* Set ATAPI state machine contorl in terminate sequence */ ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | END_ON_TERM); - /* Set transfer length to buffer len */ - for_each_sg(qc->sg, sg, qc->n_elem, si) { - ATAPI_SET_XFER_LEN(base, (sg_dma_len(sg) >> 1)); - } + /* Set transfer length to the total size of sg buffers */ + ATAPI_SET_XFER_LEN(base, size >> 1); +} - /* Enable ATA DMA operation*/ +/** + * bfin_bmdma_start - Start an IDE DMA transaction + * @qc: Info associated with this ATA transaction. + * + * Note: Original code is ata_bmdma_start(). + */ + +static void bfin_bmdma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + dev_dbg(qc->ap->dev, "in atapi dma start\n"); + + if (!(ap->udma_mask || ap->mwdma_mask)) + return; + + /* start ATAPI transfer*/ if (ap->udma_mask) ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | ULTRA_START); @@ -935,34 +935,23 @@ static void bfin_bmdma_start(struct ata_queued_cmd *qc) static void bfin_bmdma_stop(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - struct scatterlist *sg; - unsigned int si; + unsigned int dir; dev_dbg(qc->ap->dev, "in atapi dma stop\n"); + if (!(ap->udma_mask || ap->mwdma_mask)) return; /* stop ATAPI DMA controller*/ - if (qc->tf.flags & ATA_TFLAG_WRITE) + if (qc->tf.flags & ATA_TFLAG_WRITE) { + dir = DMA_TO_DEVICE; disable_dma(CH_ATAPI_TX); - else { + } else { + dir = DMA_FROM_DEVICE; disable_dma(CH_ATAPI_RX); - if (ap->hsm_task_state & HSM_ST_LAST) { - /* - * On blackfin arch, uncacheable memory is not - * allocated with flag GFP_DMA. DMA buffer from - * common kenel code should be invalidated if - * data cache is enabled. Otherwise, this loop - * is an empty loop and optimized out. - */ - for_each_sg(qc->sg, sg, qc->n_elem, si) { - invalidate_dcache_range( - sg_dma_address(sg), - sg_dma_address(sg) - + sg_dma_len(sg)); - } - } } + + dma_unmap_sg(ap->dev, qc->sg, qc->n_elem, dir); } /** @@ -1260,6 +1249,11 @@ static void bfin_port_stop(struct ata_port *ap) { dev_dbg(ap->dev, "in atapi port stop\n"); if (ap->udma_mask != 0 || ap->mwdma_mask != 0) { + dma_free_coherent(ap->dev, + BFIN_MAX_SG_SEGMENTS * sizeof(struct dma_desc_array), + ap->bmdma_prd, + ap->bmdma_prd_dma); + free_dma(CH_ATAPI_RX); free_dma(CH_ATAPI_TX); } @@ -1271,14 +1265,29 @@ static int bfin_port_start(struct ata_port *ap) if (!(ap->udma_mask || ap->mwdma_mask)) return 0; + ap->bmdma_prd = dma_alloc_coherent(ap->dev, + BFIN_MAX_SG_SEGMENTS * sizeof(struct dma_desc_array), + &ap->bmdma_prd_dma, + GFP_KERNEL); + + if (ap->bmdma_prd == NULL) { + dev_info(ap->dev, "Unable to allocate DMA descriptor array.\n"); + goto out; + } + if (request_dma(CH_ATAPI_RX, "BFIN ATAPI RX DMA") >= 0) { if (request_dma(CH_ATAPI_TX, "BFIN ATAPI TX DMA") >= 0) return 0; free_dma(CH_ATAPI_RX); + dma_free_coherent(ap->dev, + BFIN_MAX_SG_SEGMENTS * sizeof(struct dma_desc_array), + ap->bmdma_prd, + ap->bmdma_prd_dma); } +out: ap->udma_mask = 0; ap->mwdma_mask = 0; dev_err(ap->dev, "Unable to request ATAPI DMA!" @@ -1400,7 +1409,7 @@ static irqreturn_t bfin_ata_interrupt(int irq, void *dev_instance) static struct scsi_host_template bfin_sht = { ATA_BASE_SHT(DRV_NAME), - .sg_tablesize = SG_NONE, + .sg_tablesize = BFIN_MAX_SG_SEGMENTS, .dma_boundary = ATA_DMA_BOUNDARY, }; -- cgit v0.10.2 From 3a25179e068b2fae8df7a7ab63a2a5ed71a2e927 Mon Sep 17 00:00:00 2001 From: Vladimir LAVALLADE Date: Sun, 8 Jan 2012 13:50:13 +0100 Subject: [libata] add DVRTD08A and DVR-215 to NOSETXFER device quirk list Signed-off-by: Jeff Garzik diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 11c9aea..c06e0ec 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4125,6 +4125,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { * device and controller are SATA. */ { "PIONEER DVD-RW DVRTD08", NULL, ATA_HORKAGE_NOSETXFER }, + { "PIONEER DVD-RW DVRTD08A", NULL, ATA_HORKAGE_NOSETXFER }, + { "PIONEER DVD-RW DVR-215", NULL, ATA_HORKAGE_NOSETXFER }, { "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER }, { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, -- cgit v0.10.2 From b73fa4630e63e4d23407181c2bc0a54777bd0ce8 Mon Sep 17 00:00:00 2001 From: Benjamin Larsson Date: Sun, 8 Jan 2012 00:39:10 +0100 Subject: [libata] ata_piix: Add Toshiba Satellite Pro A120 to the quirks list due to broken suspend functionality. Signed-off-by: Benjamin Larsson Signed-off-by: Jeff Garzik diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 69ac373..fdf27b9 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1117,6 +1117,13 @@ static int piix_broken_suspend(void) }, }, { + .ident = "Satellite Pro A120", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite Pro A120"), + }, + }, + { .ident = "Portege M500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), -- cgit v0.10.2 From 6015ff103133c7e50a753c198c69bcabc3a5e3b0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 18 Jan 2012 01:51:22 +0000 Subject: x86-32: Fix build failure with AUDIT=y, AUDITSYSCALL=n JONGMAN HEO reports: With current linus git (commit a25a2b84), I got following build error, arch/x86/kernel/vm86_32.c: In function 'do_sys_vm86': arch/x86/kernel/vm86_32.c:340: error: implicit declaration of function '__audit_syscall_exit' make[3]: *** [arch/x86/kernel/vm86_32.o] Error 1 OK, I can reproduce it (32bit allmodconfig with AUDIT=y, AUDITSYSCALL=n) It's due to commit d7e7528bcd45: "Audit: push audit success and retcode into arch ptrace.h". Reported-by: JONGMAN HEO Signed-off-by: Al Viro Signed-off-by: Linus Torvalds diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c index af17e1c..b466cab 100644 --- a/arch/x86/kernel/vm86_32.c +++ b/arch/x86/kernel/vm86_32.c @@ -336,8 +336,10 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk mark_screen_rdonly(tsk->mm); /*call __audit_syscall_exit since we do not exit via the normal paths */ +#ifdef CONFIG_AUDITSYSCALL if (unlikely(current->audit_context)) __audit_syscall_exit(1, 0); +#endif __asm__ __volatile__( "movl %0,%%esp\n\t" -- cgit v0.10.2 From f5fffcee27c09143ba80e5257dbd1f381d86342f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Jan 2012 13:49:17 -0500 Subject: cifs: better instrumentation for coalesce_t2 When coalesce_t2 returns an error, have it throw a cFYI message that explains the reason. Also rename some variables to clarify what they represent. Reported-and-Tested-by: Konstantinos Skarlatos Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4666780..5cc1585 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -225,74 +225,90 @@ static int check2ndT2(struct smb_hdr *pSMB) static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) { - struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; + struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; - char *data_area_of_target; - char *data_area_of_buf2; + char *data_area_of_tgt; + char *data_area_of_src; int remaining; - unsigned int byte_count, total_in_buf; - __u16 total_data_size, total_in_buf2; + unsigned int byte_count, total_in_tgt; + __u16 tgt_total_cnt, src_total_cnt, total_in_src; - total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); + tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - if (total_data_size != - get_unaligned_le16(&pSMB2->t2_rsp.TotalDataCount)) - cFYI(1, "total data size of primary and secondary t2 differ"); + if (tgt_total_cnt != src_total_cnt) + cFYI(1, "total data count of primary and secondary t2 differ " + "source=%hu target=%hu", src_total_cnt, tgt_total_cnt); - total_in_buf = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - remaining = total_data_size - total_in_buf; + remaining = tgt_total_cnt - total_in_tgt; - if (remaining < 0) + if (remaining < 0) { + cFYI(1, "Server sent too much data. tgt_total_cnt=%hu " + "total_in_tgt=%hu", tgt_total_cnt, total_in_tgt); return -EPROTO; + } - if (remaining == 0) /* nothing to do, ignore */ + if (remaining == 0) { + /* nothing to do, ignore */ + cFYI(1, "no more data remains"); return 0; + } - total_in_buf2 = get_unaligned_le16(&pSMB2->t2_rsp.DataCount); - if (remaining < total_in_buf2) { + total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); + if (remaining < total_in_src) cFYI(1, "transact2 2nd response contains too much data"); - } /* find end of first SMB data area */ - data_area_of_target = (char *)&pSMBt->hdr.Protocol + + data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); - /* validate target area */ - data_area_of_buf2 = (char *)&pSMB2->hdr.Protocol + - get_unaligned_le16(&pSMB2->t2_rsp.DataOffset); + /* validate target area */ + data_area_of_src = (char *)&pSMBs->hdr.Protocol + + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); - data_area_of_target += total_in_buf; + data_area_of_tgt += total_in_tgt; - /* copy second buffer into end of first buffer */ - total_in_buf += total_in_buf2; + total_in_tgt += total_in_src; /* is the result too big for the field? */ - if (total_in_buf > USHRT_MAX) + if (total_in_tgt > USHRT_MAX) { + cFYI(1, "coalesced DataCount too large (%u)", total_in_tgt); return -EPROTO; - put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount); + } + put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); /* fix up the BCC */ byte_count = get_bcc(pTargetSMB); - byte_count += total_in_buf2; + byte_count += total_in_src; /* is the result too big for the field? */ - if (byte_count > USHRT_MAX) + if (byte_count > USHRT_MAX) { + cFYI(1, "coalesced BCC too large (%u)", byte_count); return -EPROTO; + } put_bcc(byte_count, pTargetSMB); byte_count = be32_to_cpu(pTargetSMB->smb_buf_length); - byte_count += total_in_buf2; + byte_count += total_in_src; /* don't allow buffer to overflow */ - if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) + if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); return -ENOBUFS; + } pTargetSMB->smb_buf_length = cpu_to_be32(byte_count); - memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); + /* copy second buffer into end of first buffer */ + memcpy(data_area_of_tgt, data_area_of_src, total_in_src); - if (remaining == total_in_buf2) { - cFYI(1, "found the last secondary response"); - return 0; /* we are done */ - } else /* more responses to go */ + if (remaining != total_in_src) { + /* more responses to go */ + cFYI(1, "waiting for more secondary responses"); return 1; + } + + /* we are done */ + cFYI(1, "found the last secondary response"); + return 0; } static void -- cgit v0.10.2 From ce91acb3acae26f4163c5a6f1f695d1a1e8d9009 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Jan 2012 16:08:51 -0500 Subject: cifs: lower default wsize when unix extensions are not used We've had some reports of servers (namely, the Solaris in-kernel CIFS server) that don't deal properly with writes that are "too large" even though they set CAP_LARGE_WRITE_ANDX. Change the default to better mirror what windows clients do. Cc: stable@vger.kernel.org Cc: Pavel Shilovsky Reported-by: Nick Davis Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5cc1585..a66dcb5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2930,18 +2930,33 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, #define CIFS_DEFAULT_IOSIZE (1024 * 1024) /* - * Windows only supports a max of 60k reads. Default to that when posix - * extensions aren't in force. + * Windows only supports a max of 60kb reads and 65535 byte writes. Default to + * those values when posix extensions aren't in force. In actuality here, we + * use 65536 to allow for a write that is a multiple of 4k. Most servers seem + * to be ok with the extra byte even though Windows doesn't send writes that + * are that large. + * + * Citation: + * + * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx */ #define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) +#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) static unsigned int cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) { __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize = pvolume_info->wsize ? pvolume_info->wsize : - CIFS_DEFAULT_IOSIZE; + unsigned int wsize; + + /* start with specified wsize, or default */ + if (pvolume_info->wsize) + wsize = pvolume_info->wsize; + else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) + wsize = CIFS_DEFAULT_IOSIZE; + else + wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; /* can server support 24-bit write sizes? (via UNIX extensions) */ if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) -- cgit v0.10.2 From 9f6ed2ca257fa8650b876377833e6f14e272848b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Jan 2012 16:09:11 -0500 Subject: keys: add a "logon" key type For CIFS, we want to be able to store NTLM credentials (aka username and password) in the keyring. We do not, however want to allow users to fetch those keys back out of the keyring since that would be a security risk. Unfortunately, due to the nuances of key permission bits, it's not possible to do this. We need to grant search permissions so the kernel can find these keys, but that also implies permissions to read the payload. Resolve this by adding a new key_type. This key type is essentially the same as key_type_user, but does not define a .read op. This prevents the payload from ever being visible from userspace. This key type also vets the description to ensure that it's "qualified" by checking to ensure that it has a ':' in it that is preceded by other characters. Acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/include/keys/user-type.h b/include/keys/user-type.h index c37c342..bc9ec1d 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -17,7 +17,7 @@ /*****************************************************************************/ /* - * the payload for a key of type "user" + * the payload for a key of type "user" or "logon" * - once filled in and attached to a key: * - the payload struct is invariant may not be changed, only replaced * - the payload must be read with RCU procedures or with the key semaphore @@ -33,6 +33,7 @@ struct user_key_payload { }; extern struct key_type key_type_user; +extern struct key_type key_type_logon; extern int user_instantiate(struct key *key, const void *data, size_t datalen); extern int user_update(struct key *key, const void *data, size_t datalen); diff --git a/security/keys/internal.h b/security/keys/internal.h index c7a7cae..65647f8 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -33,6 +33,7 @@ extern struct key_type key_type_dead; extern struct key_type key_type_user; +extern struct key_type key_type_logon; /*****************************************************************************/ /* diff --git a/security/keys/key.c b/security/keys/key.c index 4f64c72..7ada801 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -999,6 +999,7 @@ void __init key_init(void) list_add_tail(&key_type_keyring.link, &key_types_list); list_add_tail(&key_type_dead.link, &key_types_list); list_add_tail(&key_type_user.link, &key_types_list); + list_add_tail(&key_type_logon.link, &key_types_list); /* record the root user tracking */ rb_link_node(&root_key_user.node, diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 69ff52c..6e1a627 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -18,6 +18,8 @@ #include #include "internal.h" +static int logon_vet_description(const char *desc); + /* * user defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload @@ -36,6 +38,24 @@ struct key_type key_type_user = { EXPORT_SYMBOL_GPL(key_type_user); /* + * This key type is essentially the same as key_type_user, but it does + * not define a .read op. This is suitable for storing username and + * password pairs in the keyring that you do not want to be readable + * from userspace. + */ +struct key_type key_type_logon = { + .name = "logon", + .instantiate = user_instantiate, + .update = user_update, + .match = user_match, + .revoke = user_revoke, + .destroy = user_destroy, + .describe = user_describe, + .vet_description = logon_vet_description, +}; +EXPORT_SYMBOL_GPL(key_type_logon); + +/* * instantiate a user defined key */ int user_instantiate(struct key *key, const void *data, size_t datalen) @@ -189,3 +209,20 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen) } EXPORT_SYMBOL_GPL(user_read); + +/* Vet the description for a "logon" key */ +static int logon_vet_description(const char *desc) +{ + char *p; + + /* require a "qualified" description string */ + p = strchr(desc, ':'); + if (!p) + return -EINVAL; + + /* also reject description with ':' as first char */ + if (p == desc) + return -EINVAL; + + return 0; +} -- cgit v0.10.2 From 04febabcf55beeffb8794a0d8c539e571bd2ae29 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Jan 2012 16:09:15 -0500 Subject: cifs: sanitize username handling Currently, it's not very clear whether you're allowed to have a NULL vol->username or ses->user_name. Some places check for it and some don't. Make it clear that a NULL pointer is OK in these fields, and ensure that all the callers check for that. Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 2272fd5..e622863 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -113,9 +113,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) MAX_MECH_STR_LEN + UID_KEY_LEN + (sizeof(uid_t) * 2) + CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + - USER_KEY_LEN + strlen(sesInfo->user_name) + PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; + if (sesInfo->user_name) + desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); + spnego_key = ERR_PTR(-ENOMEM); description = kzalloc(desc_len, GFP_KERNEL); if (description == NULL) @@ -152,8 +154,10 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) dp = description + strlen(description); sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); - dp = description + strlen(description); - sprintf(dp, ";user=%s", sesInfo->user_name); + if (sesInfo->user_name) { + dp = description + strlen(description); + sprintf(dp, ";user=%s", sesInfo->user_name); + } dp = description + strlen(description); sprintf(dp, ";pid=0x%x", current->pid); diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 5d9b9ac..bce99e6 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -420,15 +420,20 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, } /* convert ses->user_name to unicode and uppercase */ - len = strlen(ses->user_name); + len = ses->user_name ? strlen(ses->user_name) : 0; user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) { cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n"); rc = -ENOMEM; return rc; } - len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp); - UniStrupr(user); + + if (len) { + len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp); + UniStrupr(user); + } else { + memset(user, '\0', 2); + } rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)user, 2 * len); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a66dcb5..b952a21 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1997,10 +1997,16 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) return 0; break; default: + /* NULL username means anonymous session */ + if (ses->user_name == NULL) { + if (!vol->nullauth) + return 0; + break; + } + /* anything else takes username/password */ - if (ses->user_name == NULL) - return 0; - if (strncmp(ses->user_name, vol->username, + if (strncmp(ses->user_name, + vol->username ? vol->username : "", MAX_USERNAME_SIZE)) return 0; if (strlen(vol->username) != 0 && @@ -3167,10 +3173,9 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, return -EINVAL; if (volume_info->nullauth) { - cFYI(1, "null user"); - volume_info->username = kzalloc(1, GFP_KERNEL); - if (volume_info->username == NULL) - return -ENOMEM; + cFYI(1, "Anonymous login"); + kfree(volume_info->username); + volume_info->username = NULL; } else if (volume_info->username) { /* BB fixme parse for domain name here */ cFYI(1, "Username: %s", volume_info->username); -- cgit v0.10.2 From 8a8798a5ff90977d6459ce1d657cf8fe13a51e97 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Jan 2012 16:09:15 -0500 Subject: cifs: fetch credentials out of keyring for non-krb5 auth multiuser mounts Fix up multiuser mounts to set the secType and set the username and password from the key payload in the vol info for non-krb5 auth types. Look for a key of type "secret" with a description of "cifs:a:" or "cifs:d:". If that's found, then scrape the username and password out of the key payload and use that to create a new user session. Finally, don't have the code enforce krb5 auth on multiuser mounts, but do require a kernel with keys support. Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b952a21..28f23c0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "cifspdu.h" #include "cifsglob.h" @@ -1594,11 +1595,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, } } - if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { - cERROR(1, "Multiuser mounts currently require krb5 " - "authentication!"); +#ifndef CONFIG_KEYS + /* Muliuser mounts require CONFIG_KEYS support */ + if (vol->multiuser) { + cERROR(1, "Multiuser mounts require kernels with " + "CONFIG_KEYS enabled."); goto cifs_parse_mount_err; } +#endif if (vol->UNCip == NULL) vol->UNCip = &vol->UNC[2]; @@ -2061,6 +2065,132 @@ cifs_put_smb_ses(struct cifs_ses *ses) cifs_put_tcp_session(server); } +#ifdef CONFIG_KEYS + +/* strlen("cifs:a:") + INET6_ADDRSTRLEN + 1 */ +#define CIFSCREDS_DESC_SIZE (7 + INET6_ADDRSTRLEN + 1) + +/* Populate username and pw fields from keyring if possible */ +static int +cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) +{ + int rc = 0; + char *desc, *delim, *payload; + ssize_t len; + struct key *key; + struct TCP_Server_Info *server = ses->server; + struct sockaddr_in *sa; + struct sockaddr_in6 *sa6; + struct user_key_payload *upayload; + + desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + /* try to find an address key first */ + switch (server->dstaddr.ss_family) { + case AF_INET: + sa = (struct sockaddr_in *)&server->dstaddr; + sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + sa6 = (struct sockaddr_in6 *)&server->dstaddr; + sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); + break; + default: + cFYI(1, "Bad ss_family (%hu)", server->dstaddr.ss_family); + rc = -EINVAL; + goto out_err; + } + + cFYI(1, "%s: desc=%s", __func__, desc); + key = request_key(&key_type_logon, desc, ""); + if (IS_ERR(key)) { + if (!ses->domainName) { + cFYI(1, "domainName is NULL"); + rc = PTR_ERR(key); + goto out_err; + } + + /* didn't work, try to find a domain key */ + sprintf(desc, "cifs:d:%s", ses->domainName); + cFYI(1, "%s: desc=%s", __func__, desc); + key = request_key(&key_type_logon, desc, ""); + if (IS_ERR(key)) { + rc = PTR_ERR(key); + goto out_err; + } + } + + down_read(&key->sem); + upayload = key->payload.data; + if (IS_ERR_OR_NULL(upayload)) { + rc = PTR_ERR(key); + goto out_key_put; + } + + /* find first : in payload */ + payload = (char *)upayload->data; + delim = strnchr(payload, upayload->datalen, ':'); + cFYI(1, "payload=%s", payload); + if (!delim) { + cFYI(1, "Unable to find ':' in payload (datalen=%d)", + upayload->datalen); + rc = -EINVAL; + goto out_key_put; + } + + len = delim - payload; + if (len > MAX_USERNAME_SIZE || len <= 0) { + cFYI(1, "Bad value from username search (len=%ld)", len); + rc = -EINVAL; + goto out_key_put; + } + + vol->username = kstrndup(payload, len, GFP_KERNEL); + if (!vol->username) { + cFYI(1, "Unable to allocate %ld bytes for username", len); + rc = -ENOMEM; + goto out_key_put; + } + cFYI(1, "%s: username=%s", __func__, vol->username); + + len = key->datalen - (len + 1); + if (len > MAX_PASSWORD_SIZE || len <= 0) { + cFYI(1, "Bad len for password search (len=%ld)", len); + rc = -EINVAL; + kfree(vol->username); + vol->username = NULL; + goto out_key_put; + } + + ++delim; + vol->password = kstrndup(delim, len, GFP_KERNEL); + if (!vol->password) { + cFYI(1, "Unable to allocate %ld bytes for password", len); + rc = -ENOMEM; + kfree(vol->username); + vol->username = NULL; + goto out_key_put; + } + +out_key_put: + up_read(&key->sem); + key_put(key); +out_err: + kfree(desc); + cFYI(1, "%s: returning %d", __func__, rc); + return rc; +} +#else /* ! CONFIG_KEYS */ +static inline int +cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)), + struct cifs_ses *ses __attribute__((unused))) +{ + return -ENOSYS; +} +#endif /* CONFIG_KEYS */ + static bool warned_on_ntlm; /* globals init to false automatically */ static struct cifs_ses * @@ -3693,16 +3823,38 @@ int cifs_setup_session(unsigned int xid, struct cifs_ses *ses, return rc; } +static int +cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) +{ + switch (ses->server->secType) { + case Kerberos: + vol->secFlg = CIFSSEC_MUST_KRB5; + return 0; + case NTLMv2: + vol->secFlg = CIFSSEC_MUST_NTLMV2; + break; + case NTLM: + vol->secFlg = CIFSSEC_MUST_NTLM; + break; + case RawNTLMSSP: + vol->secFlg = CIFSSEC_MUST_NTLMSSP; + break; + case LANMAN: + vol->secFlg = CIFSSEC_MUST_LANMAN; + break; + } + + return cifs_set_cifscreds(vol, ses); +} + static struct cifs_tcon * cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) { + int rc; struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_ses *ses; struct cifs_tcon *tcon = NULL; struct smb_vol *vol_info; - char username[28]; /* big enough for "krb50x" + hex of ULONG_MAX 6+16 */ - /* We used to have this as MAX_USERNAME which is */ - /* way too big now (256 instead of 32) */ vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); if (vol_info == NULL) { @@ -3710,8 +3862,6 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) goto out; } - snprintf(username, sizeof(username), "krb50x%x", fsuid); - vol_info->username = username; vol_info->local_nls = cifs_sb->local_nls; vol_info->linux_uid = fsuid; vol_info->cred_uid = fsuid; @@ -3721,8 +3871,11 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) vol_info->local_lease = master_tcon->local_lease; vol_info->no_linux_ext = !master_tcon->unix_ext; - /* FIXME: allow for other secFlg settings */ - vol_info->secFlg = CIFSSEC_MUST_KRB5; + rc = cifs_set_vol_auth(vol_info, master_tcon->ses); + if (rc) { + tcon = ERR_PTR(rc); + goto out; + } /* get a reference for the same TCP session */ spin_lock(&cifs_tcp_ses_lock); @@ -3745,6 +3898,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) if (ses->capabilities & CAP_UNIX) reset_cifs_unix_caps(0, tcon, NULL, vol_info); out: + kfree(vol_info->username); + kfree(vol_info->password); kfree(vol_info); return tcon; -- cgit v0.10.2 From 789b4588da40cf572ef982bdc5d590ec1b0386fe Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 Jan 2012 16:09:15 -0500 Subject: cifs: warn about impending deprecation of legacy MultiuserMount code We'll allow a grace period of 2 releases (3.3 and 3.4) and then remove the legacy code in 3.5. Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 84e8c07..24b3dfc 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -676,14 +676,23 @@ static ssize_t cifs_multiuser_mount_proc_write(struct file *file, { char c; int rc; + static bool warned; rc = get_user(c, buffer); if (rc) return rc; if (c == '0' || c == 'n' || c == 'N') multiuser_mount = 0; - else if (c == '1' || c == 'y' || c == 'Y') + else if (c == '1' || c == 'y' || c == 'Y') { multiuser_mount = 1; + if (!warned) { + warned = true; + printk(KERN_WARNING "CIFS VFS: The legacy multiuser " + "mount code is scheduled to be deprecated in " + "3.5. Please switch to using the multiuser " + "mount option."); + } + } return count; } -- cgit v0.10.2 From 766e9f1be128bcdc15aa7d07084d0d51e873b5ed Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Jan 2012 12:45:36 +0000 Subject: ipv6: fix per device IP snmp counters In commit 4ce3c183fca (snmp: 64bit ipstats_mib for all arches), I forgot to change the /proc/net/dev_snmp6/xxx output for IP counters. percpu array is 64bit per counter but the folding still used the 'long' variant, and output garbage on 32bit arches. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index fdeb6d0..da2e92d 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -237,8 +237,8 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v) struct inet6_dev *idev = (struct inet6_dev *)seq->private; seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); - snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, NULL, - snmp6_ipstats_list); + snmp6_seq_show_item64(seq, (void __percpu **)idev->stats.ipv6, + snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs, snmp6_icmp6_list); snmp6_seq_show_icmpv6msg(seq, idev->stats.icmpv6msgdev->mibs); -- cgit v0.10.2 From 77676fdbd54f0c6fdb71d55d9758bebc69a00fc4 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 17 Jan 2012 13:33:39 +0000 Subject: openvswitch: Fix multipart datapath dumps. The logic to split up the list of datapaths into multiple Netlink messages was simply wrong, causing the list to be terminated after the first part. Only about the first 50 datapaths would be dumped. This fixes the problem. Reported-by: Paul Ingram Signed-off-by: Ben Pfaff Signed-off-by: Jesse Gross Signed-off-by: David S. Miller diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index d9d01cb..ce64c18 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2011 Nicira Networks. + * Copyright (c) 2007-2012 Nicira Networks. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -1396,9 +1396,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) int i = 0; list_for_each_entry(dp, &dps, list_node) { - if (i < skip) - continue; - if (ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).pid, + if (i >= skip && + ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_DP_CMD_NEW) < 0) break; -- cgit v0.10.2 From c3b5003b628d8e373262bee42c7260d6a799c73e Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Tue, 17 Jan 2012 15:27:23 +0000 Subject: tg3: Fix single-vector MSI-X code Kdump kernels leave MSI-X interrupts (as setup by the crashed kernel) enabled. However, kdump only enables one CPU in the new environment, thus causing tg3 to abort MSI-X setup. When the driver attempts to enable INTA or MSI interrupt modes on a kdump kernel, interrupt delivery fails. This patch attempts to workaround the problem by forcing the driver to enable a single MSI-X interrupt. In such a configuration, the device's multivector interrupt mode must be disabled. Signed-off-by: Matt Carlson Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 076e02a..d529af9 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -8846,9 +8846,11 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl); udelay(100); - if (tg3_flag(tp, USING_MSIX) && tp->irq_cnt > 1) { + if (tg3_flag(tp, USING_MSIX)) { val = tr32(MSGINT_MODE); - val |= MSGINT_MODE_MULTIVEC_EN | MSGINT_MODE_ENABLE; + val |= MSGINT_MODE_ENABLE; + if (tp->irq_cnt > 1) + val |= MSGINT_MODE_MULTIVEC_EN; if (!tg3_flag(tp, 1SHOT_MSI)) val |= MSGINT_MODE_ONE_SHOT_DISABLE; tw32(MSGINT_MODE, val); @@ -9548,19 +9550,18 @@ static int tg3_request_firmware(struct tg3 *tp) static bool tg3_enable_msix(struct tg3 *tp) { - int i, rc, cpus = num_online_cpus(); + int i, rc; struct msix_entry msix_ent[tp->irq_max]; - if (cpus == 1) - /* Just fallback to the simpler MSI mode. */ - return false; - - /* - * We want as many rx rings enabled as there are cpus. - * The first MSIX vector only deals with link interrupts, etc, - * so we add one to the number of vectors we are requesting. - */ - tp->irq_cnt = min_t(unsigned, cpus + 1, tp->irq_max); + tp->irq_cnt = num_online_cpus(); + if (tp->irq_cnt > 1) { + /* We want as many rx rings enabled as there are cpus. + * In multiqueue MSI-X mode, the first MSI-X vector + * only deals with link interrupts, etc, so we add + * one to the number of vectors we are requesting. + */ + tp->irq_cnt = min_t(unsigned, tp->irq_cnt + 1, tp->irq_max); + } for (i = 0; i < tp->irq_max; i++) { msix_ent[i].entry = i; -- cgit v0.10.2 From 3bd81a8710710f8bf5d1a5ebac315c842c20bdd3 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 17 Jan 2012 22:40:07 +0100 Subject: ACPI processor: Remove unneeded variable passed by acpi_processor_hotadd_init V2 V2: Fix typo: pr->handle -> pr, here: acpi_processor_hotadd_init(pr) This is a very small part taken from patches which afaik are coming from Yunhong Jiang (for a Xen not a Linus repo?). Cleanup only: no functional change. Advantage (beside cleanup) is that other data of the pr (acpi_processor) struct in the acpi_processor_hotadd_init() is needed later, for example a newly introduced flag: pr->flags.need_hotplug_init Signed-off-by: Thomas Renninger CC: Bjorn Helgaas CC: Jiang, Yunhong Signed-off-by: Len Brown diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 3616ce5..b9cbd9b 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -82,7 +82,7 @@ MODULE_LICENSE("GPL"); static int acpi_processor_add(struct acpi_device *device); static int acpi_processor_remove(struct acpi_device *device, int type); static void acpi_processor_notify(struct acpi_device *device, u32 event); -static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu); +static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr); static int acpi_processor_handle_eject(struct acpi_processor *pr); @@ -324,10 +324,8 @@ static int acpi_processor_get_info(struct acpi_device *device) * they are physically not present. */ if (pr->id == -1) { - if (ACPI_FAILURE - (acpi_processor_hotadd_init(pr->handle, &pr->id))) { + if (ACPI_FAILURE(acpi_processor_hotadd_init(pr))) return -ENODEV; - } } /* * On some boxes several processors use the same processor bus id. @@ -721,18 +719,19 @@ processor_walk_namespace_cb(acpi_handle handle, return (AE_OK); } -static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu) +static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) { + acpi_handle handle = pr->handle; if (!is_processor_present(handle)) { return AE_ERROR; } - if (acpi_map_lsapic(handle, p_cpu)) + if (acpi_map_lsapic(handle, &pr->id)) return AE_ERROR; - if (arch_register_cpu(*p_cpu)) { - acpi_unmap_lsapic(*p_cpu); + if (arch_register_cpu(pr->id)) { + acpi_unmap_lsapic(pr->id); return AE_ERROR; } @@ -749,7 +748,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr) return (0); } #else -static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu) +static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) { return AE_ERROR; } -- cgit v0.10.2 From 65b7f839ceecc0a36c7969c0c9151d5748cd4242 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 17 Jan 2012 22:40:08 +0100 Subject: intel_idle: Split up and provide per CPU initialization func Function split up, should have no functional change. Provides entry point for physically hotplugged CPUs to initialize and activate cpuidle. Signed-off-by: Thomas Renninger CC: Deepthi Dharwar CC: Shaohua Li CC: Andrew Morton Signed-off-by: Len Brown diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 5d2f8e1..ef0c04d 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -478,64 +478,60 @@ static int intel_idle_cpuidle_driver_init(void) /* - * intel_idle_cpuidle_devices_init() + * intel_idle_cpu_init() * allocate, initialize, register cpuidle_devices + * @cpu: cpu/core to initialize */ -static int intel_idle_cpuidle_devices_init(void) +int intel_idle_cpu_init(int cpu) { - int i, cstate; + int cstate; struct cpuidle_device *dev; - intel_idle_cpuidle_devices = alloc_percpu(struct cpuidle_device); - if (intel_idle_cpuidle_devices == NULL) - return -ENOMEM; - - for_each_online_cpu(i) { - dev = per_cpu_ptr(intel_idle_cpuidle_devices, i); + dev = per_cpu_ptr(intel_idle_cpuidle_devices, cpu); - dev->state_count = 1; + dev->state_count = 1; - for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) { - int num_substates; + for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) { + int num_substates; - if (cstate > max_cstate) { - printk(PREFIX "max_cstate %d reached\n", - max_cstate); - break; - } + if (cstate > max_cstate) { + printk(PREFIX "max_cstate %d reached\n", + max_cstate); + break; + } - /* does the state exist in CPUID.MWAIT? */ - num_substates = (mwait_substates >> ((cstate) * 4)) - & MWAIT_SUBSTATE_MASK; - if (num_substates == 0) - continue; - /* is the state not enabled? */ - if (cpuidle_state_table[cstate].enter == NULL) { - continue; - } + /* does the state exist in CPUID.MWAIT? */ + num_substates = (mwait_substates >> ((cstate) * 4)) + & MWAIT_SUBSTATE_MASK; + if (num_substates == 0) + continue; + /* is the state not enabled? */ + if (cpuidle_state_table[cstate].enter == NULL) + continue; - dev->states_usage[dev->state_count].driver_data = - (void *)get_driver_data(cstate); + dev->states_usage[dev->state_count].driver_data = + (void *)get_driver_data(cstate); dev->state_count += 1; } + dev->cpu = cpu; - dev->cpu = i; - if (cpuidle_register_device(dev)) { - pr_debug(PREFIX "cpuidle_register_device %d failed!\n", - i); - intel_idle_cpuidle_devices_uninit(); - return -EIO; - } + if (cpuidle_register_device(dev)) { + pr_debug(PREFIX "cpuidle_register_device %d failed!\n", cpu); + intel_idle_cpuidle_devices_uninit(); + return -EIO; } + if (auto_demotion_disable_flags) + smp_call_function_single(cpu, auto_demotion_disable, NULL, 1); + return 0; } static int __init intel_idle_init(void) { - int retval; + int retval, i; /* Do not load intel_idle at all for now if idle= is passed */ if (boot_option_idle_override != IDLE_NO_OVERRIDE) @@ -553,10 +549,16 @@ static int __init intel_idle_init(void) return retval; } - retval = intel_idle_cpuidle_devices_init(); - if (retval) { - cpuidle_unregister_driver(&intel_idle_driver); - return retval; + intel_idle_cpuidle_devices = alloc_percpu(struct cpuidle_device); + if (intel_idle_cpuidle_devices == NULL) + return -ENOMEM; + + for_each_online_cpu(i) { + retval = intel_idle_cpu_init(i); + if (retval) { + cpuidle_unregister_driver(&intel_idle_driver); + return retval; + } } return 0; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 7408af8..93df66e 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -188,7 +188,14 @@ struct cpuidle_governor { extern int cpuidle_register_governor(struct cpuidle_governor *gov); extern void cpuidle_unregister_governor(struct cpuidle_governor *gov); +#ifdef CONFIG_INTEL_IDLE +extern int intel_idle_cpu_init(int cpu); #else +static inline int intel_idle_cpu_init(int cpu) { return -1; } +#endif + +#else +static inline int intel_idle_cpu_init(int cpu) { return -1; } static inline int cpuidle_register_governor(struct cpuidle_governor *gov) {return 0;} -- cgit v0.10.2 From c130bd6f82e5dda28b1a19741c4c2fe269713199 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 17 Jan 2012 12:10:16 -0800 Subject: acpi/apei/einj: Add extensions to EINJ from rev 5.0 of acpi spec ACPI 5.0 provides extensions to the EINJ mechanism to specify the target for the error injection - by APICID for cpu related errors, by address for memory related errors, and by segment/bus/device/function for PCIe related errors. Also extensions for vendor specific error injections. Tested-by: Chen Gong Signed-off-by: Tony Luck Signed-off-by: Len Brown diff --git a/Documentation/acpi/apei/einj.txt b/Documentation/acpi/apei/einj.txt index 5cc699b..e7cc363 100644 --- a/Documentation/acpi/apei/einj.txt +++ b/Documentation/acpi/apei/einj.txt @@ -47,20 +47,53 @@ directory apei/einj. The following files are provided. - param1 This file is used to set the first error parameter value. Effect of - parameter depends on error_type specified. For memory error, this is - physical memory address. Only available if param_extension module - parameter is specified. + parameter depends on error_type specified. - param2 This file is used to set the second error parameter value. Effect of - parameter depends on error_type specified. For memory error, this is - physical memory address mask. Only available if param_extension - module parameter is specified. + parameter depends on error_type specified. + +BIOS versions based in the ACPI 4.0 specification have limited options +to control where the errors are injected. Your BIOS may support an +extension (enabled with the param_extension=1 module parameter, or +boot command line einj.param_extension=1). This allows the address +and mask for memory injections to be specified by the param1 and +param2 files in apei/einj. + +BIOS versions using the ACPI 5.0 specification have more control over +the target of the injection. For processor related errors (type 0x1, +0x2 and 0x4) the APICID of the target should be provided using the +param1 file in apei/einj. For memory errors (type 0x8, 0x10 and 0x20) +the address is set using param1 with a mask in param2 (0x0 is equivalent +to all ones). For PCI express errors (type 0x40, 0x80 and 0x100) the +segment, bus, device and function are specified using param1: + + 31 24 23 16 15 11 10 8 7 0 + +-------------------------------------------------+ + | segment | bus | device | function | reserved | + +-------------------------------------------------+ + +An ACPI 5.0 BIOS may also allow vendor specific errors to be injected. +In this case a file named vendor will contain identifying information +from the BIOS that hopefully will allow an application wishing to use +the vendor specific extension to tell that they are running on a BIOS +that supports it. All vendor extensions have the 0x80000000 bit set in +error_type. A file vendor_flags controls the interpretation of param1 +and param2 (1 = PROCESSOR, 2 = MEMORY, 4 = PCI). See your BIOS vendor +documentation for details (and expect changes to this API if vendors +creativity in using this feature expands beyond our expectations). + +Example: +# cd /sys/kernel/debug/apei/einj +# cat available_error_type # See which errors can be injected +0x00000002 Processor Uncorrectable non-fatal +0x00000008 Memory Correctable +0x00000010 Memory Uncorrectable non-fatal +# echo 0x12345000 > param1 # Set memory address for injection +# echo 0xfffffffffffff000 > param2 # Mask - anywhere in this page +# echo 0x8 > error_type # Choose correctable memory error +# echo 1 > error_inject # Inject now -Injecting parameter support is a BIOS version specific extension, that -is, it only works on some BIOS version. If you want to use it, please -make sure your BIOS version has the proper support and specify -"param_extension=y" in module parameter. For more information about EINJ, please refer to ACPI specification -version 4.0, section 17.5. +version 4.0, section 17.5 and ACPI 5.0, section 18.6. diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 589b96c..31546fd 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -43,6 +43,42 @@ #define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) /* + * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action. + */ +static int acpi5; + +struct set_error_type_with_address { + u32 type; + u32 vendor_extension; + u32 flags; + u32 apicid; + u64 memory_address; + u64 memory_address_range; + u32 pcie_sbdf; +}; +enum { + SETWA_FLAGS_APICID = 1, + SETWA_FLAGS_MEM = 2, + SETWA_FLAGS_PCIE_SBDF = 4, +}; + +/* + * Vendor extensions for platform specific operations + */ +struct vendor_error_type_extension { + u32 length; + u32 pcie_sbdf; + u16 vendor_id; + u16 device_id; + u8 rev_id; + u8 reserved[3]; +}; + +static u32 vendor_flags; +static struct debugfs_blob_wrapper vendor_blob; +static char vendor_dev[64]; + +/* * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the * EINJ table through an unpublished extension. Use with caution as * most will ignore the parameter and make their own choice of address @@ -103,7 +139,14 @@ static struct apei_exec_ins_type einj_ins_type[] = { */ static DEFINE_MUTEX(einj_mutex); -static struct einj_parameter *einj_param; +static void *einj_param; + +#ifndef readq +static inline __u64 readq(volatile void __iomem *addr) +{ + return ((__u64)readl(addr+4) << 32) + readl(addr); +} +#endif #ifndef writeq static inline void writeq(__u64 val, volatile void __iomem *addr) @@ -158,10 +201,31 @@ static int einj_timedout(u64 *t) return 0; } -static u64 einj_get_parameter_address(void) +static void check_vendor_extension(u64 paddr, + struct set_error_type_with_address *v5param) +{ + int offset = readl(&v5param->vendor_extension); + struct vendor_error_type_extension *v; + u32 sbdf; + + if (!offset) + return; + v = ioremap(paddr + offset, sizeof(*v)); + if (!v) + return; + sbdf = readl(&v->pcie_sbdf); + sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", + sbdf >> 24, (sbdf >> 16) & 0xff, + (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, + readw(&v->vendor_id), readw(&v->device_id), + readb(&v->rev_id)); + iounmap(v); +} + +static void *einj_get_parameter_address(void) { int i; - u64 paddr = 0; + u64 paddrv4 = 0, paddrv5 = 0; struct acpi_whea_header *entry; entry = EINJ_TAB_ENTRY(einj_tab); @@ -170,12 +234,40 @@ static u64 einj_get_parameter_address(void) entry->instruction == ACPI_EINJ_WRITE_REGISTER && entry->register_region.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - memcpy(&paddr, &entry->register_region.address, - sizeof(paddr)); + memcpy(&paddrv4, &entry->register_region.address, + sizeof(paddrv4)); + if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS && + entry->instruction == ACPI_EINJ_WRITE_REGISTER && + entry->register_region.space_id == + ACPI_ADR_SPACE_SYSTEM_MEMORY) + memcpy(&paddrv5, &entry->register_region.address, + sizeof(paddrv5)); entry++; } + if (paddrv5) { + struct set_error_type_with_address *v5param; + + v5param = ioremap(paddrv5, sizeof(*v5param)); + if (v5param) { + acpi5 = 1; + check_vendor_extension(paddrv5, v5param); + return v5param; + } + } + if (paddrv4) { + struct einj_parameter *v4param; + + v4param = ioremap(paddrv4, sizeof(*v4param)); + if (!v4param) + return 0; + if (readq(&v4param->reserved1) || readq(&v4param->reserved2)) { + iounmap(v4param); + return 0; + } + return v4param; + } - return paddr; + return 0; } /* do sanity check to trigger table */ @@ -293,12 +385,56 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) if (rc) return rc; apei_exec_ctx_set_input(&ctx, type); - rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); - if (rc) - return rc; - if (einj_param) { - writeq(param1, &einj_param->param1); - writeq(param2, &einj_param->param2); + if (acpi5) { + struct set_error_type_with_address *v5param = einj_param; + + writel(type, &v5param->type); + if (type & 0x80000000) { + switch (vendor_flags) { + case SETWA_FLAGS_APICID: + writel(param1, &v5param->apicid); + break; + case SETWA_FLAGS_MEM: + writeq(param1, &v5param->memory_address); + writeq(param2, &v5param->memory_address_range); + break; + case SETWA_FLAGS_PCIE_SBDF: + writel(param1, &v5param->pcie_sbdf); + break; + } + writel(vendor_flags, &v5param->flags); + } else { + switch (type) { + case ACPI_EINJ_PROCESSOR_CORRECTABLE: + case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: + case ACPI_EINJ_PROCESSOR_FATAL: + writel(param1, &v5param->apicid); + writel(SETWA_FLAGS_APICID, &v5param->flags); + break; + case ACPI_EINJ_MEMORY_CORRECTABLE: + case ACPI_EINJ_MEMORY_UNCORRECTABLE: + case ACPI_EINJ_MEMORY_FATAL: + writeq(param1, &v5param->memory_address); + writeq(param2, &v5param->memory_address_range); + writel(SETWA_FLAGS_MEM, &v5param->flags); + break; + case ACPI_EINJ_PCIX_CORRECTABLE: + case ACPI_EINJ_PCIX_UNCORRECTABLE: + case ACPI_EINJ_PCIX_FATAL: + writel(param1, &v5param->pcie_sbdf); + writel(SETWA_FLAGS_PCIE_SBDF, &v5param->flags); + break; + } + } + } else { + rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); + if (rc) + return rc; + if (einj_param) { + struct einj_parameter *v4param = einj_param; + writeq(param1, &v4param->param1); + writeq(param2, &v4param->param2); + } } rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); if (rc) @@ -408,15 +544,25 @@ static int error_type_set(void *data, u64 val) { int rc; u32 available_error_type = 0; + u32 tval, vendor; + + /* + * Vendor defined types have 0x80000000 bit set, and + * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE + */ + vendor = val & 0x80000000; + tval = val & 0x7fffffff; /* Only one error type can be specified */ - if (val & (val - 1)) - return -EINVAL; - rc = einj_get_available_error_type(&available_error_type); - if (rc) - return rc; - if (!(val & available_error_type)) + if (tval & (tval - 1)) return -EINVAL; + if (!vendor) { + rc = einj_get_available_error_type(&available_error_type); + if (rc) + return rc; + if (!(val & available_error_type)) + return -EINVAL; + } error_type = val; return 0; @@ -455,7 +601,6 @@ static int einj_check_table(struct acpi_table_einj *einj_tab) static int __init einj_init(void) { int rc; - u64 param_paddr; acpi_status status; struct dentry *fentry; struct apei_exec_context ctx; @@ -509,23 +654,30 @@ static int __init einj_init(void) rc = apei_exec_pre_map_gars(&ctx); if (rc) goto err_release; - if (param_extension) { - param_paddr = einj_get_parameter_address(); - if (param_paddr) { - einj_param = ioremap(param_paddr, sizeof(*einj_param)); - rc = -ENOMEM; - if (!einj_param) - goto err_unmap; - fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR, - einj_debug_dir, &error_param1); - if (!fentry) - goto err_unmap; - fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR, - einj_debug_dir, &error_param2); - if (!fentry) - goto err_unmap; - } else - pr_warn(EINJ_PFX "Parameter extension is not supported.\n"); + + einj_param = einj_get_parameter_address(); + if ((param_extension || acpi5) && einj_param) { + fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_param1); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_param2); + if (!fentry) + goto err_unmap; + } + + if (vendor_dev[0]) { + vendor_blob.data = vendor_dev; + vendor_blob.size = strlen(vendor_dev); + fentry = debugfs_create_blob("vendor", S_IRUSR, + einj_debug_dir, &vendor_blob); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR, + einj_debug_dir, &vendor_flags); + if (!fentry) + goto err_unmap; } pr_info(EINJ_PFX "Error INJection is initialized.\n"); diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 7504bc9..f25d7ef 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -228,7 +228,8 @@ enum acpi_einj_actions { ACPI_EINJ_EXECUTE_OPERATION = 5, ACPI_EINJ_CHECK_BUSY_STATUS = 6, ACPI_EINJ_GET_COMMAND_STATUS = 7, - ACPI_EINJ_ACTION_RESERVED = 8, /* 8 and greater are reserved */ + ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS = 8, + ACPI_EINJ_ACTION_RESERVED = 9, /* 9 and greater are reserved */ ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */ }; -- cgit v0.10.2 From 48cfe37cc03f616e6c139796962e7ec677cde8a9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 21 Dec 2011 14:20:23 -0500 Subject: target: don't allocate bio headroom in iblock We never embedd the bio into a structure, so there is no need to allocate 64 bytes of headroom per bio. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index cc8e6b5..628e877 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -129,7 +129,7 @@ static struct se_device *iblock_create_virtdevice( /* * These settings need to be made tunable.. */ - ib_dev->ibd_bio_set = bioset_create(32, 64); + ib_dev->ibd_bio_set = bioset_create(32, 0); if (!ib_dev->ibd_bio_set) { pr_err("IBLOCK: Unable to create bioset()\n"); return ERR_PTR(-ENOMEM); -- cgit v0.10.2 From 9e08e34e3735ae057eb3834da3570995811b7eb9 Mon Sep 17 00:00:00 2001 From: Marco Sanvido Date: Tue, 3 Jan 2012 17:12:57 -0800 Subject: target: Use correct preempted registration sense code The comments quote the right parts of the spec: * d) Establish a unit attention condition for the * initiator port associated with every I_T nexus * that lost its registration other than the I_T * nexus on which the PERSISTENT RESERVE OUT command * was received, with the additional sense code set * to REGISTRATIONS PREEMPTED. and * e) Establish a unit attention condition for the initiator * port associated with every I_T nexus that lost its * persistent reservation and/or registration, with the * additional sense code set to REGISTRATIONS PREEMPTED; but the actual code accidentally uses ASCQ_2AH_RESERVATIONS_PREEMPTED instead of ASCQ_2AH_REGISTRATIONS_PREEMPTED. Fix this. Signed-off-by: Marco Sanvido Signed-off-by: Roland Dreier Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 429ad72..d14860f 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -3120,7 +3120,7 @@ static int core_scsi3_pro_preempt( if (!calling_it_nexus) core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A, - ASCQ_2AH_RESERVATIONS_PREEMPTED); + ASCQ_2AH_REGISTRATIONS_PREEMPTED); } spin_unlock(&pr_tmpl->registration_lock); /* @@ -3233,7 +3233,7 @@ static int core_scsi3_pro_preempt( * additional sense code set to REGISTRATIONS PREEMPTED; */ core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A, - ASCQ_2AH_RESERVATIONS_PREEMPTED); + ASCQ_2AH_REGISTRATIONS_PREEMPTED); } spin_unlock(&pr_tmpl->registration_lock); /* -- cgit v0.10.2 From 6816966a8418b980481b4dced7eddd1796b145e8 Mon Sep 17 00:00:00 2001 From: Marco Sanvido Date: Tue, 3 Jan 2012 17:12:58 -0800 Subject: target: Allow PERSISTENT RESERVE IN for non-reservation holder Initiators that aren't the active reservation holder should be able to do a PERSISTENT RESERVE IN command in all cases, so add it to the list of allowed CDBs in core_scsi3_pr_seq_non_holder(). Signed-off-by: Marco Sanvido Signed-off-by: Roland Dreier Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index d14860f..68c71cd 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -478,6 +478,7 @@ static int core_scsi3_pr_seq_non_holder( case READ_MEDIA_SERIAL_NUMBER: case REPORT_LUNS: case REQUEST_SENSE: + case PERSISTENT_RESERVE_IN: ret = 0; /*/ Allowed CDBs */ break; default: -- cgit v0.10.2 From 9db9da332250dbe662995703a4dcdd692112f0c3 Mon Sep 17 00:00:00 2001 From: "roland@purestorage.com" Date: Wed, 4 Jan 2012 15:59:58 -0800 Subject: target: Don't zero pages used for data buffers Doing alloc_page(GFP_KERNEL | __GFP_ZERO) to get pages used for data buffers wastes a lot of CPU clearing pages that will be quickly be overwritten by the actual data. However, for emulated control commands such as INQUIRY and so on, the code does assume that the buffer is zeroed. To avoid this CPU overhead, skip the __GFP_ZERO for commands that are actually moving data, ie cmds that have SCF_SCSI_DATA_SG_IO_CDB set. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index d3ddd13..289bc0f 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3516,6 +3516,7 @@ transport_generic_get_mem(struct se_cmd *cmd) u32 length = cmd->data_length; unsigned int nents; struct page *page; + gfp_t zero_flag; int i = 0; nents = DIV_ROUND_UP(length, PAGE_SIZE); @@ -3526,9 +3527,11 @@ transport_generic_get_mem(struct se_cmd *cmd) cmd->t_data_nents = nents; sg_init_table(cmd->t_data_sg, nents); + zero_flag = cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB ? 0 : __GFP_ZERO; + while (length) { u32 page_len = min_t(u32, length, PAGE_SIZE); - page = alloc_page(GFP_KERNEL | __GFP_ZERO); + page = alloc_page(GFP_KERNEL | zero_flag); if (!page) goto out; -- cgit v0.10.2 From 9fbc8909876a2160044e71d376848973b9bfdc3f Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Mon, 9 Jan 2012 17:54:00 -0800 Subject: target: Correct sense key for INVALID FIELD IN {PARAMETER LIST,CDB} According to SPC-4, the sense key for commands that are failed with INVALID FIELD IN PARAMETER LIST and INVALID FIELD IN CDB should be ILLEGAL REQUEST (5h) rather than ABORTED COMMAND (Bh). Without this patch, a tcm_loop LUN incorrectly gives: # sg_raw -r 1 -v /dev/sda 3 1 0 0 ff 0 Sense Information: Fixed format, current; Sense key: Aborted Command Additional sense: Invalid field in cdb Raw sense data (in hex): 70 00 0b 00 00 00 00 0a 00 00 00 00 24 00 00 00 00 00 While a real SCSI disk gives: Sense Information: Fixed format, current; Sense key: Illegal Request Additional sense: Invalid field in cdb Raw sense data (in hex): 70 00 05 00 00 00 00 18 00 00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 with the main point being that the real disk gives a sense key of ILLEGAL REQUEST (5h). Signed-off-by: Roland Dreier Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 289bc0f..2869fb7 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4451,8 +4451,8 @@ int transport_send_check_condition_and_sense( /* CURRENT ERROR */ buffer[offset] = 0x70; buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; + /* ILLEGAL REQUEST */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID FIELD IN CDB */ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x24; break; @@ -4460,8 +4460,8 @@ int transport_send_check_condition_and_sense( /* CURRENT ERROR */ buffer[offset] = 0x70; buffer[offset+SPC_ADD_SENSE_LEN_OFFSET] = 10; - /* ABORTED COMMAND */ - buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND; + /* ILLEGAL REQUEST */ + buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* INVALID FIELD IN PARAMETER LIST */ buffer[offset+SPC_ASC_KEY_OFFSET] = 0x26; break; -- cgit v0.10.2 From 91ec1d3535b2acf12c599045cc19ad9be3c6a47b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 13 Jan 2012 12:01:34 -0800 Subject: target: Add workaround for zero-length control CDB handling This patch adds a work-around for handling zero allocation length control CDBs (type SCF_SCSI_CONTROL_SG_IO_CDB) that was causing an OOPs with the following raw calls: # sg_raw -v /dev/sdd 3 0 0 0 0 0 # sg_raw -v /dev/sdd 0x1a 0 1 0 0 0 This patch will follow existing zero-length handling for data I/O and silently return with GOOD status. This addresses the zero length issue, but the proper long-term resolution for handling arbitary allocation lengths will be to refactor out data-phase handling in individual CDB emulation logic within target_core_cdb.c Reported-by: Roland Dreier Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2869fb7..50d6911 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3759,6 +3759,11 @@ transport_allocate_control_task(struct se_cmd *cmd) struct se_task *task; unsigned long flags; + /* Workaround for handling zero-length control CDBs */ + if ((cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) && + !cmd->data_length) + return 0; + task = transport_generic_get_task(cmd, cmd->data_direction); if (!task) return -ENOMEM; @@ -3830,6 +3835,14 @@ int transport_generic_new_cmd(struct se_cmd *cmd) else if (!task_cdbs && (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)) { cmd->t_state = TRANSPORT_COMPLETE; atomic_set(&cmd->t_transport_active, 1); + + if (cmd->t_task_cdb[0] == REQUEST_SENSE) { + u8 ua_asc = 0, ua_ascq = 0; + + core_scsi3_ua_clear_for_request_sense(cmd, + &ua_asc, &ua_ascq); + } + INIT_WORK(&cmd->work, target_complete_ok_work); queue_work(target_completion_wq, &cmd->work); return 0; -- cgit v0.10.2 From e59a41b69a8e116d5ac8c95c4222f5a971f66bbd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 10 Jan 2012 14:16:57 +0100 Subject: target: avoid multiple outputs in scsi_dump_inquiry() The multiple calls to pr_debug() each with one letter results in a new line. This patch merges the multiple requests into one call per line so we don't have the multiple line cuts. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 50d6911..e186f7d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1255,32 +1255,34 @@ static void core_setup_task_attr_emulation(struct se_device *dev) static void scsi_dump_inquiry(struct se_device *dev) { struct t10_wwn *wwn = &dev->se_sub_dev->t10_wwn; + char buf[17]; int i, device_type; /* * Print Linux/SCSI style INQUIRY formatting to the kernel ring buffer */ - pr_debug(" Vendor: "); for (i = 0; i < 8; i++) if (wwn->vendor[i] >= 0x20) - pr_debug("%c", wwn->vendor[i]); + buf[i] = wwn->vendor[i]; else - pr_debug(" "); + buf[i] = ' '; + buf[i] = '\0'; + pr_debug(" Vendor: %s\n", buf); - pr_debug(" Model: "); for (i = 0; i < 16; i++) if (wwn->model[i] >= 0x20) - pr_debug("%c", wwn->model[i]); + buf[i] = wwn->model[i]; else - pr_debug(" "); + buf[i] = ' '; + buf[i] = '\0'; + pr_debug(" Model: %s\n", buf); - pr_debug(" Revision: "); for (i = 0; i < 4; i++) if (wwn->revision[i] >= 0x20) - pr_debug("%c", wwn->revision[i]); + buf[i] = wwn->revision[i]; else - pr_debug(" "); - - pr_debug("\n"); + buf[i] = ' '; + buf[i] = '\0'; + pr_debug(" Revision: %s\n", buf); device_type = dev->transport->get_device_type(dev); pr_debug(" Type: %s ", scsi_device_type(device_type)); -- cgit v0.10.2 From 1dd0a0674530da61cdbfadd88c96949b483a7c19 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 10 Jan 2012 14:16:58 +0100 Subject: target: use save/restore lock primitive in core_dec_lacl_count() It may happen that uasp will free the request in irq conntext, the callchain: uasp_cmd_release() -> transport_generic_free_cmd() -> core_dec_lacl_count() where the last function enables the IRQ. Those irqs are re-disabled later (due to the spin.*irq_restore) but in between we could get hurt. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 0c5992f..00159a4 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -320,11 +320,12 @@ int core_free_device_list_for_node( void core_dec_lacl_count(struct se_node_acl *se_nacl, struct se_cmd *se_cmd) { struct se_dev_entry *deve; + unsigned long flags; - spin_lock_irq(&se_nacl->device_list_lock); + spin_lock_irqsave(&se_nacl->device_list_lock, flags); deve = &se_nacl->device_list[se_cmd->orig_fe_lun]; deve->deve_cmds--; - spin_unlock_irq(&se_nacl->device_list_lock); + spin_unlock_irqrestore(&se_nacl->device_list_lock, flags); } void core_update_device_list_access( -- cgit v0.10.2 From 8d9efe539cf78f6a90947d47100e4a86d907750f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 21:43:38 +0100 Subject: target: fix return code of core_tpg_.*_lun - core_tpg_pre_addlun() returns always ERR_PTR() or the pointer, never NULL. The additional check for NULL in core_dev_add_lun() is not required. - core_tpg_pre_dellun() returns always ERR_PTR() or the pointer, never NULL. The check for NULL in core_dev_del_lun() is wrong. The third argument (int *) is never used, remove it. - core_dev_add_lun() returns always NULL or the pointer, never ERR_PTR. The check for IS_ERR() is not required. (nab: Convert core_dev_add_lun() use err.h macros for failure handling to be consistent with the rest of target_core_fabric_configfs.c callers) Signed-off-by: Sebastian Andrzej Siewior Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 00159a4..de5f4fe 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1295,24 +1295,26 @@ struct se_lun *core_dev_add_lun( { struct se_lun *lun_p; u32 lun_access = 0; + int rc; if (atomic_read(&dev->dev_access_obj.obj_access_count) != 0) { pr_err("Unable to export struct se_device while dev_access_obj: %d\n", atomic_read(&dev->dev_access_obj.obj_access_count)); - return NULL; + return ERR_PTR(-EACCES); } lun_p = core_tpg_pre_addlun(tpg, lun); - if ((IS_ERR(lun_p)) || !lun_p) - return NULL; + if (IS_ERR(lun_p)) + return lun_p; if (dev->dev_flags & DF_READ_ONLY) lun_access = TRANSPORT_LUNFLAGS_READ_ONLY; else lun_access = TRANSPORT_LUNFLAGS_READ_WRITE; - if (core_tpg_post_addlun(tpg, lun_p, lun_access, dev) < 0) - return NULL; + rc = core_tpg_post_addlun(tpg, lun_p, lun_access, dev); + if (rc < 0) + return ERR_PTR(rc); pr_debug("%s_TPG[%u]_LUN[%u] - Activated %s Logical Unit from" " CORE HBA: %u\n", tpg->se_tpg_tfo->get_fabric_name(), @@ -1349,11 +1351,10 @@ int core_dev_del_lun( u32 unpacked_lun) { struct se_lun *lun; - int ret = 0; - lun = core_tpg_pre_dellun(tpg, unpacked_lun, &ret); - if (!lun) - return ret; + lun = core_tpg_pre_dellun(tpg, unpacked_lun); + if (IS_ERR(lun)) + return PTR_ERR(lun); core_tpg_post_dellun(tpg, lun); diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 4f77cce..9a2ce11 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -766,9 +766,9 @@ static int target_fabric_port_link( lun_p = core_dev_add_lun(se_tpg, dev->se_hba, dev, lun->unpacked_lun); - if (IS_ERR(lun_p) || !lun_p) { + if (IS_ERR(lun_p)) { pr_err("core_dev_add_lun() failed\n"); - ret = -EINVAL; + ret = PTR_ERR(lun_p); goto out; } diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 26f135e..4500136 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -90,7 +90,7 @@ void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *); struct se_lun *core_tpg_pre_addlun(struct se_portal_group *, u32); int core_tpg_post_addlun(struct se_portal_group *, struct se_lun *, u32, void *); -struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32, int *); +struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun); int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); /* target_core_transport.c */ diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index b766802..06336ec 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -807,8 +807,7 @@ static void core_tpg_shutdown_lun( struct se_lun *core_tpg_pre_dellun( struct se_portal_group *tpg, - u32 unpacked_lun, - int *ret) + u32 unpacked_lun) { struct se_lun *lun; -- cgit v0.10.2 From c1ce4bd56f2846de55043374598fd929ad3b711b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 16 Jan 2012 16:04:15 -0800 Subject: iscsi-target: Fix reject release handling in iscsit_free_cmd() This patch addresses a bug where iscsit_free_cmd() was incorrectly calling iscsit_release_cmd() for ISCSI_OP_REJECT because iscsi_add_reject*() will overwrite the original iscsi_cmd->iscsi_opcode assignment. This bug was introduced with the following commit: commit 0be67f2ed8f577d2c72d917928394c5885fa9134 Author: Nicholas Bellinger Date: Sun Oct 9 01:48:14 2011 -0700 iscsi-target: Remove SCF_SE_LUN_CMD flag abuses and was manifesting itself as list corruption with the following: [ 131.191092] ------------[ cut here ]------------ [ 131.191092] WARNING: at lib/list_debug.c:53 __list_del_entry+0x8d/0x98() [ 131.191092] Hardware name: VMware Virtual Platform [ 131.191092] list_del corruption. prev->next should be ffff880022d3c100, but was 6b6b6b6b6b6b6b6b [ 131.191092] Modules linked in: tcm_vhost ib_srpt ib_cm ib_sa ib_mad ib_core tcm_qla2xxx qla2xxx tcm_loop tcm_fc libfc scsi_transport_fc crc32c iscsi_target_mod target_core_stgt scsi_tgt target_core_pscsi target_core_file target_core_iblock target_core_mod configfs ipv6 iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi sr_mod cdrom sd_mod e1000 ata_piix libata mptspi mptscsih mptbase [last unloaded: scsi_wait_scan] [ 131.191092] Pid: 2250, comm: iscsi_ttx Tainted: G W 3.2.0-rc4+ #42 [ 131.191092] Call Trace: [ 131.191092] [] warn_slowpath_common+0x80/0x98 [ 131.191092] [] warn_slowpath_fmt+0x41/0x43 [ 131.191092] [] __list_del_entry+0x8d/0x98 [ 131.191092] [] transport_lun_remove_cmd+0x9b/0xb7 [target_core_mod] [ 131.191092] [] transport_generic_free_cmd+0x5d/0x71 [target_core_mod] [ 131.191092] [] iscsit_free_cmd+0x1e/0x27 [iscsi_target_mod] [ 131.191092] [] iscsit_close_connection+0x14d/0x5b2 [iscsi_target_mod] [ 131.191092] [] iscsit_take_action_for_connection_exit+0xdb/0xe0 [iscsi_target_mod] [ 131.191092] [] iscsi_target_tx_thread+0x15cb/0x1608 [iscsi_target_mod] [ 131.191092] [] ? check_preempt_wakeup+0x121/0x185 [ 131.191092] [] ? __dequeue_entity+0x2e/0x33 [ 131.191092] [] ? iscsit_send_text_rsp+0x25f/0x25f [iscsi_target_mod] [ 131.191092] [] ? iscsit_send_text_rsp+0x25f/0x25f [iscsi_target_mod] [ 131.191092] [] ? schedule+0x55/0x57 [ 131.191092] [] kthread+0x7d/0x85 [ 131.191092] [] kernel_thread_helper+0x4/0x10 [ 131.191092] [] ? kthread_worker_fn+0x16d/0x16d [ 131.191092] [] ? gs_change+0x13/0x13 Reported-by: Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index a05ca1c..11287e1 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -849,6 +849,17 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd) case ISCSI_OP_SCSI_TMFUNC: transport_generic_free_cmd(&cmd->se_cmd, 1); break; + case ISCSI_OP_REJECT: + /* + * Handle special case for REJECT when iscsi_add_reject*() has + * overwritten the original iscsi_opcode assignment, and the + * associated cmd->se_cmd needs to be released. + */ + if (cmd->se_cmd.se_tfo != NULL) { + transport_generic_free_cmd(&cmd->se_cmd, 1); + break; + } + /* Fall-through */ default: iscsit_release_cmd(cmd); break; -- cgit v0.10.2 From cd931ee62fd0258fc85c76a7c5499fe85e0f3436 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 16 Jan 2012 17:11:54 -0800 Subject: iscsi-target: Fix double list_add with iscsit_alloc_buffs reject This patch fixes a bug where the iscsit_add_reject_from_cmd() call from a failure to iscsit_alloc_buffs() was incorrectly passing add_to_conn=1 and causing a double list_add after iscsi_cmd->i_list had already been added in iscsit_handle_scsi_cmd(). Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index ac44af1..6e070e0 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1061,7 +1061,7 @@ attach_cmd: if (ret < 0) return iscsit_add_reject_from_cmd( ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + 1, 0, buf, cmd); /* * Check the CmdSN against ExpCmdSN/MaxCmdSN here if * the Immediate Bit is not set, and no Immediate -- cgit v0.10.2 From f8d48ae52eeec906d7fb42485eb26a5d305bab0a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 15 Jan 2012 14:30:24 +0300 Subject: iscsi-target: make one-bit bitfields unsigned Signed bitfields are a problem because instead of being 1 or 0 like you'd expect they are 0 and -1. It doesn't cause a problem in this case but sparse complains: drivers/target/iscsi/iscsi_target_core.h:564:56: error: dubious one-bit signed bitfield Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index f1a02da..ebf81fd 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -561,8 +561,8 @@ struct iscsi_conn { struct hash_desc conn_tx_hash; /* Used for scheduling TX and RX connection kthreads */ cpumask_var_t conn_cpumask; - int conn_rx_reset_cpumask:1; - int conn_tx_reset_cpumask:1; + unsigned int conn_rx_reset_cpumask:1; + unsigned int conn_tx_reset_cpumask:1; /* list_head of struct iscsi_cmd for this connection */ struct list_head conn_cmd_list; struct list_head immed_queue_list; -- cgit v0.10.2 From e8904dc5008ef92f0f62391d6557f03f921eeb32 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 15 Jan 2012 19:33:30 +0100 Subject: iscsi-target: Fix up a few assignments A statement such as struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); has undefined behaviour since there are two assignments to 'na', strictly speaking (the order in which side-effects from the assignments take place is undefined since there's no intervening sequence point), and it looks unintentional in any case. Signed-off-by: Jesper Juhl Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 255c0d6..27901e3 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -1238,7 +1238,7 @@ void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd) { struct iscsi_conn *conn = cmd->conn; struct iscsi_session *sess = conn->sess; - struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); + struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); spin_lock_bh(&cmd->dataout_timeout_lock); if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) { @@ -1261,7 +1261,7 @@ void iscsit_start_dataout_timer( struct iscsi_conn *conn) { struct iscsi_session *sess = conn->sess; - struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); + struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING) return; -- cgit v0.10.2 From 4949314c7283ea4f9ade182ca599583b89f7edd6 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Mon, 16 Jan 2012 16:57:08 -0800 Subject: target: Allow control CDBs with data > 1 page We need to handle >1 page control cdbs, so extend the code to do a vmap if bigger than 1 page. It seems like kmap() is still preferable if just a page, fewer TLB shootdowns(?), so keep using that when possible. Rename function pair for their new scope. Signed-off-by: Andy Grover Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 1b1edd1..01a2691 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -78,7 +78,7 @@ int target_emulate_report_target_port_groups(struct se_task *task) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); spin_lock(&su_dev->t10_alua.tg_pt_gps_lock); list_for_each_entry(tg_pt_gp, &su_dev->t10_alua.tg_pt_gps_list, @@ -163,7 +163,7 @@ int target_emulate_report_target_port_groups(struct se_task *task) buf[2] = ((rd_len >> 8) & 0xff); buf[3] = (rd_len & 0xff); - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -194,7 +194,7 @@ int target_emulate_set_target_port_groups(struct se_task *task) cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); /* * Determine if explict ALUA via SET_TARGET_PORT_GROUPS is allowed @@ -351,7 +351,7 @@ int target_emulate_set_target_port_groups(struct se_task *task) } out: - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); return 0; diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 2f2235e..07a3025 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -83,7 +83,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); if (dev == tpg->tpg_virt_lun0.lun_se_dev) { buf[0] = 0x3f; /* Not connected */ @@ -134,7 +134,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd) buf[4] = 31; /* Set additional length to 31 */ out: - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); return 0; } @@ -716,7 +716,7 @@ int target_emulate_inquiry(struct se_task *task) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = dev->transport->get_device_type(dev); @@ -733,7 +733,7 @@ int target_emulate_inquiry(struct se_task *task) ret = -EINVAL; out_unmap: - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); out: if (!ret) { task->task_scsi_status = GOOD; @@ -755,7 +755,7 @@ int target_emulate_readcapacity(struct se_task *task) else blocks = (u32)blocks_long; - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = (blocks >> 24) & 0xff; buf[1] = (blocks >> 16) & 0xff; @@ -771,7 +771,7 @@ int target_emulate_readcapacity(struct se_task *task) if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws) put_unaligned_be32(0xFFFFFFFF, &buf[0]); - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -785,7 +785,7 @@ int target_emulate_readcapacity_16(struct se_task *task) unsigned char *buf; unsigned long long blocks = dev->transport->get_blocks(dev); - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = (blocks >> 56) & 0xff; buf[1] = (blocks >> 48) & 0xff; @@ -806,7 +806,7 @@ int target_emulate_readcapacity_16(struct se_task *task) if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws) buf[14] = 0x80; - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -1019,9 +1019,9 @@ int target_emulate_modesense(struct se_task *task) offset = cmd->data_length; } - rbuf = transport_kmap_first_data_page(cmd); + rbuf = transport_kmap_data_sg(cmd); memcpy(rbuf, buf, offset); - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); @@ -1043,7 +1043,7 @@ int target_emulate_request_sense(struct se_task *task) return -ENOSYS; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) { /* @@ -1089,7 +1089,7 @@ int target_emulate_request_sense(struct se_task *task) } end: - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); task->task_scsi_status = GOOD; transport_complete_task(task, 1); return 0; @@ -1123,7 +1123,7 @@ int target_emulate_unmap(struct se_task *task) dl = get_unaligned_be16(&cdb[0]); bd_dl = get_unaligned_be16(&cdb[2]); - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); ptr = &buf[offset]; pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu" @@ -1147,7 +1147,7 @@ int target_emulate_unmap(struct se_task *task) } err: - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); if (!ret) { task->task_scsi_status = GOOD; transport_complete_task(task, 1); diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index de5f4fe..edbcabb 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -657,7 +657,7 @@ int target_report_luns(struct se_task *se_task) unsigned char *buf; u32 cdb_offset = 0, lun_count = 0, offset = 8, i; - buf = transport_kmap_first_data_page(se_cmd); + buf = (unsigned char *) transport_kmap_data_sg(se_cmd); /* * If no struct se_session pointer is present, this struct se_cmd is @@ -695,7 +695,7 @@ int target_report_luns(struct se_task *se_task) * See SPC3 r07, page 159. */ done: - transport_kunmap_first_data_page(se_cmd); + transport_kunmap_data_sg(se_cmd); lun_count *= 8; buf[0] = ((lun_count >> 24) & 0xff); buf[1] = ((lun_count >> 16) & 0xff); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 68c71cd..b7c7793 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1535,7 +1535,7 @@ static int core_scsi3_decode_spec_i_port( tidh_new->dest_local_nexus = 1; list_add_tail(&tidh_new->dest_list, &tid_dest_list); - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); /* * For a PERSISTENT RESERVE OUT specify initiator ports payload, * first extract TransportID Parameter Data Length, and make sure @@ -1786,7 +1786,7 @@ static int core_scsi3_decode_spec_i_port( } - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); /* * Go ahead and create a registrations from tid_dest_list for the @@ -1834,7 +1834,7 @@ static int core_scsi3_decode_spec_i_port( return 0; out: - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); /* * For the failure case, release everything from tid_dest_list * including *dest_pr_reg and the configfs dependances.. @@ -3411,14 +3411,14 @@ static int core_scsi3_emulate_pro_register_and_move( * will be moved to for the TransportID containing SCSI initiator WWN * information. */ - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); rtpi = (buf[18] & 0xff) << 8; rtpi |= buf[19] & 0xff; tid_len = (buf[20] & 0xff) << 24; tid_len |= (buf[21] & 0xff) << 16; tid_len |= (buf[22] & 0xff) << 8; tid_len |= buf[23] & 0xff; - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); buf = NULL; if ((tid_len + 24) != cmd->data_length) { @@ -3470,7 +3470,7 @@ static int core_scsi3_emulate_pro_register_and_move( return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); proto_ident = (buf[24] & 0x0f); #if 0 pr_debug("SPC-3 PR REGISTER_AND_MOVE: Extracted Protocol Identifier:" @@ -3504,7 +3504,7 @@ static int core_scsi3_emulate_pro_register_and_move( goto out; } - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); buf = NULL; pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s" @@ -3769,13 +3769,13 @@ after_iport_check: " REGISTER_AND_MOVE\n"); } - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); core_scsi3_put_pr_reg(dest_pr_reg); return 0; out: if (buf) - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); if (dest_se_deve) core_scsi3_lunacl_undepend_item(dest_se_deve); if (dest_node_acl) @@ -3849,7 +3849,7 @@ int target_scsi3_emulate_pr_out(struct se_task *task) scope = (cdb[2] & 0xf0); type = (cdb[2] & 0x0f); - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); /* * From PERSISTENT_RESERVE_OUT parameter list (payload) */ @@ -3867,7 +3867,7 @@ int target_scsi3_emulate_pr_out(struct se_task *task) aptpl = (buf[17] & 0x01); unreg = (buf[17] & 0x02); } - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); buf = NULL; /* @@ -3967,7 +3967,7 @@ static int core_scsi3_pri_read_keys(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = ((su_dev->t10_pr.pr_generation >> 24) & 0xff); buf[1] = ((su_dev->t10_pr.pr_generation >> 16) & 0xff); buf[2] = ((su_dev->t10_pr.pr_generation >> 8) & 0xff); @@ -4001,7 +4001,7 @@ static int core_scsi3_pri_read_keys(struct se_cmd *cmd) buf[6] = ((add_len >> 8) & 0xff); buf[7] = (add_len & 0xff); - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); return 0; } @@ -4027,7 +4027,7 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = ((su_dev->t10_pr.pr_generation >> 24) & 0xff); buf[1] = ((su_dev->t10_pr.pr_generation >> 16) & 0xff); buf[2] = ((su_dev->t10_pr.pr_generation >> 8) & 0xff); @@ -4086,7 +4086,7 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd) err: spin_unlock(&se_dev->dev_reservation_lock); - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); return 0; } @@ -4110,7 +4110,7 @@ static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = ((add_len << 8) & 0xff); buf[1] = (add_len & 0xff); @@ -4142,7 +4142,7 @@ static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd) buf[4] |= 0x02; /* PR_TYPE_WRITE_EXCLUSIVE */ buf[5] |= 0x01; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */ - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); return 0; } @@ -4172,7 +4172,7 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd) return -EINVAL; } - buf = transport_kmap_first_data_page(cmd); + buf = transport_kmap_data_sg(cmd); buf[0] = ((su_dev->t10_pr.pr_generation >> 24) & 0xff); buf[1] = ((su_dev->t10_pr.pr_generation >> 16) & 0xff); @@ -4293,7 +4293,7 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd) buf[6] = ((add_len >> 8) & 0xff); buf[7] = (add_len & 0xff); - transport_kunmap_first_data_page(cmd); + transport_kunmap_data_sg(cmd); return 0; } diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index d35467d..8d4def3 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -693,7 +693,7 @@ static int pscsi_transport_complete(struct se_task *task) if (task->task_se_cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) { - unsigned char *buf = transport_kmap_first_data_page(task->task_se_cmd); + unsigned char *buf = transport_kmap_data_sg(task->task_se_cmd); if (cdb[0] == MODE_SENSE_10) { if (!(buf[3] & 0x80)) @@ -703,7 +703,7 @@ static int pscsi_transport_complete(struct se_task *task) buf[2] |= 0x80; } - transport_kunmap_first_data_page(task->task_se_cmd); + transport_kunmap_data_sg(task->task_se_cmd); } } after_mode_sense: diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index e186f7d..cf996d8 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3084,11 +3084,6 @@ static int transport_generic_cmd_sequencer( (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB))) goto out_unsupported_cdb; - /* Let's limit control cdbs to a page, for simplicity's sake. */ - if ((cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) && - size > PAGE_SIZE) - goto out_invalid_cdb_field; - transport_set_supported_SAM_opcode(cmd); return ret; @@ -3492,9 +3487,11 @@ int transport_generic_map_mem_to_cmd( } EXPORT_SYMBOL(transport_generic_map_mem_to_cmd); -void *transport_kmap_first_data_page(struct se_cmd *cmd) +void *transport_kmap_data_sg(struct se_cmd *cmd) { struct scatterlist *sg = cmd->t_data_sg; + struct page **pages; + int i; BUG_ON(!sg); /* @@ -3502,15 +3499,41 @@ void *transport_kmap_first_data_page(struct se_cmd *cmd) * tcm_loop who may be using a contig buffer from the SCSI midlayer for * control CDBs passed as SGLs via transport_generic_map_mem_to_cmd() */ - return kmap(sg_page(sg)) + sg->offset; + if (!cmd->t_data_nents) + return NULL; + else if (cmd->t_data_nents == 1) + return kmap(sg_page(sg)) + sg->offset; + + /* >1 page. use vmap */ + pages = kmalloc(sizeof(*pages) * cmd->t_data_nents, GFP_KERNEL); + if (!pages) + return NULL; + + /* convert sg[] to pages[] */ + for_each_sg(cmd->t_data_sg, sg, cmd->t_data_nents, i) { + pages[i] = sg_page(sg); + } + + cmd->t_data_vmap = vmap(pages, cmd->t_data_nents, VM_MAP, PAGE_KERNEL); + kfree(pages); + if (!cmd->t_data_vmap) + return NULL; + + return cmd->t_data_vmap + cmd->t_data_sg[0].offset; } -EXPORT_SYMBOL(transport_kmap_first_data_page); +EXPORT_SYMBOL(transport_kmap_data_sg); -void transport_kunmap_first_data_page(struct se_cmd *cmd) +void transport_kunmap_data_sg(struct se_cmd *cmd) { - kunmap(sg_page(cmd->t_data_sg)); + if (!cmd->t_data_nents) + return; + else if (cmd->t_data_nents == 1) + kunmap(sg_page(cmd->t_data_sg)); + + vunmap(cmd->t_data_vmap); + cmd->t_data_vmap = NULL; } -EXPORT_SYMBOL(transport_kunmap_first_data_page); +EXPORT_SYMBOL(transport_kunmap_data_sg); static int transport_generic_get_mem(struct se_cmd *cmd) diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 4866499..e5e6ff9 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -59,7 +59,7 @@ int transport_set_vpd_ident_type(struct t10_vpd *, unsigned char *); int transport_set_vpd_ident(struct t10_vpd *, unsigned char *); /* core helpers also used by command snooping in pscsi */ -void *transport_kmap_first_data_page(struct se_cmd *); -void transport_kunmap_first_data_page(struct se_cmd *); +void *transport_kmap_data_sg(struct se_cmd *); +void transport_kunmap_data_sg(struct se_cmd *); #endif /* TARGET_CORE_BACKEND_H */ diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index daf532b..dc4e345 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -582,6 +582,7 @@ struct se_cmd { struct scatterlist *t_data_sg; unsigned int t_data_nents; + void *t_data_vmap; struct scatterlist *t_bidi_data_sg; unsigned int t_bidi_data_nents; -- cgit v0.10.2 From 2f9bc894c67dbacae5a6a9875818d2a18a918d18 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 16 Jan 2012 23:33:48 -0800 Subject: iscsi-target: Fix discovery with INADDR_ANY and IN6ADDR_ANY_INIT This patch addresses a bug with sendtargets discovery where INADDR_ANY (0.0.0.0) + IN6ADDR_ANY_INIT ([0:0:0:0:0:0:0:0]) network portals where incorrectly being reported back to initiators instead of the address of the connecting interface. To address this, save local socket ->getname() output during iscsi login setup, and makes iscsit_build_sendtargets_response() return these TargetAddress keys when INADDR_ANY or IN6ADDR_ANY_INIT portals are in use. Reported-by: Dax Kelson Reported-by: Andy Grover Cc: David S. Miller Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 6e070e0..4426290 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3164,6 +3164,30 @@ static int iscsit_send_task_mgt_rsp( return 0; } +static bool iscsit_check_inaddr_any(struct iscsi_np *np) +{ + bool ret = false; + + if (np->np_sockaddr.ss_family == AF_INET6) { + const struct sockaddr_in6 sin6 = { + .sin6_addr = IN6ADDR_ANY_INIT }; + struct sockaddr_in6 *sock_in6 = + (struct sockaddr_in6 *)&np->np_sockaddr; + + if (!memcmp(sock_in6->sin6_addr.s6_addr, + sin6.sin6_addr.s6_addr, 16)) + ret = true; + } else { + struct sockaddr_in * sock_in = + (struct sockaddr_in *)&np->np_sockaddr; + + if (sock_in->sin_addr.s_addr == INADDR_ANY) + ret = true; + } + + return ret; +} + static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) { char *payload = NULL; @@ -3213,12 +3237,17 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) spin_lock(&tpg->tpg_np_lock); list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) { + struct iscsi_np *np = tpg_np->tpg_np; + bool inaddr_any = iscsit_check_inaddr_any(np); + len = sprintf(buf, "TargetAddress=" "%s%s%s:%hu,%hu", - (tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ? - "[" : "", tpg_np->tpg_np->np_ip, - (tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ? - "]" : "", tpg_np->tpg_np->np_port, + (np->np_sockaddr.ss_family == AF_INET6) ? + "[" : "", (inaddr_any == false) ? + np->np_ip : conn->local_ip, + (np->np_sockaddr.ss_family == AF_INET6) ? + "]" : "", (inaddr_any == false) ? + np->np_port : conn->local_port, tpg->tpgt); len += 1; diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index ebf81fd..0ec3b77 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -508,6 +508,7 @@ struct iscsi_conn { u16 cid; /* Remote TCP Port */ u16 login_port; + u16 local_port; int net_size; u32 auth_id; #define CONNFLAG_SCTP_STRUCT_FILE 0x01 @@ -527,6 +528,7 @@ struct iscsi_conn { unsigned char bad_hdr[ISCSI_HDR_LEN]; #define IPV6_ADDRESS_SPACE 48 unsigned char login_ip[IPV6_ADDRESS_SPACE]; + unsigned char local_ip[IPV6_ADDRESS_SPACE]; int conn_usage_count; int conn_waiting_on_uc; atomic_t check_immediate_queue; diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 373b0cc..ec47a7c 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -615,8 +615,8 @@ static int iscsi_post_login_handler( } pr_debug("iSCSI Login successful on CID: %hu from %s to" - " %s:%hu,%hu\n", conn->cid, conn->login_ip, np->np_ip, - np->np_port, tpg->tpgt); + " %s:%hu,%hu\n", conn->cid, conn->login_ip, + conn->local_ip, conn->local_port, tpg->tpgt); list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); @@ -658,7 +658,8 @@ static int iscsi_post_login_handler( sess->session_state = TARG_SESS_STATE_LOGGED_IN; pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n", - conn->cid, conn->login_ip, np->np_ip, np->np_port, tpg->tpgt); + conn->cid, conn->login_ip, conn->local_ip, conn->local_port, + tpg->tpgt); spin_lock_bh(&sess->conn_lock); list_add_tail(&conn->conn_list, &sess->sess_conn_list); @@ -1020,6 +1021,18 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c", &sock_in6.sin6_addr.in6_u); conn->login_port = ntohs(sock_in6.sin6_port); + + if (conn->sock->ops->getname(conn->sock, + (struct sockaddr *)&sock_in6, &err, 0) < 0) { + pr_err("sock_ops->getname() failed.\n"); + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, + ISCSI_LOGIN_STATUS_TARGET_ERROR); + goto new_sess_out; + } + snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c", + &sock_in6.sin6_addr.in6_u); + conn->local_port = ntohs(sock_in6.sin6_port); + } else { memset(&sock_in, 0, sizeof(struct sockaddr_in)); @@ -1032,6 +1045,16 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) } sprintf(conn->login_ip, "%pI4", &sock_in.sin_addr.s_addr); conn->login_port = ntohs(sock_in.sin_port); + + if (conn->sock->ops->getname(conn->sock, + (struct sockaddr *)&sock_in, &err, 0) < 0) { + pr_err("sock_ops->getname() failed.\n"); + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, + ISCSI_LOGIN_STATUS_TARGET_ERROR); + goto new_sess_out; + } + sprintf(conn->local_ip, "%pI4", &sock_in.sin_addr.s_addr); + conn->local_port = ntohs(sock_in.sin_port); } conn->network_transport = np->np_network_transport; @@ -1039,7 +1062,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) pr_debug("Received iSCSI login request from %s on %s Network" " Portal %s:%hu\n", conn->login_ip, (conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP", - np->np_ip, np->np_port); + conn->local_ip, conn->local_port); pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n"); conn->conn_state = TARG_CONN_STATE_IN_LOGIN; -- cgit v0.10.2 From bb1acb2ee038a6c13ee99e0b9fb44dacb4a9de84 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 17 Jan 2012 18:00:56 -0800 Subject: target: Return correct ASC for unimplemented VPD pages My draft of SPC-4 says: If the device server does not implement the requested vital product data page, then the command shall be terminated with CHECK CONDITION status, with the sense key set to ILLEGAL REQUEST, and the additional sense code set to INVALID FIELD IN CDB. Signed-off-by: Roland Dreier Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 07a3025..370ad13 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -729,7 +729,7 @@ int target_emulate_inquiry(struct se_task *task) } pr_err("Unknown VPD Code: 0x%02x\n", cdb[2]); - cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE; + cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; ret = -EINVAL; out_unmap: -- cgit v0.10.2 From bf0053550aebe56f3bb5dd793e9de69238b5b945 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 17 Jan 2012 18:00:57 -0800 Subject: target: Fail INQUIRY commands with EVPD==0 but PAGE CODE!=0 My draft of SPC-4 says: If the PAGE CODE field is not set to zero when the EVPD bit is set to zero, the command shall be terminated with CHECK CONDITION status, with the sense key set to ILLEGAL REQUEST, and the additional sense code set to INVALID FIELD IN CDB. Signed-off-by: Roland Dreier Cc: Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 370ad13..a9bbf5a 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -698,6 +698,13 @@ int target_emulate_inquiry(struct se_task *task) int p, ret; if (!(cdb[1] & 0x1)) { + if (cdb[2]) { + pr_err("INQUIRY with EVPD==0 but PAGE CODE=%02x\n", + cdb[2]); + cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; + return -EINVAL; + } + ret = target_emulate_inquiry_std(cmd); goto out; } -- cgit v0.10.2 From f8b5a31877b93f7136ce8c22ce44930e39b41204 Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Thu, 5 Jan 2012 09:40:56 +0800 Subject: powerpc/85xx: Fix cmd12 bug and add the chip compatible for eSDHC According to latest kernel, the auto-cmd12 property should be "sdhci,auto-cmd12", and according to the SDHC binding and the workaround for the special chip, add the chip compatible for eSDHC: "fsl,p1022-esdhc", "fsl,mpc8536-esdhc", "fsl,p1020-esdhc", "fsl,p2020-esdhc" and "fsl,p1010-esdhc". Signed-off-by: Jerry Huang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi b/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi index 89af626..b37da56 100644 --- a/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi @@ -236,6 +236,10 @@ }; /include/ "pq3-esdhc-0.dtsi" + sdhc@2e000 { + compatible = "fsl,mpc8536-esdhc", "fsl,esdhc"; + }; + /include/ "pq3-sec3.0-0.dtsi" /include/ "pq3-mpic.dtsi" /include/ "pq3-mpic-timer-B.dtsi" diff --git a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi index bd9e163..a97d126 100644 --- a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi @@ -158,7 +158,8 @@ /include/ "pq3-usb2-dr-0.dtsi" /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { - fsl,sdhci-auto-cmd12; + compatible = "fsl,p1010-esdhc", "fsl,esdhc"; + sdhci,auto-cmd12; }; /include/ "pq3-sec4.4-0.dtsi" diff --git a/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi index fc924c5..5de5fc3 100644 --- a/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi @@ -145,6 +145,10 @@ /include/ "pq3-usb2-dr-1.dtsi" /include/ "pq3-esdhc-0.dtsi" + sdhc@2e000 { + compatible = "fsl,p1020-esdhc", "fsl,esdhc"; + sdhci,auto-cmd12; + }; /include/ "pq3-sec3.3-0.dtsi" /include/ "pq3-mpic.dtsi" diff --git a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi index 16239b1..ff9ed1d 100644 --- a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi @@ -203,7 +203,8 @@ /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { - fsl,sdhci-auto-cmd12; + compatible = "fsl,p1022-esdhc", "fsl,esdhc"; + sdhci,auto-cmd12; }; /include/ "pq3-sec3.3-0.dtsi" diff --git a/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi index c041050..332e9e7 100644 --- a/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi @@ -182,6 +182,10 @@ /include/ "pq3-etsec1-1.dtsi" /include/ "pq3-etsec1-2.dtsi" /include/ "pq3-esdhc-0.dtsi" + sdhc@2e000 { + compatible = "fsl,p2020-esdhc", "fsl,esdhc"; + }; + /include/ "pq3-sec3.1-0.dtsi" /include/ "pq3-mpic.dtsi" /include/ "pq3-mpic-timer-B.dtsi" -- cgit v0.10.2 From 70a0ac66869907ce3914f26fe1eba984c9eb438e Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Wed, 18 Jan 2012 11:20:39 +0530 Subject: powerpc/85xx: Enable USB2 controller node for P1020RDB Enable USB2 controller node for P1020RDB. USB2 controller is used only when board boots from SPI or SD as it is muxed with eLBC Signed-off-by: Ramneek Mehresh Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1020rdb.dtsi b/arch/powerpc/boot/dts/p1020rdb.dtsi index b5bd86f..17559c4 100644 --- a/arch/powerpc/boot/dts/p1020rdb.dtsi +++ b/arch/powerpc/boot/dts/p1020rdb.dtsi @@ -1,7 +1,7 @@ /* * P1020 RDB Device Tree Source stub (no addresses or top-level ranges) * - * Copyright 2011 Freescale Semiconductor Inc. + * Copyright 2011-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -192,15 +192,12 @@ phy_type = "ulpi"; }; - /* USB2 is shared with localbus, so it must be disabled - by default. We can't put 'status = "disabled";' here - since U-Boot doesn't clear the status property when - it enables USB2. OTOH, U-Boot does create a new node - when there isn't any. So, just comment it out. + /* USB2 is shared with localbus. It is used + only in case of SPI and SD boot after + appropriate device-tree fixup done by uboot */ usb@23000 { phy_type = "ulpi"; }; - */ mdio@24000 { phy0: ethernet-phy@0 { -- cgit v0.10.2 From 621c4b999e3e1dc6f72b0c1680029981edc03caa Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Wed, 18 Jan 2012 12:18:46 +0530 Subject: powerpc/85xx: Add dr_mode property in USB nodes Add usb2 controller node for P1020RDB, P2020RDB, P2020DS, P1021MDS Signed-off-by: Ramneek Mehresh Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1020rdb.dtsi b/arch/powerpc/boot/dts/p1020rdb.dtsi index 17559c4..1fb7e0e 100644 --- a/arch/powerpc/boot/dts/p1020rdb.dtsi +++ b/arch/powerpc/boot/dts/p1020rdb.dtsi @@ -190,6 +190,7 @@ usb@22000 { phy_type = "ulpi"; + dr_mode = "host"; }; /* USB2 is shared with localbus. It is used @@ -197,6 +198,7 @@ appropriate device-tree fixup done by uboot */ usb@23000 { phy_type = "ulpi"; + dr_mode = "host"; }; mdio@24000 { diff --git a/arch/powerpc/boot/dts/p1021mds.dts b/arch/powerpc/boot/dts/p1021mds.dts index d954079..97116f1 100644 --- a/arch/powerpc/boot/dts/p1021mds.dts +++ b/arch/powerpc/boot/dts/p1021mds.dts @@ -1,7 +1,7 @@ /* * P1021 MDS Device Tree Source * - * Copyright 2010 Freescale Semiconductor Inc. + * Copyright 2010,2012 Freescale Semiconductor Inc. * * 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 @@ -151,6 +151,7 @@ usb@22000 { phy_type = "ulpi"; + dr_mode = "host"; }; mdio@24000 { diff --git a/arch/powerpc/boot/dts/p2020ds.dtsi b/arch/powerpc/boot/dts/p2020ds.dtsi index c1cf6ce..d3b939c 100644 --- a/arch/powerpc/boot/dts/p2020ds.dtsi +++ b/arch/powerpc/boot/dts/p2020ds.dtsi @@ -1,7 +1,7 @@ /* * P2020DS Device Tree Source stub (no addresses or top-level ranges) * - * Copyright 2011 Freescale Semiconductor Inc. + * Copyright 2011-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -134,6 +134,7 @@ &board_soc { usb@22000 { phy_type = "ulpi"; + dr_mode = "host"; }; mdio@24520 { diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts index 26759a5..eb8a6aa 100644 --- a/arch/powerpc/boot/dts/p2020rdb.dts +++ b/arch/powerpc/boot/dts/p2020rdb.dts @@ -1,7 +1,7 @@ /* * P2020 RDB Device Tree Source * - * Copyright 2009-2011 Freescale Semiconductor Inc. + * Copyright 2009-2012 Freescale Semiconductor Inc. * * 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 @@ -197,6 +197,7 @@ usb@22000 { phy_type = "ulpi"; + dr_mode = "host"; }; mdio@24520 { -- cgit v0.10.2 From e0a15d5bf4e2fc46a867c43c41b6a41622f673ad Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 18 Jan 2012 18:03:39 +0100 Subject: [S390] cleanup entry point definition The vmlinux file for s390 contains a currently unused entry point, which is specified in two different locations: the linker script and the makefile. As it happens both definitions are different and the linker file is broken (_start does not exist) and the makefile specifies an entry point which makes no sense (the SALIPL loader entry point). So lets get rid of one definition (the makefile) and use the entry point of all other ipl methods (0x10000 -> startup) to be consistent. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/Makefile b/arch/s390/Makefile index e9f3533..0ad2f1e 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -88,7 +88,6 @@ KBUILD_CFLAGS += -pipe -fno-strength-reduce -Wno-sign-compare KBUILD_AFLAGS += $(aflags-y) OBJCOPYFLAGS := -O binary -LDFLAGS_vmlinux := -e start head-y := arch/s390/kernel/head.o head-y += arch/s390/kernel/$(if $(CONFIG_64BIT),head64.o,head31.o) diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index e4c79eb..21109c6 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -9,12 +9,12 @@ #ifndef CONFIG_64BIT OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390") OUTPUT_ARCH(s390) -ENTRY(_start) +ENTRY(startup) jiffies = jiffies_64 + 4; #else OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") OUTPUT_ARCH(s390:64-bit) -ENTRY(_start) +ENTRY(startup) jiffies = jiffies_64; #endif -- cgit v0.10.2 From f9f8d02fae0dc47d8868fd069bb88d12f8d1d71f Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 18 Jan 2012 18:03:40 +0100 Subject: [S390] dasd: revert LCU optimization Remove the optimization that validate server is only called once per LCU. If a device is set online we only know that we already know the LCU. But if the pathgroup was lost in between we have to do a validate server again to activate some features. Since we have no indication when a pathgroup gets lost we have to do a validate server every time a device is set online. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 553b3c5..b3beed5 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -189,14 +189,12 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) unsigned long flags; struct alias_server *server, *newserver; struct alias_lcu *lcu, *newlcu; - int is_lcu_known; struct dasd_uid uid; private = (struct dasd_eckd_private *) device->private; device->discipline->get_uid(device, &uid); spin_lock_irqsave(&aliastree.lock, flags); - is_lcu_known = 1; server = _find_server(&uid); if (!server) { spin_unlock_irqrestore(&aliastree.lock, flags); @@ -208,7 +206,6 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) if (!server) { list_add(&newserver->server, &aliastree.serverlist); server = newserver; - is_lcu_known = 0; } else { /* someone was faster */ _free_server(newserver); @@ -226,12 +223,10 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) if (!lcu) { list_add(&newlcu->lcu, &server->lculist); lcu = newlcu; - is_lcu_known = 0; } else { /* someone was faster */ _free_lcu(newlcu); } - is_lcu_known = 0; } spin_lock(&lcu->lock); list_add(&device->alias_list, &lcu->inactive_devices); @@ -239,64 +234,7 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) spin_unlock(&lcu->lock); spin_unlock_irqrestore(&aliastree.lock, flags); - return is_lcu_known; -} - -/* - * The first device to be registered on an LCU will have to do - * some additional setup steps to configure that LCU on the - * storage server. All further devices should wait with their - * initialization until the first device is done. - * To synchronize this work, the first device will call - * dasd_alias_lcu_setup_complete when it is done, and all - * other devices will wait for it with dasd_alias_wait_for_lcu_setup. - */ -void dasd_alias_lcu_setup_complete(struct dasd_device *device) -{ - unsigned long flags; - struct alias_server *server; - struct alias_lcu *lcu; - struct dasd_uid uid; - - device->discipline->get_uid(device, &uid); - lcu = NULL; - spin_lock_irqsave(&aliastree.lock, flags); - server = _find_server(&uid); - if (server) - lcu = _find_lcu(server, &uid); - spin_unlock_irqrestore(&aliastree.lock, flags); - if (!lcu) { - DBF_EVENT_DEVID(DBF_ERR, device->cdev, - "could not find lcu for %04x %02x", - uid.ssid, uid.real_unit_addr); - WARN_ON(1); - return; - } - complete_all(&lcu->lcu_setup); -} - -void dasd_alias_wait_for_lcu_setup(struct dasd_device *device) -{ - unsigned long flags; - struct alias_server *server; - struct alias_lcu *lcu; - struct dasd_uid uid; - - device->discipline->get_uid(device, &uid); - lcu = NULL; - spin_lock_irqsave(&aliastree.lock, flags); - server = _find_server(&uid); - if (server) - lcu = _find_lcu(server, &uid); - spin_unlock_irqrestore(&aliastree.lock, flags); - if (!lcu) { - DBF_EVENT_DEVID(DBF_ERR, device->cdev, - "could not find lcu for %04x %02x", - uid.ssid, uid.real_unit_addr); - WARN_ON(1); - return; - } - wait_for_completion(&lcu->lcu_setup); + return 0; } /* diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index bbcd5e9..1b6e7ea 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1534,6 +1534,10 @@ static void dasd_eckd_validate_server(struct dasd_device *device) struct dasd_eckd_private *private; int enable_pav; + private = (struct dasd_eckd_private *) device->private; + if (private->uid.type == UA_BASE_PAV_ALIAS || + private->uid.type == UA_HYPER_PAV_ALIAS) + return; if (dasd_nopav || MACHINE_IS_VM) enable_pav = 0; else @@ -1542,7 +1546,6 @@ static void dasd_eckd_validate_server(struct dasd_device *device) /* may be requested feature is not available on server, * therefore just report error and go ahead */ - private = (struct dasd_eckd_private *) device->private; DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " "returned rc=%d", private->uid.ssid, rc); } @@ -1588,7 +1591,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) struct dasd_eckd_private *private; struct dasd_block *block; struct dasd_uid temp_uid; - int is_known, rc, i; + int rc, i; int readonly; unsigned long value; @@ -1651,22 +1654,12 @@ dasd_eckd_check_characteristics(struct dasd_device *device) block->base = device; } - /* register lcu with alias handling, enable PAV if this is a new lcu */ - is_known = dasd_alias_make_device_known_to_lcu(device); - if (is_known < 0) { - rc = is_known; + /* register lcu with alias handling, enable PAV */ + rc = dasd_alias_make_device_known_to_lcu(device); + if (rc) goto out_err2; - } - /* - * dasd_eckd_validate_server is done on the first device that - * is found for an LCU. All later other devices have to wait - * for it, so they will read the correct feature codes. - */ - if (!is_known) { - dasd_eckd_validate_server(device); - dasd_alias_lcu_setup_complete(device); - } else - dasd_alias_wait_for_lcu_setup(device); + + dasd_eckd_validate_server(device); /* device may report different configuration data after LCU setup */ rc = dasd_eckd_read_conf(device); @@ -4098,7 +4091,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device) { struct dasd_eckd_private *private; struct dasd_eckd_characteristics temp_rdc_data; - int is_known, rc; + int rc; struct dasd_uid temp_uid; unsigned long flags; @@ -4121,14 +4114,10 @@ static int dasd_eckd_restore_device(struct dasd_device *device) goto out_err; /* register lcu with alias handling, enable PAV if this is a new lcu */ - is_known = dasd_alias_make_device_known_to_lcu(device); - if (is_known < 0) - return is_known; - if (!is_known) { - dasd_eckd_validate_server(device); - dasd_alias_lcu_setup_complete(device); - } else - dasd_alias_wait_for_lcu_setup(device); + rc = dasd_alias_make_device_known_to_lcu(device); + if (rc) + return rc; + dasd_eckd_validate_server(device); /* RE-Read Configuration Data */ rc = dasd_eckd_read_conf(device); -- cgit v0.10.2 From f16330316321d1c388d13096f6858f5d2dac29dc Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 18 Jan 2012 18:03:41 +0100 Subject: [S390] dasd: revalidate server for new pathgroup If a pathgroup is established we get an event and have to revalidate the server to propagate supported features like PAV and enable them. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index eef27a1..110137e 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3261,6 +3261,12 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event) device->path_data.tbvpm |= eventlpm; dasd_schedule_device_bh(device); } + if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) { + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "Pathgroup re-established\n"); + if (device->discipline->kick_validate) + device->discipline->kick_validate(device); + } } dasd_put_device(device); } diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 1b6e7ea..70880be 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1550,6 +1550,24 @@ static void dasd_eckd_validate_server(struct dasd_device *device) "returned rc=%d", private->uid.ssid, rc); } +/* + * worker to do a validate server in case of a lost pathgroup + */ +static void dasd_eckd_do_validate_server(struct work_struct *work) +{ + struct dasd_device *device = container_of(work, struct dasd_device, + kick_validate); + dasd_eckd_validate_server(device); + dasd_put_device(device); +} + +static void dasd_eckd_kick_validate_server(struct dasd_device *device) +{ + dasd_get_device(device); + /* queue call to do_validate_server to the kernel event daemon. */ + schedule_work(&device->kick_validate); +} + static u32 get_fcx_max_data(struct dasd_device *device) { #if defined(CONFIG_64BIT) @@ -1595,6 +1613,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) int readonly; unsigned long value; + /* setup work queue for validate server*/ + INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server); + if (!ccw_device_is_pathgroup(device->cdev)) { dev_warn(&device->cdev->dev, "A channel path group could not be established\n"); @@ -4259,6 +4280,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .restore = dasd_eckd_restore_device, .reload = dasd_eckd_reload_device, .get_uid = dasd_eckd_get_uid, + .kick_validate = dasd_eckd_kick_validate_server, }; static int __init diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index afe8c33..33a6743 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -355,6 +355,7 @@ struct dasd_discipline { int (*reload) (struct dasd_device *); int (*get_uid) (struct dasd_device *, struct dasd_uid *); + void (*kick_validate) (struct dasd_device *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -455,6 +456,7 @@ struct dasd_device { struct work_struct kick_work; struct work_struct restore_device; struct work_struct reload_device; + struct work_struct kick_validate; struct timer_list timer; debug_info_t *debug_area; -- cgit v0.10.2 From 9446f3efc53512e5ad9e0966539021a2a41fe5a0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 17 Jan 2012 10:32:01 +0100 Subject: mac80211: fix debugfs key->station symlink Since stations moved into a virtual interface subdirectory, this link has been broken. Fix it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 38e6101..59edcd9 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -225,9 +225,9 @@ KEY_OPS(key); key, &key_##name##_ops); void ieee80211_debugfs_key_add(struct ieee80211_key *key) - { +{ static int keycount; - char buf[50]; + char buf[100]; struct sta_info *sta; if (!key->local->debugfs.keys) @@ -244,7 +244,8 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) sta = key->sta; if (sta) { - sprintf(buf, "../../stations/%pM", sta->sta.addr); + sprintf(buf, "../../netdev:%s/stations/%pM", + sta->sdata->name, sta->sta.addr); key->debugfs.stalink = debugfs_create_symlink("station", key->debugfs.dir, buf); } -- cgit v0.10.2 From f96b08a7e6f69c0f0a576554df3df5b1b519c479 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 17 Jan 2012 12:38:50 +0100 Subject: brcmsmac: fix tx queue flush infinite loop This patch workaround live deadlock problem caused by infinite loop in brcms_c_wait_for_tx_completion(). I do not consider the patch as the proper fix, which should fix the real reason of tx queue flush failure, but patch helps with system lockup. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=42576 Reported-and-tested-by: Patrick Cc: stable@vger.kernel.org # 3.2+ Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index f7ed340..f6affc6 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -7981,13 +7981,21 @@ int brcms_c_get_curband(struct brcms_c_info *wlc) void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) { + int timeout = 20; + /* flush packet queue when requested */ if (drop) brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL); /* wait for queue and DMA fifos to run dry */ - while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) + while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) { brcms_msleep(wlc->wl, 1); + + if (--timeout == 0) + break; + } + + WARN_ON_ONCE(timeout == 0); } void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) -- cgit v0.10.2 From 65e8b0ccb6cf176f8eddb1b05534be46580da9dd Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 17 Jan 2012 18:17:46 -0800 Subject: mac80211: Use the right headroom size for mesh mgmt frames Use local->tx_headroom instad of local->hw.extra_tx_headroom. local->tx_headroom is the max of hw.extra_tx_headroom required by the driver and the headroom required by mac80211 for status reporting. On drivers where hw.extra_tx_headroom is smaller than what mac80211 requires (e.g. ath5k), we would not reserve sufficient buffer space to report tx status. Also, don't reserve local->tx_headroom + local->hw.extra_tx_headroom. Reported-by: Simon Morgenthaler Reported-by: Kai Scharwies Signed-off-by: Javier Cardona Signed-off-by: John W. Linville diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 73abb75..54df1b2b 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -119,12 +119,12 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + sizeof(mgmt->u.action.u.mesh_action); - skb = dev_alloc_skb(local->hw.extra_tx_headroom + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + 37); /* max HWMP IE */ if (!skb) return -1; - skb_reserve(skb, local->hw.extra_tx_headroom); + skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | @@ -250,12 +250,12 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, if (time_before(jiffies, ifmsh->next_perr)) return -EAGAIN; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + 15 /* PERR IE */); if (!skb) return -1; - skb_reserve(skb, local->tx_headroom + local->hw.extra_tx_headroom); + skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 41ef1b4..a172517 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -172,7 +172,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + sizeof(mgmt->u.action.u.self_prot); - skb = dev_alloc_skb(local->hw.extra_tx_headroom + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + /* capability info */ 2 + /* AID */ @@ -186,7 +186,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, sdata->u.mesh.ie_len); if (!skb) return -1; - skb_reserve(skb, local->hw.extra_tx_headroom); + skb_reserve(skb, local->tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | -- cgit v0.10.2 From bc4934bc61d0a11fd62c5187ff83645628f8be8b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jan 2012 14:10:25 +0100 Subject: mac80211: fix work removal on deauth request When deauth is requested while an auth or assoc work item is in progress, we currently delete it without regard for any state it might need to clean up. Fix it by cleaning up for those items. In the case Pontus found, the problem manifested itself as such: authenticate with 00:23:69:aa:dd:7b (try 1) authenticated failed to insert Dummy STA entry for the AP (error -17) deauthenticating from 00:23:69:aa:dd:7b by local choice (reason=2) It could also happen differently if the driver uses the tx_sync callback. We can't just call the ->done() method of the work items because that will lock up due to the locking in cfg80211. This fix isn't very clean, but that seems acceptable since I have patches pending to remove this code completely. Cc: stable@vger.kernel.org Reported-by: Pontus Fuchs Tested-by: Pontus Fuchs Signed-off-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ecb4c84..295be92 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2750,7 +2750,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_work *wk; u8 bssid[ETH_ALEN]; bool assoc_bss = false; @@ -2763,30 +2762,47 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, assoc_bss = true; } else { bool not_auth_yet = false; + struct ieee80211_work *tmp, *wk = NULL; mutex_unlock(&ifmgd->mtx); mutex_lock(&local->mtx); - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) + list_for_each_entry(tmp, &local->work_list, list) { + if (tmp->sdata != sdata) continue; - if (wk->type != IEEE80211_WORK_DIRECT_PROBE && - wk->type != IEEE80211_WORK_AUTH && - wk->type != IEEE80211_WORK_ASSOC && - wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) + if (tmp->type != IEEE80211_WORK_DIRECT_PROBE && + tmp->type != IEEE80211_WORK_AUTH && + tmp->type != IEEE80211_WORK_ASSOC && + tmp->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) continue; - if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN)) + if (memcmp(req->bss->bssid, tmp->filter_ta, ETH_ALEN)) continue; - not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE; - list_del_rcu(&wk->list); - free_work(wk); + not_auth_yet = tmp->type == IEEE80211_WORK_DIRECT_PROBE; + list_del_rcu(&tmp->list); + synchronize_rcu(); + wk = tmp; break; } mutex_unlock(&local->mtx); + if (wk && wk->type == IEEE80211_WORK_ASSOC) { + /* clean up dummy sta & TX sync */ + sta_info_destroy_addr(wk->sdata, wk->filter_ta); + if (wk->assoc.synced) + drv_finish_tx_sync(local, wk->sdata, + wk->filter_ta, + IEEE80211_TX_SYNC_ASSOC); + } else if (wk && wk->type == IEEE80211_WORK_AUTH) { + if (wk->probe_auth.synced) + drv_finish_tx_sync(local, wk->sdata, + wk->filter_ta, + IEEE80211_TX_SYNC_AUTH); + } + kfree(wk); + /* * If somebody requests authentication and we haven't * sent out an auth frame yet there's no need to send -- cgit v0.10.2 From 4f3d09de38d234ce7ffba5ec5a7e6704f983d375 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 11 Jan 2012 15:50:15 -0500 Subject: b43: add option to avoid duplicating device support with brcmsmac Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index b97a40e..3876c7e 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -31,6 +31,12 @@ config B43_BCMA depends on B43 && BCMA default y +config B43_BCMA_EXTRA + bool "Hardware support that overlaps with the brcmsmac driver" + depends on B43_BCMA + default n if BRCMSMAC || BRCMSMAC_MODULE + default y + config B43_SSB bool depends on B43 && SSB diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index b91f28e..23ffb1b 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -116,8 +116,10 @@ MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO"); #ifdef CONFIG_B43_BCMA static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), +#ifdef CONFIG_B43_BCMA_EXTRA BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), +#endif BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS), BCMA_CORETABLE_END }; -- cgit v0.10.2 From 94bd2442d25454a874e070d871f50f4ce9d57101 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 18 Jan 2012 20:07:35 +0530 Subject: gpio: tps65910: Use correct offset for gpio initialization Using the correct gpio offset for setting the initial value of gpio when setting output direction. Signed-off-by: Laxman Dewangan Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index b9c1c29..91f45b9 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -52,7 +52,7 @@ static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio); /* Set the initial value */ - tps65910_gpio_set(gc, 0, value); + tps65910_gpio_set(gc, offset, value); return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); -- cgit v0.10.2 From d00a9dd21bdf7908b70866794c8313ee8a5abd5c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Jan 2012 07:21:42 +0000 Subject: net: bpf_jit: fix divide by 0 generation Several problems fixed in this patch : 1) Target of the conditional jump in case a divide by 0 is performed by a bpf is wrong. 2) Must 'generate' the full function prologue/epilogue at pass=0, or else we can stop too early in pass=1 if the proglen doesnt change. (if the increase of prologue/epilogue equals decrease of all instructions length because some jumps are converted to near jumps) 3) Change the wrong length detection at the end of code generation to issue a more explicit message, no need for a full stack trace. Reported-by: Phil Oester Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 7b65f75..7c1b765 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -151,17 +151,18 @@ void bpf_jit_compile(struct sk_filter *fp) cleanup_addr = proglen; /* epilogue address */ for (pass = 0; pass < 10; pass++) { + u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen; /* no prologue/epilogue for trivial filters (RET something) */ proglen = 0; prog = temp; - if (seen) { + if (seen_or_pass0) { EMIT4(0x55, 0x48, 0x89, 0xe5); /* push %rbp; mov %rsp,%rbp */ EMIT4(0x48, 0x83, 0xec, 96); /* subq $96,%rsp */ /* note : must save %rbx in case bpf_error is hit */ - if (seen & (SEEN_XREG | SEEN_DATAREF)) + if (seen_or_pass0 & (SEEN_XREG | SEEN_DATAREF)) EMIT4(0x48, 0x89, 0x5d, 0xf8); /* mov %rbx, -8(%rbp) */ - if (seen & SEEN_XREG) + if (seen_or_pass0 & SEEN_XREG) CLEAR_X(); /* make sure we dont leek kernel memory */ /* @@ -170,7 +171,7 @@ void bpf_jit_compile(struct sk_filter *fp) * r9 = skb->len - skb->data_len * r8 = skb->data */ - if (seen & SEEN_DATAREF) { + if (seen_or_pass0 & SEEN_DATAREF) { if (offsetof(struct sk_buff, len) <= 127) /* mov off8(%rdi),%r9d */ EMIT4(0x44, 0x8b, 0x4f, offsetof(struct sk_buff, len)); @@ -260,9 +261,14 @@ void bpf_jit_compile(struct sk_filter *fp) case BPF_S_ALU_DIV_X: /* A /= X; */ seen |= SEEN_XREG; EMIT2(0x85, 0xdb); /* test %ebx,%ebx */ - if (pc_ret0 != -1) - EMIT_COND_JMP(X86_JE, addrs[pc_ret0] - (addrs[i] - 4)); - else { + if (pc_ret0 > 0) { + /* addrs[pc_ret0 - 1] is start address of target + * (addrs[i] - 4) is the address following this jmp + * ("xor %edx,%edx; div %ebx" being 4 bytes long) + */ + EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] - + (addrs[i] - 4)); + } else { EMIT_COND_JMP(X86_JNE, 2 + 5); CLEAR_A(); EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 4)); /* jmp .+off32 */ @@ -335,12 +341,12 @@ void bpf_jit_compile(struct sk_filter *fp) } /* fallinto */ case BPF_S_RET_A: - if (seen) { + if (seen_or_pass0) { if (i != flen - 1) { EMIT_JMP(cleanup_addr - addrs[i]); break; } - if (seen & SEEN_XREG) + if (seen_or_pass0 & SEEN_XREG) EMIT4(0x48, 0x8b, 0x5d, 0xf8); /* mov -8(%rbp),%rbx */ EMIT1(0xc9); /* leaveq */ } @@ -483,8 +489,9 @@ common_load: seen |= SEEN_DATAREF; goto common_load; case BPF_S_LDX_B_MSH: if ((int)K < 0) { - if (pc_ret0 != -1) { - EMIT_JMP(addrs[pc_ret0] - addrs[i]); + if (pc_ret0 > 0) { + /* addrs[pc_ret0 - 1] is the start address */ + EMIT_JMP(addrs[pc_ret0 - 1] - addrs[i]); break; } CLEAR_A(); @@ -599,13 +606,14 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; * use it to give the cleanup instruction(s) addr */ cleanup_addr = proglen - 1; /* ret */ - if (seen) + if (seen_or_pass0) cleanup_addr -= 1; /* leaveq */ - if (seen & SEEN_XREG) + if (seen_or_pass0 & SEEN_XREG) cleanup_addr -= 4; /* mov -8(%rbp),%rbx */ if (image) { - WARN_ON(proglen != oldproglen); + if (proglen != oldproglen) + pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", proglen, oldproglen); break; } if (proglen == oldproglen) { -- cgit v0.10.2 From 93d3e3678f23109363cd6f99f2944d2cda616b23 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Tue, 17 Jan 2012 22:54:55 +0000 Subject: mlx4_en: set number of rx rings used by RSS using ethtool Value must be a power of 2 due to HW limitation. Driver supports only 'equal' mode in ethtool and can't be set by using weights. Signed-off-by: Amir Vadai Reviewed-by: Yevgeny Petrilin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 7dbc6a2..53c6686 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -479,6 +479,95 @@ static void mlx4_en_get_ringparam(struct net_device *dev, param->tx_pending = priv->tx_ring[0].size; } +static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + + return priv->rx_ring_num; +} + +static int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_rss_map *rss_map = &priv->rss_map; + int rss_rings; + size_t n = priv->rx_ring_num; + int err = 0; + + rss_rings = priv->prof->rss_rings ?: priv->rx_ring_num; + + while (n--) { + ring_index[n] = rss_map->qps[n % rss_rings].qpn - + rss_map->base_qpn; + } + + return err; +} + +static int mlx4_en_set_rxfh_indir(struct net_device *dev, + const u32 *ring_index) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; + int port_up = 0; + int err = 0; + int i; + int rss_rings = 0; + + /* Calculate RSS table size and make sure flows are spread evenly + * between rings + */ + for (i = 0; i < priv->rx_ring_num; i++) { + if (i > 0 && !ring_index[i] && !rss_rings) + rss_rings = i; + + if (ring_index[i] != (i % (rss_rings ?: priv->rx_ring_num))) + return -EINVAL; + } + + if (!rss_rings) + rss_rings = priv->rx_ring_num; + + /* RSS table size must be an order of 2 */ + if (!is_power_of_2(rss_rings)) + return -EINVAL; + + mutex_lock(&mdev->state_lock); + if (priv->port_up) { + port_up = 1; + mlx4_en_stop_port(dev); + } + + priv->prof->rss_rings = rss_rings; + + if (port_up) { + err = mlx4_en_start_port(dev); + if (err) + en_err(priv, "Failed starting port\n"); + } + + mutex_unlock(&mdev->state_lock); + return err; +} + +static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + int err = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = priv->rx_ring_num; + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + const struct ethtool_ops mlx4_en_ethtool_ops = { .get_drvinfo = mlx4_en_get_drvinfo, .get_settings = mlx4_en_get_settings, @@ -498,6 +587,10 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { .set_pauseparam = mlx4_en_set_pauseparam, .get_ringparam = mlx4_en_get_ringparam, .set_ringparam = mlx4_en_set_ringparam, + .get_rxnfc = mlx4_en_get_rxnfc, + .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size, + .get_rxfh_indir = mlx4_en_get_rxfh_indir, + .set_rxfh_indir = mlx4_en_set_rxfh_indir, }; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index a06096f..2097a7d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -62,10 +62,6 @@ static const char mlx4_en_version[] = * Device scope module parameters */ - -/* Enable RSS TCP traffic */ -MLX4_EN_PARM_INT(tcp_rss, 1, - "Enable RSS for incomming TCP traffic or disabled (0)"); /* Enable RSS UDP traffic */ MLX4_EN_PARM_INT(udp_rss, 1, "Enable RSS for incomming UDP traffic or disabled (0)"); @@ -104,7 +100,6 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) struct mlx4_en_profile *params = &mdev->profile; int i; - params->tcp_rss = tcp_rss; params->udp_rss = udp_rss; if (params->udp_rss && !(mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UDP_RSS)) { @@ -120,6 +115,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE; params->prof[i].tx_ring_num = MLX4_EN_NUM_TX_RINGS + (!!pfcrx) * MLX4_EN_NUM_PPP_RINGS; + params->prof[i].rss_rings = 0; } return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index e8d6ad2..971d4b6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -853,6 +853,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) struct mlx4_en_rss_map *rss_map = &priv->rss_map; struct mlx4_qp_context context; struct mlx4_rss_context *rss_context; + int rss_rings; void *ptr; u8 rss_mask = (MLX4_RSS_IPV4 | MLX4_RSS_TCP_IPV4 | MLX4_RSS_IPV6 | MLX4_RSS_TCP_IPV6); @@ -893,10 +894,15 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn, priv->rx_ring[0].cqn, &context); + if (!priv->prof->rss_rings || priv->prof->rss_rings > priv->rx_ring_num) + rss_rings = priv->rx_ring_num; + else + rss_rings = priv->prof->rss_rings; + ptr = ((void *) &context) + offsetof(struct mlx4_qp_context, pri_path) + MLX4_RSS_OFFSET_IN_QPC_PRI_PATH; rss_context = ptr; - rss_context->base_qpn = cpu_to_be32(ilog2(priv->rx_ring_num) << 24 | + rss_context->base_qpn = cpu_to_be32(ilog2(rss_rings) << 24 | (rss_map->base_qpn)); rss_context->default_qpn = cpu_to_be32(rss_map->base_qpn); if (priv->mdev->profile.udp_rss) { diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index f2a8e65..f4d189a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -325,11 +325,11 @@ struct mlx4_en_port_profile { u8 rx_ppp; u8 tx_pause; u8 tx_ppp; + int rss_rings; }; struct mlx4_en_profile { int rss_xor; - int tcp_rss; int udp_rss; u8 rss_mask; u32 active_ports; -- cgit v0.10.2 From 3a4adef5c0adbbd0d898578e13ba1adbbdecbbd5 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Wed, 18 Jan 2012 04:23:55 +0000 Subject: enic: This patch adds pci id 0x71 for SRIOV VF's This patch adds pci id 0x71 for sriov VF's. Signed-off-by: Roopa Prabhu Signed-off-by: Christian Benvenuti Signed-off-by: Sujith Sankar Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index fe0c29a..ee93a20 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -32,7 +32,7 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "2.1.1.28" +#define DRV_VERSION "2.1.1.31" #define DRV_COPYRIGHT "Copyright 2008-2011 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 2fd9db4..a634f58 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -57,11 +57,13 @@ #define PCI_DEVICE_ID_CISCO_VIC_ENET 0x0043 /* ethernet vnic */ #define PCI_DEVICE_ID_CISCO_VIC_ENET_DYN 0x0044 /* enet dynamic vnic */ +#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF 0x0071 /* enet SRIOV VF */ /* Supported devices */ static DEFINE_PCI_DEVICE_TABLE(enic_id_table) = { { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET) }, { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_DYN) }, + { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET_VF) }, { 0, } /* end of table */ }; @@ -132,6 +134,11 @@ int enic_sriov_enabled(struct enic *enic) return (enic->priv_flags & ENIC_SRIOV_ENABLED) ? 1 : 0; } +static int enic_is_sriov_vf(struct enic *enic) +{ + return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_VF; +} + int enic_is_valid_vf(struct enic *enic, int vf) { #ifdef CONFIG_PCI_IOV -- cgit v0.10.2 From 7335903cba523fc2fd801a178a0e4fcf8ccae756 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Wed, 18 Jan 2012 04:24:02 +0000 Subject: enic: Add sriov vf device id checks in port profile code This patch adds checks for sriov vf's in enic port profile handling code. sriov vf's are same as dynamic vnics but with a different device id. Signed-off-by: Roopa Prabhu Signed-off-by: Christian Benvenuti Signed-off-by: Sujith Sankar Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index a634f58..9f37d92 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -444,7 +444,7 @@ static void enic_mtu_check(struct enic *enic) if (mtu && mtu != enic->port_mtu) { enic->port_mtu = mtu; - if (enic_is_dynamic(enic)) { + if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) { mtu = max_t(int, ENIC_MIN_MTU, min_t(int, ENIC_MAX_MTU, mtu)); if (mtu != netdev->mtu) @@ -856,7 +856,7 @@ static int enic_set_mac_addr(struct net_device *netdev, char *addr) { struct enic *enic = netdev_priv(netdev); - if (enic_is_dynamic(enic)) { + if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) { if (!is_valid_ether_addr(addr) && !is_zero_ether_addr(addr)) return -EADDRNOTAVAIL; } else { @@ -1615,7 +1615,7 @@ static int enic_open(struct net_device *netdev) for (i = 0; i < enic->rq_count; i++) vnic_rq_enable(&enic->rq[i]); - if (!enic_is_dynamic(enic)) + if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) enic_dev_add_station_addr(enic); enic_set_rx_mode(netdev); @@ -1666,7 +1666,7 @@ static int enic_stop(struct net_device *netdev) netif_carrier_off(netdev); netif_tx_disable(netdev); - if (!enic_is_dynamic(enic)) + if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) enic_dev_del_station_addr(enic); for (i = 0; i < enic->wq_count; i++) { @@ -1703,7 +1703,7 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu) if (new_mtu < ENIC_MIN_MTU || new_mtu > ENIC_MAX_MTU) return -EINVAL; - if (enic_is_dynamic(enic)) + if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) return -EOPNOTSUPP; if (running) @@ -2433,7 +2433,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, * called later by an upper layer. */ - if (!enic_is_dynamic(enic)) { + if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) { err = vnic_dev_init(enic->vdev, 0); if (err) { dev_err(dev, "vNIC dev init failed, aborting\n"); @@ -2467,8 +2467,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, (void)enic_change_mtu(netdev, enic->port_mtu); #ifdef CONFIG_PCI_IOV - if (enic_is_dynamic(enic) && pdev->is_virtfn && - is_zero_ether_addr(enic->mac_addr)) + if (enic_is_sriov_vf(enic) && is_zero_ether_addr(enic->mac_addr)) random_ether_addr(enic->mac_addr); #endif @@ -2481,7 +2480,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, enic->tx_coalesce_usecs = enic->config.intr_timer_usec; enic->rx_coalesce_usecs = enic->tx_coalesce_usecs; - if (enic_is_dynamic(enic)) + if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic)) netdev->netdev_ops = &enic_netdev_dynamic_ops; else netdev->netdev_ops = &enic_netdev_ops; -- cgit v0.10.2 From ca2b721de25b9893310fd695a38b495d14c70ff3 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Wed, 18 Jan 2012 04:24:07 +0000 Subject: enic: rearrange some of the port profile code This patch rearranges some of the port profile code in enic_probe. It moves out some lines of port profile related code currently inside CONFIG_PCI_IOV. This is only done to move all port profile related code together so that it can help isolate the port profile handling code under a separate #ifdef in our internal build scripts. Signed-off-by: Roopa Prabhu Signed-off-by: Christian Benvenuti Signed-off-by: Sujith Sankar Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 9f37d92..03b0577 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2270,10 +2270,10 @@ static int __devinit enic_probe(struct pci_dev *pdev, int using_dac = 0; unsigned int i; int err; - int num_pps = 1; #ifdef CONFIG_PCI_IOV int pos = 0; #endif + int num_pps; /* Allocate net device structure and initialize. Private * instance data is initialized to zero. @@ -2380,17 +2380,17 @@ static int __devinit enic_probe(struct pci_dev *pdev, goto err_out_vnic_unregister; } enic->priv_flags |= ENIC_SRIOV_ENABLED; - num_pps = enic->num_vfs; } } - #endif + + num_pps = enic->num_vfs ? enic->num_vfs : 1; /* Allocate structure for port profiles */ enic->pp = kcalloc(num_pps, sizeof(*enic->pp), GFP_KERNEL); if (!enic->pp) { pr_err("port profile alloc failed, aborting\n"); err = -ENOMEM; - goto err_out_disable_sriov; + goto err_out_disable_sriov_pp; } /* Issue device open to get device in known state @@ -2399,7 +2399,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, err = enic_dev_open(enic); if (err) { dev_err(dev, "vNIC dev open failed, aborting\n"); - goto err_out_free_pp; + goto err_out_disable_sriov; } /* Setup devcmd lock @@ -2522,9 +2522,9 @@ err_out_dev_deinit: enic_dev_deinit(enic); err_out_dev_close: vnic_dev_close(enic->vdev); -err_out_free_pp: - kfree(enic->pp); err_out_disable_sriov: + kfree(enic->pp); +err_out_disable_sriov_pp: #ifdef CONFIG_PCI_IOV if (enic_sriov_enabled(enic)) { pci_disable_sriov(pdev); -- cgit v0.10.2 From 35d87e3325e91ec6bc446bf71f50eccc9a7b3b8f Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Wed, 18 Jan 2012 04:24:12 +0000 Subject: enic: fix location of vnic dev unregister in enic_probe cleanup code The vnic_dev_unregister is erroneously under CONFIG_PCI_IOV. This patch moves it out of CONFIG_PCI_IOV Signed-off-by: Roopa Prabhu Signed-off-by: Christian Benvenuti Signed-off-by: Sujith Sankar Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 03b0577..045c468 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2531,8 +2531,8 @@ err_out_disable_sriov_pp: enic->priv_flags &= ~ENIC_SRIOV_ENABLED; } err_out_vnic_unregister: - vnic_dev_unregister(enic->vdev); #endif + vnic_dev_unregister(enic->vdev); err_out_iounmap: enic_iounmap(enic); err_out_release_regions: -- cgit v0.10.2 From 013d97e9da1877f1334aa8ff3a19921ebbfe99b5 Mon Sep 17 00:00:00 2001 From: Francesco Ruggeri Date: Mon, 16 Jan 2012 10:40:10 +0000 Subject: net: race condition in ipv6 forwarding and disable_ipv6 parameters There is a race condition in addrconf_sysctl_forward() and addrconf_sysctl_disable(). These functions change idev->cnf.forwarding (resp. idev->cnf.disable_ipv6) and then try to grab the rtnl lock before performing any actions. If that fails they restore the original value and restart the syscall. This creates race conditions if ipv6 code tries to access these parameters, or if multiple instances try to do the same operation. As an example of the former, if __ipv6_ifa_notify() finds a 0 in idev->cnf.forwarding when invoked by addrconf_ifdown() it may not free anycast addresses, ultimately resulting in the net_device not being freed. This patch reads the user parameters into a temporary location and only writes the actual parameters when the rtnl lock is acquired. Tested in 2.6.38.8. Signed-off-by: Francesco Ruggeri Signed-off-by: David S. Miller diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index a225d5e..c02280a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -502,29 +502,31 @@ static void addrconf_forward_change(struct net *net, __s32 newf) rcu_read_unlock(); } -static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) +static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) { struct net *net; + int old; + + if (!rtnl_trylock()) + return restart_syscall(); net = (struct net *)table->extra2; - if (p == &net->ipv6.devconf_dflt->forwarding) - return 0; + old = *p; + *p = newf; - if (!rtnl_trylock()) { - /* Restore the original values before restarting */ - *p = old; - return restart_syscall(); + if (p == &net->ipv6.devconf_dflt->forwarding) { + rtnl_unlock(); + return 0; } if (p == &net->ipv6.devconf_all->forwarding) { - __s32 newf = net->ipv6.devconf_all->forwarding; net->ipv6.devconf_dflt->forwarding = newf; addrconf_forward_change(net, newf); - } else if ((!*p) ^ (!old)) + } else if ((!newf) ^ (!old)) dev_forward_change((struct inet6_dev *)table->extra1); rtnl_unlock(); - if (*p) + if (newf) rt6_purge_dflt_routers(net); return 1; } @@ -4260,9 +4262,17 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, int *valp = ctl->data; int val = *valp; loff_t pos = *ppos; + ctl_table lctl; int ret; - ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + /* + * ctl->data points to idev->cnf.forwarding, we should + * not modify it until we get the rtnl lock. + */ + lctl = *ctl; + lctl.data = &val; + + ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); if (write) ret = addrconf_fixup_forwarding(ctl, valp, val); @@ -4300,26 +4310,27 @@ static void addrconf_disable_change(struct net *net, __s32 newf) rcu_read_unlock(); } -static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old) +static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf) { struct net *net; + int old; + + if (!rtnl_trylock()) + return restart_syscall(); net = (struct net *)table->extra2; + old = *p; + *p = newf; - if (p == &net->ipv6.devconf_dflt->disable_ipv6) + if (p == &net->ipv6.devconf_dflt->disable_ipv6) { + rtnl_unlock(); return 0; - - if (!rtnl_trylock()) { - /* Restore the original values before restarting */ - *p = old; - return restart_syscall(); } if (p == &net->ipv6.devconf_all->disable_ipv6) { - __s32 newf = net->ipv6.devconf_all->disable_ipv6; net->ipv6.devconf_dflt->disable_ipv6 = newf; addrconf_disable_change(net, newf); - } else if ((!*p) ^ (!old)) + } else if ((!newf) ^ (!old)) dev_disable_change((struct inet6_dev *)table->extra1); rtnl_unlock(); @@ -4333,9 +4344,17 @@ int addrconf_sysctl_disable(ctl_table *ctl, int write, int *valp = ctl->data; int val = *valp; loff_t pos = *ppos; + ctl_table lctl; int ret; - ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + /* + * ctl->data points to idev->cnf.disable_ipv6, we should + * not modify it until we get the rtnl lock. + */ + lctl = *ctl; + lctl.data = &val; + + ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); if (write) ret = addrconf_disable_ipv6(ctl, valp, val); -- cgit v0.10.2 From 2b53d07891630dead46d65c8f896955fd3ae0302 Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Mon, 16 Jan 2012 09:50:19 +0000 Subject: pch_gbe: Do not abort probe on bad MAC If the MAC is invalid or not implemented, do not abort the probe. Issue a warning and prevent bringing the interface up until a MAC is set manually (via ifconfig $IFACE hw ether $MAC). Tested on two platforms, one with a valid MAC, the other without a MAC. The real MAC is used if present, the interface fails to come up until the MAC is set on the other. They successfully get an IP over DHCP and pass a simple ping and login over ssh test. This is meant to allow the Inforce SYS940X development board: http://www.inforcecomputing.com/SYS940X_ECX.html (and others suffering from a missing MAC) to work with the mainline kernel. Without this patch, the probe will fail and the interface will not be created, preventing the user from configuring the MAC manually. This does not make any attempt to address a missing or invalid MAC for the pch_phub driver. Signed-off-by: Darren Hart CC: Arjan van de Ven CC: Alan Cox CC: Tomoya MORINAGA CC: Jeff Kirsher CC: "David S. Miller" CC: Paul Gortmaker CC: Jon Mason CC: netdev@vger.kernel.org CC: Mark Brown CC: David Laight CC: Joe Perches Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 964e9c0..3ead111 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -1745,6 +1745,12 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) struct pch_gbe_rx_ring *rx_ring = adapter->rx_ring; int err; + /* Ensure we have a valid MAC */ + if (!is_valid_ether_addr(adapter->hw.mac.addr)) { + pr_err("Error: Invalid MAC address\n"); + return -EINVAL; + } + /* hardware has been reset, we need to reload some things */ pch_gbe_set_multi(netdev); @@ -2468,9 +2474,14 @@ static int pch_gbe_probe(struct pci_dev *pdev, memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len); if (!is_valid_ether_addr(netdev->dev_addr)) { - dev_err(&pdev->dev, "Invalid MAC Address\n"); - ret = -EIO; - goto err_free_adapter; + /* + * If the MAC is invalid (or just missing), display a warning + * but do not abort setting up the device. pch_gbe_up will + * prevent the interface from being brought up until a valid MAC + * is set. + */ + dev_err(&pdev->dev, "Invalid MAC address, " + "interface disabled.\n"); } setup_timer(&adapter->watchdog_timer, pch_gbe_watchdog, (unsigned long)adapter); -- cgit v0.10.2 From 88a4412b798236bfdd9284d5c251d76679f944e1 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 18 Jan 2012 17:13:47 -0600 Subject: [CIFS] Fix build break with multiuser patch when LANMAN disabled CC: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ba53c1c..76e7d8b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -879,6 +879,8 @@ require use of the stronger protocol */ #define CIFSSEC_MASK 0xB70B7 /* current flags supported if weak */ #endif /* UPCALL */ #else /* do not allow weak pw hash */ +#define CIFSSEC_MUST_LANMAN 0 +#define CIFSSEC_MUST_PLNTXT 0 #ifdef CONFIG_CIFS_UPCALL #define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ #else -- cgit v0.10.2 From 9ef9b20bd0eef609f07960a997c13cab8fe15d2e Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 19 Jan 2012 10:24:31 +1100 Subject: xen: using EXPORT_SYMBOL requires including export.h Fix these warnings: drivers/xen/biomerge.c:14:1: warning: data definition has no type or storage class [enabled by default] drivers/xen/biomerge.c:14:1: warning: type defaults to 'int' in declaration of 'EXPORT_SYMBOL' [-Wimplicit-int] drivers/xen/biomerge.c:14:1: warning: parameter names (without types) in function declaration [enabled by default] And this build error: ERROR: "xen_biovec_phys_mergeable" [drivers/block/nvme.ko] undefined! Signed-off-by: Stephen Rothwell Signed-off-by: Linus Torvalds diff --git a/drivers/xen/biomerge.c b/drivers/xen/biomerge.c index 18c1bb6..0edb91c 100644 --- a/drivers/xen/biomerge.c +++ b/drivers/xen/biomerge.c @@ -1,5 +1,6 @@ #include #include +#include #include bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, -- cgit v0.10.2 From 93c3d65b28bab6da520c2add9cb387a0303f8b2d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 18 Jan 2012 15:41:27 -0800 Subject: nvme: fix merge error due to change of 'make_request_fn' fn type The type of 'make_request_fn' changed in 5a7bbad27a4 ("block: remove support for bio remapping from ->make_request"), but the merge of the nvme driver didn't take that into account, and as a result the driver would compile with a warning: drivers/block/nvme.c: In function 'nvme_alloc_ns': drivers/block/nvme.c:1336:2: warning: passing argument 2 of 'blk_queue_make_request' from incompatible pointer type [enabled by default] include/linux/blkdev.h:830:13: note: expected 'void (*)(struct request_queue *, struct bio *)' but argument is of type 'int (*)(struct request_queue *, struct bio *)' It's benign, but the warning is annoying. Reported-by: Stephen Rothwell Cc: Matthew Wilcox Signed-off-by: Linus Torvalds diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index f4996b0..c1dc4d8 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -613,11 +613,7 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, return result; } -/* - * NB: return value of non-zero would mean that we were a stacking driver. - * make_request must always succeed. - */ -static int nvme_make_request(struct request_queue *q, struct bio *bio) +static void nvme_make_request(struct request_queue *q, struct bio *bio) { struct nvme_ns *ns = q->queuedata; struct nvme_queue *nvmeq = get_nvmeq(ns->dev); @@ -634,8 +630,6 @@ static int nvme_make_request(struct request_queue *q, struct bio *bio) spin_unlock_irq(&nvmeq->q_lock); put_nvmeq(nvmeq); - - return 0; } static irqreturn_t nvme_process_cq(struct nvme_queue *nvmeq) -- cgit v0.10.2 From be405411f712489f2f780ab085e1069e8fb85f19 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 17 Jan 2012 22:07:30 -0500 Subject: tpm: fix (ACPI S3) suspend regression This patch fixes an (ACPI S3) suspend regression introduced in commit 68d6e6713fcb ("tpm: Introduce function to poll for result of self test") and occurring with an Infineon TPM and tpm_tis and tpm_infineon drivers active. The suspend problem occurred if the TPM was disabled and/or deactivated and therefore the TPM_PCRRead checking the result of the (asynchronous) self test returned an error code which then caused the tpm_tis driver to become inactive and this then seemed to have negatively influenced the suspend support by the tpm_infineon driver... Besides that the tpm_tis drive may stay active even if the TPM is disabled and/or deactivated. Signed-off-by: Stefan Berger Tested-by: Rafael J. Wysocki Acked-by: Rajiv Andrade Signed-off-by: Linus Torvalds diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 6a8771f..32362cf 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -846,6 +846,15 @@ int tpm_do_selftest(struct tpm_chip *chip) do { rc = __tpm_pcr_read(chip, 0, digest); + if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) { + dev_info(chip->dev, + "TPM is disabled/deactivated (0x%X)\n", rc); + /* TPM is disabled and/or deactivated; driver can + * proceed and TPM does handle commands for + * suspend/resume correctly + */ + return 0; + } if (rc != TPM_WARN_DOING_SELFTEST) return rc; msleep(delay_msec); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 8c1df30..0105471 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -39,6 +39,9 @@ enum tpm_addr { }; #define TPM_WARN_DOING_SELFTEST 0x802 +#define TPM_ERR_DEACTIVATED 0x6 +#define TPM_ERR_DISABLED 0x7 + #define TPM_HEADER_SIZE 10 extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr, char *); -- cgit v0.10.2 From c56001879bc091eee0c7a8e6e94ea0bea63c3012 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 18 Jan 2012 17:19:11 -0600 Subject: [CIFS] ACL and FSCACHE support no longer EXPERIMENTAL CIFS ACL support and FSCACHE support have been in long enough to be no longer considered experimental. Remove obsolete Kconfig dependency. Signed-off-by: Steve French Acked-by: Jeff Layton diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index f66cc16..0554b00 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -140,7 +140,6 @@ config CIFS_DFS_UPCALL config CIFS_FSCACHE bool "Provide CIFS client caching support (EXPERIMENTAL)" - depends on EXPERIMENTAL depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data @@ -149,7 +148,7 @@ config CIFS_FSCACHE config CIFS_ACL bool "Provide CIFS ACL support (EXPERIMENTAL)" - depends on EXPERIMENTAL && CIFS_XATTR && KEYS + depends on CIFS_XATTR && KEYS help Allows to fetch CIFS/NTFS ACL from the server. The DACL blob is handed over to the application/caller. -- cgit v0.10.2 From b924551bed09f61b64f21bffe241afc5526b091a Mon Sep 17 00:00:00 2001 From: Jiri Bohac Date: Wed, 18 Jan 2012 12:24:54 +0000 Subject: bonding: fix enslaving in alb mode when link down bond_alb_init_slave() is called from bond_enslave() and sets the slave's MAC address. This is done differently for TLB and ALB modes. bond->alb_info.rlb_enabled is used to discriminate between the two modes but this flag may be uninitialized if the slave is being enslaved prior to calling bond_open() -> bond_alb_initialize() on the master. It turns out all the callers of alb_set_slave_mac_addr() pass bond->alb_info.rlb_enabled as the hw parameter. This patch cleans up the unnecessary parameter of alb_set_slave_mac_addr() and makes the function decide based on the bonding mode instead, which fixes the above problem. Reported-by: Narendra K Signed-off-by: Jiri Bohac Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 342626f..f820b26 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -909,16 +909,12 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]) } } -/* hw is a boolean parameter that determines whether we should try and - * set the hw address of the device as well as the hw address of the - * net_device - */ -static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[], int hw) +static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) { struct net_device *dev = slave->dev; struct sockaddr s_addr; - if (!hw) { + if (slave->bond->params.mode == BOND_MODE_TLB) { memcpy(dev->dev_addr, addr, dev->addr_len); return 0; } @@ -948,8 +944,8 @@ static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct u8 tmp_mac_addr[ETH_ALEN]; memcpy(tmp_mac_addr, slave1->dev->dev_addr, ETH_ALEN); - alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr, bond->alb_info.rlb_enabled); - alb_set_slave_mac_addr(slave2, tmp_mac_addr, bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr); + alb_set_slave_mac_addr(slave2, tmp_mac_addr); } @@ -1096,8 +1092,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav /* Try setting slave mac to bond address and fall-through to code handling that situation below... */ - alb_set_slave_mac_addr(slave, bond->dev->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave, bond->dev->dev_addr); } /* The slave's address is equal to the address of the bond. @@ -1133,8 +1128,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav } if (free_mac_slave) { - alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr); pr_warning("%s: Warning: the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", bond->dev->name, slave->dev->name, @@ -1491,8 +1485,7 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave) { int res; - res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr, - bond->alb_info.rlb_enabled); + res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr); if (res) { return res; } @@ -1643,8 +1636,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave alb_swap_mac_addr(bond, swap_slave, new_slave); } else { /* set the new_slave to the bond mac address */ - alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); } if (swap_slave) { @@ -1704,8 +1696,7 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave); alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave); } else { - alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); read_lock(&bond->lock); alb_send_learning_packets(bond->curr_active_slave, bond_dev->dev_addr); -- cgit v0.10.2 From 17f1bbca1baf2f652fa9c102ec239d6abded94c1 Mon Sep 17 00:00:00 2001 From: Thomas Faber Date: Wed, 18 Jan 2012 13:45:44 +0000 Subject: net: ftgmac100/ftmac100: add missing interrupt.h include Fixes compilation failure of these modules due to missing irqreturn_t type for the ft(g)mac100_interrupt definition. Signed-off-by: Thomas Faber Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index fb5579a..47f85c3 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index a127cb2..bb336a0 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 4bf1924c008dffdc154f82507b4052e49263a6f4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 18 Jan 2012 10:03:54 +0000 Subject: MPILIB: Add a missing ENOMEM check Add a missing ENOMEM check. Signed-off-by: David Howells Acked-by: Mimi Zohar Signed-off-by: James Morris diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index fe84bb9..716802b 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -255,6 +255,8 @@ void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign) if (!n) n++; /* avoid zero length allocation */ p = buffer = kmalloc(n, GFP_KERNEL); + if (!p) + return NULL; for (i = a->nlimbs - 1; i >= 0; i--) { alimb = a->d[i]; -- cgit v0.10.2 From 90a4c0f51e8e44111a926be6f4c87af3938a79c3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 18 Jan 2012 19:26:11 -0800 Subject: uml: fix compile for x86-64 Randy Dunlap reports that we get arch/x86/um/shared/sysdep/ptrace.h:7:20: error: redefinition of 'regs_return_value' arch/x86/um/shared/sysdep/ptrace.h:7:20: note: previous definition of 'regs_return_value' was here when compiling UML for x86-64. Stephen Rothwell root-caused it and says: "Caused by commit d7e7528bcd45 ("Audit: push audit success and retcode into arch ptrace.h") (another patch that was never in linux-next :-(). This file now needs protection against double inclusion." so let's do as the man says. Reported-by: Randy Dunlap Analyzed-by: Stephen Rothwell Signed-off-by: Linus Torvalds diff --git a/arch/x86/um/shared/sysdep/ptrace.h b/arch/x86/um/shared/sysdep/ptrace.h index 5ef9344..2bbe1ec2 100644 --- a/arch/x86/um/shared/sysdep/ptrace.h +++ b/arch/x86/um/shared/sysdep/ptrace.h @@ -1,3 +1,6 @@ +#ifndef __SYSDEP_X86_PTRACE_H +#define __SYSDEP_X86_PTRACE_H + #ifdef __i386__ #include "ptrace_32.h" #else @@ -8,3 +11,5 @@ static inline long regs_return_value(struct uml_pt_regs *regs) { return UPT_SYSCALL_RET(regs); } + +#endif /* __SYSDEP_X86_PTRACE_H */ -- cgit v0.10.2 From acbbb76a26648dfae6fed0989879e40d75692bfc Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 18 Jan 2012 22:32:33 -0600 Subject: CIFS: Rename *UCS* functions to *UTF16* to reflect the unicode encoding used by CIFS protocol. Signed-off-by: Pavel Shilovsky Acked-by: Jeff Layton Reviewed-by: Shirish Pargaonkar diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 1b2e180..fbb9da9 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -27,17 +27,17 @@ #include "cifs_debug.h" /* - * cifs_ucs2_bytes - how long will a string be after conversion? - * @ucs - pointer to input string + * cifs_utf16_bytes - how long will a string be after conversion? + * @utf16 - pointer to input string * @maxbytes - don't go past this many bytes of input string * @codepage - destination codepage * - * Walk a ucs2le string and return the number of bytes that the string will + * Walk a utf16le string and return the number of bytes that the string will * be after being converted to the given charset, not including any null * termination required. Don't walk past maxbytes in the source buffer. */ int -cifs_ucs2_bytes(const __le16 *from, int maxbytes, +cifs_utf16_bytes(const __le16 *from, int maxbytes, const struct nls_table *codepage) { int i; @@ -122,7 +122,7 @@ cp_convert: } /* - * cifs_from_ucs2 - convert utf16le string to local charset + * cifs_from_utf16 - convert utf16le string to local charset * @to - destination buffer * @from - source buffer * @tolen - destination buffer size (in bytes) @@ -130,7 +130,7 @@ cp_convert: * @codepage - codepage to which characters should be converted * @mapchar - should characters be remapped according to the mapchars option? * - * Convert a little-endian ucs2le string (as sent by the server) to a string + * Convert a little-endian utf16le string (as sent by the server) to a string * in the provided codepage. The tolen and fromlen parameters are to ensure * that the code doesn't walk off of the end of the buffer (which is always * a danger if the alignment of the source buffer is off). The destination @@ -139,12 +139,12 @@ cp_convert: * null terminator). * * Note that some windows versions actually send multiword UTF-16 characters - * instead of straight UCS-2. The linux nls routines however aren't able to + * instead of straight UTF16-2. The linux nls routines however aren't able to * deal with those characters properly. In the event that we get some of * those characters, they won't be translated properly. */ int -cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, +cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, const struct nls_table *codepage, bool mapchar) { int i, charlen, safelen; @@ -190,13 +190,13 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, } /* - * NAME: cifs_strtoUCS() + * NAME: cifs_strtoUTF16() * * FUNCTION: Convert character string to unicode string * */ int -cifs_strtoUCS(__le16 *to, const char *from, int len, +cifs_strtoUTF16(__le16 *to, const char *from, int len, const struct nls_table *codepage) { int charlen; @@ -206,7 +206,7 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, for (i = 0; len && *from; i++, from += charlen, len -= charlen) { charlen = codepage->char2uni(from, len, &wchar_to); if (charlen < 1) { - cERROR(1, "strtoUCS: char2uni of 0x%x returned %d", + cERROR(1, "strtoUTF16: char2uni of 0x%x returned %d", *from, charlen); /* A question mark */ wchar_to = 0x003f; @@ -220,7 +220,8 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, } /* - * cifs_strndup_from_ucs - copy a string from wire format to the local codepage + * cifs_strndup_from_utf16 - copy a string from wire format to the local + * codepage * @src - source string * @maxlen - don't walk past this many bytes in the source string * @is_unicode - is this a unicode string? @@ -231,19 +232,19 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, * error. */ char * -cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, - const struct nls_table *codepage) +cifs_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *codepage) { int len; char *dst; if (is_unicode) { - len = cifs_ucs2_bytes((__le16 *) src, maxlen, codepage); + len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage); len += nls_nullsize(codepage); dst = kmalloc(len, GFP_KERNEL); if (!dst) return NULL; - cifs_from_ucs2(dst, (__le16 *) src, len, maxlen, codepage, + cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, false); } else { len = strnlen(src, maxlen); @@ -264,7 +265,7 @@ cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, * names are little endian 16 bit Unicode on the wire */ int -cifsConvertToUCS(__le16 *target, const char *source, int srclen, +cifsConvertToUTF16(__le16 *target, const char *source, int srclen, const struct nls_table *cp, int mapChars) { int i, j, charlen; @@ -273,7 +274,7 @@ cifsConvertToUCS(__le16 *target, const char *source, int srclen, wchar_t tmp; if (!mapChars) - return cifs_strtoUCS(target, source, PATH_MAX, cp); + return cifs_strtoUTF16(target, source, PATH_MAX, cp); for (i = 0, j = 0; i < srclen; j++) { src_char = source[i]; @@ -281,7 +282,7 @@ cifsConvertToUCS(__le16 *target, const char *source, int srclen, switch (src_char) { case 0: put_unaligned(0, &target[j]); - goto ctoUCS_out; + goto ctoUTF16_out; case ':': dst_char = cpu_to_le16(UNI_COLON); break; @@ -326,7 +327,7 @@ cifsConvertToUCS(__le16 *target, const char *source, int srclen, put_unaligned(dst_char, &target[j]); } -ctoUCS_out: +ctoUTF16_out: return i; } diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 6d02fd56..a513a54 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -74,16 +74,16 @@ extern const struct UniCaseRange CifsUniLowerRange[]; #endif /* UNIUPR_NOLOWER */ #ifdef __KERNEL__ -int cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *codepage, bool mapchar); -int cifs_ucs2_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage); -int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); -char *cifs_strndup_from_ucs(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage); -extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen, - const struct nls_table *cp, int mapChars); +int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar); +int cifs_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage); +int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); +char *cifs_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars); #endif diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index bce99e6..63c460e 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -327,7 +327,7 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp) attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); attrptr->length = cpu_to_le16(2 * dlen); blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); - cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); + cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp); return 0; } @@ -376,7 +376,7 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) kmalloc(attrsize + 1, GFP_KERNEL); if (!ses->domainName) return -ENOMEM; - cifs_from_ucs2(ses->domainName, + cifs_from_utf16(ses->domainName, (__le16 *)blobptr, attrsize, attrsize, nls_cp, false); break; @@ -429,7 +429,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, } if (len) { - len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp); + len = cifs_strtoUTF16((__le16 *)user, ses->user_name, len, nls_cp); UniStrupr(user); } else { memset(user, '\0', 2); @@ -453,8 +453,8 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, rc = -ENOMEM; return rc; } - len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, - nls_cp); + len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len, + nls_cp); rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)domain, 2 * len); @@ -473,7 +473,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, rc = -ENOMEM; return rc; } - len = cifs_strtoUCS((__le16 *)server, ses->serverName, len, + len = cifs_strtoUTF16((__le16 *)server, ses->serverName, len, nls_cp); rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 6600aa2..8b7794c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -821,8 +821,8 @@ PsxDelete: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB add path length overrun check */ @@ -893,8 +893,8 @@ DelFileRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->fileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->fileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ @@ -938,8 +938,8 @@ RmDirRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName, - PATH_MAX, nls_codepage, remap); + name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, dirName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ @@ -981,8 +981,8 @@ MkDirRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name, - PATH_MAX, nls_codepage, remap); + name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ @@ -1030,8 +1030,8 @@ PsxCreat: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, name, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, name, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -1197,8 +1197,8 @@ OldOpenRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { count = 1; /* account for one byte pad to word boundary */ name_len = - cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), - fileName, PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), + fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve check for buffer overruns BB */ @@ -1304,8 +1304,8 @@ openRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { count = 1; /* account for one byte pad to word boundary */ name_len = - cifsConvertToUCS((__le16 *) (pSMB->fileName + 1), - fileName, PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), + fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->NameLength = cpu_to_le16(name_len); @@ -2649,16 +2649,16 @@ renameRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->OldFileName, fromName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->OldFileName[name_len] = 0x04; /* pad */ /* protocol requires ASCII signature byte on Unicode string */ pSMB->OldFileName[name_len + 1] = 0x00; name_len2 = - cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], - toName, PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ @@ -2738,10 +2738,12 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifs_tcon *pTcon, /* unicode only call */ if (target_name == NULL) { sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); - len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, + len_of_str = + cifsConvertToUTF16((__le16 *)rename_info->target_name, dummy_string, 24, nls_codepage, remap); } else { - len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, + len_of_str = + cifsConvertToUTF16((__le16 *)rename_info->target_name, target_name, PATH_MAX, nls_codepage, remap); } @@ -2795,17 +2797,17 @@ copyRetry: pSMB->Flags = cpu_to_le16(flags & COPY_TREE); if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, - fromName, PATH_MAX, nls_codepage, - remap); + name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, + fromName, PATH_MAX, nls_codepage, + remap); name_len++; /* trailing null */ name_len *= 2; pSMB->OldFileName[name_len] = 0x04; /* pad */ /* protocol requires ASCII signature byte on Unicode string */ pSMB->OldFileName[name_len + 1] = 0x00; name_len2 = - cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], - toName, PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ @@ -2861,9 +2863,9 @@ createSymLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX - /* find define for this maxpathcomponent */ - , nls_codepage); + cifs_strtoUTF16((__le16 *) pSMB->FileName, fromName, + /* find define for this maxpathcomponent */ + PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; @@ -2885,9 +2887,9 @@ createSymLinkRetry: data_offset = (char *) (&pSMB->hdr.Protocol) + offset; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len_target = - cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX - /* find define for this maxpathcomponent */ - , nls_codepage); + cifs_strtoUTF16((__le16 *) data_offset, toName, PATH_MAX + /* find define for this maxpathcomponent */ + , nls_codepage); name_len_target++; /* trailing null */ name_len_target *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -2949,8 +2951,8 @@ createHardLinkRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName, - PATH_MAX, nls_codepage, remap); + name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -2972,8 +2974,8 @@ createHardLinkRetry: data_offset = (char *) (&pSMB->hdr.Protocol) + offset; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len_target = - cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX, - nls_codepage, remap); + cifsConvertToUTF16((__le16 *) data_offset, fromName, + PATH_MAX, nls_codepage, remap); name_len_target++; /* trailing null */ name_len_target *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -3042,8 +3044,8 @@ winCreateHardLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->OldFileName, fromName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -3051,8 +3053,8 @@ winCreateHardLinkRetry: pSMB->OldFileName[name_len] = 0x04; pSMB->OldFileName[name_len + 1] = 0x00; /* pad */ name_len2 = - cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], - toName, PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ } else { /* BB improve the check for buffer overruns BB */ @@ -3108,8 +3110,8 @@ querySymLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage); + cifs_strtoUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -3166,8 +3168,8 @@ querySymLinkRetry: is_unicode = false; /* BB FIXME investigate remapping reserved chars here */ - *symlinkinfo = cifs_strndup_from_ucs(data_start, count, - is_unicode, nls_codepage); + *symlinkinfo = cifs_strndup_from_utf16(data_start, + count, is_unicode, nls_codepage); if (!*symlinkinfo) rc = -ENOMEM; } @@ -3450,8 +3452,9 @@ queryAclRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); name_len++; /* trailing null */ name_len *= 2; pSMB->FileName[name_len] = 0; @@ -3537,8 +3540,8 @@ setAclRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -3948,8 +3951,9 @@ QInfRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); name_len++; /* trailing null */ name_len *= 2; } else { @@ -4086,8 +4090,8 @@ QPathInfoRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -4255,8 +4259,8 @@ UnixQPathInfoRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -4344,8 +4348,8 @@ findFirstRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); /* We can not add the asterik earlier in case it got remapped to 0xF03A as if it were part of the directory name instead of a wildcard */ @@ -4656,8 +4660,9 @@ GetInodeNumberRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -4794,9 +4799,9 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, rc = -ENOMEM; goto parse_DFS_referrals_exit; } - cifsConvertToUCS((__le16 *) tmp, searchName, - PATH_MAX, nls_codepage, remap); - node->path_consumed = cifs_ucs2_bytes(tmp, + cifsConvertToUTF16((__le16 *) tmp, searchName, + PATH_MAX, nls_codepage, remap); + node->path_consumed = cifs_utf16_bytes(tmp, le16_to_cpu(pSMBr->PathConsumed), nls_codepage); kfree(tmp); @@ -4809,8 +4814,8 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, /* copy DfsPath */ temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); max_len = data_end - temp; - node->path_name = cifs_strndup_from_ucs(temp, max_len, - is_unicode, nls_codepage); + node->path_name = cifs_strndup_from_utf16(temp, max_len, + is_unicode, nls_codepage); if (!node->path_name) { rc = -ENOMEM; goto parse_DFS_referrals_exit; @@ -4819,8 +4824,8 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, /* copy link target UNC */ temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); max_len = data_end - temp; - node->node_name = cifs_strndup_from_ucs(temp, max_len, - is_unicode, nls_codepage); + node->node_name = cifs_strndup_from_utf16(temp, max_len, + is_unicode, nls_codepage); if (!node->node_name) rc = -ENOMEM; } @@ -4873,8 +4878,9 @@ getDFSRetry: if (ses->capabilities & CAP_UNICODE) { pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; name_len = - cifsConvertToUCS((__le16 *) pSMB->RequestFileName, - searchName, PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->RequestFileName, + searchName, PATH_MAX, nls_codepage, + remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -5506,8 +5512,8 @@ SetEOFRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -5796,8 +5802,8 @@ SetTimesRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -5877,8 +5883,8 @@ SetAttrLgcyRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - ConvertToUCS((__le16 *) pSMB->fileName, fileName, - PATH_MAX, nls_codepage); + ConvertToUTF16((__le16 *) pSMB->fileName, fileName, + PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -6030,8 +6036,8 @@ setPermsRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -6123,8 +6129,8 @@ QAllEAsRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { list_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); list_len++; /* trailing null */ list_len *= 2; } else { /* BB improve the check for buffer overruns BB */ @@ -6301,8 +6307,8 @@ SetEARetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; } else { /* BB improve the check for buffer overruns BB */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 28f23c0..986709a 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3644,7 +3644,7 @@ CIFSTCon(unsigned int xid, struct cifs_ses *ses, if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; length = - cifs_strtoUCS((__le16 *) bcc_ptr, tree, + cifs_strtoUTF16((__le16 *) bcc_ptr, tree, 6 /* max utf8 char length in bytes */ * (/* server len*/ + 256 /* share len */), nls_codepage); bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ @@ -3699,7 +3699,7 @@ CIFSTCon(unsigned int xid, struct cifs_ses *ses, /* mostly informational -- no need to fail on error here */ kfree(tcon->nativeFileSystem); - tcon->nativeFileSystem = cifs_strndup_from_ucs(bcc_ptr, + tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, bytes_left, is_unicode, nls_codepage); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index a090bbe..e2bbc68 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -647,10 +647,11 @@ static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir, name.name = scratch_buf; name.len = - cifs_from_ucs2((char *)name.name, (__le16 *)de.name, - UNICODE_NAME_MAX, - min(de.namelen, (size_t)max_len), nlt, - cifs_sb->mnt_cifs_flags & + cifs_from_utf16((char *)name.name, (__le16 *)de.name, + UNICODE_NAME_MAX, + min_t(size_t, de.namelen, + (size_t)max_len), nlt, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); name.len -= nls_nullsize(nlt); } else { diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 4ec3ee9..d85efad 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -167,16 +167,16 @@ unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) int bytes_ret = 0; /* Copy OS version */ - bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32, - nls_cp); + bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, + nls_cp); bcc_ptr += 2 * bytes_ret; - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, init_utsname()->release, - 32, nls_cp); + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, + 32, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* trailing null */ - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 32, nls_cp); + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, + 32, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* trailing null */ @@ -197,8 +197,8 @@ static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, *(bcc_ptr+1) = 0; bytes_ret = 0; } else - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, - 256, nls_cp); + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, + 256, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null terminator */ @@ -226,8 +226,8 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, *bcc_ptr = 0; *(bcc_ptr+1) = 0; } else { - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->user_name, - MAX_USERNAME_SIZE, nls_cp); + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, + MAX_USERNAME_SIZE, nls_cp); } bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null termination */ @@ -287,7 +287,7 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, cFYI(1, "bleft %d", bleft); kfree(ses->serverOS); - ses->serverOS = cifs_strndup_from_ucs(data, bleft, true, nls_cp); + ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); cFYI(1, "serverOS=%s", ses->serverOS); len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; data += len; @@ -296,7 +296,7 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, return; kfree(ses->serverNOS); - ses->serverNOS = cifs_strndup_from_ucs(data, bleft, true, nls_cp); + ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); cFYI(1, "serverNOS=%s", ses->serverNOS); len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; data += len; @@ -305,7 +305,7 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, return; kfree(ses->serverDomain); - ses->serverDomain = cifs_strndup_from_ucs(data, bleft, true, nls_cp); + ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); cFYI(1, "serverDomain=%s", ses->serverDomain); return; @@ -502,8 +502,8 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, tmp += 2; } else { int len; - len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, - MAX_USERNAME_SIZE, nls_cp); + len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, + MAX_USERNAME_SIZE, nls_cp); len *= 2; /* unicode is 2 bytes each */ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.Length = cpu_to_le16(len); @@ -518,8 +518,8 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, tmp += 2; } else { int len; - len = cifs_strtoUCS((__le16 *)tmp, ses->user_name, - MAX_USERNAME_SIZE, nls_cp); + len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, + MAX_USERNAME_SIZE, nls_cp); len *= 2; /* unicode is 2 bytes each */ sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->UserName.Length = cpu_to_le16(len); diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 80d8508..d5cd9aa 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -213,7 +213,7 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16, /* Password cannot be longer than 128 characters */ if (passwd) /* Password must be converted to NT unicode */ - len = cifs_strtoUCS(wpwd, passwd, 128, codepage); + len = cifs_strtoUTF16(wpwd, passwd, 128, codepage); else { len = 0; *wpwd = 0; /* Ensure string is null terminated */ -- cgit v0.10.2 From 3db59dd93309710c40aaf1571c607cb0feef3ecb Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 17 Jan 2012 22:11:28 -0500 Subject: ima: fix cred sparse warning Fix ima_policy.c sparse "warning: dereference of noderef expression" message, by accessing cred->uid using current_cred(). Changelog v1: - Change __cred to just cred (based on David Howell's comment) Signed-off-by: Mimi Zohar Signed-off-by: James Morris diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index d661afb..d45061d 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -99,6 +99,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, struct inode *inode, enum ima_hooks func, int mask) { struct task_struct *tsk = current; + const struct cred *cred = current_cred(); int i; if ((rule->flags & IMA_FUNC) && rule->func != func) @@ -108,7 +109,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, if ((rule->flags & IMA_FSMAGIC) && rule->fsmagic != inode->i_sb->s_magic) return false; - if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) + if ((rule->flags & IMA_UID) && rule->uid != cred->uid) return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; -- cgit v0.10.2 From f6b24579d099ebb67f39cd7924a72a7eec0ce6ae Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 18 Jan 2012 10:03:14 +0000 Subject: keys: fix user_defined key sparse messages Replace the rcu_assign_pointer() calls with rcu_assign_keypointer(). Signed-off-by: Mimi Zohar Signed-off-by: David Howells Signed-off-by: James Morris diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 69ff52c..2aee3c5 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -59,7 +59,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen) /* attach the data */ upayload->datalen = datalen; memcpy(upayload->data, data, datalen); - rcu_assign_pointer(key->payload.data, upayload); + rcu_assign_keypointer(key, upayload); ret = 0; error: @@ -98,7 +98,7 @@ int user_update(struct key *key, const void *data, size_t datalen) if (ret == 0) { /* attach the new data, displacing the old */ zap = key->payload.data; - rcu_assign_pointer(key->payload.data, upayload); + rcu_assign_keypointer(key, upayload); key->expiry = 0; } @@ -133,7 +133,7 @@ void user_revoke(struct key *key) key_payload_reserve(key, 0); if (upayload) { - rcu_assign_pointer(key->payload.data, NULL); + rcu_assign_keypointer(key, NULL); kfree_rcu(upayload, rcu); } } -- cgit v0.10.2 From 456a8167e94b66f406c27400a46a707b870452b0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 18 Jan 2012 10:04:29 +0000 Subject: KEYS: Permit key_serial() to be called with a const key pointer Permit key_serial() to be called with a const key pointer. Signed-off-by: David Howells Signed-off-by: James Morris diff --git a/include/linux/key.h b/include/linux/key.h index bfc014c..5253471 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -271,7 +271,7 @@ extern int keyring_add_key(struct key *keyring, extern struct key *key_lookup(key_serial_t id); -static inline key_serial_t key_serial(struct key *key) +static inline key_serial_t key_serial(const struct key *key) { return key ? key->serial : 0; } -- cgit v0.10.2 From df0793abb929e66606fa25f3875ff1b89de5ad32 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 19 Jan 2012 09:20:09 +0100 Subject: block,cfq: change code order cfq_slice_expired will change saved_workload_slice. It should be called first so saved_workload_slice is correctly set to 0 after workload type is changed. This fixes the code order changed by 54b466e44b1c7. Tested-by: Tetsuo Handa Signed-off-by: Shaohua Li Signed-off-by: Jens Axboe diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index ee55019..da21c24 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3117,17 +3117,18 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, */ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { + enum wl_type_t old_type = cfqq_type(cfqd->active_queue); + cfq_log_cfqq(cfqd, cfqq, "preempt"); + cfq_slice_expired(cfqd, 1); /* * workload type is changed, don't save slice, otherwise preempt * doesn't happen */ - if (cfqq_type(cfqd->active_queue) != cfqq_type(cfqq)) + if (old_type != cfqq_type(cfqq)) cfqq->cfqg->saved_workload_slice = 0; - cfq_slice_expired(cfqd, 1); - /* * Put the new queue at the front of the of the current list, * so we know that it will be selected next. -- cgit v0.10.2 From 05c30b9551f1904d9950ad0d28e65fc4ff3c8a8e Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 19 Jan 2012 09:20:10 +0100 Subject: block: fix NULL icq_cache reference Vivek reported a kernel crash: [ 94.217015] BUG: unable to handle kernel NULL pointer dereference at 000000000000001c [ 94.218004] IP: [] kmem_cache_free+0x5e/0x200 [ 94.218004] PGD 13abda067 PUD 137d52067 PMD 0 [ 94.218004] Oops: 0000 [#1] SMP DEBUG_PAGEALLOC [ 94.218004] CPU 0 [ 94.218004] Modules linked in: [last unloaded: scsi_wait_scan] [ 94.218004] [ 94.218004] Pid: 0, comm: swapper/0 Not tainted 3.2.0+ #16 Hewlett-Packard HP xw6600 Workstation/0A9Ch [ 94.218004] RIP: 0010:[] [] kmem_cache_free+0x5e/0x200 [ 94.218004] RSP: 0018:ffff88013fc03de0 EFLAGS: 00010006 [ 94.218004] RAX: ffffffff81e0d020 RBX: ffff880138b3c680 RCX: 00000001801c001b [ 94.218004] RDX: 00000000003aac1d RSI: ffff880138b3c680 RDI: ffffffff81142fae [ 94.218004] RBP: ffff88013fc03e10 R08: ffff880137830238 R09: 0000000000000001 [ 94.218004] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 [ 94.218004] R13: ffffea0004e2cf00 R14: ffffffff812f6eb6 R15: 0000000000000246 [ 94.218004] FS: 0000000000000000(0000) GS:ffff88013fc00000(0000) knlGS:0000000000000000 [ 94.218004] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 94.218004] CR2: 000000000000001c CR3: 00000001395ab000 CR4: 00000000000006f0 [ 94.218004] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 94.218004] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 94.218004] Process swapper/0 (pid: 0, threadinfo ffffffff81e00000, task ffffffff81e0d020) [ 94.218004] Stack: [ 94.218004] 0000000000000102 ffff88013fc0db20 ffffffff81e22700 ffff880139500f00 [ 94.218004] 0000000000000001 000000000000000a ffff88013fc03e20 ffffffff812f6eb6 [ 94.218004] ffff88013fc03e90 ffffffff810c8da2 ffffffff81e01fd8 ffff880137830240 [ 94.218004] Call Trace: [ 94.218004] [ 94.218004] [] icq_free_icq_rcu+0x16/0x20 [ 94.218004] [] __rcu_process_callbacks+0x1c2/0x420 [ 94.218004] [] rcu_process_callbacks+0x38/0x250 [ 94.218004] [] __do_softirq+0xce/0x3e0 [ 94.218004] [] ? clockevents_program_event+0x74/0x100 [ 94.218004] [] ? tick_program_event+0x24/0x30 [ 94.218004] [] call_softirq+0x1c/0x30 [ 94.218004] [] do_softirq+0x8d/0xc0 [ 94.218004] [] irq_exit+0xae/0xe0 [ 94.218004] [] smp_apic_timer_interrupt+0x6e/0x99 [ 94.218004] [] apic_timer_interrupt+0x70/0x80 Once a queue is quiesced, it's not supposed to have any elvpriv data or icq's, and elevator switching depends on that. Request alloc path followed the rule for elvpriv data but forgot apply it to icq's leading to the following crash during elevator switch. Fix it by not allocating icq's if ELVPRIV is not set for the request. Reported-by: Vivek Goyal Tested-by: Vivek Goyal Signed-off-by: Shaohua Li Acked-by: Tejun Heo Signed-off-by: Jens Axboe diff --git a/block/blk-core.c b/block/blk-core.c index e6c05a9..6367025 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -872,13 +872,15 @@ retry: spin_unlock_irq(q->queue_lock); /* create icq if missing */ - if (unlikely(et->icq_cache && !icq)) + if ((rw_flags & REQ_ELVPRIV) && unlikely(et->icq_cache && !icq)) { icq = ioc_create_icq(q, gfp_mask); + if (!icq) + goto fail_icq; + } - /* rqs are guaranteed to have icq on elv_set_request() if requested */ - if (likely(!et->icq_cache || icq)) - rq = blk_alloc_request(q, icq, rw_flags, gfp_mask); + rq = blk_alloc_request(q, icq, rw_flags, gfp_mask); +fail_icq: if (unlikely(!rq)) { /* * Allocation failed presumably due to memory. Undo anything -- cgit v0.10.2 From b90bf1de7cb65e7f61798fcfbcf74ae72207b0dc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Jan 2012 11:42:55 +0100 Subject: ALSA: hda/realtek - Avoid multi-ios conflicting with multi-speakers When a machine has multiple speakers, we don't need to create the controls for multi-ios. Check the number of primary outputs beforehand. Note that this workaround might not work always with new codecs in future; this assumes that both speakers and multi-io jacks share the same mixers/DACs. If they are routed with different mixers, the individual mixer controls should be needed. But, so far, this doesn't happen with the existing ALC codecs. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5e82acf..61ccbe8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3233,7 +3233,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, int i, err, noutputs; noutputs = cfg->line_outs; - if (spec->multi_ios > 0) + if (spec->multi_ios > 0 && cfg->line_outs < 3) noutputs += spec->multi_ios; for (i = 0; i < noutputs; i++) { -- cgit v0.10.2 From f21d78e2698b6380a5387461e3b126bb2dee23aa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Jan 2012 12:10:29 +0100 Subject: ALSA: hda/realtek - Avoid conflict of unsol-events with static quirks The recently added jack-kctl support sets the unsol event tags dynamically, while static quirks usually set the fixed tags in the init_verbs array. Due to this conflict, the own unsol event handler can't retrieve the tag and handle it properly any more. For fixing this, avoid calling snd_hda_jack_add_kctls() for static quirks, and always let them use own handlers instead of the standard one for the auto-pareser. Reported-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/alc880_quirks.c b/sound/pci/hda/alc880_quirks.c index 5b68435..501501e 100644 --- a/sound/pci/hda/alc880_quirks.c +++ b/sound/pci/hda/alc880_quirks.c @@ -762,16 +762,22 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec, /* Looks like the unsol event is incompatible with the standard * definition. 4bit tag is placed at 28 bit! */ - switch (res >> 28) { + res >>= 28; + switch (res) { case ALC_MIC_EVENT: alc88x_simple_mic_automute(codec); break; default: - alc_sku_unsol_event(codec, res); + alc_exec_unsol_event(codec, res); break; } } +static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) +{ + alc_exec_unsol_event(codec, res >> 28); +} + static void alc880_uniwill_p53_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -800,10 +806,11 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, /* Looks like the unsol event is incompatible with the standard * definition. 4bit tag is placed at 28 bit! */ - if ((res >> 28) == ALC_DCVOL_EVENT) + res >>= 28; + if (res == ALC_DCVOL_EVENT) alc880_uniwill_p53_dcvol_automute(codec); else - alc_sku_unsol_event(codec, res); + alc_exec_unsol_event(codec, res); } /* @@ -1677,7 +1684,7 @@ static const struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_lg_ch_modes, .need_dac_fix = 1, .input_mux = &alc880_lg_capture_source, - .unsol_event = alc_sku_unsol_event, + .unsol_event = alc880_unsol_event, .setup = alc880_lg_setup, .init_hook = alc_hp_automute, #ifdef CONFIG_SND_HDA_POWER_SAVE diff --git a/sound/pci/hda/alc882_quirks.c b/sound/pci/hda/alc882_quirks.c index bdf0ed4..bb364a5 100644 --- a/sound/pci/hda/alc882_quirks.c +++ b/sound/pci/hda/alc882_quirks.c @@ -730,6 +730,11 @@ static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) alc889A_mb31_automute(codec); } +static void alc882_unsol_event(struct hda_codec *codec, unsigned int res) +{ + alc_exec_unsol_event(codec, res >> 26); +} + /* * configuration and preset */ @@ -775,7 +780,7 @@ static const struct alc_config_preset alc882_presets[] = { .channel_mode = alc885_mba21_ch_modes, .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), .input_mux = &alc882_capture_source, - .unsol_event = alc_sku_unsol_event, + .unsol_event = alc882_unsol_event, .setup = alc885_mba21_setup, .init_hook = alc_hp_automute, }, @@ -791,7 +796,7 @@ static const struct alc_config_preset alc882_presets[] = { .input_mux = &alc882_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, + .unsol_event = alc882_unsol_event, .setup = alc885_mbp3_setup, .init_hook = alc_hp_automute, }, @@ -806,7 +811,7 @@ static const struct alc_config_preset alc882_presets[] = { .input_mux = &mb5_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, + .unsol_event = alc882_unsol_event, .setup = alc885_mb5_setup, .init_hook = alc_hp_automute, }, @@ -821,7 +826,7 @@ static const struct alc_config_preset alc882_presets[] = { .input_mux = &macmini3_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, + .unsol_event = alc882_unsol_event, .setup = alc885_macmini3_setup, .init_hook = alc_hp_automute, }, @@ -836,7 +841,7 @@ static const struct alc_config_preset alc882_presets[] = { .input_mux = &alc889A_imac91_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_sku_unsol_event, + .unsol_event = alc882_unsol_event, .setup = alc885_imac91_setup, .init_hook = alc_hp_automute, }, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 61ccbe8..2326bf3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -621,17 +621,10 @@ static void alc_mic_automute(struct hda_codec *codec) alc_mux_select(codec, 0, spec->int_mic_idx, false); } -/* unsolicited event for HP jack sensing */ -static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) +/* handle the specified unsol action (ALC_XXX_EVENT) */ +static void alc_exec_unsol_event(struct hda_codec *codec, int action) { - struct alc_spec *spec = codec->spec; - if (codec->vendor_id == 0x10ec0880) - res >>= 28; - else - res >>= 26; - if (spec->use_jack_tbl) - res = snd_hda_jack_get_action(codec, res); - switch (res) { + switch (action) { case ALC_HP_EVENT: alc_hp_automute(codec); break; @@ -645,6 +638,19 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_jack_report_sync(codec); } +/* unsolicited event for HP jack sensing */ +static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct alc_spec *spec = codec->spec; + if (codec->vendor_id == 0x10ec0880) + res >>= 28; + else + res >>= 26; + if (spec->use_jack_tbl) + res = snd_hda_jack_get_action(codec, res); + alc_exec_unsol_event(codec, res); +} + /* call init functions of standard auto-mute helpers */ static void alc_inithook(struct hda_codec *codec) { @@ -1883,7 +1889,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = { }; #endif -static int alc_build_controls(struct hda_codec *codec) +static int __alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct snd_kcontrol *kctl = NULL; @@ -2029,11 +2035,16 @@ static int alc_build_controls(struct hda_codec *codec) alc_free_kctls(codec); /* no longer needed */ - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + return 0; +} + +static int alc_build_controls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err = __alc_build_controls(codec); if (err < 0) return err; - - return 0; + return snd_hda_jack_add_kctls(codec, &spec->autocfg); } @@ -4168,6 +4179,8 @@ static int patch_alc880(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc_auto_init_std; + else + codec->patch_ops.build_controls = __alc_build_controls; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc880_loopbacks; @@ -4297,6 +4310,8 @@ static int patch_alc260(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc_auto_init_std; + else + codec->patch_ops.build_controls = __alc_build_controls; spec->shutup = alc_eapd_shutup; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) @@ -4691,6 +4706,8 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc_auto_init_std; + else + codec->patch_ops.build_controls = __alc_build_controls; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) -- cgit v0.10.2 From 8bd92669199be1739b0430e9e96eb98de88aee42 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Jan 2012 11:48:23 +0000 Subject: Revert "ARM: sa1100: clean up of the clock support" This reverts commit edf3ff5bac2582b57de4e7c6569fee5d7c1c0a42. This revert is necessary to revert the broken "RTC: sa1100: support sa1100, pxa and mmp soc families" change. diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 24626b0..bb68e65 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -754,7 +754,7 @@ config ARCH_SA1100 select ARCH_HAS_CPUFREQ select CPU_FREQ select GENERIC_CLOCKEVENTS - select CLKDEV_LOOKUP + select HAVE_CLK select HAVE_SCHED_CLOCK select TICK_ONESHOT select ARCH_REQUIRE_GPIOLIB diff --git a/arch/arm/mach-sa1100/clock.c b/arch/arm/mach-sa1100/clock.c index d6df9f6..dab3c63 100644 --- a/arch/arm/mach-sa1100/clock.c +++ b/arch/arm/mach-sa1100/clock.c @@ -11,39 +11,17 @@ #include #include #include -#include -#include #include -struct clkops { - void (*enable)(struct clk *); - void (*disable)(struct clk *); - unsigned long (*getrate)(struct clk *); -}; - +/* + * Very simple clock implementation - we only have one clock to deal with. + */ struct clk { - const struct clkops *ops; - unsigned long rate; unsigned int enabled; }; -#define INIT_CLKREG(_clk, _devname, _conname) \ - { \ - .clk = _clk, \ - .dev_id = _devname, \ - .con_id = _conname, \ - } - -#define DEFINE_CLK(_name, _ops, _rate) \ -struct clk clk_##_name = { \ - .ops = _ops, \ - .rate = _rate, \ - } - -static DEFINE_SPINLOCK(clocks_lock); - -static void clk_gpio27_enable(struct clk *clk) +static void clk_gpio27_enable(void) { /* * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: @@ -54,22 +32,38 @@ static void clk_gpio27_enable(struct clk *clk) TUCR = TUCR_3_6864MHz; } -static void clk_gpio27_disable(struct clk *clk) +static void clk_gpio27_disable(void) { TUCR = 0; GPDR &= ~GPIO_32_768kHz; GAFR &= ~GPIO_32_768kHz; } +static struct clk clk_gpio27; + +static DEFINE_SPINLOCK(clocks_lock); + +struct clk *clk_get(struct device *dev, const char *id) +{ + const char *devname = dev_name(dev); + + return strcmp(devname, "sa1111.0") ? ERR_PTR(-ENOENT) : &clk_gpio27; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_put); + int clk_enable(struct clk *clk) { unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); if (clk->enabled++ == 0) - clk->ops->enable(clk); + clk_gpio27_enable(); spin_unlock_irqrestore(&clocks_lock, flags); - return 0; } EXPORT_SYMBOL(clk_enable); @@ -82,48 +76,13 @@ void clk_disable(struct clk *clk) spin_lock_irqsave(&clocks_lock, flags); if (--clk->enabled == 0) - clk->ops->disable(clk); + clk_gpio27_disable(); spin_unlock_irqrestore(&clocks_lock, flags); } EXPORT_SYMBOL(clk_disable); unsigned long clk_get_rate(struct clk *clk) { - unsigned long rate; - - rate = clk->rate; - if (clk->ops->getrate) - rate = clk->ops->getrate(clk); - - return rate; + return 3686400; } EXPORT_SYMBOL(clk_get_rate); - -const struct clkops clk_gpio27_ops = { - .enable = clk_gpio27_enable, - .disable = clk_gpio27_disable, -}; - -static void clk_dummy_enable(struct clk *clk) { } -static void clk_dummy_disable(struct clk *clk) { } - -const struct clkops clk_dummy_ops = { - .enable = clk_dummy_enable, - .disable = clk_dummy_disable, -}; - -static DEFINE_CLK(gpio27, &clk_gpio27_ops, 3686400); -static DEFINE_CLK(dummy, &clk_dummy_ops, 0); - -static struct clk_lookup sa11xx_clkregs[] = { - INIT_CLKREG(&clk_gpio27, "sa1111.0", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), -}; - -static int __init sa11xx_clk_init(void) -{ - clkdev_add_table(sa11xx_clkregs, ARRAY_SIZE(sa11xx_clkregs)); - return 0; -} - -postcore_initcall(sa11xx_clk_init); -- cgit v0.10.2 From a55b5adaf403c4d032e0871ad4ee3367782f4db6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Jan 2012 11:48:51 +0000 Subject: Revert "ARM: pxa: add dummy clock for sa1100-rtc" This reverts commit 7557c175f60d8d40baf16b22caf79beadef8f081. This revert is necessary to revert the broken "RTC: sa1100: support sa1100, pxa and mmp soc families" change. diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c index adf058f..91e4f6c 100644 --- a/arch/arm/mach-pxa/pxa25x.c +++ b/arch/arm/mach-pxa/pxa25x.c @@ -209,8 +209,6 @@ static struct clk_lookup pxa25x_clkregs[] = { INIT_CLKREG(&clk_pxa25x_gpio11, NULL, "GPIO11_CLK"), INIT_CLKREG(&clk_pxa25x_gpio12, NULL, "GPIO12_CLK"), INIT_CLKREG(&clk_pxa25x_mem, "pxa2xx-pcmcia", NULL), - INIT_CLKREG(&clk_dummy, "pxa-gpio", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; static struct clk_lookup pxa25x_hwuart_clkreg = diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 180bd86..aed6cbc 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -230,8 +230,6 @@ static struct clk_lookup pxa27x_clkregs[] = { INIT_CLKREG(&clk_pxa27x_im, NULL, "IMCLK"), INIT_CLKREG(&clk_pxa27x_memc, NULL, "MEMCLK"), INIT_CLKREG(&clk_pxa27x_mem, "pxa2xx-pcmcia", NULL), - INIT_CLKREG(&clk_dummy, "pxa-gpio", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; #ifdef CONFIG_PM diff --git a/arch/arm/mach-pxa/pxa300.c b/arch/arm/mach-pxa/pxa300.c index 0388eda..40bb165 100644 --- a/arch/arm/mach-pxa/pxa300.c +++ b/arch/arm/mach-pxa/pxa300.c @@ -89,7 +89,6 @@ static DEFINE_PXA3_CKEN(gcu, PXA300_GCU, 0, 0); static struct clk_lookup common_clkregs[] = { INIT_CLKREG(&clk_common_nand, "pxa3xx-nand", NULL), INIT_CLKREG(&clk_gcu, "pxa3xx-gcu", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; static DEFINE_PXA3_CKEN(pxa310_mmc3, MMC3, 19500000, 0); diff --git a/arch/arm/mach-pxa/pxa320.c b/arch/arm/mach-pxa/pxa320.c index d487e1f..8d614ec 100644 --- a/arch/arm/mach-pxa/pxa320.c +++ b/arch/arm/mach-pxa/pxa320.c @@ -83,7 +83,6 @@ static DEFINE_PXA3_CKEN(gcu, PXA320_GCU, 0, 0); static struct clk_lookup pxa320_clkregs[] = { INIT_CLKREG(&clk_pxa320_nand, "pxa3xx-nand", NULL), INIT_CLKREG(&clk_gcu, "pxa3xx-gcu", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), }; static int __init pxa320_init(void) diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index f107c71..4f402af 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -67,7 +67,6 @@ static struct clk_lookup pxa3xx_clkregs[] = { INIT_CLKREG(&clk_pxa3xx_pout, NULL, "CLK_POUT"), /* Power I2C clock is always on */ INIT_CLKREG(&clk_dummy, "pxa3xx-pwri2c.1", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), INIT_CLKREG(&clk_pxa3xx_lcd, "pxa2xx-fb", NULL), INIT_CLKREG(&clk_pxa3xx_camera, NULL, "CAMCLK"), INIT_CLKREG(&clk_pxa3xx_ac97, NULL, "AC97CLK"), diff --git a/arch/arm/mach-pxa/pxa95x.c b/arch/arm/mach-pxa/pxa95x.c index fccc644..d082a58 100644 --- a/arch/arm/mach-pxa/pxa95x.c +++ b/arch/arm/mach-pxa/pxa95x.c @@ -217,7 +217,6 @@ static struct clk_lookup pxa95x_clkregs[] = { INIT_CLKREG(&clk_pxa95x_pout, NULL, "CLK_POUT"), /* Power I2C clock is always on */ INIT_CLKREG(&clk_dummy, "pxa3xx-pwri2c.1", NULL), - INIT_CLKREG(&clk_dummy, "sa1100-rtc", NULL), INIT_CLKREG(&clk_pxa95x_lcd, "pxa2xx-fb", NULL), INIT_CLKREG(&clk_pxa95x_ffuart, "pxa2xx-uart.0", NULL), INIT_CLKREG(&clk_pxa95x_btuart, "pxa2xx-uart.1", NULL), -- cgit v0.10.2 From a7309792c4e313d4e4c30084dc0ecbc834082433 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Jan 2012 15:03:48 +0100 Subject: ALSA: hda/realtek - Remove use_jack_tbl field Now that all quirks have the own unsol handlers, we don't need to check use_jack_tbl flag any more. Let's kill it. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2326bf3..ddbed97 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -185,7 +185,6 @@ struct alc_spec { unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ - unsigned int use_jack_tbl:1; /* 1 for model=auto */ /* auto-mute control */ int automute_mode; @@ -646,8 +645,7 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) res >>= 28; else res >>= 26; - if (spec->use_jack_tbl) - res = snd_hda_jack_get_action(codec, res); + res = snd_hda_jack_get_action(codec, res); alc_exec_unsol_event(codec, res); } @@ -3915,7 +3913,6 @@ static void set_capture_mixer(struct hda_codec *codec) static void alc_auto_init_std(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->use_jack_tbl = 1; alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); alc_auto_init_analog_input(codec); -- cgit v0.10.2 From a0164a574a3f284f438081c53fb864d275e54560 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Jan 2012 11:55:21 +0000 Subject: Revert "RTC: sa1100: support sa1100, pxa and mmp soc families" This reverts commit 7cea00657dd4daef66ad95e976d5d67ed94cb97e. The sa1100 cleanups fatally broke the SA1100 RTC driver - the first hint that something is wrong are these compiler warnings: drivers/rtc/rtc-sa1100.c:42:1: warning: "RCNR" redefined In file included from arch/arm/mach-sa1100/include/mach/hardware.h:73, from drivers/rtc/rtc-sa1100.c:35: arch/arm/mach-sa1100/include/mach/SA-1100.h:877:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:43:1: warning: "RTAR" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:876:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:44:1: warning: "RTSR" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:879:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:45:1: warning: "RTTR" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:878:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:47:1: warning: "RTSR_HZE" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:891:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:48:1: warning: "RTSR_ALE" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:890:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:49:1: warning: "RTSR_HZ" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:889:1: warning: this is the location of the previous definition drivers/rtc/rtc-sa1100.c:50:1: warning: "RTSR_AL" redefined arch/arm/mach-sa1100/include/mach/SA-1100.h:888:1: warning: this is the location of the previous definition and the second problem, which is far more severe, are the different register layouts, resulting in the wrong registers being read on SA11x0 platforms. This patch adds: #define RCNR 0x00 /* RTC Count Register */ #define RTAR 0x04 /* RTC Alarm Register */ #define RTSR 0x08 /* RTC Status Register */ #define RTTR 0x0c /* RTC Timer Trim Register */ but the SA11x0 registers are: #define RTAR __REG(0x90010000) /* RTC Alarm Reg. */ #define RCNR __REG(0x90010004) /* RTC CouNt Reg. */ #define RTTR __REG(0x90010008) /* RTC Trim Reg. */ #define RTSR __REG(0x90010010) /* RTC Status Reg. */ diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c index 18fd177..5bc1312 100644 --- a/arch/arm/mach-pxa/devices.c +++ b/arch/arm/mach-pxa/devices.c @@ -415,29 +415,9 @@ static struct resource pxa_rtc_resources[] = { }, }; -static struct resource sa1100_rtc_resources[] = { - [0] = { - .start = 0x40900000, - .end = 0x409000ff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_RTC1Hz, - .end = IRQ_RTC1Hz, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_RTCAlrm, - .end = IRQ_RTCAlrm, - .flags = IORESOURCE_IRQ, - }, -}; - struct platform_device sa1100_device_rtc = { .name = "sa1100-rtc", .id = -1, - .num_resources = ARRAY_SIZE(sa1100_rtc_resources), - .resource = sa1100_rtc_resources, }; struct platform_device pxa_device_rtc = { diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index e3a28ca..a7c0df6 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -350,29 +350,9 @@ void sa11x0_register_irda(struct irda_platform_data *irda) sa11x0_register_device(&sa11x0ir_device, irda); } -static struct resource sa11x0rtc_resources[] = { - [0] = { - .start = 0x90010000, - .end = 0x900100ff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_RTC1Hz, - .end = IRQ_RTC1Hz, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_RTCAlrm, - .end = IRQ_RTCAlrm, - .flags = IORESOURCE_IRQ, - }, -}; - static struct platform_device sa11x0rtc_device = { .name = "sa1100-rtc", .id = -1, - .resource = sa11x0rtc_resources, - .num_resources = ARRAY_SIZE(sa11x0rtc_resources), }; static struct platform_device *sa11x0_devices[] __initdata = { diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e19a403..3a125b8 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -774,7 +774,7 @@ config RTC_DRV_EP93XX config RTC_DRV_SA1100 tristate "SA11x0/PXA2xx" - depends on ARCH_SA1100 || ARCH_PXA || ARCH_MMP + depends on ARCH_SA1100 || ARCH_PXA help If you say Y here you will get access to the real time clock built into your SA11x0 or PXA2xx CPU. diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 4595d3e..9683daf 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -27,42 +27,24 @@ #include #include #include +#include #include -#include -#include -#include +#include #include #include +#ifdef CONFIG_ARCH_PXA +#include +#endif + #define RTC_DEF_DIVIDER (32768 - 1) #define RTC_DEF_TRIM 0 -#define RTC_FREQ 1024 - -#define RCNR 0x00 /* RTC Count Register */ -#define RTAR 0x04 /* RTC Alarm Register */ -#define RTSR 0x08 /* RTC Status Register */ -#define RTTR 0x0c /* RTC Timer Trim Register */ - -#define RTSR_HZE (1 << 3) /* HZ interrupt enable */ -#define RTSR_ALE (1 << 2) /* RTC alarm interrupt enable */ -#define RTSR_HZ (1 << 1) /* HZ rising-edge detected */ -#define RTSR_AL (1 << 0) /* RTC alarm detected */ - -#define rtc_readl(sa1100_rtc, reg) \ - readl_relaxed((sa1100_rtc)->base + (reg)) -#define rtc_writel(sa1100_rtc, reg, value) \ - writel_relaxed((value), (sa1100_rtc)->base + (reg)) - -struct sa1100_rtc { - struct resource *ress; - void __iomem *base; - struct clk *clk; - int irq_1Hz; - int irq_Alrm; - struct rtc_device *rtc; - spinlock_t lock; /* Protects this structure */ -}; + +static const unsigned long RTC_FREQ = 1024; +static struct rtc_time rtc_alarm; +static DEFINE_SPINLOCK(sa1100_rtc_lock); + /* * Calculate the next alarm time given the requested alarm time mask * and the current time. @@ -93,23 +75,22 @@ static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) { struct platform_device *pdev = to_platform_device(dev_id); - struct sa1100_rtc *sa1100_rtc = platform_get_drvdata(pdev); + struct rtc_device *rtc = platform_get_drvdata(pdev); unsigned int rtsr; unsigned long events = 0; - spin_lock(&sa1100_rtc->lock); + spin_lock(&sa1100_rtc_lock); + rtsr = RTSR; /* clear interrupt sources */ - rtsr = rtc_readl(sa1100_rtc, RTSR); - rtc_writel(sa1100_rtc, RTSR, 0); - + RTSR = 0; /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_probe(). */ if (rtsr & (RTSR_ALE | RTSR_HZE)) { /* This is the original code, before there was the if test * above. This code does not clear interrupts that were not * enabled. */ - rtc_writel(sa1100_rtc, RTSR, (RTSR_AL | RTSR_HZ) & (rtsr >> 2)); + RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); } else { /* For some reason, it is possible to enter this routine * without interruptions enabled, it has been tested with @@ -118,13 +99,13 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) * This situation leads to an infinite "loop" of interrupt * routine calling and as a result the processor seems to * lock on its first call to open(). */ - rtc_writel(sa1100_rtc, RTSR, (RTSR_AL | RTSR_HZ)); + RTSR = RTSR_AL | RTSR_HZ; } /* clear alarm interrupt if it has occurred */ if (rtsr & RTSR_AL) rtsr &= ~RTSR_ALE; - rtc_writel(sa1100_rtc, RTSR, rtsr & (RTSR_ALE | RTSR_HZE)); + RTSR = rtsr & (RTSR_ALE | RTSR_HZE); /* update irq data & counter */ if (rtsr & RTSR_AL) @@ -132,100 +113,86 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) if (rtsr & RTSR_HZ) events |= RTC_UF | RTC_IRQF; - rtc_update_irq(sa1100_rtc->rtc, 1, events); + rtc_update_irq(rtc, 1, events); - spin_unlock(&sa1100_rtc->lock); + spin_unlock(&sa1100_rtc_lock); return IRQ_HANDLED; } static int sa1100_rtc_open(struct device *dev) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); int ret; + struct platform_device *plat_dev = to_platform_device(dev); + struct rtc_device *rtc = platform_get_drvdata(plat_dev); - ret = request_irq(sa1100_rtc->irq_1Hz, sa1100_rtc_interrupt, - IRQF_DISABLED, "rtc 1Hz", dev); + ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED, + "rtc 1Hz", dev); if (ret) { - dev_err(dev, "IRQ %d already in use.\n", sa1100_rtc->irq_1Hz); + dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz); goto fail_ui; } - ret = request_irq(sa1100_rtc->irq_Alrm, sa1100_rtc_interrupt, - IRQF_DISABLED, "rtc Alrm", dev); + ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED, + "rtc Alrm", dev); if (ret) { - dev_err(dev, "IRQ %d already in use.\n", sa1100_rtc->irq_Alrm); + dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm); goto fail_ai; } - sa1100_rtc->rtc->max_user_freq = RTC_FREQ; - rtc_irq_set_freq(sa1100_rtc->rtc, NULL, RTC_FREQ); + rtc->max_user_freq = RTC_FREQ; + rtc_irq_set_freq(rtc, NULL, RTC_FREQ); return 0; fail_ai: - free_irq(sa1100_rtc->irq_1Hz, dev); + free_irq(IRQ_RTC1Hz, dev); fail_ui: return ret; } static void sa1100_rtc_release(struct device *dev) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + spin_lock_irq(&sa1100_rtc_lock); + RTSR = 0; + spin_unlock_irq(&sa1100_rtc_lock); - spin_lock_irq(&sa1100_rtc->lock); - rtc_writel(sa1100_rtc, RTSR, 0); - spin_unlock_irq(&sa1100_rtc->lock); - - free_irq(sa1100_rtc->irq_Alrm, dev); - free_irq(sa1100_rtc->irq_1Hz, dev); + free_irq(IRQ_RTCAlrm, dev); + free_irq(IRQ_RTC1Hz, dev); } static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); - unsigned int rtsr; - - spin_lock_irq(&sa1100_rtc->lock); - - rtsr = rtc_readl(sa1100_rtc, RTSR); + spin_lock_irq(&sa1100_rtc_lock); if (enabled) - rtsr |= RTSR_ALE; + RTSR |= RTSR_ALE; else - rtsr &= ~RTSR_ALE; - rtc_writel(sa1100_rtc, RTSR, rtsr); - - spin_unlock_irq(&sa1100_rtc->lock); + RTSR &= ~RTSR_ALE; + spin_unlock_irq(&sa1100_rtc_lock); return 0; } static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); - - rtc_time_to_tm(rtc_readl(sa1100_rtc, RCNR), tm); + rtc_time_to_tm(RCNR, tm); return 0; } static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); unsigned long time; int ret; ret = rtc_tm_to_time(tm, &time); if (ret == 0) - rtc_writel(sa1100_rtc, RCNR, time); + RCNR = time; return ret; } static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); - unsigned long time; - unsigned int rtsr; + u32 rtsr; - time = rtc_readl(sa1100_rtc, RCNR); - rtc_time_to_tm(time, &alrm->time); - rtsr = rtc_readl(sa1100_rtc, RTSR); + memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); + rtsr = RTSR; alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; return 0; @@ -233,39 +200,31 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); struct rtc_time now_tm, alarm_tm; - unsigned long time, alarm; - unsigned int rtsr; - - spin_lock_irq(&sa1100_rtc->lock); + int ret; - time = rtc_readl(sa1100_rtc, RCNR); - rtc_time_to_tm(time, &now_tm); - rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time); - rtc_tm_to_time(&alarm_tm, &alarm); - rtc_writel(sa1100_rtc, RTAR, alarm); + spin_lock_irq(&sa1100_rtc_lock); - rtsr = rtc_readl(sa1100_rtc, RTSR); + now = RCNR; + rtc_time_to_tm(now, &now_tm); + rtc_next_alarm_time(&alarm_tm, &now_tm, alrm->time); + rtc_tm_to_time(&alarm_tm, &time); + RTAR = time; if (alrm->enabled) - rtsr |= RTSR_ALE; + RTSR |= RTSR_ALE; else - rtsr &= ~RTSR_ALE; - rtc_writel(sa1100_rtc, RTSR, rtsr); + RTSR &= ~RTSR_ALE; - spin_unlock_irq(&sa1100_rtc->lock); + spin_unlock_irq(&sa1100_rtc_lock); - return 0; + return ret; } static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); + seq_printf(seq, "trim/divider\t\t: 0x%08x\n", (u32) RTTR); + seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", (u32)RTSR); - seq_printf(seq, "trim/divider\t\t: 0x%08x\n", - rtc_readl(sa1100_rtc, RTTR)); - seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", - rtc_readl(sa1100_rtc, RTSR)); return 0; } @@ -282,51 +241,7 @@ static const struct rtc_class_ops sa1100_rtc_ops = { static int sa1100_rtc_probe(struct platform_device *pdev) { - struct sa1100_rtc *sa1100_rtc; - unsigned int rttr; - int ret; - - sa1100_rtc = kzalloc(sizeof(struct sa1100_rtc), GFP_KERNEL); - if (!sa1100_rtc) - return -ENOMEM; - - spin_lock_init(&sa1100_rtc->lock); - platform_set_drvdata(pdev, sa1100_rtc); - - ret = -ENXIO; - sa1100_rtc->ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!sa1100_rtc->ress) { - dev_err(&pdev->dev, "No I/O memory resource defined\n"); - goto err_ress; - } - - sa1100_rtc->irq_1Hz = platform_get_irq(pdev, 0); - if (sa1100_rtc->irq_1Hz < 0) { - dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n"); - goto err_ress; - } - sa1100_rtc->irq_Alrm = platform_get_irq(pdev, 1); - if (sa1100_rtc->irq_Alrm < 0) { - dev_err(&pdev->dev, "No alarm IRQ resource defined\n"); - goto err_ress; - } - - ret = -ENOMEM; - sa1100_rtc->base = ioremap(sa1100_rtc->ress->start, - resource_size(sa1100_rtc->ress)); - if (!sa1100_rtc->base) { - dev_err(&pdev->dev, "Unable to map pxa RTC I/O memory\n"); - goto err_map; - } - - sa1100_rtc->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(sa1100_rtc->clk)) { - dev_err(&pdev->dev, "failed to find rtc clock source\n"); - ret = PTR_ERR(sa1100_rtc->clk); - goto err_clk; - } - clk_prepare(sa1100_rtc->clk); - clk_enable(sa1100_rtc->clk); + struct rtc_device *rtc; /* * According to the manual we should be able to let RTTR be zero @@ -335,24 +250,24 @@ static int sa1100_rtc_probe(struct platform_device *pdev) * If the clock divider is uninitialized then reset it to the * default value to get the 1Hz clock. */ - if (rtc_readl(sa1100_rtc, RTTR) == 0) { - rttr = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); - rtc_writel(sa1100_rtc, RTTR, rttr); - dev_warn(&pdev->dev, "warning: initializing default clock" - " divider/trim value\n"); + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + dev_warn(&pdev->dev, "warning: " + "initializing default clock divider/trim value\n"); /* The current RTC value probably doesn't make sense either */ - rtc_writel(sa1100_rtc, RCNR, 0); + RCNR = 0; } device_init_wakeup(&pdev->dev, 1); - sa1100_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, - &sa1100_rtc_ops, THIS_MODULE); - if (IS_ERR(sa1100_rtc->rtc)) { - dev_err(&pdev->dev, "Failed to register RTC device -> %d\n", - ret); - goto err_rtc_reg; - } + rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(pdev, rtc); + /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_interrupt(). * @@ -375,46 +290,33 @@ static int sa1100_rtc_probe(struct platform_device *pdev) * * Notice that clearing bit 1 and 0 is accomplished by writting ONES to * the corresponding bits in RTSR. */ - rtc_writel(sa1100_rtc, RTSR, (RTSR_AL | RTSR_HZ)); + RTSR = RTSR_AL | RTSR_HZ; return 0; - -err_rtc_reg: -err_clk: - iounmap(sa1100_rtc->base); -err_ress: -err_map: - kfree(sa1100_rtc); - return ret; } static int sa1100_rtc_remove(struct platform_device *pdev) { - struct sa1100_rtc *sa1100_rtc = platform_get_drvdata(pdev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + + if (rtc) + rtc_device_unregister(rtc); - rtc_device_unregister(sa1100_rtc->rtc); - clk_disable(sa1100_rtc->clk); - clk_unprepare(sa1100_rtc->clk); - iounmap(sa1100_rtc->base); return 0; } #ifdef CONFIG_PM static int sa1100_rtc_suspend(struct device *dev) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) - enable_irq_wake(sa1100_rtc->irq_Alrm); + enable_irq_wake(IRQ_RTCAlrm); return 0; } static int sa1100_rtc_resume(struct device *dev) { - struct sa1100_rtc *sa1100_rtc = dev_get_drvdata(dev); - if (device_may_wakeup(dev)) - disable_irq_wake(sa1100_rtc->irq_Alrm); + disable_irq_wake(IRQ_RTCAlrm); return 0; } -- cgit v0.10.2 From 57270fcdc7925def3c80d75344467dff2bec8025 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Jan 2012 11:50:40 +0000 Subject: Revert "RTC: sa1100: remove redundant code of setting alarm" This reverts commit 42874759d7494648e42e6e0465fc9c4f3752bba4. This wasn't tested as a stand-alone patch, and it has build errors without the following patches applied: drivers/rtc/rtc-sa1100.c: In function 'sa1100_rtc_set_alarm': drivers/rtc/rtc-sa1100.c:208: error: 'now' undeclared (first use in this function) drivers/rtc/rtc-sa1100.c:208: error: (Each undeclared identifier is reported only once drivers/rtc/rtc-sa1100.c:208: error: for each function it appears in.) drivers/rtc/rtc-sa1100.c:210: error: incompatible type for argument 3 of 'rtc_next_alarm_time' drivers/rtc/rtc-sa1100.c:211: error: 'time' undeclared (first use in this function) So it too gets reverted to bring us back to a working point. diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 9683daf..cb9a585 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -45,6 +45,16 @@ static const unsigned long RTC_FREQ = 1024; static struct rtc_time rtc_alarm; static DEFINE_SPINLOCK(sa1100_rtc_lock); +static inline int rtc_periodic_alarm(struct rtc_time *tm) +{ + return (tm->tm_year == -1) || + ((unsigned)tm->tm_mon >= 12) || + ((unsigned)(tm->tm_mday - 1) >= 31) || + ((unsigned)tm->tm_hour > 23) || + ((unsigned)tm->tm_min > 59) || + ((unsigned)tm->tm_sec > 59); +} + /* * Calculate the next alarm time given the requested alarm time mask * and the current time. @@ -72,6 +82,27 @@ static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, } } +static int rtc_update_alarm(struct rtc_time *alrm) +{ + struct rtc_time alarm_tm, now_tm; + unsigned long now, time; + int ret; + + do { + now = RCNR; + rtc_time_to_tm(now, &now_tm); + rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); + ret = rtc_tm_to_time(&alarm_tm, &time); + if (ret != 0) + break; + + RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); + RTAR = time; + } while (now != RCNR); + + return ret; +} + static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) { struct platform_device *pdev = to_platform_device(dev_id); @@ -115,6 +146,9 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) rtc_update_irq(rtc, 1, events); + if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) + rtc_update_alarm(&rtc_alarm); + spin_unlock(&sa1100_rtc_lock); return IRQ_HANDLED; @@ -200,21 +234,16 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - struct rtc_time now_tm, alarm_tm; int ret; spin_lock_irq(&sa1100_rtc_lock); - - now = RCNR; - rtc_time_to_tm(now, &now_tm); - rtc_next_alarm_time(&alarm_tm, &now_tm, alrm->time); - rtc_tm_to_time(&alarm_tm, &time); - RTAR = time; - if (alrm->enabled) - RTSR |= RTSR_ALE; - else - RTSR &= ~RTSR_ALE; - + ret = rtc_update_alarm(&alrm->time); + if (ret == 0) { + if (alrm->enabled) + RTSR |= RTSR_ALE; + else + RTSR &= ~RTSR_ALE; + } spin_unlock_irq(&sa1100_rtc_lock); return ret; -- cgit v0.10.2 From 5f76559a7736d049705400b5546d524485d5ed0d Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 18 Jan 2012 23:46:28 +0000 Subject: ARM: sa11x0: fix collie build error f408c985cefc (GPIO: sa1100: implement proper gpiolib gpio_to_irq conversion) made gpio_to_irq() a function. This breaks collie where it's used to initialize some static data. Fix that by moving the initialization to the init code. arch/arm/mach-sa1100/collie.c:139: error: initializer element is not constant arch/arm/mach-sa1100/collie.c:139: error: (near initialization for 'collie_power_resource[0].start') arch/arm/mach-sa1100/collie.c:140: error: initializer element is not constant arch/arm/mach-sa1100/collie.c:140: error: (near initialization for 'collie_power_resource[0].end') Signed-off-by: Russell King diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index c483912..cce8763 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -144,8 +144,6 @@ static struct pda_power_pdata collie_power_data = { static struct resource collie_power_resource[] = { { .name = "ac", - .start = gpio_to_irq(COLLIE_GPIO_AC_IN), - .end = gpio_to_irq(COLLIE_GPIO_AC_IN), .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, @@ -347,7 +345,8 @@ static void __init collie_init(void) GPSR |= _COLLIE_GPIO_UCB1x00_RESET; - + collie_power_resource[0].start = gpio_to_irq(COLLIE_GPIO_AC_IN); + collie_power_resource[0].end = gpio_to_irq(COLLIE_GPIO_AC_IN); platform_scoop_config = &collie_pcmcia_config; ret = platform_add_devices(devices, ARRAY_SIZE(devices)); -- cgit v0.10.2 From 7a28b5a25f212b5f17cc0c973d1b8baa16069dd5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 18 Jan 2012 23:45:55 +0000 Subject: ARM: sa11x0: fix section mismatch in cpu-sa1100.c WARNING: arch/arm/mach-sa1100/built-in.o(.data+0x11b8): Section mismatch in reference from the variable sa1100_driver to the function .init.text:sa1100_cpu_init() The variable sa1100_driver references the function __init sa1100_cpu_init() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console Signed-off-by: Russell King diff --git a/arch/arm/mach-sa1100/cpu-sa1100.c b/arch/arm/mach-sa1100/cpu-sa1100.c index aaa8acf..19b2053 100644 --- a/arch/arm/mach-sa1100/cpu-sa1100.c +++ b/arch/arm/mach-sa1100/cpu-sa1100.c @@ -228,7 +228,7 @@ static int __init sa1100_cpu_init(struct cpufreq_policy *policy) return 0; } -static struct cpufreq_driver sa1100_driver = { +static struct cpufreq_driver sa1100_driver __refdata = { .flags = CPUFREQ_STICKY, .verify = sa11x0_verify_speed, .target = sa1100_target, -- cgit v0.10.2 From bc2827d08cb31de5ab3a467a3e1572d8437340e6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Jan 2012 14:35:19 +0000 Subject: ARM: fix a section mismatch warning with our use of memblock Commit 716a3dc2008 (ARM: Add arm_memblock_steal() to allocate memory away from the kernel) added a function which calls memblock_alloc(). This causes a section conflict: WARNING: vmlinux.o(.text+0xc614): Section mismatch in reference from the function arm_memblock_steal() to the function .init.text:memblock_alloc() The function arm_memblock_steal() references the function __init memblock_alloc(). Signed-off-by: Russell King diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 6ec1226..5dc7d12 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -310,7 +310,7 @@ static void arm_memory_present(void) static bool arm_memblock_steal_permitted = true; -phys_addr_t arm_memblock_steal(phys_addr_t size, phys_addr_t align) +phys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align) { phys_addr_t phys; -- cgit v0.10.2 From 94ae0275d7d6cae84b3af11f9e3d88f529528ac7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 18 Jan 2012 19:40:13 +0000 Subject: ARM: vexpress: fix two section mismatch warnings WARNING: vmlinux.o(.text+0x1bc9c): Section mismatch in reference from the function ct_ca9x4_init_cpu_map() to the function .init.text:scu_get_core_count() The function ct_ca9x4_init_cpu_map() references the function __init scu_get_core_count(). WARNING: vmlinux.o(.text+0x1bce8): Section mismatch in reference from the function ct_ca9x4_init_cpu_map() to the function .init.text:set_smp_cross_call() The function ct_ca9x4_init_cpu_map() references the function __init set_smp_cross_call(). Signed-off-by: Russell King diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index 2b1e836..b1e87c1 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -217,7 +217,7 @@ static void __init ct_ca9x4_init(void) } #ifdef CONFIG_SMP -static void ct_ca9x4_init_cpu_map(void) +static void __init ct_ca9x4_init_cpu_map(void) { int i, ncores = scu_get_core_count(MMIO_P2V(A9_MPCORE_SCU)); @@ -233,7 +233,7 @@ static void ct_ca9x4_init_cpu_map(void) set_smp_cross_call(gic_raise_softirq); } -static void ct_ca9x4_smp_enable(unsigned int max_cpus) +static void __init ct_ca9x4_smp_enable(unsigned int max_cpus) { scu_enable(MMIO_P2V(A9_MPCORE_SCU)); } -- cgit v0.10.2 From 7deabca0acfe02b8e18f59a4c95676012f49a304 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Jan 2012 15:20:58 +0000 Subject: ARM: fix rcu stalls on SMP platforms We can stall RCU processing on SMP platforms if a CPU sits in its idle loop for a long time. This happens because we don't call irq_enter() and irq_exit() around generic_smp_call_function_interrupt() and friends. Add the necessary calls, and remove the one from within ipi_timer(), so that they're all in a common place. Signed-off-by: Russell King diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 57db122..26cdc49 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -443,9 +443,7 @@ static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent); static void ipi_timer(void) { struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent); - irq_enter(); evt->event_handler(evt); - irq_exit(); } #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST @@ -548,7 +546,9 @@ void handle_IPI(int ipinr, struct pt_regs *regs) switch (ipinr) { case IPI_TIMER: + irq_enter(); ipi_timer(); + irq_exit(); break; case IPI_RESCHEDULE: @@ -556,15 +556,21 @@ void handle_IPI(int ipinr, struct pt_regs *regs) break; case IPI_CALL_FUNC: + irq_enter(); generic_smp_call_function_interrupt(); + irq_exit(); break; case IPI_CALL_FUNC_SINGLE: + irq_enter(); generic_smp_call_function_single_interrupt(); + irq_exit(); break; case IPI_CPU_STOP: + irq_enter(); ipi_cpu_stop(cpu); + irq_exit(); break; default: -- cgit v0.10.2 From a36d8e5bc27316163c9d753af5966ee92ecbec59 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 18 Jan 2012 01:57:21 +0100 Subject: ARM: 7279/1: standardize /proc/iomem "Kernel code" name All other ports use "Kernel code" to identify the Kernel text segment in /proc/iomem. Change the ARM resources to do the same. Signed-off-by: Kees Cook Acked-by: Nicolas Pitre Signed-off-by: Russell King diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 129fbd5..95653d0 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -160,7 +160,7 @@ static struct resource mem_res[] = { .flags = IORESOURCE_MEM }, { - .name = "Kernel text", + .name = "Kernel code", .start = 0, .end = 0, .flags = IORESOURCE_MEM -- cgit v0.10.2 From 06e9905152cd124c53f571296e9904ea89c1a39a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 13 Jan 2012 17:06:59 +0100 Subject: ARM: 7277/1: setup.c: Fix build warning by removing unneeded header file Fix the following build warning: CC arch/arm/kernel/setup.o In file included from arch/arm/kernel/setup.c:39: arch/arm/include/asm/elf.h:102:1: warning: "vmcore_elf64_check_arch" redefined In file included from arch/arm/kernel/setup.c:24: include/linux/crash_dump.h:30:1: warning: this is the location of the previous definition Since commit 93a72052 (crash_dump: export is_kdump_kernel to modules, consolidate elfcorehdr_addr, setup_elfcorehdr and saved_max_pfn) the inclusion of is no longer needed. Remove the inclusion of and the build warning is fixed. Signed-off-by: Fabio Estevam Signed-off-by: Russell King diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 95653d0..ab70c91 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include -- cgit v0.10.2 From c50b52a0c7ec8522983f5021c0b0952b4d678adf Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 18 Jan 2012 22:13:26 +0000 Subject: igb: make local functions static Sparse caught two functions that were only being used in one file. Signed-off-by: Stephen Hemminger Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 73aac08..36450d3 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -151,7 +151,7 @@ void igb_clear_vfta_i350(struct e1000_hw *hw) * Writes value at the given offset in the register array which stores * the VLAN filter table. **/ -void igb_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value) +static void igb_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value) { int i; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 01e5e89..92fd642 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -4003,8 +4003,8 @@ set_itr_now: } } -void igb_tx_ctxtdesc(struct igb_ring *tx_ring, u32 vlan_macip_lens, - u32 type_tucmd, u32 mss_l4len_idx) +static void igb_tx_ctxtdesc(struct igb_ring *tx_ring, u32 vlan_macip_lens, + u32 type_tucmd, u32 mss_l4len_idx) { struct e1000_adv_tx_context_desc *context_desc; u16 i = tx_ring->next_to_use; @@ -5623,7 +5623,7 @@ static irqreturn_t igb_intr(int irq, void *data) return IRQ_HANDLED; } -void igb_ring_irq_enable(struct igb_q_vector *q_vector) +static void igb_ring_irq_enable(struct igb_q_vector *q_vector) { struct igb_adapter *adapter = q_vector->adapter; struct e1000_hw *hw = &adapter->hw; -- cgit v0.10.2 From 6e861326b1d78bb439c0724864a6ca83ec23d289 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Wed, 18 Jan 2012 22:13:27 +0000 Subject: igb: Update Copyright on all Intel copyrighted files. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile index c6e4621..6565c46 100644 --- a/drivers/net/ethernet/intel/igb/Makefile +++ b/drivers/net/ethernet/intel/igb/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel 82575 PCI-Express Ethernet Linux driver -# Copyright(c) 1999 - 2011 Intel Corporation. +# Copyright(c) 1999 - 2012 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index b8e20f0..08bdc33 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h index 08a757e..b927d79 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.h +++ b/drivers/net/ethernet/intel/igb/e1000_82575.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index f5fc572..aed2174 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 4519a13..f67cbd3 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 36450d3..f57338a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.h b/drivers/net/ethernet/intel/igb/e1000_mac.h index e45996b..cbddc4e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.h +++ b/drivers/net/ethernet/intel/igb/e1000_mac.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c index 469d95e..5988b89 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.c +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index eddb0f8..dbcfa3d 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c index 4040712..fa2c6ba 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.h b/drivers/net/ethernet/intel/igb/e1000_nvm.h index a2a7ca9..825b022 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.h +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2011 Intel Corporation. + Copyright(c) 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index b17d7c2..789de5b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index 8510797..4c32ac6 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h index 0a860bc..ccdf36d 100644 --- a/drivers/net/ethernet/intel/igb/e1000_regs.h +++ b/drivers/net/ethernet/intel/igb/e1000_regs.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 3d12e67..8e33bdd 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 7998bf4..aa399a8 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 92fd642..e91d73c 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) Gigabit Ethernet Linux driver - Copyright(c) 2007-2011 Intel Corporation. + Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -68,7 +68,7 @@ char igb_driver_name[] = "igb"; char igb_driver_version[] = DRV_VERSION; static const char igb_driver_string[] = "Intel(R) Gigabit Ethernet Network Driver"; -static const char igb_copyright[] = "Copyright (c) 2007-2011 Intel Corporation."; +static const char igb_copyright[] = "Copyright (c) 2007-2012 Intel Corporation."; static const struct e1000_info *igb_info_tbl[] = { [board_82575] = &e1000_82575_info, -- cgit v0.10.2 From b2f0f6bb874df645766930e0f023a208d93c12cd Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 18 Jan 2012 22:13:28 +0000 Subject: igbvf: remove unneeded cast The cast and comment are unnecessary in the current upstream kernel. Signed-off-by: Stephen Hemminger Tested-by: Garrett, RobertX E Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 7b600a1..2dba534 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -468,6 +468,5 @@ static const struct ethtool_ops igbvf_ethtool_ops = { void igbvf_set_ethtool_ops(struct net_device *netdev) { - /* have to "undeclare" const on this struct to remove warnings */ - SET_ETHTOOL_OPS(netdev, (struct ethtool_ops *)&igbvf_ethtool_ops); + SET_ETHTOOL_OPS(netdev, &igbvf_ethtool_ops); } -- cgit v0.10.2 From 56b68960ce996db7947440179cc153652a504e9a Mon Sep 17 00:00:00 2001 From: Mitch A Williams Date: Wed, 18 Jan 2012 22:13:29 +0000 Subject: igbvf: Remove unnecessary irq disable/enable This irq disable/enable pair used to wrap access to the driver's vlgrp struct, which is no longer present. So, then, this could also so no longer be present. Signed-off-by: Mitch Williams Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index fd3da30..a4b20c8 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1194,11 +1194,6 @@ static int igbvf_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - igbvf_irq_disable(adapter); - - if (!test_bit(__IGBVF_DOWN, &adapter->state)) - igbvf_irq_enable(adapter); - if (hw->mac.ops.set_vfta(hw, vid, false)) { dev_err(&adapter->pdev->dev, "Failed to remove vlan id %d\n", vid); -- cgit v0.10.2 From 795be954dcc4a72329c8591aafe5eadb03b264af Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 18 Jan 2012 22:13:30 +0000 Subject: ixgbe: Fix register defines to correctly handle complex expressions This patch is meant to address possible issues with the IXGBE register defines generating incorrect values when given a complex expression for the register offset. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 802bfa0..775602e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -161,19 +161,19 @@ /* Receive DMA Registers */ #define IXGBE_RDBAL(_i) (((_i) < 64) ? (0x01000 + ((_i) * 0x40)) : \ - (0x0D000 + ((_i - 64) * 0x40))) + (0x0D000 + (((_i) - 64) * 0x40))) #define IXGBE_RDBAH(_i) (((_i) < 64) ? (0x01004 + ((_i) * 0x40)) : \ - (0x0D004 + ((_i - 64) * 0x40))) + (0x0D004 + (((_i) - 64) * 0x40))) #define IXGBE_RDLEN(_i) (((_i) < 64) ? (0x01008 + ((_i) * 0x40)) : \ - (0x0D008 + ((_i - 64) * 0x40))) + (0x0D008 + (((_i) - 64) * 0x40))) #define IXGBE_RDH(_i) (((_i) < 64) ? (0x01010 + ((_i) * 0x40)) : \ - (0x0D010 + ((_i - 64) * 0x40))) + (0x0D010 + (((_i) - 64) * 0x40))) #define IXGBE_RDT(_i) (((_i) < 64) ? (0x01018 + ((_i) * 0x40)) : \ - (0x0D018 + ((_i - 64) * 0x40))) + (0x0D018 + (((_i) - 64) * 0x40))) #define IXGBE_RXDCTL(_i) (((_i) < 64) ? (0x01028 + ((_i) * 0x40)) : \ - (0x0D028 + ((_i - 64) * 0x40))) + (0x0D028 + (((_i) - 64) * 0x40))) #define IXGBE_RSCCTL(_i) (((_i) < 64) ? (0x0102C + ((_i) * 0x40)) : \ - (0x0D02C + ((_i - 64) * 0x40))) + (0x0D02C + (((_i) - 64) * 0x40))) #define IXGBE_RSCDBU 0x03028 #define IXGBE_RDDCC 0x02F20 #define IXGBE_RXMEMWRAP 0x03190 @@ -186,7 +186,7 @@ */ #define IXGBE_SRRCTL(_i) (((_i) <= 15) ? (0x02100 + ((_i) * 4)) : \ (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \ - (0x0D014 + ((_i - 64) * 0x40)))) + (0x0D014 + (((_i) - 64) * 0x40)))) /* * Rx DCA Control Register: * 00-15 : 0x02200 + n*4 @@ -195,7 +195,7 @@ */ #define IXGBE_DCA_RXCTRL(_i) (((_i) <= 15) ? (0x02200 + ((_i) * 4)) : \ (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \ - (0x0D00C + ((_i - 64) * 0x40)))) + (0x0D00C + (((_i) - 64) * 0x40)))) #define IXGBE_RDRXCTL 0x02F00 #define IXGBE_RXPBSIZE(_i) (0x03C00 + ((_i) * 4)) /* 8 of these 0x03C00 - 0x03C1C */ @@ -344,9 +344,9 @@ #define IXGBE_WUPL 0x05900 #define IXGBE_WUPM 0x05A00 /* wake up pkt memory 0x5A00-0x5A7C */ -#define IXGBE_FHFT(_n) (0x09000 + (_n * 0x100)) /* Flex host filter table */ -#define IXGBE_FHFT_EXT(_n) (0x09800 + (_n * 0x100)) /* Ext Flexible Host - * Filter Table */ +#define IXGBE_FHFT(_n) (0x09000 + ((_n) * 0x100)) /* Flex host filter table */ +#define IXGBE_FHFT_EXT(_n) (0x09800 + ((_n) * 0x100)) /* Ext Flexible Host + * Filter Table */ #define IXGBE_FLEXIBLE_FILTER_COUNT_MAX 4 #define IXGBE_EXT_FLEXIBLE_FILTER_COUNT_MAX 2 @@ -1485,7 +1485,7 @@ enum { #define IXGBE_LED_BLINK_BASE 0x00000080 #define IXGBE_LED_MODE_MASK_BASE 0x0000000F #define IXGBE_LED_OFFSET(_base, _i) (_base << (8 * (_i))) -#define IXGBE_LED_MODE_SHIFT(_i) (8*(_i)) +#define IXGBE_LED_MODE_SHIFT(_i) (8 * (_i)) #define IXGBE_LED_IVRT(_i) IXGBE_LED_OFFSET(IXGBE_LED_IVRT_BASE, _i) #define IXGBE_LED_BLINK(_i) IXGBE_LED_OFFSET(IXGBE_LED_BLINK_BASE, _i) #define IXGBE_LED_MODE_MASK(_i) IXGBE_LED_OFFSET(IXGBE_LED_MODE_MASK_BASE, _i) @@ -2068,9 +2068,9 @@ enum { /* SR-IOV specific macros */ #define IXGBE_MBVFICR_INDEX(vf_number) (vf_number >> 4) -#define IXGBE_MBVFICR(_i) (0x00710 + (_i * 4)) -#define IXGBE_VFLRE(_i) (((_i & 1) ? 0x001C0 : 0x00600)) -#define IXGBE_VFLREC(_i) (0x00700 + (_i * 4)) +#define IXGBE_MBVFICR(_i) (0x00710 + ((_i) * 4)) +#define IXGBE_VFLRE(_i) ((((_i) & 1) ? 0x001C0 : 0x00600)) +#define IXGBE_VFLREC(_i) (0x00700 + ((_i) * 4)) enum ixgbe_fdir_pballoc_type { IXGBE_FDIR_PBALLOC_NONE = 0, -- cgit v0.10.2 From 375b27cf5db963e2bc2a34dc5643d0d7ceca1ee2 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Wed, 18 Jan 2012 22:13:31 +0000 Subject: ixgbevf: Prevent possible race condition by checking for message The mailbox interrupt routine might cause a race condition sometimes and cause a message to be missed. Signed-off-by: Greg Rose Tested-by: Sibai Li Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 891162d..1cad3b6 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -917,31 +917,34 @@ static irqreturn_t ixgbevf_msix_mbx(int irq, void *data) struct ixgbe_hw *hw = &adapter->hw; u32 eicr; u32 msg; + bool got_ack = false; eicr = IXGBE_READ_REG(hw, IXGBE_VTEICS); IXGBE_WRITE_REG(hw, IXGBE_VTEICR, eicr); - if (!hw->mbx.ops.check_for_ack(hw)) { - /* - * checking for the ack clears the PFACK bit. Place - * it back in the v2p_mailbox cache so that anyone - * polling for an ack will not miss it. Also - * avoid the read below because the code to read - * the mailbox will also clear the ack bit. This was - * causing lost acks. Just cache the bit and exit - * the IRQ handler. - */ - hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFACK; - goto out; - } + if (!hw->mbx.ops.check_for_ack(hw)) + got_ack = true; - /* Not an ack interrupt, go ahead and read the message */ - hw->mbx.ops.read(hw, &msg, 1); + if (!hw->mbx.ops.check_for_msg(hw)) { + hw->mbx.ops.read(hw, &msg, 1); - if ((msg & IXGBE_MBVFICR_VFREQ_MASK) == IXGBE_PF_CONTROL_MSG) - mod_timer(&adapter->watchdog_timer, - round_jiffies(jiffies + 1)); + if ((msg & IXGBE_MBVFICR_VFREQ_MASK) == IXGBE_PF_CONTROL_MSG) + mod_timer(&adapter->watchdog_timer, + round_jiffies(jiffies + 1)); + if (msg & IXGBE_VT_MSGTYPE_NACK) + pr_warn("Last Request of type %2.2x to PF Nacked\n", + msg & 0xFF); + goto out; + } + + /* + * checking for the ack clears the PFACK bit. Place + * it back in the v2p_mailbox cache so that anyone + * polling for an ack will not miss it + */ + if (got_ack) + hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFACK; out: return IRQ_HANDLED; } -- cgit v0.10.2 From b47aca135d6df5f676c768368baf3c099f054fcd Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 18 Jan 2012 22:13:32 +0000 Subject: ixgbevf: make ethtool ops and strings const Signed-off-by: Stephen Hemminger Tested-by: Sibai Li Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index dc8e651..c857003 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -56,7 +56,8 @@ struct ixgbe_stats { offsetof(struct ixgbevf_adapter, m), \ offsetof(struct ixgbevf_adapter, b), \ offsetof(struct ixgbevf_adapter, r) -static struct ixgbe_stats ixgbe_gstrings_stats[] = { + +static const struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_packets", IXGBEVF_STAT(stats.vfgprc, stats.base_vfgprc, stats.saved_reset_vfgprc)}, {"tx_packets", IXGBEVF_STAT(stats.vfgptc, stats.base_vfgptc, @@ -671,7 +672,7 @@ static int ixgbevf_nway_reset(struct net_device *netdev) return 0; } -static struct ethtool_ops ixgbevf_ethtool_ops = { +static const struct ethtool_ops ixgbevf_ethtool_ops = { .get_settings = ixgbevf_get_settings, .get_drvinfo = ixgbevf_get_drvinfo, .get_regs_len = ixgbevf_get_regs_len, -- cgit v0.10.2 From b5417bf8e8952401bca69ded67c30ead484af823 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 18 Jan 2012 22:13:33 +0000 Subject: ixgbevf: fix sparse warnings Fixes sparse warnings: drivers/net/ethernet/intel/ixgbevf/vf.c:418:21: warning: symbol 'ixgbevf_82599_vf_info' was not declared. Should it be static? drivers/net/ethernet/intel/ixgbevf/vf.c:423:21: warning: symbol 'ixgbevf_X540_vf_info' was not declared. Should it be static? drivers/net/ethernet/intel/ixgbevf/mbx.c:331:29: warning: symbol 'ixgbevf_mbx_ops' was not declared. Should it be static? Signed-off-by: Stephen Hemminger Tested-by: Sibai Li Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index e6c9d1a..74b22ee 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -281,7 +281,7 @@ enum ixgbevf_boards { extern struct ixgbevf_info ixgbevf_82599_vf_info; extern struct ixgbevf_info ixgbevf_X540_vf_info; -extern struct ixgbe_mbx_operations ixgbevf_mbx_ops; +extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops; /* needed by ethtool.c */ extern char ixgbevf_driver_name[]; diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.c b/drivers/net/ethernet/intel/ixgbevf/mbx.c index 930fa83..13532d9 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.c +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.c @@ -26,6 +26,7 @@ *******************************************************************************/ #include "mbx.h" +#include "ixgbevf.h" /** * ixgbevf_poll_for_msg - Wait for message notification @@ -328,7 +329,7 @@ static s32 ixgbevf_init_mbx_params_vf(struct ixgbe_hw *hw) return 0; } -struct ixgbe_mbx_operations ixgbevf_mbx_ops = { +const struct ixgbe_mbx_operations ixgbevf_mbx_ops = { .init_params = ixgbevf_init_mbx_params_vf, .read = ixgbevf_read_mbx_vf, .write = ixgbevf_write_mbx_vf, diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 21533e3..5c8c23f 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -26,6 +26,7 @@ *******************************************************************************/ #include "vf.h" +#include "ixgbevf.h" /** * ixgbevf_start_hw_vf - Prepare hardware for Tx/Rx -- cgit v0.10.2 From 3d8fe98f8d5cb303d907d8f94ea6dc1f9a1d8b7a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 18 Jan 2012 22:13:34 +0000 Subject: ixgbevf: make operations tables const The arrays of function pointers should be const to make life harder for rootkits. Signed-off-by: Stephen Hemminger Tested-by: Sibai Li Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index 74b22ee..9075c1d 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -279,12 +279,12 @@ enum ixgbevf_boards { board_X540_vf, }; -extern struct ixgbevf_info ixgbevf_82599_vf_info; -extern struct ixgbevf_info ixgbevf_X540_vf_info; +extern const struct ixgbevf_info ixgbevf_82599_vf_info; +extern const struct ixgbevf_info ixgbevf_X540_vf_info; extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops; /* needed by ethtool.c */ -extern char ixgbevf_driver_name[]; +extern const char ixgbevf_driver_name[]; extern const char ixgbevf_driver_version[]; extern int ixgbevf_up(struct ixgbevf_adapter *adapter); diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 1cad3b6..bed411b 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -53,7 +53,7 @@ #include "ixgbevf.h" -char ixgbevf_driver_name[] = "ixgbevf"; +const char ixgbevf_driver_name[] = "ixgbevf"; static const char ixgbevf_driver_string[] = "Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver"; diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 5c8c23f..d0138d7 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -402,7 +402,7 @@ static s32 ixgbevf_check_mac_link_vf(struct ixgbe_hw *hw, return 0; } -static struct ixgbe_mac_operations ixgbevf_mac_ops = { +static const struct ixgbe_mac_operations ixgbevf_mac_ops = { .init_hw = ixgbevf_init_hw_vf, .reset_hw = ixgbevf_reset_hw_vf, .start_hw = ixgbevf_start_hw_vf, @@ -416,12 +416,12 @@ static struct ixgbe_mac_operations ixgbevf_mac_ops = { .set_vfta = ixgbevf_set_vfta_vf, }; -struct ixgbevf_info ixgbevf_82599_vf_info = { +const struct ixgbevf_info ixgbevf_82599_vf_info = { .mac = ixgbe_mac_82599_vf, .mac_ops = &ixgbevf_mac_ops, }; -struct ixgbevf_info ixgbevf_X540_vf_info = { +const struct ixgbevf_info ixgbevf_X540_vf_info = { .mac = ixgbe_mac_X540_vf, .mac_ops = &ixgbevf_mac_ops, }; diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h index 10306b4..d556619 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -167,7 +167,7 @@ struct ixgbevf_hw_stats { struct ixgbevf_info { enum ixgbe_mac_type mac; - struct ixgbe_mac_operations *mac_ops; + const struct ixgbe_mac_operations *mac_ops; }; #endif /* __IXGBE_VF_H__ */ -- cgit v0.10.2 From 1aab323ea5cd67d2d2572a1f2794978583ff8545 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Jan 2012 13:19:42 -0500 Subject: qnx4: di_fname is an array, for crying out loud... (struct qnx4_inode_entry *)(bh->b_data + some_offset)->di_fname is not going to be NULL, TYVM... Signed-off-by: Al Viro diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 2bfd987..63e0f74 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -194,20 +194,18 @@ static const char *qnx4_checkroot(struct super_block *sb) } for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); - if (rootdir->di_fname != NULL) { - QNX4DEBUG((KERN_INFO "rootdir entry found : [%s]\n", rootdir->di_fname)); - if (!strcmp(rootdir->di_fname, - QNX4_BMNAME)) { - found = 1; - qnx4_sb(sb)->BitMap = kmemdup(rootdir, - sizeof(struct qnx4_inode_entry), - GFP_KERNEL); - if (!qnx4_sb(sb)->BitMap) { - brelse (bh); - return "not enough memory for bitmap inode"; - }/* keep bitmap inode known */ - break; - } + QNX4DEBUG((KERN_INFO "rootdir entry found : [%s]\n", rootdir->di_fname)); + if (!strcmp(rootdir->di_fname, + QNX4_BMNAME)) { + found = 1; + qnx4_sb(sb)->BitMap = kmemdup(rootdir, + sizeof(struct qnx4_inode_entry), + GFP_KERNEL); + if (!qnx4_sb(sb)->BitMap) { + brelse (bh); + return "not enough memory for bitmap inode"; + }/* keep bitmap inode known */ + break; } } brelse(bh); -- cgit v0.10.2 From 4134bf81ffd962f4de9bbeca55130d2238bd3698 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Jan 2012 13:40:57 -0500 Subject: qnx4: reduce the insane nesting in qnx4_checkroot() Signed-off-by: Al Viro diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 63e0f74..3fd121c 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -179,45 +179,33 @@ static const char *qnx4_checkroot(struct super_block *sb) struct qnx4_inode_entry *rootdir; int rd, rl; int i, j; - int found = 0; - if (*(qnx4_sb(sb)->sb->RootDir.di_fname) != '/') { + if (*(qnx4_sb(sb)->sb->RootDir.di_fname) != '/') return "no qnx4 filesystem (no root dir)."; - } else { - QNX4DEBUG((KERN_NOTICE "QNX4 filesystem found on dev %s.\n", sb->s_id)); - rd = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_blk) - 1; - rl = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_size); - for (j = 0; j < rl; j++) { - bh = sb_bread(sb, rd + j); /* root dir, first block */ - if (bh == NULL) { - return "unable to read root entry."; - } - for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) { - rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE); - QNX4DEBUG((KERN_INFO "rootdir entry found : [%s]\n", rootdir->di_fname)); - if (!strcmp(rootdir->di_fname, - QNX4_BMNAME)) { - found = 1; - qnx4_sb(sb)->BitMap = kmemdup(rootdir, - sizeof(struct qnx4_inode_entry), - GFP_KERNEL); - if (!qnx4_sb(sb)->BitMap) { - brelse (bh); - return "not enough memory for bitmap inode"; - }/* keep bitmap inode known */ - break; - } - } + QNX4DEBUG((KERN_NOTICE "QNX4 filesystem found on dev %s.\n", sb->s_id)); + rd = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_blk) - 1; + rl = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_size); + for (j = 0; j < rl; j++) { + bh = sb_bread(sb, rd + j); /* root dir, first block */ + if (bh == NULL) + return "unable to read root entry."; + rootdir = (struct qnx4_inode_entry *) bh->b_data; + for (i = 0; i < QNX4_INODES_PER_BLOCK; i++, rootdir++) { + QNX4DEBUG((KERN_INFO "rootdir entry found : [%s]\n", rootdir->di_fname)); + if (strcmp(rootdir->di_fname, QNX4_BMNAME) != 0) + continue; + qnx4_sb(sb)->BitMap = kmemdup(rootdir, + sizeof(struct qnx4_inode_entry), + GFP_KERNEL); brelse(bh); - if (found != 0) { - break; - } - } - if (found == 0) { - return "bitmap file not found."; + if (!qnx4_sb(sb)->BitMap) + return "not enough memory for bitmap inode"; + /* keep bitmap inode known */ + return NULL; } + brelse(bh); } - return NULL; + return "bitmap file not found."; } static int qnx4_fill_super(struct super_block *s, void *data, int silent) -- cgit v0.10.2 From 8bc5191b261c4fd9a5e9052cebe04ce2ef05f2e7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Jan 2012 13:54:36 -0500 Subject: qnx4: don't leak ->BitMap on late failure exits Signed-off-by: Al Viro diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 3fd121c..6b00954 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -256,7 +256,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) if (IS_ERR(root)) { printk(KERN_ERR "qnx4: get inode failed\n"); ret = PTR_ERR(root); - goto out; + goto outb; } ret = -ENOMEM; @@ -269,6 +269,8 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) outi: iput(root); + outb: + kfree(qs->BitMap); out: brelse(bh); outnobh: -- cgit v0.10.2 From 9f1f46a45a681d357d1ceedecec3671a5ae957f4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Dec 2011 13:57:03 +0100 Subject: drm/i915: protect force_wake_(get|put) with the gt_lock The problem this patch solves is that the forcewake accounting necessary for register reads is protected by dev->struct_mutex. But the hangcheck and error_capture code need to access registers without grabbing this mutex because we hold it while waiting for the gpu. So a new lock is required. Because currently the error_state capture is called from the error irq handler and the hangcheck code runs from a timer, it needs to be an irqsafe spinlock (note that the registers used by the irq handler (neglecting the error handling part) only uses registers that don't need the forcewake dance). We could tune this down to a normal spinlock when we rework the error_state capture and hangcheck code to run from a workqueue. But we don't have any read in a fastpath that needs forcewake, so I've decided to not care much about overhead. This prevents tests/gem_hangcheck_forcewake from i-g-t from killing my snb on recent kernels - something must have slightly changed the timings. On previous kernels it only trigger a WARN about the broken locking. v2: Drop the previous patch for the register writes. v3: Improve the commit message per Chris Wilson's suggestions. Signed-Off-by: Daniel Vetter Reviewed-by: Chris Wilson Reviewed-by: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f8b8ed2..a017b98 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1398,9 +1398,13 @@ static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; + unsigned forcewake_count; - seq_printf(m, "forcewake count = %d\n", - atomic_read(&dev_priv->forcewake_count)); + spin_lock_irq(&dev_priv->gt_lock); + forcewake_count = dev_priv->forcewake_count; + spin_unlock_irq(&dev_priv->gt_lock); + + seq_printf(m, "forcewake count = %u\n", forcewake_count); return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5f4d589..ddfe3d9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -2045,6 +2045,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!IS_I945G(dev) && !IS_I945GM(dev)) pci_enable_msi(dev->pdev); + spin_lock_init(&dev_priv->gt_lock); spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->rps_lock); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 46c36f5..bdf6a1b 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -368,11 +368,12 @@ void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) */ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { - WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + unsigned long irqflags; - /* Forcewake is atomic in case we get in here without the lock */ - if (atomic_add_return(1, &dev_priv->forcewake_count) == 1) + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (dev_priv->forcewake_count++ == 0) dev_priv->display.force_wake_get(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); } void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) @@ -392,10 +393,12 @@ void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) */ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { - WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + unsigned long irqflags; - if (atomic_dec_and_test(&dev_priv->forcewake_count)) + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (--dev_priv->forcewake_count == 0) dev_priv->display.force_wake_put(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); } void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) @@ -626,6 +629,7 @@ int i915_reset(struct drm_device *dev, u8 flags) * need to */ bool need_display = true; + unsigned long irqflags; int ret; if (!i915_try_reset) @@ -644,8 +648,10 @@ int i915_reset(struct drm_device *dev, u8 flags) case 6: ret = gen6_do_reset(dev, flags); /* If reset with a user forcewake, try to restore */ - if (atomic_read(&dev_priv->forcewake_count)) + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (dev_priv->forcewake_count) dev_priv->display.force_wake_get(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); break; case 5: ret = ironlake_do_reset(dev, flags); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 602bc80..9689ca3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -288,7 +288,13 @@ typedef struct drm_i915_private { int relative_constants_mode; void __iomem *regs; - u32 gt_fifo_count; + /** gt_fifo_count and the subsequent register write are synchronized + * with dev->struct_mutex. */ + unsigned gt_fifo_count; + /** forcewake_count is protected by gt_lock */ + unsigned forcewake_count; + /** gt_lock is also taken in irq contexts. */ + struct spinlock gt_lock; struct intel_gmbus { struct i2c_adapter adapter; @@ -741,8 +747,6 @@ typedef struct drm_i915_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; - - atomic_t forcewake_count; } drm_i915_private_t; enum i915_cache_level { -- cgit v0.10.2 From b6e45f866465f42b53d803b0c574da0fc508a0e9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 6 Jan 2012 11:34:04 -0800 Subject: drm/i915: Move reset forcewake processing to gen6_do_reset No reason to have half of the reset split from the other half. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index bdf6a1b..a6fcd941 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -600,9 +600,17 @@ static int ironlake_do_reset(struct drm_device *dev, u8 flags) static int gen6_do_reset(struct drm_device *dev, u8 flags) { struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + unsigned long irqflags; I915_WRITE(GEN6_GDRST, GEN6_GRDOM_FULL); - return wait_for((I915_READ(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); + ret = wait_for((I915_READ(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); + /* If reset with a user forcewake, try to restore */ + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + if (dev_priv->forcewake_count) + dev_priv->display.force_wake_get(dev_priv); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); + return ret; } /** @@ -629,7 +637,6 @@ int i915_reset(struct drm_device *dev, u8 flags) * need to */ bool need_display = true; - unsigned long irqflags; int ret; if (!i915_try_reset) @@ -647,11 +654,6 @@ int i915_reset(struct drm_device *dev, u8 flags) case 7: case 6: ret = gen6_do_reset(dev, flags); - /* If reset with a user forcewake, try to restore */ - spin_lock_irqsave(&dev_priv->gt_lock, irqflags); - if (dev_priv->forcewake_count) - dev_priv->display.force_wake_get(dev_priv); - spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); break; case 5: ret = ironlake_do_reset(dev, flags); -- cgit v0.10.2 From 286fed412a134e76be55899bc628c6fa59cb70da Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 6 Jan 2012 11:44:11 -0800 Subject: drm/i915: Hold gt_lock during reset This ensures that no register reads occur while the forcewake state of the hardware is indeterminate during the reset operation. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a6fcd941..062d1d2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -603,12 +603,31 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) int ret; unsigned long irqflags; - I915_WRITE(GEN6_GDRST, GEN6_GRDOM_FULL); - ret = wait_for((I915_READ(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); - /* If reset with a user forcewake, try to restore */ + /* Hold gt_lock across reset to prevent any register access + * with forcewake not set correctly + */ spin_lock_irqsave(&dev_priv->gt_lock, irqflags); + + /* Reset the chip */ + + /* GEN6_GDRST is not in the gt power well, no need to check + * for fifo space for the write or forcewake the chip for + * the read + */ + I915_WRITE_NOTRACE(GEN6_GDRST, GEN6_GRDOM_FULL); + + /* Spin waiting for the device to ack the reset request */ + ret = wait_for((I915_READ_NOTRACE(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500); + + /* If reset with a user forcewake, try to restore, otherwise turn it off */ if (dev_priv->forcewake_count) dev_priv->display.force_wake_get(dev_priv); + else + dev_priv->display.force_wake_put(dev_priv); + + /* Restore fifo count */ + dev_priv->gt_fifo_count = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); return ret; } -- cgit v0.10.2 From c937504e2b96af3b281b1ef859e063ef4af656c1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 6 Jan 2012 11:48:38 -0800 Subject: drm/i915: Hold gt_lock across forcewake register reads Along with the previous patch to make the reset operation protected by the gt_lock as well, this ensures that all register read operations will occur with the forcewake hardware enabled. As an added bonus, this makes read operations more efficient by taking the spinlock only once per read instead of twice. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 062d1d2..308f819 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -954,9 +954,14 @@ MODULE_LICENSE("GPL and additional rights"); u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ u##x val = 0; \ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ - gen6_gt_force_wake_get(dev_priv); \ + unsigned long irqflags; \ + spin_lock_irqsave(&dev_priv->gt_lock, irqflags); \ + if (dev_priv->forcewake_count == 0) \ + dev_priv->display.force_wake_get(dev_priv); \ val = read##y(dev_priv->regs + reg); \ - gen6_gt_force_wake_put(dev_priv); \ + if (dev_priv->forcewake_count == 0) \ + dev_priv->display.force_wake_put(dev_priv); \ + spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags); \ } else { \ val = read##y(dev_priv->regs + reg); \ } \ -- cgit v0.10.2 From 4cd53c0c8b01fc05c3ad5b2acdad02e37d3c2f55 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 14 Dec 2012 16:01:25 +0100 Subject: drm/i915: paper over missed irq issues with force wake voodoo Two things seem to do the trick on my ivb machine here: - prevent the gt from powering down while waiting for seqno notification interrupts by grabbing the force_wake in get_irq (and dropping it in put_irq again). - ordering writes from the ring's CS by reading a CS register, ACTHD seems to work. Only the blt&bsd ring on ivb seem to be massively affected by this, but for paranoia do this dance also on the render ring and on snb (i.e. all gpus with forcewake). Tested with Eric's glCopyPixels loop which without this patch scores a missed irq every few seconds. This patch needs my forcewake rework to use a spinlock instead of dev->struct_mutex. After crawling through docs a lot I've found the following nugget: Internal doc "SNB GT PM Programming Guide", Section 4.3.1: "GT does not generate interrupts while in RC6 (by design)" So it looks like rc6 and irq generation are indeed related. v2: Improve the comment per Eugeni Dodonov's suggestion. v3: Add the documentation snipped. Also restrict the w/a to ivb only for -fixes, as suggested by Keith Packard. Cc: stable@kernel.org Cc: Eric Anholt Cc: Kenneth Graunke Cc: Eugeni Dodonov Tested-by: Eugeni Dodonov Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 77e729d..fa5702c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -636,6 +636,19 @@ render_ring_add_request(struct intel_ring_buffer *ring, } static u32 +gen6_ring_get_seqno(struct intel_ring_buffer *ring) +{ + struct drm_device *dev = ring->dev; + + /* Workaround to force correct ordering between irq and seqno writes on + * ivb (and maybe also on snb) by reading from a CS register (like + * ACTHD) before reading the status page. */ + if (IS_GEN7(dev)) + intel_ring_get_active_head(ring); + return intel_read_status_page(ring, I915_GEM_HWS_INDEX); +} + +static u32 ring_get_seqno(struct intel_ring_buffer *ring) { return intel_read_status_page(ring, I915_GEM_HWS_INDEX); @@ -811,6 +824,12 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) if (!dev->irq_enabled) return false; + /* It looks like we need to prevent the gt from suspending while waiting + * for an notifiy irq, otherwise irqs seem to get lost on at least the + * blt/bsd rings on ivb. */ + if (IS_GEN7(dev)) + gen6_gt_force_wake_get(dev_priv); + spin_lock(&ring->irq_lock); if (ring->irq_refcount++ == 0) { ring->irq_mask &= ~rflag; @@ -835,6 +854,9 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) ironlake_disable_irq(dev_priv, gflag); } spin_unlock(&ring->irq_lock); + + if (IS_GEN7(dev)) + gen6_gt_force_wake_put(dev_priv); } static bool @@ -1341,7 +1363,7 @@ static const struct intel_ring_buffer gen6_bsd_ring = { .write_tail = gen6_bsd_ring_write_tail, .flush = gen6_ring_flush, .add_request = gen6_add_request, - .get_seqno = ring_get_seqno, + .get_seqno = gen6_ring_get_seqno, .irq_get = gen6_bsd_ring_get_irq, .irq_put = gen6_bsd_ring_put_irq, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, @@ -1476,7 +1498,7 @@ static const struct intel_ring_buffer gen6_blt_ring = { .write_tail = ring_write_tail, .flush = blt_ring_flush, .add_request = gen6_add_request, - .get_seqno = ring_get_seqno, + .get_seqno = gen6_ring_get_seqno, .irq_get = blt_ring_get_irq, .irq_put = blt_ring_put_irq, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer, @@ -1499,6 +1521,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->flush = gen6_render_ring_flush; ring->irq_get = gen6_render_ring_get_irq; ring->irq_put = gen6_render_ring_put_irq; + ring->get_seqno = gen6_ring_get_seqno; } else if (IS_GEN5(dev)) { ring->add_request = pc_render_add_request; ring->get_seqno = pc_render_get_seqno; -- cgit v0.10.2 From 4f2f81a5621de47d42476d0b929be2e0d565df84 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 19 Jan 2012 12:41:25 -0800 Subject: x86, syscall: Need __ARCH_WANT_SYS_IPC for 32 bits In checkin 303395ac3bf3 x86: Generate system call tables and unistd_*.h from tables the feature macros in were unified between 32 and 64 bits. Unfortunately 32 bits requires __ARCH_WANT_SYS_IPC and this was inadvertently dropped. Reported-by: Dmitry Kasatkin Cc: Linus Torvalds Signed-off-by: H. Peter Anvin Link: http://lkml.kernel.org/r/CALLzPKbeXN5gdngo8uYYU8mAow=XhrwBFBhKfG811f37BubQOg@mail.gmail.com diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index b4a3db7..21f77b8 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -7,6 +7,7 @@ # include # define __ARCH_WANT_IPC_PARSE_VERSION # define __ARCH_WANT_STAT64 +# define __ARCH_WANT_SYS_IPC # define __ARCH_WANT_SYS_OLD_MMAP # define __ARCH_WANT_SYS_OLD_SELECT -- cgit v0.10.2 From f581b63aa1049ac030d6eb6c24e1be1ce2072ae7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Jan 2012 23:22:38 +0100 Subject: PM / Documentation: Fix spelling mistake in basic-pm-debugging.txt Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/power/basic-pm-debugging.txt b/Documentation/power/basic-pm-debugging.txt index 40a4c65..262acf5 100644 --- a/Documentation/power/basic-pm-debugging.txt +++ b/Documentation/power/basic-pm-debugging.txt @@ -15,7 +15,7 @@ test at least a couple of times in a row for confidence. [This is necessary, because some problems only show up on a second attempt at suspending and resuming the system.] Moreover, hibernating in the "reboot" and "shutdown" modes causes the PM core to skip some platform-related callbacks which on ACPI -systems might be necessary to make hibernation work. Thus, if you machine fails +systems might be necessary to make hibernation work. Thus, if your machine fails to hibernate or resume in the "reboot" mode, you should try the "platform" mode: # echo platform > /sys/power/disk -- cgit v0.10.2 From 5eb6f9ad96967be4e0da55521a253e11b534bd3f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Jan 2012 23:22:49 +0100 Subject: PM / Documentation: Fix minor issue in freezing_of_tasks.txt In a paragraph, "kernel thread" is mistakenly written as "kernel". Fix this by adding thread after word "kernel". Changes are shown in multiple lines, as they are realigned to 80 col width. Signed-off-by: Viresh Kumar Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/power/freezing-of-tasks.txt b/Documentation/power/freezing-of-tasks.txt index 6ccb68f..ebd7490 100644 --- a/Documentation/power/freezing-of-tasks.txt +++ b/Documentation/power/freezing-of-tasks.txt @@ -120,10 +120,10 @@ So in practice, the 'at all' may become a 'why freeze kernel threads?' and freezing user threads I don't find really objectionable." Still, there are kernel threads that may want to be freezable. For example, if -a kernel that belongs to a device driver accesses the device directly, it in -principle needs to know when the device is suspended, so that it doesn't try to -access it at that time. However, if the kernel thread is freezable, it will be -frozen before the driver's .suspend() callback is executed and it will be +a kernel thread that belongs to a device driver accesses the device directly, it +in principle needs to know when the device is suspended, so that it doesn't try +to access it at that time. However, if the kernel thread is freezable, it will +be frozen before the driver's .suspend() callback is executed and it will be thawed after the driver's .resume() callback has run, so it won't be accessing the device while it's suspended. -- cgit v0.10.2 From 160cb5a97daef0cb894685d84c9d4700bb7cccb4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 19 Jan 2012 23:23:10 +0100 Subject: PM / Hibernate: Correct additional pages number calculation The struct bm_block is allocated by chain_alloc(), so it'd better counting it in LINKED_PAGE_DATA_SIZE. Signed-off-by: Namhyung Kim Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 1cf8890..6a768e5 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -812,7 +812,8 @@ unsigned int snapshot_additional_pages(struct zone *zone) unsigned int res; res = DIV_ROUND_UP(zone->spanned_pages, BM_BITS_PER_BLOCK); - res += DIV_ROUND_UP(res * sizeof(struct bm_block), PAGE_SIZE); + res += DIV_ROUND_UP(res * sizeof(struct bm_block), + LINKED_PAGE_DATA_SIZE); return 2 * res; } -- cgit v0.10.2 From 72081624d5ad3cf56deb6e727b78c4e7a55e4eec Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Thu, 19 Jan 2012 23:25:33 +0100 Subject: PM / Hibernate: Rewrite unlock_system_sleep() to fix s2disk regression Commit 33e638b, "PM / Sleep: Use the freezer_count() functions in [un]lock_system_sleep() APIs" introduced an undesirable change in the behaviour of unlock_system_sleep() since freezer_count() internally calls try_to_freeze() - which we don't need in unlock_system_sleep(). And commit bcda53f, "PM / Sleep: Replace mutex_[un]lock(&pm_mutex) with [un]lock_system_sleep()" made these APIs wide-spread. This caused a regression in suspend-to-disk where snapshot_read() and snapshot_write() were getting frozen due to the try_to_freeze embedded in unlock_system_sleep(), since these functions were invoked when the freezing condition was still in effect. Fix this by rewriting unlock_system_sleep() by open-coding freezer_count() and dropping the try_to_freeze() part. Not only will this fix the regression but this will also ensure that the API only does what it is intended to do, and nothing more, under the hood. While at it, make the code more correct and robust by ensuring that the PF_FREEZER_SKIP flag gets cleared with pm_mutex held, to avoid a race with the freezer. Also, to be on the safer side, open-code freezer_do_not_count() as well (inside lock_system_sleep()), to ensure that any unrelated modification to freezer[_do_not]_count() does not break things again! Reported-and-tested-by: Rafael J. Wysocki Signed-off-by: Srivatsa S. Bhat Acked-by: Tejun Heo Signed-off-by: Rafael J. Wysocki diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 95040cc..91784a4 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -357,14 +357,29 @@ extern bool pm_save_wakeup_count(unsigned int count); static inline void lock_system_sleep(void) { - freezer_do_not_count(); + current->flags |= PF_FREEZER_SKIP; mutex_lock(&pm_mutex); } static inline void unlock_system_sleep(void) { + /* + * Don't use freezer_count() because we don't want the call to + * try_to_freeze() here. + * + * Reason: + * Fundamentally, we just don't need it, because freezing condition + * doesn't come into effect until we release the pm_mutex lock, + * since the freezer always works with pm_mutex held. + * + * More importantly, in the case of hibernation, + * unlock_system_sleep() gets called in snapshot_read() and + * snapshot_write() when the freezing condition is still in effect. + * Which means, if we use try_to_freeze() here, it would make them + * enter the refrigerator, thus causing hibernation to lockup. + */ + current->flags &= ~PF_FREEZER_SKIP; mutex_unlock(&pm_mutex); - freezer_count(); } #else /* !CONFIG_PM_SLEEP */ -- cgit v0.10.2 From dcd6c92267155e70a94b3927bce681ce74b80d1f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 19 Jan 2012 15:04:48 -0800 Subject: Linux 3.3-rc1 diff --git a/Makefile b/Makefile index 156ac69..71e6ed2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 -PATCHLEVEL = 2 +PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc1 NAME = Saber-toothed Squirrel # *DOCUMENTATION* -- cgit v0.10.2 From bdfcdb63795b058bba9e78d32102b39014f649fe Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jan 2012 01:05:26 +0100 Subject: drm/i915: rip out the HWSTAM missed irq workaround With the new ducttape of much finer quality, this seems to be no longer necessary. Tested on my ivb and snb machine with the usual suspects of testcases. (v2 by keithp -- limited change to IVB only for now) Signed-Off-by: Daniel Vetter Acked-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 5d433fc..5bd4361 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1751,7 +1751,8 @@ static void ironlake_irq_preinstall(struct drm_device *dev) INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); I915_WRITE(HWSTAM, 0xeffe); - if (IS_GEN6(dev) || IS_GEN7(dev)) { + + if (IS_GEN6(dev)) { /* Workaround stalls observed on Sandy Bridge GPUs by * making the blitter command streamer generate a * write to the Hardware Status Page for -- cgit v0.10.2 From 5e540a5a7fd145ff7b2cdf8779b53349287c64a9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 13 Jan 2012 14:18:49 +0800 Subject: ARM: imx6: add missing twd_clk for imx6q clock With commit 5def51b (ARM: 7211/1: smp_twd: get the rate from a clock) hitting mainline, if we do not have a twd_clk for lookup, we will see the following error message in boot log. smp_twd: clock not found: -2 Actually we should add this clock regardless of the error message, so that we can: * Avoid the local timer calibrating at boot time * Make the local timer cpufreq aware on imx6q Signed-off-by: Shawn Guo diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index 9273c2a..2d88f8b 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -814,6 +814,16 @@ DEF_PFD(pll3_pfd_540m, PFD_480, PFD1, &pll3_usb_otg); DEF_PFD(pll3_pfd_508m, PFD_480, PFD2, &pll3_usb_otg); DEF_PFD(pll3_pfd_454m, PFD_480, PFD3, &pll3_usb_otg); +static unsigned long twd_clk_get_rate(struct clk *clk) +{ + return clk_get_rate(clk->parent) / 2; +} + +static struct clk twd_clk = { + .parent = &arm_clk, + .get_rate = twd_clk_get_rate, +}; + static unsigned long pll2_200m_get_rate(struct clk *clk) { return clk_get_rate(clk->parent) / 2; @@ -1894,6 +1904,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("20ec000.sdma", NULL, sdma_clk), _REGISTER_CLOCK("20bc000.wdog", NULL, dummy_clk), _REGISTER_CLOCK("20c0000.wdog", NULL, dummy_clk), + _REGISTER_CLOCK("smp_twd", NULL, twd_clk), _REGISTER_CLOCK(NULL, "ckih", ckih_clk), _REGISTER_CLOCK(NULL, "ckil_clk", ckil_clk), _REGISTER_CLOCK(NULL, "aips_tz1_clk", aips_tz1_clk), -- cgit v0.10.2 From 54d5dcc45af7adbb907072d042bbece4c2b4de6e Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 19 Jan 2012 18:18:42 +0100 Subject: ACPI processor hotplug: Split up acpi_processor_add No functional change. This is needed because: When a CPU gets hotplugged, it's totally uninitialized and offline. cpuinfo_x86 struct (cpu_data(cpu)) is mostly zero (CPU feature flags, model, family,..). When a CPU gets hotplugged, struct processor is alloc'd, some sysfs files are set up but acpi_processor_add() must not try to access a MSR on this CPU or try to read out CPU feature,family, etc. This must be done in acpi_processor_start(). The next patch will delay the call of acpi_processor_start() for physically hotpluggedcores, to the time when they are onlined the first time. There it is safe then to access cpu_data(cpu) cpuinfo_x86 struct or access MSRs which is needed to set up cpuidle, throttling and other features. Tested and Signed-off-by: Thomas Renninger Signed-off-by: Len Brown diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0034ede..bec5593 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -440,6 +440,58 @@ static struct notifier_block acpi_cpu_notifier = .notifier_call = acpi_cpu_soft_notify, }; +static int __cpuinit acpi_processor_start(struct acpi_processor *pr) +{ + struct acpi_device *device = per_cpu(processor_device_array, pr->id); + int result = 0; + +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr, 0); +#endif + acpi_processor_get_throttling_info(pr); + acpi_processor_get_limit_info(pr); + + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) + acpi_processor_power_init(pr, device); + + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (IS_ERR(pr->cdev)) { + result = PTR_ERR(pr->cdev); + goto err_power_exit; + } + + dev_dbg(&device->dev, "registered as cooling_device%d\n", + pr->cdev->id); + + result = sysfs_create_link(&device->dev.kobj, + &pr->cdev->device.kobj, + "thermal_cooling"); + if (result) { + printk(KERN_ERR PREFIX "Create sysfs link\n"); + goto err_thermal_unregister; + } + result = sysfs_create_link(&pr->cdev->device.kobj, + &device->dev.kobj, + "device"); + if (result) { + printk(KERN_ERR PREFIX "Create sysfs link\n"); + goto err_remove_sysfs_thermal; + } + + return 0; + +err_remove_sysfs_thermal: + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); +err_thermal_unregister: + thermal_cooling_device_unregister(pr->cdev); +err_power_exit: + acpi_processor_power_exit(pr, device); + + return result; +} + + static int __cpuinit acpi_processor_add(struct acpi_device *device) { struct acpi_processor *pr = NULL; @@ -494,49 +546,13 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) result = -EFAULT; goto err_free_cpumask; } - -#ifdef CONFIG_CPU_FREQ - acpi_processor_ppc_has_changed(pr, 0); -#endif - acpi_processor_get_throttling_info(pr); - acpi_processor_get_limit_info(pr); - - if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) - acpi_processor_power_init(pr, device); - - pr->cdev = thermal_cooling_device_register("Processor", device, - &processor_cooling_ops); - if (IS_ERR(pr->cdev)) { - result = PTR_ERR(pr->cdev); - goto err_power_exit; - } - - dev_dbg(&device->dev, "registered as cooling_device%d\n", - pr->cdev->id); - - result = sysfs_create_link(&device->dev.kobj, - &pr->cdev->device.kobj, - "thermal_cooling"); - if (result) { - printk(KERN_ERR PREFIX "Create sysfs link\n"); - goto err_thermal_unregister; - } - result = sysfs_create_link(&pr->cdev->device.kobj, - &device->dev.kobj, - "device"); - if (result) { - printk(KERN_ERR PREFIX "Create sysfs link\n"); + result = acpi_processor_start(pr); + if (result) goto err_remove_sysfs; - } return 0; err_remove_sysfs: - sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); -err_thermal_unregister: - thermal_cooling_device_unregister(pr->cdev); -err_power_exit: - acpi_processor_power_exit(pr, device); sysfs_remove_link(&device->dev.kobj, "sysdev"); err_free_cpumask: free_cpumask_var(pr->throttling.shared_cpu_map); -- cgit v0.10.2 From 99b725084450bbc6f8e1ab20a0df4cc291c342b5 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 19 Jan 2012 18:18:43 +0100 Subject: ACPI processor hotplug: Delay acpi_processor_start() call for hotplugged cores Delay the setting up of features (cpuidle, throttling by calling acpi_processor_start()) to the time when the hotplugged core got onlined the first time and got fully initialized. Signed-off-by: Thomas Renninger Signed-off-by: Len Brown diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index bec5593..2b805d7 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -84,7 +84,7 @@ static int acpi_processor_remove(struct acpi_device *device, int type); static void acpi_processor_notify(struct acpi_device *device, u32 event); static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr); static int acpi_processor_handle_eject(struct acpi_processor *pr); - +static int acpi_processor_start(struct acpi_processor *pr); static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, @@ -423,10 +423,29 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, struct acpi_processor *pr = per_cpu(processors, cpu); if (action == CPU_ONLINE && pr) { - acpi_processor_ppc_has_changed(pr, 0); - acpi_processor_hotplug(pr); - acpi_processor_reevaluate_tstate(pr, action); - acpi_processor_tstate_has_changed(pr); + /* CPU got physically hotplugged and onlined the first time: + * Initialize missing things + */ + if (pr->flags.need_hotplug_init) { + struct cpuidle_driver *idle_driver = + cpuidle_get_driver(); + + printk(KERN_INFO "Will online and init hotplugged " + "CPU: %d\n", pr->id); + WARN(acpi_processor_start(pr), "Failed to start CPU:" + " %d\n", pr->id); + pr->flags.need_hotplug_init = 0; + if (idle_driver && !strcmp(idle_driver->name, + "intel_idle")) { + intel_idle_cpu_init(pr->id); + } + /* Normal CPU soft online event */ + } else { + acpi_processor_ppc_has_changed(pr, 0); + acpi_processor_cst_has_changed(pr); + acpi_processor_reevaluate_tstate(pr, action); + acpi_processor_tstate_has_changed(pr); + } } if (action == CPU_DEAD && pr) { /* invalidate the flag.throttling after one CPU is offline */ @@ -440,7 +459,15 @@ static struct notifier_block acpi_cpu_notifier = .notifier_call = acpi_cpu_soft_notify, }; -static int __cpuinit acpi_processor_start(struct acpi_processor *pr) +/* + * acpi_processor_start() is called by the cpu_hotplug_notifier func: + * acpi_cpu_soft_notify(). Getting it __cpuinit{data} is difficult, the + * root cause seem to be that acpi_processor_uninstall_hotplug_notify() + * is in the module_exit (__exit) func. Allowing acpi_processor_start() + * to not be in __cpuinit section, but being called from __cpuinit funcs + * via __ref looks like the right thing to do here. + */ +static __ref int acpi_processor_start(struct acpi_processor *pr) { struct acpi_device *device = per_cpu(processor_device_array, pr->id); int result = 0; @@ -491,7 +518,12 @@ err_power_exit: return result; } - +/* + * Do not put anything in here which needs the core to be online. + * For example MSR access or setting up things which check for cpuinfo_x86 + * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. + * Such things have to be put in and set up above in acpi_processor_start() + */ static int __cpuinit acpi_processor_add(struct acpi_device *device) { struct acpi_processor *pr = NULL; @@ -546,6 +578,21 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) result = -EFAULT; goto err_free_cpumask; } + + /* + * Do not start hotplugged CPUs now, but when they + * are onlined the first time + */ + if (pr->flags.need_hotplug_init) + return 0; + + /* + * Do not start hotplugged CPUs now, but when they + * are onlined the first time + */ + if (pr->flags.need_hotplug_init) + return 0; + result = acpi_processor_start(pr); if (result) goto err_remove_sysfs; @@ -751,6 +798,17 @@ static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) return AE_ERROR; } + /* CPU got hot-plugged, but cpu_data is not initialized yet + * Set flag to delay cpu_idle/throttling initialization + * in: + * acpi_processor_add() + * acpi_processor_get_info() + * and do it when the CPU gets online the first time + * TBD: Cleanup above functions and try to do this more elegant. + */ + printk(KERN_INFO "CPU %d got hotplugged\n", pr->id); + pr->flags.need_hotplug_init = 1; + return AE_OK; } diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 20bce51..54ab97b 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -527,7 +527,7 @@ int intel_idle_cpu_init(int cpu) return 0; } - +EXPORT_SYMBOL_GPL(intel_idle_cpu_init); static int __init intel_idle_init(void) { diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 610f6fb..8cf7e98 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -195,6 +195,7 @@ struct acpi_processor_flags { u8 has_cst:1; u8 power_setup_done:1; u8 bm_rld_set:1; + u8 need_hotplug_init:1; }; struct acpi_processor { -- cgit v0.10.2 From eacb6ec9ae5932ea02a44268684a56e4b5996ccf Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 19 Jan 2012 12:37:13 +0100 Subject: microblaze: generic atomic64 support This tiny patch adds generic atomic64 support for the Microblaze architecture. The patch is against the latest linux-2.6-microblaze tree. It also fixes the kernel build for microblaze: Error log: CC kernel/trace/trace_clock.o kernel/trace/trace_clock.c:117: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'trace_counter' kernel/trace/trace_clock.c: In function 'trace_clock_counter': kernel/trace/trace_clock.c:126: error: implicit declaration of function 'atomic64_add_return' kernel/trace/trace_clock.c:126: error: 'trace_counter' undeclared (first use in this function) kernel/trace/trace_clock.c:126: error: (Each undeclared identifier is reported only once kernel/trace/trace_clock.c:126: error: for each function it appears in.) make[2]: *** [kernel/trace/trace_clock.o] Error 1 make[1]: *** [kernel/trace] Error 2 make: *** [kernel] Error 2 Signed-off-by: Ariane Keller Signed-off-by: Daniel Borkmann Signed-off-by: Michal Simek diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 74f23a4..c8d6efb 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -19,6 +19,7 @@ config MICROBLAZE select GENERIC_IRQ_SHOW select GENERIC_PCI_IOMAP select GENERIC_CPU_DEVICES + select GENERIC_ATOMIC64 config SWAP def_bool n diff --git a/arch/microblaze/include/asm/atomic.h b/arch/microblaze/include/asm/atomic.h index 6d2e1d4..615f539 100644 --- a/arch/microblaze/include/asm/atomic.h +++ b/arch/microblaze/include/asm/atomic.h @@ -2,6 +2,7 @@ #define _ASM_MICROBLAZE_ATOMIC_H #include +#include /* * Atomically test *v and decrement if it is greater than 0. -- cgit v0.10.2 From b9ecc4ee28a5ff5b3997da247cd9df1320c602a9 Mon Sep 17 00:00:00 2001 From: Albert Pool Date: Thu, 19 Jan 2012 22:08:50 +0100 Subject: snd-hda-intel: better Alienware M17x R3 quirk I have been told that this way the rear headphone connector is working as well; with model=alienware only laptop speakers work. The subsystem of both controller and codec is 1028:0490. Signed-off-by: Albert Pool Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3556408..1a26dbc 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1608,7 +1608,7 @@ static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a, "Alienware M17x", STAC_ALIENWARE_M17X), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490, - "Alienware M17x", STAC_ALIENWARE_M17X), + "Alienware M17x R3", STAC_DELL_EQ), {} /* terminator */ }; -- cgit v0.10.2 From cb0cdebbf8b834110ef67ed9335d5bafed7835df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 20 Jan 2012 12:14:12 +0100 Subject: ALSA: hda - Fix a unused variable warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just overlooked. sound/pci/hda/patch_realtek.c: In function ‘alc_sku_unsol_event’: sound/pci/hda/patch_realtek.c:643:19: warning: unused variable ‘spec’ [-Wunused-variable] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ddbed97..c95c8bd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -640,7 +640,6 @@ static void alc_exec_unsol_event(struct hda_codec *codec, int action) /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) { - struct alc_spec *spec = codec->spec; if (codec->vendor_id == 0x10ec0880) res >>= 28; else -- cgit v0.10.2 From a5fea953bbcff57f30cf5027ae63069a869f8e31 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 19 Dec 2011 08:52:43 +0100 Subject: mach-ux500: musb: now musb is always in OTG mode This change is needed after 622859634 "usb: musb: drop a gigantic amount of ifdeferry", where CONFIG_USB_MUSB_PERIPHERAL & CONFIG_USB_MUSB_HOST has been removed. Signed-off-by: Philippe Langlais Acked-by: Felipe Balbi Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c index 0a01cbd..9f9e1c2 100644 --- a/arch/arm/mach-ux500/usb.c +++ b/arch/arm/mach-ux500/usb.c @@ -95,13 +95,7 @@ static struct musb_hdrc_config musb_hdrc_config = { }; static struct musb_hdrc_platform_data musb_platform_data = { -#if defined(CONFIG_USB_MUSB_OTG) .mode = MUSB_OTG, -#elif defined(CONFIG_USB_MUSB_PERIPHERAL) - .mode = MUSB_PERIPHERAL, -#else /* defined(CONFIG_USB_MUSB_HOST) */ - .mode = MUSB_HOST, -#endif .config = &musb_hdrc_config, .board_data = &musb_board_data, }; -- cgit v0.10.2 From dd821823fad54d6ccc328e2f1b9698a6f9bd8f3e Mon Sep 17 00:00:00 2001 From: srinidhi kasagar Date: Tue, 17 Jan 2012 11:29:39 +0530 Subject: mach-ux500: do not override outer.inv_all Invalidating outer cache without disabling it is a big nono, and so, remove the machine specific outer.inv_all And at the same time it does not prevent us overriding outer.disable as we do not have any such secure SMI to handle the same while kexec disables the outer cache. Signed-off-by: Srinidhi Kasagar Reviewed-by: Will Deacon Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index 122ddde..da5569d 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -12,44 +12,6 @@ static void __iomem *l2x0_base; -static inline void ux500_cache_wait(void __iomem *reg, unsigned long mask) -{ - /* wait for the operation to complete */ - while (readl_relaxed(reg) & mask) - cpu_relax(); -} - -static inline void ux500_cache_sync(void) -{ - writel_relaxed(0, l2x0_base + L2X0_CACHE_SYNC); - ux500_cache_wait(l2x0_base + L2X0_CACHE_SYNC, 1); -} - -/* - * The L2 cache cannot be turned off in the non-secure world. - * Dummy until a secure service is in place. - */ -static void ux500_l2x0_disable(void) -{ -} - -/* - * This is only called when doing a kexec, just after turning off the L2 - * and L1 cache, and it is surrounded by a spinlock in the generic version. - * However, we're not really turning off the L2 cache right now and the - * PL310 does not support exclusive accesses (used to implement the spinlock). - * So, the invalidation needs to be done without the spinlock. - */ -static void ux500_l2x0_inv_all(void) -{ - uint32_t l2x0_way_mask = (1<<16) - 1; /* Bitmask of active ways */ - - /* invalidate all ways */ - writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY); - ux500_cache_wait(l2x0_base + L2X0_INV_WAY, l2x0_way_mask); - ux500_cache_sync(); -} - static int __init ux500_l2x0_unlock(void) { int i; @@ -85,9 +47,13 @@ static int __init ux500_l2x0_init(void) /* 64KB way size, 8 way associativity, force WA */ l2x0_init(l2x0_base, 0x3e060000, 0xc0000fff); - /* Override invalidate function */ - outer_cache.disable = ux500_l2x0_disable; - outer_cache.inv_all = ux500_l2x0_inv_all; + /* + * We can't disable l2 as we are in non secure mode, currently + * this seems be called only during kexec path. So let's + * override outer.disable with nasty assignment until we have + * some SMI service available. + */ + outer_cache.disable = NULL; return 0; } -- cgit v0.10.2 From d65015f7c5c5be9fd3f5e567889c844ba81bdc9c Mon Sep 17 00:00:00 2001 From: Srinidhi KASAGAR Date: Thu, 12 Jan 2012 11:07:43 +0530 Subject: mach-ux500: enable ARM errata 764369 This applies ARM errata 764369 for all ux500 platforms. Cc: stable@kernel.org Signed-off-by: Srinidhi Kasagar Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index a3e0c86..52af004 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -7,6 +7,7 @@ config UX500_SOC_COMMON select HAS_MTU select ARM_ERRATA_753970 select ARM_ERRATA_754322 + select ARM_ERRATA_764369 menu "Ux500 SoC" -- cgit v0.10.2 From 2ab1159e80e8f416071e9f51e4f77b9173948296 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 20 Jan 2012 09:20:40 +0100 Subject: mach-ux500: no MMC_CAP_SD_HIGHSPEED on Snowball MMC_CAP_SD_HIGHSPEED is not supported on Snowball board resulting on initialization errors. Cc: stable@kernel.org Signed-off-by: Mathieu Poirier Signed-off-by: Fredrik Soderstedt Signed-off-by: Philippe Langlais Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 23be34b..5dde4d4 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -261,6 +261,8 @@ void __init mop500_sdi_init(void) void __init snowball_sdi_init(void) { + /* On Snowball MMC_CAP_SD_HIGHSPEED isn't supported (Hardware issue?) */ + mop500_sdi0_data.capabilities &= ~MMC_CAP_SD_HIGHSPEED; /* On-board eMMC */ db8500_add_sdi4(&mop500_sdi4_data, U8500_SDI_V2_PERIPHID); /* External Micro SD slot */ -- cgit v0.10.2 From 421e8d2de3bd8b089dc6322d8589b7eb38437a23 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Jan 2012 13:39:37 +0000 Subject: regmap: Reset cache status when reinitialsing the cache When we reinitialise the cache make sure that we reset the cache access flags, ensuring that the reinitialised cache is in the default state which is what callers would and do expect given the function name. This is particularly likely to cause issues in systems where there was no cache previously as those systems have cache bypass enabled, as for the wm8994 driver where this was noticed. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index be10a4f..6555803 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -284,6 +284,9 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; + map->cache_bypass = false; + map->cache_only = false; + ret = regcache_init(map, config); mutex_unlock(&map->lock); -- cgit v0.10.2 From bb362e2e4f4874f3fd4cbc2497385b9bceb3a08a Mon Sep 17 00:00:00 2001 From: Zeng Zhaoming Date: Wed, 18 Jan 2012 13:58:07 +0800 Subject: ASoC: sgtl5000: Fix wrong register name in restore Correct SGTL5000_CHIP_CLK_CTRL to SGTL5000_CHIP_REF_CTRL in sgtl5000_restore_regs(), and add comment to explain the restore order. Reported-by: Julia Lawall Signed-off-by: Zeng Zhaoming Acked-by: Dong Aisheng Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index f8863eb..7f4ba81 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -987,12 +987,12 @@ static int sgtl5000_restore_regs(struct snd_soc_codec *codec) /* restore regular registers */ for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) { - /* this regs depends on the others */ + /* These regs should restore in particular order */ if (reg == SGTL5000_CHIP_ANA_POWER || reg == SGTL5000_CHIP_CLK_CTRL || reg == SGTL5000_CHIP_LINREG_CTRL || reg == SGTL5000_CHIP_LINE_OUT_CTRL || - reg == SGTL5000_CHIP_CLK_CTRL) + reg == SGTL5000_CHIP_REF_CTRL) continue; snd_soc_write(codec, reg, cache[reg]); @@ -1003,8 +1003,17 @@ static int sgtl5000_restore_regs(struct snd_soc_codec *codec) snd_soc_write(codec, reg, cache[reg]); /* - * restore power and other regs according - * to set_power() and set_clock() + * restore these regs according to the power setting sequence in + * sgtl5000_set_power_regs() and clock setting sequence in + * sgtl5000_set_clock(). + * + * The order of restore is: + * 1. SGTL5000_CHIP_CLK_CTRL MCLK_FREQ bits (1:0) should be restore after + * SGTL5000_CHIP_ANA_POWER PLL bits set + * 2. SGTL5000_CHIP_LINREG_CTRL should be set before + * SGTL5000_CHIP_ANA_POWER LINREG_D restored + * 3. SGTL5000_CHIP_REF_CTRL controls Analog Ground Voltage, + * prefer to resotre it after SGTL5000_CHIP_ANA_POWER restored */ snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, cache[SGTL5000_CHIP_LINREG_CTRL]); -- cgit v0.10.2 From 01b37e94c04bc6dae2c4837a2eb6fff6819ea82a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 18 Jan 2012 11:48:58 +0100 Subject: ASoC: tlv320aic32x4: always enable dividers Dividers (such as MDAC) are always needed, independent of the codec being I2S master or slave. Needed on a custom board where the codec has to be slave. Signed-off-by: Wolfram Sang Acked-by: Javier Martin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index eb401ef..3806cb6 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -60,7 +60,6 @@ struct aic32x4_rate_divs { struct aic32x4_priv { u32 sysclk; - s32 master; u8 page_no; void *control_data; u32 power_cfg; @@ -369,7 +368,6 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); u8 iface_reg_1; u8 iface_reg_2; u8 iface_reg_3; @@ -384,11 +382,9 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - aic32x4->master = 1; iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; break; case SND_SOC_DAIFMT_CBS_CFS: - aic32x4->master = 0; break; default: printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); @@ -526,64 +522,58 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute) static int aic32x4_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); - switch (level) { case SND_SOC_BIAS_ON: - if (aic32x4->master) { - /* Switch on PLL */ - snd_soc_update_bits(codec, AIC32X4_PLLPR, - AIC32X4_PLLEN, AIC32X4_PLLEN); - - /* Switch on NDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_NDAC, - AIC32X4_NDACEN, AIC32X4_NDACEN); - - /* Switch on MDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_MDAC, - AIC32X4_MDACEN, AIC32X4_MDACEN); - - /* Switch on NADC Divider */ - snd_soc_update_bits(codec, AIC32X4_NADC, - AIC32X4_NADCEN, AIC32X4_NADCEN); - - /* Switch on MADC Divider */ - snd_soc_update_bits(codec, AIC32X4_MADC, - AIC32X4_MADCEN, AIC32X4_MADCEN); - - /* Switch on BCLK_N Divider */ - snd_soc_update_bits(codec, AIC32X4_BCLKN, - AIC32X4_BCLKEN, AIC32X4_BCLKEN); - } + /* Switch on PLL */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, + AIC32X4_PLLEN, AIC32X4_PLLEN); + + /* Switch on NDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_NDAC, + AIC32X4_NDACEN, AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_MDAC, + AIC32X4_MDACEN, AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + snd_soc_update_bits(codec, AIC32X4_NADC, + AIC32X4_NADCEN, AIC32X4_NADCEN); + + /* Switch on MADC Divider */ + snd_soc_update_bits(codec, AIC32X4_MADC, + AIC32X4_MADCEN, AIC32X4_MADCEN); + + /* Switch on BCLK_N Divider */ + snd_soc_update_bits(codec, AIC32X4_BCLKN, + AIC32X4_BCLKEN, AIC32X4_BCLKEN); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (aic32x4->master) { - /* Switch off PLL */ - snd_soc_update_bits(codec, AIC32X4_PLLPR, - AIC32X4_PLLEN, 0); - - /* Switch off NDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_NDAC, - AIC32X4_NDACEN, 0); - - /* Switch off MDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_MDAC, - AIC32X4_MDACEN, 0); - - /* Switch off NADC Divider */ - snd_soc_update_bits(codec, AIC32X4_NADC, - AIC32X4_NADCEN, 0); - - /* Switch off MADC Divider */ - snd_soc_update_bits(codec, AIC32X4_MADC, - AIC32X4_MADCEN, 0); - - /* Switch off BCLK_N Divider */ - snd_soc_update_bits(codec, AIC32X4_BCLKN, - AIC32X4_BCLKEN, 0); - } + /* Switch off PLL */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, + AIC32X4_PLLEN, 0); + + /* Switch off NDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_NDAC, + AIC32X4_NDACEN, 0); + + /* Switch off MDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_MDAC, + AIC32X4_MDACEN, 0); + + /* Switch off NADC Divider */ + snd_soc_update_bits(codec, AIC32X4_NADC, + AIC32X4_NADCEN, 0); + + /* Switch off MADC Divider */ + snd_soc_update_bits(codec, AIC32X4_MADC, + AIC32X4_MADCEN, 0); + + /* Switch off BCLK_N Divider */ + snd_soc_update_bits(codec, AIC32X4_BCLKN, + AIC32X4_BCLKEN, 0); break; case SND_SOC_BIAS_OFF: break; -- cgit v0.10.2 From 0c93a167a6b3fa510c74e88477852c41defda075 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 18 Jan 2012 11:48:59 +0100 Subject: ASoC: tlv320aic32x4: always enable analouge block Register LDOCTLEN must always be initialized to clear the analog power control bit, otherwise the analog block will stay deactivated. Signed-off-by: Wolfram Sang Acked-by: Javier Martin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 3806cb6..372b0b8 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -641,9 +641,11 @@ static int aic32x4_probe(struct snd_soc_codec *codec) if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); } - if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { - snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); - } + + tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ? + AIC32X4_LDOCTLEN : 0; + snd_soc_write(codec, AIC32X4_LDOCTL, tmp_reg); + tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { tmp_reg |= AIC32X4_LDOIN_18_36; -- cgit v0.10.2 From e53e417331c57b9b97e3f8be870214a02c99265c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 Jan 2012 20:02:38 +0000 Subject: ASoC: Mark WM5100 register map cache only when going into BIAS_OFF Writing to the registers won't work if we do actually manage to hit a fully powered off state. Signed-off-by: Mark Brown Cc: stable@kernel.org diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 8b24323..3fd9cfe 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1402,6 +1402,7 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: + regcache_cache_only(wm5100->regmap, true); if (wm5100->pdata.ldo_ena) gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), -- cgit v0.10.2 From 495174a8ffbaa0d15153d855cf206cdc46d51cf4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 19 Jan 2012 11:16:37 +0000 Subject: ASoC: Don't go through cache when applying WM5100 rev A updates These are all to either uncached registers or fixes to register defaults, in the former case the cache won't do anything and in the latter case we're fixing things so the cache sync will do the right thing. Signed-off-by: Mark Brown Cc: stable@kernel.org diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 3fd9cfe..66f0611 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1377,6 +1377,7 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, switch (wm5100->rev) { case 0: + regcache_cache_bypass(wm5100->regmap, true); snd_soc_write(codec, 0x11, 0x3); snd_soc_write(codec, 0x203, 0xc); snd_soc_write(codec, 0x206, 0); @@ -1392,6 +1393,7 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, wm5100_reva_patches[i].reg, wm5100_reva_patches[i].val); + regcache_cache_bypass(wm5100->regmap, false); break; default: break; -- cgit v0.10.2 From fed22007113cb857e917913ce016d9b539dc3a80 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 Jan 2012 19:17:06 +0000 Subject: ASoC: Disable register synchronisation for low frequency WM8996 SYSCLK With a low frequency SYSCLK and a fast I2C clock register synchronisation may occasionally take too long to take effect, causing I/O issues. Disable synchronisation in order to avoid any issues. Signed-off-by: Mark Brown Cc: stable@kernel.org diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index d8da10f..86f5b6b 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2007,6 +2007,7 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int lfclk = 0; int ratediv = 0; + int sync = WM8996_REG_SYNC; int src; int old; @@ -2051,6 +2052,7 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, case 32000: case 32768: lfclk = WM8996_LFCLK_ENA; + sync = 0; break; default: dev_warn(codec->dev, "Unsupported clock rate %dHz\n", @@ -2064,6 +2066,8 @@ static int wm8996_set_sysclk(struct snd_soc_dai *dai, WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK, src << WM8996_SYSCLK_SRC_SHIFT | ratediv); snd_soc_update_bits(codec, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk); + snd_soc_update_bits(codec, WM8996_CONTROL_INTERFACE_1, + WM8996_REG_SYNC, sync); snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1, WM8996_SYSCLK_ENA, old); diff --git a/sound/soc/codecs/wm8996.h b/sound/soc/codecs/wm8996.h index 0fde643..de9ac3e 100644 --- a/sound/soc/codecs/wm8996.h +++ b/sound/soc/codecs/wm8996.h @@ -1567,6 +1567,10 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, /* * R257 (0x101) - Control Interface (1) */ +#define WM8996_REG_SYNC 0x8000 /* REG_SYNC */ +#define WM8996_REG_SYNC_MASK 0x8000 /* REG_SYNC */ +#define WM8996_REG_SYNC_SHIFT 15 /* REG_SYNC */ +#define WM8996_REG_SYNC_WIDTH 1 /* REG_SYNC */ #define WM8996_AUTO_INC 0x0004 /* AUTO_INC */ #define WM8996_AUTO_INC_MASK 0x0004 /* AUTO_INC */ #define WM8996_AUTO_INC_SHIFT 2 /* AUTO_INC */ -- cgit v0.10.2 From 3e90772f76010c315474bde59eaca7cc4c94d645 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 28 Dec 2011 13:10:04 +0200 Subject: ARM: at91: fix at91rm9200 soc subtype handling Currently setting it to PQFP changes subtype to BGA as subtypes are swapped in at91rm9200_set_type(). Wrong subtype causes GPIO bank D not to work at all. After this fix, subtype is still set as unknown. But board code should fill it in with proper value. Another information is thus printed. Bug discovery and first implementation made by Veli-Pekka Peltola. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Cc: stable diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 8bdcc3c..f524718 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -29,9 +29,12 @@ EXPORT_SYMBOL(at91_soc_initdata); void __init at91rm9200_set_type(int type) { if (type == ARCH_REVISON_9200_PQFP) - at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA; - else at91_soc_initdata.subtype = AT91_SOC_RM9200_PQFP; + else + at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA; + + pr_info("AT91: filled in soc subtype: %s\n", + at91_get_soc_subtype(&at91_soc_initdata)); } void __init at91_init_irq_default(void) -- cgit v0.10.2 From 08a52e1bef7974176520df4ece9aafcf7c7d9ac3 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 6 Jan 2012 16:09:37 +0100 Subject: ARM: at91: removal of CAP9 SoC family Atmel CAP9 family is not maintained well and products may be difficult to find now. It will allow to save workforce and remove LOC during current cleanup process. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 1bea46a..a0ffac0 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -510,3 +510,17 @@ Why: The pci_scan_bus_parented() interface creates a new root bus. The convert to using pci_scan_root_bus() so they can supply a list of bus resources when the bus is created. Who: Bjorn Helgaas + +---------------------------- + +What: The CAP9 SoC family will be removed +When: 3.4 +Files: arch/arm/mach-at91/at91cap9.c + arch/arm/mach-at91/at91cap9_devices.c + arch/arm/mach-at91/include/mach/at91cap9.h + arch/arm/mach-at91/include/mach/at91cap9_matrix.h + arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h + arch/arm/mach-at91/board-cap9adk.c +Why: The code is not actively maintained and platforms are now hard to find. +Who: Nicolas Ferre + Jean-Christophe PLAGNIOL-VILLARD -- cgit v0.10.2 From 6813463cebf25089053cbe25245d15908f896716 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 8 Dec 2011 16:32:00 +0100 Subject: USB: at91: fix clk_get error handling Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Acked-by: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 5df0b0e..dc33517 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -139,8 +139,23 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, } iclk = clk_get(&pdev->dev, "ohci_clk"); + if (IS_ERR(iclk)) { + dev_err(&pdev->dev, "failed to get ohci_clk\n"); + retval = PTR_ERR(iclk); + goto err3; + } fclk = clk_get(&pdev->dev, "uhpck"); + if (IS_ERR(fclk)) { + dev_err(&pdev->dev, "failed to get uhpck\n"); + retval = PTR_ERR(fclk); + goto err4; + } hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) { + dev_err(&pdev->dev, "failed to get hclk\n"); + retval = PTR_ERR(hclk); + goto err5; + } at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -153,9 +168,12 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, at91_stop_hc(pdev); clk_put(hclk); + err5: clk_put(fclk); + err4: clk_put(iclk); + err3: iounmap(hcd->regs); err2: -- cgit v0.10.2 From 8134ff55646bd2c059ddfd0d479882a06d6ef09a Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 8 Dec 2011 16:32:01 +0100 Subject: ARM/USB: at91/ohci-at91: rename vbus_pin_inverted to vbus_pin_active_low Allows to configure independently the vbus_pin associated with each port. Matches usual naming scheme. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Acked-by: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index d0b377b..3b33f07 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -88,7 +88,7 @@ extern void __init at91_add_device_eth(struct macb_platform_data *data); struct at91_usbh_data { u8 ports; /* number of ports on root hub */ int vbus_pin[2]; /* port power-control pin */ - u8 vbus_pin_inverted; + u8 vbus_pin_active_low[2]; u8 overcurrent_supported; int overcurrent_pin[2]; u8 overcurrent_status[2]; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index dc33517..77afabc 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -244,7 +244,8 @@ static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int if (!gpio_is_valid(pdata->vbus_pin[port])) return; - gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable); + gpio_set_value(pdata->vbus_pin[port], + !pdata->vbus_pin_active_low[port] ^ enable); } static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) @@ -255,7 +256,8 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) if (!gpio_is_valid(pdata->vbus_pin[port])) return -EINVAL; - return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted; + return gpio_get_value(pdata->vbus_pin[port]) ^ + !pdata->vbus_pin_active_low[port]; } /* -- cgit v0.10.2 From 6522ecdcfaae90ad1d2dd868fbdf3a2ddfc5f257 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 18 Nov 2011 00:28:29 +0800 Subject: ARM: at91: fix cap9 ddrsdr register fix AT91_DDRSDRC_MODE it's 3bit add missing AT91_DDRSDRC_NR_14, AT91_DDRSDRC_DBW (16 and 32 bits support) Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h b/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h index 976f4a6..d21932d 100644 --- a/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h +++ b/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h @@ -16,7 +16,7 @@ #define AT91CAP9_DDRSDR_H #define AT91_DDRSDRC_MR 0x00 /* Mode Register */ -#define AT91_DDRSDRC_MODE (0xf << 0) /* Command Mode */ +#define AT91_DDRSDRC_MODE (0x7 << 0) /* Command Mode */ #define AT91_DDRSDRC_MODE_NORMAL 0 #define AT91_DDRSDRC_MODE_NOP 1 #define AT91_DDRSDRC_MODE_PRECHARGE 2 @@ -42,6 +42,7 @@ #define AT91_DDRSDRC_NR_11 (0 << 2) #define AT91_DDRSDRC_NR_12 (1 << 2) #define AT91_DDRSDRC_NR_13 (2 << 2) +#define AT91_DDRSDRC_NR_14 (3 << 2) #define AT91_DDRSDRC_CAS (7 << 4) /* CAS Latency */ #define AT91_DDRSDRC_CAS_2 (2 << 4) #define AT91_DDRSDRC_CAS_3 (3 << 4) @@ -86,6 +87,9 @@ #define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 #define AT91_DDRSDRC_MD_DDR 2 #define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 +#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */ +#define AT91_DDRSDRC_DBW_32BITS (0 << 4) +#define AT91_DDRSDRC_DBW_16BITS (1 << 4) #define AT91_DDRSDRC_DLLR 0x20 /* DLL Information Register */ #define AT91_DDRSDRC_MDINC (1 << 0) /* Master Delay increment */ -- cgit v0.10.2 From 17d2cc25f04462fd5d835318f02fb5492576ab4b Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 18 Nov 2011 00:36:21 +0800 Subject: ARM: at91: merge at91cap9_ddrsdr.h in at91sam9_ddrsdr.h Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h b/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h deleted file mode 100644 index d21932d..0000000 --- a/arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * arch/arm/mach-at91/include/mach/at91cap9_ddrsdr.h - * - * (C) 2008 Andrew Victor - * - * DDR/SDR Controller (DDRSDRC) - System peripherals registers. - * Based on AT91CAP9 datasheet revision B. - * - * 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. - */ - -#ifndef AT91CAP9_DDRSDR_H -#define AT91CAP9_DDRSDR_H - -#define AT91_DDRSDRC_MR 0x00 /* Mode Register */ -#define AT91_DDRSDRC_MODE (0x7 << 0) /* Command Mode */ -#define AT91_DDRSDRC_MODE_NORMAL 0 -#define AT91_DDRSDRC_MODE_NOP 1 -#define AT91_DDRSDRC_MODE_PRECHARGE 2 -#define AT91_DDRSDRC_MODE_LMR 3 -#define AT91_DDRSDRC_MODE_REFRESH 4 -#define AT91_DDRSDRC_MODE_EXT_LMR 5 -#define AT91_DDRSDRC_MODE_DEEP 6 - -#define AT91_DDRSDRC_RTR 0x04 /* Refresh Timer Register */ -#define AT91_DDRSDRC_COUNT (0xfff << 0) /* Refresh Timer Counter */ - -#define AT91_DDRSDRC_CR 0x08 /* Configuration Register */ -#define AT91_DDRSDRC_NC (3 << 0) /* Number of Column Bits */ -#define AT91_DDRSDRC_NC_SDR8 (0 << 0) -#define AT91_DDRSDRC_NC_SDR9 (1 << 0) -#define AT91_DDRSDRC_NC_SDR10 (2 << 0) -#define AT91_DDRSDRC_NC_SDR11 (3 << 0) -#define AT91_DDRSDRC_NC_DDR9 (0 << 0) -#define AT91_DDRSDRC_NC_DDR10 (1 << 0) -#define AT91_DDRSDRC_NC_DDR11 (2 << 0) -#define AT91_DDRSDRC_NC_DDR12 (3 << 0) -#define AT91_DDRSDRC_NR (3 << 2) /* Number of Row Bits */ -#define AT91_DDRSDRC_NR_11 (0 << 2) -#define AT91_DDRSDRC_NR_12 (1 << 2) -#define AT91_DDRSDRC_NR_13 (2 << 2) -#define AT91_DDRSDRC_NR_14 (3 << 2) -#define AT91_DDRSDRC_CAS (7 << 4) /* CAS Latency */ -#define AT91_DDRSDRC_CAS_2 (2 << 4) -#define AT91_DDRSDRC_CAS_3 (3 << 4) -#define AT91_DDRSDRC_CAS_25 (6 << 4) -#define AT91_DDRSDRC_DLL (1 << 7) /* Reset DLL */ -#define AT91_DDRSDRC_DICDS (1 << 8) /* Output impedance control */ - -#define AT91_DDRSDRC_T0PR 0x0C /* Timing 0 Register */ -#define AT91_DDRSDRC_TRAS (0xf << 0) /* Active to Precharge delay */ -#define AT91_DDRSDRC_TRCD (0xf << 4) /* Row to Column delay */ -#define AT91_DDRSDRC_TWR (0xf << 8) /* Write recovery delay */ -#define AT91_DDRSDRC_TRC (0xf << 12) /* Row cycle delay */ -#define AT91_DDRSDRC_TRP (0xf << 16) /* Row precharge delay */ -#define AT91_DDRSDRC_TRRD (0xf << 20) /* Active BankA to BankB */ -#define AT91_DDRSDRC_TWTR (1 << 24) /* Internal Write to Read delay */ -#define AT91_DDRSDRC_TMRD (0xf << 28) /* Load mode to active/refresh delay */ - -#define AT91_DDRSDRC_T1PR 0x10 /* Timing 1 Register */ -#define AT91_DDRSDRC_TRFC (0x1f << 0) /* Row Cycle Delay */ -#define AT91_DDRSDRC_TXSNR (0xff << 8) /* Exit self-refresh to non-read */ -#define AT91_DDRSDRC_TXSRD (0xff << 16) /* Exit self-refresh to read */ -#define AT91_DDRSDRC_TXP (0xf << 24) /* Exit power-down delay */ - -#define AT91_DDRSDRC_LPR 0x18 /* Low Power Register */ -#define AT91_DDRSDRC_LPCB (3 << 0) /* Low-power Configurations */ -#define AT91_DDRSDRC_LPCB_DISABLE 0 -#define AT91_DDRSDRC_LPCB_SELF_REFRESH 1 -#define AT91_DDRSDRC_LPCB_POWER_DOWN 2 -#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3 -#define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */ -#define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */ -#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */ -#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */ -#define AT91_DDRSDRC_TIMEOUT (3 << 12) /* Time to define when Low Power Mode is enabled */ -#define AT91_DDRSDRC_TIMEOUT_0_CLK_CYCLES (0 << 12) -#define AT91_DDRSDRC_TIMEOUT_64_CLK_CYCLES (1 << 12) -#define AT91_DDRSDRC_TIMEOUT_128_CLK_CYCLES (2 << 12) - -#define AT91_DDRSDRC_MDR 0x1C /* Memory Device Register */ -#define AT91_DDRSDRC_MD (3 << 0) /* Memory Device Type */ -#define AT91_DDRSDRC_MD_SDR 0 -#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 -#define AT91_DDRSDRC_MD_DDR 2 -#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 -#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */ -#define AT91_DDRSDRC_DBW_32BITS (0 << 4) -#define AT91_DDRSDRC_DBW_16BITS (1 << 4) - -#define AT91_DDRSDRC_DLLR 0x20 /* DLL Information Register */ -#define AT91_DDRSDRC_MDINC (1 << 0) /* Master Delay increment */ -#define AT91_DDRSDRC_MDDEC (1 << 1) /* Master Delay decrement */ -#define AT91_DDRSDRC_MDOVF (1 << 2) /* Master Delay Overflow */ -#define AT91_DDRSDRC_SDCOVF (1 << 3) /* Slave Delay Correction Overflow */ -#define AT91_DDRSDRC_SDCUDF (1 << 4) /* Slave Delay Correction Underflow */ -#define AT91_DDRSDRC_SDERF (1 << 5) /* Slave Delay Correction error */ -#define AT91_DDRSDRC_MDVAL (0xff << 8) /* Master Delay value */ -#define AT91_DDRSDRC_SDVAL (0xff << 16) /* Slave Delay value */ -#define AT91_DDRSDRC_SDCVAL (0xff << 24) /* Slave Delay Correction value */ - -/* Register access macros */ -#define at91_ramc_read(num, reg) \ - at91_sys_read(AT91_DDRSDRC##num + reg) -#define at91_ramc_write(num, reg, value) \ - at91_sys_write(AT91_DDRSDRC##num + reg, value) - - -#endif diff --git a/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h b/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h index d27b15b..e2f8da8 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h +++ b/arch/arm/mach-at91/include/mach/at91sam9_ddrsdr.h @@ -46,10 +46,10 @@ #define AT91_DDRSDRC_CAS_25 (6 << 4) #define AT91_DDRSDRC_RST_DLL (1 << 7) /* Reset DLL */ #define AT91_DDRSDRC_DICDS (1 << 8) /* Output impedance control */ -#define AT91_DDRSDRC_DIS_DLL (1 << 9) /* Disable DLL */ -#define AT91_DDRSDRC_OCD (1 << 12) /* Off-Chip Driver */ -#define AT91_DDRSDRC_DQMS (1 << 16) /* Mask Data is Shared */ -#define AT91_DDRSDRC_ACTBST (1 << 18) /* Active Bank X to Burst Stop Read Access Bank Y */ +#define AT91_DDRSDRC_DIS_DLL (1 << 9) /* Disable DLL [SAM9 Only] */ +#define AT91_DDRSDRC_OCD (1 << 12) /* Off-Chip Driver [SAM9 Only] */ +#define AT91_DDRSDRC_DQMS (1 << 16) /* Mask Data is Shared [SAM9 Only] */ +#define AT91_DDRSDRC_ACTBST (1 << 18) /* Active Bank X to Burst Stop Read Access Bank Y [SAM9 Only] */ #define AT91_DDRSDRC_T0PR 0x0C /* Timing 0 Register */ #define AT91_DDRSDRC_TRAS (0xf << 0) /* Active to Precharge delay */ @@ -59,7 +59,8 @@ #define AT91_DDRSDRC_TRP (0xf << 16) /* Row precharge delay */ #define AT91_DDRSDRC_TRRD (0xf << 20) /* Active BankA to BankB */ #define AT91_DDRSDRC_TWTR (0x7 << 24) /* Internal Write to Read delay */ -#define AT91_DDRSDRC_RED_WRRD (0x1 << 27) /* Reduce Write to Read Delay */ +#define AT91CAP9_DDRSDRC_TWTR (1 << 24) /* Internal Write to Read delay */ +#define AT91_DDRSDRC_RED_WRRD (0x1 << 27) /* Reduce Write to Read Delay [SAM9 Only] */ #define AT91_DDRSDRC_TMRD (0xf << 28) /* Load mode to active/refresh delay */ #define AT91_DDRSDRC_T1PR 0x10 /* Timing 1 Register */ @@ -68,13 +69,14 @@ #define AT91_DDRSDRC_TXSRD (0xff << 16) /* Exit self-refresh to read */ #define AT91_DDRSDRC_TXP (0xf << 24) /* Exit power-down delay */ -#define AT91_DDRSDRC_T2PR 0x14 /* Timing 2 Register */ +#define AT91_DDRSDRC_T2PR 0x14 /* Timing 2 Register [SAM9 Only] */ #define AT91_DDRSDRC_TXARD (0xf << 0) /* Exit active power down delay to read command in mode "Fast Exit" */ #define AT91_DDRSDRC_TXARDS (0xf << 4) /* Exit active power down delay to read command in mode "Slow Exit" */ #define AT91_DDRSDRC_TRPA (0xf << 8) /* Row Precharge All delay */ #define AT91_DDRSDRC_TRTP (0x7 << 12) /* Read to Precharge delay */ #define AT91_DDRSDRC_LPR 0x1C /* Low Power Register */ +#define AT91CAP9_DDRSDRC_LPR 0x18 /* Low Power Register */ #define AT91_DDRSDRC_LPCB (3 << 0) /* Low-power Configurations */ #define AT91_DDRSDRC_LPCB_DISABLE 0 #define AT91_DDRSDRC_LPCB_SELF_REFRESH 1 @@ -92,32 +94,40 @@ #define AT91_DDRSDRC_UPD_MR (3 << 20) /* Update load mode register and extended mode register */ #define AT91_DDRSDRC_MDR 0x20 /* Memory Device Register */ +#define AT91CAP9_DDRSDRC_MDR 0x1C /* Memory Device Register */ #define AT91_DDRSDRC_MD (3 << 0) /* Memory Device Type */ #define AT91_DDRSDRC_MD_SDR 0 #define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 +#define AT91CAP9_DDRSDRC_MD_DDR 2 #define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 -#define AT91_DDRSDRC_MD_DDR2 6 +#define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */ #define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */ #define AT91_DDRSDRC_DBW_32BITS (0 << 4) #define AT91_DDRSDRC_DBW_16BITS (1 << 4) #define AT91_DDRSDRC_DLL 0x24 /* DLL Information Register */ +#define AT91CAP9_DDRSDRC_DLL 0x20 /* DLL Information Register */ #define AT91_DDRSDRC_MDINC (1 << 0) /* Master Delay increment */ #define AT91_DDRSDRC_MDDEC (1 << 1) /* Master Delay decrement */ #define AT91_DDRSDRC_MDOVF (1 << 2) /* Master Delay Overflow */ +#define AT91CAP9_DDRSDRC_SDCOVF (1 << 3) /* Slave Delay Correction Overflow */ +#define AT91CAP9_DDRSDRC_SDCUDF (1 << 4) /* Slave Delay Correction Underflow */ +#define AT91CAP9_DDRSDRC_SDERF (1 << 5) /* Slave Delay Correction error */ #define AT91_DDRSDRC_MDVAL (0xff << 8) /* Master Delay value */ +#define AT91CAP9_DDRSDRC_SDVAL (0xff << 16) /* Slave Delay value */ +#define AT91CAP9_DDRSDRC_SDCVAL (0xff << 24) /* Slave Delay Correction value */ -#define AT91_DDRSDRC_HS 0x2C /* High Speed Register */ +#define AT91_DDRSDRC_HS 0x2C /* High Speed Register [SAM9 Only] */ #define AT91_DDRSDRC_DIS_ATCP_RD (1 << 2) /* Anticip read access is disabled */ #define AT91_DDRSDRC_DELAY(n) (0x30 + (0x4 * (n))) /* Delay I/O Register n */ -#define AT91_DDRSDRC_WPMR 0xE4 /* Write Protect Mode Register */ +#define AT91_DDRSDRC_WPMR 0xE4 /* Write Protect Mode Register [SAM9 Only] */ #define AT91_DDRSDRC_WP (1 << 0) /* Write protect enable */ #define AT91_DDRSDRC_WPKEY (0xffffff << 8) /* Write protect key */ #define AT91_DDRSDRC_KEY (0x444452 << 8) /* Write protect key = "DDR" */ -#define AT91_DDRSDRC_WPSR 0xE8 /* Write Protect Status Register */ +#define AT91_DDRSDRC_WPSR 0xE8 /* Write Protect Status Register [SAM9 Only] */ #define AT91_DDRSDRC_WPVS (1 << 0) /* Write protect violation status */ #define AT91_DDRSDRC_WPVSRC (0xffff << 8) /* Write protect violation source */ diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h index ce9a206..7eb40d2 100644 --- a/arch/arm/mach-at91/pm.h +++ b/arch/arm/mach-at91/pm.h @@ -25,21 +25,21 @@ static inline u32 sdram_selfrefresh_enable(void) : : "r" (0)) #elif defined(CONFIG_ARCH_AT91CAP9) -#include +#include static inline u32 sdram_selfrefresh_enable(void) { u32 saved_lpr, lpr; - saved_lpr = at91_ramc_read(0, AT91_DDRSDRC_LPR); + saved_lpr = at91_ramc_read(0, AT91CAP9_DDRSDRC_LPR); lpr = saved_lpr & ~AT91_DDRSDRC_LPCB; - at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH); + at91_ramc_write(0, AT91CAP9_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH); return saved_lpr; } -#define sdram_selfrefresh_disable(saved_lpr) at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr) +#define sdram_selfrefresh_disable(saved_lpr) at91_ramc_write(0, AT91CAP9_DDRSDRC_LPR, saved_lpr) #define wait_for_interrupt_enable() cpu_do_idle() #elif defined(CONFIG_ARCH_AT91SAM9G45) diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index f7922a4..92dfb84 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S @@ -18,9 +18,8 @@ #if defined(CONFIG_ARCH_AT91RM9200) #include -#elif defined(CONFIG_ARCH_AT91CAP9) -#include -#elif defined(CONFIG_ARCH_AT91SAM9G45) +#elif defined(CONFIG_ARCH_AT91CAP9) \ + || defined(CONFIG_ARCH_AT91SAM9G45) #include #else #include -- cgit v0.10.2 From c017759418fa4956f995e5eb595ea353ca6d9a3c Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Tue, 29 Nov 2011 22:01:08 +0800 Subject: ARM: at91: introduce AT91_SAM9_ALT_RESET to select the at91sam9 alternative reset Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 4f991f2..4275577 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -18,6 +18,9 @@ config HAVE_AT91_USART4 config HAVE_AT91_USART5 bool +config AT91_SAM9_ALT_RESET + bool + menu "Atmel AT91 System-on-Chip" choice @@ -39,6 +42,7 @@ config ARCH_AT91SAM9260 select HAVE_AT91_USART4 select HAVE_AT91_USART5 select HAVE_NET_MACB + select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9261 bool "AT91SAM9261" @@ -46,6 +50,7 @@ config ARCH_AT91SAM9261 select GENERIC_CLOCKEVENTS select HAVE_FB_ATMEL select HAVE_AT91_DBGU0 + select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9G10 bool "AT91SAM9G10" @@ -53,6 +58,7 @@ config ARCH_AT91SAM9G10 select GENERIC_CLOCKEVENTS select HAVE_AT91_DBGU0 select HAVE_FB_ATMEL + select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9263 bool "AT91SAM9263" @@ -61,6 +67,7 @@ config ARCH_AT91SAM9263 select HAVE_FB_ATMEL select HAVE_NET_MACB select HAVE_AT91_DBGU1 + select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9RL bool "AT91SAM9RL" @@ -69,6 +76,7 @@ config ARCH_AT91SAM9RL select HAVE_AT91_USART3 select HAVE_FB_ATMEL select HAVE_AT91_DBGU0 + select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9G20 bool "AT91SAM9G20" @@ -79,6 +87,7 @@ config ARCH_AT91SAM9G20 select HAVE_AT91_USART4 select HAVE_AT91_USART5 select HAVE_NET_MACB + select AT91_SAM9_ALT_RESET config ARCH_AT91SAM9G45 bool "AT91SAM9G45" diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 242174f..e8e961b 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -8,15 +8,16 @@ obj-n := obj- := obj-$(CONFIG_AT91_PMC_UNIT) += clock.o +obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o # CPU-specific support obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o -obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o at91sam9_alt_reset.o -obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o at91sam9_alt_reset.o -obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o at91sam9_alt_reset.o -obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o at91sam9_alt_reset.o -obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o at91sam9_alt_reset.o -obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o at91sam9_alt_reset.o +obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o +obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o +obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o +obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o +obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o +obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o -- cgit v0.10.2 From e9f68b5cc6160a473fc668054fd13f435fd4508b Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 18 Nov 2011 01:25:52 +0800 Subject: ARM: at91: make rstc soc independent Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/at91cap9.c b/arch/arm/mach-at91/at91cap9.c index edb879a..7a2309e 100644 --- a/arch/arm/mach-at91/at91cap9.c +++ b/arch/arm/mach-at91/at91cap9.c @@ -331,6 +331,7 @@ static void __init at91cap9_map_io(void) static void __init at91cap9_ioremap_registers(void) { at91_ioremap_shdwc(AT91CAP9_BASE_SHDWC); + at91_ioremap_rstc(AT91CAP9_BASE_RSTC); at91sam926x_ioremap_pit(AT91CAP9_BASE_PIT); at91sam9_ioremap_smc(0, AT91CAP9_BASE_SMC); } diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index 5e46e4a..d4036ba 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -323,6 +323,7 @@ static void __init at91sam9260_map_io(void) static void __init at91sam9260_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9260_BASE_SHDWC); + at91_ioremap_rstc(AT91SAM9260_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9260_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9260_BASE_SMC); } diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index b85b9ea..023c2ff 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -281,6 +281,7 @@ static void __init at91sam9261_map_io(void) static void __init at91sam9261_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9261_BASE_SHDWC); + at91_ioremap_rstc(AT91SAM9261_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9261_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9261_BASE_SMC); } diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 79e3669..75e876c 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -301,6 +301,7 @@ static void __init at91sam9263_map_io(void) static void __init at91sam9263_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9263_BASE_SHDWC); + at91_ioremap_rstc(AT91SAM9263_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9263_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9263_BASE_SMC0); at91sam9_ioremap_smc(1, AT91SAM9263_BASE_SMC1); diff --git a/arch/arm/mach-at91/at91sam9_alt_reset.S b/arch/arm/mach-at91/at91sam9_alt_reset.S index d3f931c..518e423 100644 --- a/arch/arm/mach-at91/at91sam9_alt_reset.S +++ b/arch/arm/mach-at91/at91sam9_alt_reset.S @@ -23,7 +23,8 @@ .globl at91sam9_alt_restart at91sam9_alt_restart: ldr r0, .at91_va_base_sdramc @ preload constants - ldr r1, .at91_va_base_rstc_cr + ldr r1, =at91_rstc_base + ldr r1, [r1] mov r2, #1 mov r3, #AT91_SDRAMC_LPCB_POWER_DOWN @@ -33,11 +34,9 @@ at91sam9_alt_restart: ldr r0, .at91_va_base_sdramc @ preload constants str r2, [r0, #AT91_SDRAMC_TR] @ disable SDRAM access str r3, [r0, #AT91_SDRAMC_LPR] @ power down SDRAM - str r4, [r1] @ reset processor + str r4, [r1, #AT91_RSTC_CR] @ reset processor b . .at91_va_base_sdramc: .word AT91_VA_BASE_SYS + AT91_SDRAMC0 -.at91_va_base_rstc_cr: - .word AT91_VA_BASE_SYS + AT91_RSTC_CR diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 7032dd3..ec6a2db 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -336,6 +336,7 @@ static void __init at91sam9g45_map_io(void) static void __init at91sam9g45_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9G45_BASE_SHDWC); + at91_ioremap_rstc(AT91SAM9G45_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9G45_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9G45_BASE_SMC); } diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index d6bcb1d..d2c91a8 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -286,6 +286,7 @@ static void __init at91sam9rl_map_io(void) static void __init at91sam9rl_ioremap_registers(void) { at91_ioremap_shdwc(AT91SAM9RL_BASE_SHDWC); + at91_ioremap_rstc(AT91SAM9RL_BASE_RSTC); at91sam926x_ioremap_pit(AT91SAM9RL_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9RL_BASE_SMC); } diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 4866b81..62e508b 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -58,6 +58,7 @@ extern void at91_irq_suspend(void); extern void at91_irq_resume(void); /* reset */ +extern void at91_ioremap_rstc(u32 base_addr); extern void at91sam9_alt_restart(char, const char *); /* shutdown */ diff --git a/arch/arm/mach-at91/include/mach/at91_rstc.h b/arch/arm/mach-at91/include/mach/at91_rstc.h index cbd2bf0..875fa33 100644 --- a/arch/arm/mach-at91/include/mach/at91_rstc.h +++ b/arch/arm/mach-at91/include/mach/at91_rstc.h @@ -16,13 +16,25 @@ #ifndef AT91_RSTC_H #define AT91_RSTC_H -#define AT91_RSTC_CR (AT91_RSTC + 0x00) /* Reset Controller Control Register */ +#ifndef __ASSEMBLY__ +extern void __iomem *at91_rstc_base; + +#define at91_rstc_read(field) \ + __raw_readl(at91_rstc_base + field) + +#define at91_rstc_write(field, value) \ + __raw_writel(value, at91_rstc_base + field); +#else +.extern at91_rstc_base +#endif + +#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ #define AT91_RSTC_PROCRST (1 << 0) /* Processor Reset */ #define AT91_RSTC_PERRST (1 << 2) /* Peripheral Reset */ #define AT91_RSTC_EXTRST (1 << 3) /* External Reset */ #define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ -#define AT91_RSTC_SR (AT91_RSTC + 0x04) /* Reset Controller Status Register */ +#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */ #define AT91_RSTC_URSTS (1 << 0) /* User Reset Status */ #define AT91_RSTC_RSTTYP (7 << 8) /* Reset Type */ #define AT91_RSTC_RSTTYP_GENERAL (0 << 8) @@ -33,7 +45,7 @@ #define AT91_RSTC_NRSTL (1 << 16) /* NRST Pin Level */ #define AT91_RSTC_SRCMP (1 << 17) /* Software Reset Command in Progress */ -#define AT91_RSTC_MR (AT91_RSTC + 0x08) /* Reset Controller Mode Register */ +#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ #define AT91_RSTC_URSTEN (1 << 0) /* User Reset Enable */ #define AT91_RSTC_URSTIEN (1 << 4) /* User Reset Interrupt Enable */ #define AT91_RSTC_ERSTL (0xf << 8) /* External Reset Length */ diff --git a/arch/arm/mach-at91/include/mach/at91cap9.h b/arch/arm/mach-at91/include/mach/at91cap9.h index 4c0e2f6..61d9529 100644 --- a/arch/arm/mach-at91/include/mach/at91cap9.h +++ b/arch/arm/mach-at91/include/mach/at91cap9.h @@ -83,7 +83,6 @@ #define AT91_DDRSDRC0 (0xffffe600 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffea00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) -#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (cpu_is_at91cap9_revB() ? \ (0xfffffd50 - AT91_BASE_SYS) : \ (0xfffffd60 - AT91_BASE_SYS)) @@ -96,6 +95,7 @@ #define AT91CAP9_BASE_PIOB 0xfffff400 #define AT91CAP9_BASE_PIOC 0xfffff600 #define AT91CAP9_BASE_PIOD 0xfffff800 +#define AT91CAP9_BASE_RSTC 0xfffffd00 #define AT91CAP9_BASE_SHDWC 0xfffffd10 #define AT91CAP9_BASE_RTT 0xfffffd20 #define AT91CAP9_BASE_PIT 0xfffffd30 diff --git a/arch/arm/mach-at91/include/mach/at91sam9260.h b/arch/arm/mach-at91/include/mach/at91sam9260.h index f937c47..fa5ca27 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9260.h +++ b/arch/arm/mach-at91/include/mach/at91sam9260.h @@ -83,7 +83,6 @@ #define AT91_SDRAMC0 (0xffffea00 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffee00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) -#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd50 - AT91_BASE_SYS) #define AT91SAM9260_BASE_ECC 0xffffe800 @@ -92,6 +91,7 @@ #define AT91SAM9260_BASE_PIOA 0xfffff400 #define AT91SAM9260_BASE_PIOB 0xfffff600 #define AT91SAM9260_BASE_PIOC 0xfffff800 +#define AT91SAM9260_BASE_RSTC 0xfffffd00 #define AT91SAM9260_BASE_SHDWC 0xfffffd10 #define AT91SAM9260_BASE_RTT 0xfffffd20 #define AT91SAM9260_BASE_PIT 0xfffffd30 diff --git a/arch/arm/mach-at91/include/mach/at91sam9261.h b/arch/arm/mach-at91/include/mach/at91sam9261.h index 175604e..7cde2d3 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9261.h +++ b/arch/arm/mach-at91/include/mach/at91sam9261.h @@ -68,7 +68,6 @@ #define AT91_SDRAMC0 (0xffffea00 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffee00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) -#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd50 - AT91_BASE_SYS) #define AT91SAM9261_BASE_SMC 0xffffec00 @@ -76,6 +75,7 @@ #define AT91SAM9261_BASE_PIOA 0xfffff400 #define AT91SAM9261_BASE_PIOB 0xfffff600 #define AT91SAM9261_BASE_PIOC 0xfffff800 +#define AT91SAM9261_BASE_RSTC 0xfffffd00 #define AT91SAM9261_BASE_SHDWC 0xfffffd10 #define AT91SAM9261_BASE_RTT 0xfffffd20 #define AT91SAM9261_BASE_PIT 0xfffffd30 diff --git a/arch/arm/mach-at91/include/mach/at91sam9263.h b/arch/arm/mach-at91/include/mach/at91sam9263.h index 80c9150..5949abd 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9263.h +++ b/arch/arm/mach-at91/include/mach/at91sam9263.h @@ -78,7 +78,6 @@ #define AT91_SDRAMC1 (0xffffe800 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffec00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) -#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) #define AT91SAM9263_BASE_ECC0 0xffffe000 @@ -91,6 +90,7 @@ #define AT91SAM9263_BASE_PIOC 0xfffff600 #define AT91SAM9263_BASE_PIOD 0xfffff800 #define AT91SAM9263_BASE_PIOE 0xfffffa00 +#define AT91SAM9263_BASE_RSTC 0xfffffd00 #define AT91SAM9263_BASE_SHDWC 0xfffffd10 #define AT91SAM9263_BASE_RTT0 0xfffffd20 #define AT91SAM9263_BASE_PIT 0xfffffd30 diff --git a/arch/arm/mach-at91/include/mach/at91sam9g45.h b/arch/arm/mach-at91/include/mach/at91sam9g45.h index f0c23c9..dd9c95e 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9g45.h +++ b/arch/arm/mach-at91/include/mach/at91sam9g45.h @@ -90,7 +90,6 @@ #define AT91_DDRSDRC0 (0xffffe600 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffea00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) -#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) #define AT91SAM9G45_BASE_ECC 0xffffe200 @@ -102,6 +101,7 @@ #define AT91SAM9G45_BASE_PIOC 0xfffff600 #define AT91SAM9G45_BASE_PIOD 0xfffff800 #define AT91SAM9G45_BASE_PIOE 0xfffffa00 +#define AT91SAM9G45_BASE_RSTC 0xfffffd00 #define AT91SAM9G45_BASE_SHDWC 0xfffffd10 #define AT91SAM9G45_BASE_RTT 0xfffffd20 #define AT91SAM9G45_BASE_PIT 0xfffffd30 diff --git a/arch/arm/mach-at91/include/mach/at91sam9rl.h b/arch/arm/mach-at91/include/mach/at91sam9rl.h index 2bb359e..d7bead7 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9rl.h +++ b/arch/arm/mach-at91/include/mach/at91sam9rl.h @@ -72,7 +72,6 @@ #define AT91_SDRAMC0 (0xffffea00 - AT91_BASE_SYS) #define AT91_MATRIX (0xffffee00 - AT91_BASE_SYS) #define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) -#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) #define AT91_SCKCR (0xfffffd50 - AT91_BASE_SYS) #define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) @@ -84,6 +83,7 @@ #define AT91SAM9RL_BASE_PIOB 0xfffff600 #define AT91SAM9RL_BASE_PIOC 0xfffff800 #define AT91SAM9RL_BASE_PIOD 0xfffffa00 +#define AT91SAM9RL_BASE_RSTC 0xfffffd00 #define AT91SAM9RL_BASE_SHDWC 0xfffffd10 #define AT91SAM9RL_BASE_RTT 0xfffffd20 #define AT91SAM9RL_BASE_PIT 0xfffffd30 diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 62ad955..1606379 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -34,7 +34,6 @@ /* * Show the reason for the previous system reset. */ -#if defined(AT91_RSTC) #include #include @@ -58,10 +57,10 @@ static void __init show_reset_status(void) char *reason, *r2 = reset; u32 reset_type, wake_type; - if (!at91_shdwc_base) + if (!at91_shdwc_base || !at91_rstc_base) return; - reset_type = at91_sys_read(AT91_RSTC_SR) & AT91_RSTC_RSTTYP; + reset_type = at91_rstc_read(AT91_RSTC_SR) & AT91_RSTC_RSTTYP; wake_type = at91_shdwc_read(AT91_SHDW_SR); switch (reset_type) { @@ -102,10 +101,6 @@ static void __init show_reset_status(void) } pr_info("AT91: Starting after %s %s\n", reason, r2); } -#else -static void __init show_reset_status(void) {} -#endif - static int at91_pm_valid_state(suspend_state_t state) { diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index f524718..69d3fc4 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -284,6 +284,15 @@ void __init at91_ioremap_shdwc(u32 base_addr) pm_power_off = at91sam9_poweroff; } +void __iomem *at91_rstc_base; + +void __init at91_ioremap_rstc(u32 base_addr) +{ + at91_rstc_base = ioremap(base_addr, 16); + if (!at91_rstc_base) + panic("Impossible to ioremap at91_rstc_base\n"); +} + void __init at91_initialize(unsigned long main_clock) { at91_boot_soc.ioremap_registers(); -- cgit v0.10.2 From 14f991a730f453a1c8f114ccb686f83e158fdd92 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 18 Nov 2011 01:41:28 +0800 Subject: ARM: at91: Fix at91sam9g45 and at91cap9 reset As on the other sam9 we need to cleanly shutdown the DDRAM before rebooting. On those SoC the SDRAM/DDRAM controller is different. So, the assembly code ends up being not cleanly combined with previous at91sam9_alt_restart function. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 4275577..71feb00 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -21,6 +21,9 @@ config HAVE_AT91_USART5 config AT91_SAM9_ALT_RESET bool +config AT91_SAM9G45_RESET + bool + menu "Atmel AT91 System-on-Chip" choice @@ -97,6 +100,7 @@ config ARCH_AT91SAM9G45 select HAVE_FB_ATMEL select HAVE_NET_MACB select HAVE_AT91_DBGU1 + select AT91_SAM9G45_RESET config ARCH_AT91CAP9 bool "AT91CAP9" @@ -105,6 +109,7 @@ config ARCH_AT91CAP9 select HAVE_FB_ATMEL select HAVE_NET_MACB select HAVE_AT91_DBGU1 + select AT91_SAM9G45_RESET config ARCH_AT91X40 bool "AT91x40" diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index e8e961b..705e1fb 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -9,6 +9,7 @@ obj- := obj-$(CONFIG_AT91_PMC_UNIT) += clock.o obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o +obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o # CPU-specific support obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o diff --git a/arch/arm/mach-at91/at91cap9.c b/arch/arm/mach-at91/at91cap9.c index 7a2309e..a42edc2 100644 --- a/arch/arm/mach-at91/at91cap9.c +++ b/arch/arm/mach-at91/at91cap9.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "soc.h" #include "generic.h" @@ -314,11 +313,6 @@ static struct at91_gpio_bank at91cap9_gpio[] __initdata = { } }; -static void at91cap9_restart(char mode, const char *cmd) -{ - at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); -} - /* -------------------------------------------------------------------- * AT91CAP9 processor initialization * -------------------------------------------------------------------- */ @@ -338,7 +332,7 @@ static void __init at91cap9_ioremap_registers(void) static void __init at91cap9_initialize(void) { - arm_pm_restart = at91cap9_restart; + arm_pm_restart = at91sam9g45_restart; at91_extern_irq = (1 << AT91CAP9_ID_IRQ0) | (1 << AT91CAP9_ID_IRQ1); /* Register GPIO subsystem */ diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index ec6a2db..1cb6a96 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "soc.h" @@ -318,11 +317,6 @@ static struct at91_gpio_bank at91sam9g45_gpio[] __initdata = { } }; -static void at91sam9g45_restart(char mode, const char *cmd) -{ - at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); -} - /* -------------------------------------------------------------------- * AT91SAM9G45 processor initialization * -------------------------------------------------------------------- */ diff --git a/arch/arm/mach-at91/at91sam9g45_reset.S b/arch/arm/mach-at91/at91sam9g45_reset.S new file mode 100644 index 0000000..0468be1 --- /dev/null +++ b/arch/arm/mach-at91/at91sam9g45_reset.S @@ -0,0 +1,40 @@ +/* + * reset AT91SAM9G45 as per errata + * + * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD + * + * unless the SDRAM is cleanly shutdown before we hit the + * reset register it can be left driving the data bus and + * killing the chance of a subsequent boot from NAND + * + * GPLv2 Only + */ + +#include +#include +#include +#include + + .arm + + .globl at91sam9g45_restart + +at91sam9g45_restart: + ldr r0, .at91_va_base_sdramc0 @ preload constants + ldr r1, =at91_rstc_base + ldr r1, [r1] + + mov r2, #1 + mov r3, #AT91_DDRSDRC_LPCB_POWER_DOWN + ldr r4, =AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST + + .balign 32 @ align to cache line + + str r2, [r0, #AT91_DDRSDRC_RTR] @ disable DDR0 access + str r3, [r0, #AT91_DDRSDRC_LPR] @ power down DDR0 + str r4, [r1, #AT91_RSTC_CR] @ reset processor + + b . + +.at91_va_base_sdramc0: + .word AT91_VA_BASE_SYS + AT91_DDRSDRC0 diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 62e508b..5941334 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -60,6 +60,7 @@ extern void at91_irq_resume(void); /* reset */ extern void at91_ioremap_rstc(u32 base_addr); extern void at91sam9_alt_restart(char, const char *); +extern void at91sam9g45_restart(char, const char *); /* shutdown */ extern void at91_ioremap_shdwc(u32 base_addr); -- cgit v0.10.2 From 6c5aa407d52e9e40c2e31a3dfa5cb19a9672bf36 Mon Sep 17 00:00:00 2001 From: "Cousson, Benoit" Date: Fri, 20 Jan 2012 16:55:04 +0100 Subject: i2c: OMAP: Fix OMAP1 build error CONFIG_OF is not defined for OMAP1 yet and thus the omap1_defconfig build generate an error for 3.3-rc1. drivers/i2c/busses/i2c-omap.c: In function 'omap_i2c_probe': drivers/i2c/busses/i2c-omap.c:1021:26: error: 'omap_i2c_of_match' undeclared (first use in this function) drivers/i2c/busses/i2c-omap.c:1021:26: note: each undeclared identifier is reported only once for each function it appears in Wrap omap_i2c_of_match with of_match_ptr() to prevent compilation error in case of OMAP1 build. Signed-off-by: Benoit Cousson Cc: Ben Dooks Signed-off-by: Tony Lindgren diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index f713eac..801df60 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1018,7 +1018,7 @@ omap_i2c_probe(struct platform_device *pdev) goto err_release_region; } - match = of_match_device(omap_i2c_of_match, &pdev->dev); + match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev); if (match) { u32 freq = 100000; /* default to 100000 Hz */ -- cgit v0.10.2 From 7080727c207f569751e94aaed7d94e873c69115f Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 20 Jan 2012 05:38:30 -0800 Subject: ARM: OMAP2: fix omap3 touchbook kconfig warning warning: (MACH_OMAP3_TOUCHBOOK && DRM_RADEON_KMS && DRM_I915 && STUB_POULSBO && FB_BACKLIGHT && USB_APPLEDISPLAY && FB_OLPC_DCON && ASUS_LAPTOP && SONY_LAPTOP && THINKPAD_ACPI && EEEPC_LAPTOP && ACPI_ASUS && ACPI_CMPC && SAMSUNG_Q10) selects BACKLIGHT_CLASS_DEVICE which has unmet direct dependencies (HAS_IOMEM && BACKLIGHT_LCD_SUPPORT) A lot of boards need BACKLIGHT_CLASS_DEVICE for the framebuffers to work, but it's not *needed* for the device itself. It might be nice to enable it by default somewhoe if graphics stuff is enabled, but that's another story. Signed-off-by: Felipe Contreras Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index a8ba7b9..782a909 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -220,7 +220,6 @@ config MACH_OMAP3_TOUCHBOOK bool "OMAP3 Touch Book" depends on ARCH_OMAP3 default y - select BACKLIGHT_CLASS_DEVICE config MACH_OMAP_3430SDP bool "OMAP 3430 SDP board" -- cgit v0.10.2 From a075ccc6810dd2532d6bca548bd6e3b59105bd82 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 20 Jan 2012 05:38:31 -0800 Subject: ARM: OMAP2: fix regulator warnings warning: (MACH_OMAP_ZOOM2 && MACH_OMAP_ZOOM3 && MACH_OMAP_4430SDP && MACH_OMAP4_PANDA && TPS6105X) selects REGULATOR_FIXED_VOLTAGE which has unmet direct dependencies (REGULATOR) Signed-off-by: Felipe Contreras Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 782a909..c726108 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -214,7 +214,7 @@ config MACH_OMAP3_PANDORA depends on ARCH_OMAP3 default y select OMAP_PACKAGE_CBB - select REGULATOR_FIXED_VOLTAGE + select REGULATOR_FIXED_VOLTAGE if REGULATOR config MACH_OMAP3_TOUCHBOOK bool "OMAP3 Touch Book" @@ -265,7 +265,7 @@ config MACH_OMAP_ZOOM2 select SERIAL_8250 select SERIAL_CORE_CONSOLE select SERIAL_8250_CONSOLE - select REGULATOR_FIXED_VOLTAGE + select REGULATOR_FIXED_VOLTAGE if REGULATOR config MACH_OMAP_ZOOM3 bool "OMAP3630 Zoom3 board" @@ -275,7 +275,7 @@ config MACH_OMAP_ZOOM3 select SERIAL_8250 select SERIAL_CORE_CONSOLE select SERIAL_8250_CONSOLE - select REGULATOR_FIXED_VOLTAGE + select REGULATOR_FIXED_VOLTAGE if REGULATOR config MACH_CM_T35 bool "CompuLab CM-T35/CM-T3730 modules" @@ -334,7 +334,7 @@ config MACH_OMAP_4430SDP depends on ARCH_OMAP4 select OMAP_PACKAGE_CBL select OMAP_PACKAGE_CBS - select REGULATOR_FIXED_VOLTAGE + select REGULATOR_FIXED_VOLTAGE if REGULATOR config MACH_OMAP4_PANDA bool "OMAP4 Panda Board" @@ -342,7 +342,7 @@ config MACH_OMAP4_PANDA depends on ARCH_OMAP4 select OMAP_PACKAGE_CBL select OMAP_PACKAGE_CBS - select REGULATOR_FIXED_VOLTAGE + select REGULATOR_FIXED_VOLTAGE if REGULATOR config OMAP3_EMU bool "OMAP3 debugging peripherals" -- cgit v0.10.2 From 7c655099b25c889c1268dba894fbc5d515097752 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Mon, 16 Jan 2012 07:46:02 +0100 Subject: ARM: davinci: DA850: remove non-existing pll1_sysclk4-7 clocks DA850: sysclk4-7 only exist for pll0. for pll1 sysclk1-3 exist. Remove the non-existing clocks. Signed-off-by: Bas van den Berg Signed-off-by: Sekhar Nori diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 0ed7fdb..992c4c4 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -153,34 +153,6 @@ static struct clk pll1_sysclk3 = { .div_reg = PLLDIV3, }; -static struct clk pll1_sysclk4 = { - .name = "pll1_sysclk4", - .parent = &pll1_clk, - .flags = CLK_PLL, - .div_reg = PLLDIV4, -}; - -static struct clk pll1_sysclk5 = { - .name = "pll1_sysclk5", - .parent = &pll1_clk, - .flags = CLK_PLL, - .div_reg = PLLDIV5, -}; - -static struct clk pll1_sysclk6 = { - .name = "pll0_sysclk6", - .parent = &pll0_clk, - .flags = CLK_PLL, - .div_reg = PLLDIV6, -}; - -static struct clk pll1_sysclk7 = { - .name = "pll1_sysclk7", - .parent = &pll1_clk, - .flags = CLK_PLL, - .div_reg = PLLDIV7, -}; - static struct clk i2c0_clk = { .name = "i2c0", .parent = &pll0_aux_clk, @@ -397,10 +369,6 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "pll1_aux", &pll1_aux_clk), CLK(NULL, "pll1_sysclk2", &pll1_sysclk2), CLK(NULL, "pll1_sysclk3", &pll1_sysclk3), - CLK(NULL, "pll1_sysclk4", &pll1_sysclk4), - CLK(NULL, "pll1_sysclk5", &pll1_sysclk5), - CLK(NULL, "pll1_sysclk6", &pll1_sysclk6), - CLK(NULL, "pll1_sysclk7", &pll1_sysclk7), CLK("i2c_davinci.1", NULL, &i2c0_clk), CLK(NULL, "timer0", &timerp64_0_clk), CLK("watchdog", NULL, &timerp64_1_clk), -- cgit v0.10.2 From b469d4329cf949043f9b93a6644f2c64015ef8cd Mon Sep 17 00:00:00 2001 From: Tiejun Chen Date: Wed, 11 Jan 2012 05:51:10 +0000 Subject: kmemleak: Only scan non-zero-size areas Kmemleak should only track valid scan areas with a non-zero size. Otherwise, such area may reside just at the end of an object and kmemleak would report "Adding scan area to unknown object". Signed-off-by: Tiejun Chen Signed-off-by: Catalin Marinas diff --git a/mm/kmemleak.c b/mm/kmemleak.c index c833add..f9f7310 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1036,7 +1036,7 @@ void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (atomic_read(&kmemleak_enabled) && ptr && size && !IS_ERR(ptr)) add_scan_area((unsigned long)ptr, size, gfp); else if (atomic_read(&kmemleak_early_log)) log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0); -- cgit v0.10.2 From 53b6b3e00b8454aeab497119443d4f365ee4e46e Mon Sep 17 00:00:00 2001 From: Sumit Semwal Date: Fri, 20 Jan 2012 15:04:25 +0530 Subject: MAINTAINERS: Add dma-buf sharing framework maintainer Adding maintainer info for dma-buf buffer sharing framework; some mailing lists interested in this work are also added. Signed-off-by: Sumit Semwal Signed-off-by: Sumit Semwal Acked-by: Arnd Bergmann Acked-by: Daniel Vetter Acked-by: Dave Airlie diff --git a/MAINTAINERS b/MAINTAINERS index 89b70df..93c68d5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2246,6 +2246,17 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/teigland/dlm.git S: Supported F: fs/dlm/ +DMA BUFFER SHARING FRAMEWORK +M: Sumit Semwal +S: Maintained +L: linux-media@vger.kernel.org +L: dri-devel@lists.freedesktop.org +L: linaro-mm-sig@lists.linaro.org +F: drivers/base/dma-buf* +F: include/linux/dma-buf* +F: Documentation/dma-buf-sharing.txt +T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git + DMA GENERIC OFFLOAD ENGINE SUBSYSTEM M: Vinod Koul M: Dan Williams -- cgit v0.10.2 From 546edd83abe4f03472d721c60011c5ff95e25474 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Jan 2012 13:38:31 -0700 Subject: pinctrl: fix pinconf_pins_show iteration Commit 706e852 "pinctrl: correct a offset while enumerating pins" modified the variable used by pinconf_pin_show()'s for loop, but didn't update the for loop test expression. Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 1259872..1892a37 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -236,7 +236,7 @@ static int pinconf_pins_show(struct seq_file *s, void *what) seq_puts(s, "Format: pin (name): pinmux setting array\n"); /* The pin number can be retrived from the pin controller descriptor */ - for (i = 0; pin < pctldev->desc->npins; i++) { + for (i = 0; i < pctldev->desc->npins; i++) { struct pin_desc *desc; pin = pctldev->desc->pins[i].number; -- cgit v0.10.2 From b370d29ea7565a638ccf85389488364b5abb39fa Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 20 Jan 2012 10:42:40 +0000 Subject: kmemleak: Disable early logging when kmemleak is off by default Commit b6693005 (kmemleak: When the early log buffer is exceeded, report the actual number) deferred the disabling of the early logging to kmemleak_init(). However, when CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y, the early logging was no longer disabled causing __init kmemleak functions to be called even after the kernel freed the init memory. This patch disables the early logging during kmemleak_init() if kmemleak is left disabled. Reported-by: Dirk Gouders Tested-by: Dirk Gouders Tested-by: Josh Boyer Signed-off-by: Catalin Marinas diff --git a/mm/kmemleak.c b/mm/kmemleak.c index f9f7310..45eb621 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1757,6 +1757,7 @@ void __init kmemleak_init(void) #ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF if (!kmemleak_skip_disable) { + atomic_set(&kmemleak_early_log, 0); kmemleak_disable(); return; } -- cgit v0.10.2 From 86b2bbfdbd1fcc4a3aa62ccd3f245c40c5ad5b85 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 20 Jan 2012 10:09:23 -0500 Subject: hwmon: (f71805f) Fix clamping of temperature limits Properly clamp temperature limits set by the user. Without this fix, attempts to write temperature limits above the maximum supported by the chip (255 degrees Celsius) would arbitrarily and unexpectedly result in the limit being set to 0 degree Celsius. Signed-off-by: Jean Delvare Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 92f9497..6dbfd3e 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -283,11 +283,11 @@ static inline long temp_from_reg(u8 reg) static inline u8 temp_to_reg(long val) { - if (val < 0) - val = 0; - else if (val > 1000 * 0xff) - val = 0xff; - return ((val + 500) / 1000); + if (val <= 0) + return 0; + if (val >= 1000 * 0xff) + return 0xff; + return (val + 500) / 1000; } /* -- cgit v0.10.2 From 216f63c41cac9f9f8f181fc19be399293c8c934e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 17:37:21 +0000 Subject: Revert "ARM: sa1100: Refactor mcp-sa11x0 to use platform resources." This reverts commit af9081ae64b941d32239b947882cd59ba855c5db. This revert is necessary to revert 5dd7bf59e0e8563265b3e5b33276099ef628fcc7. diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index ebafe8a..d8aa1c2 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -253,17 +253,6 @@ static void __init assabet_init(void) sa11x0_register_mtd(&assabet_flash_data, assabet_flash_resources, ARRAY_SIZE(assabet_flash_resources)); sa11x0_register_irda(&assabet_irda_data); - - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - - ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); sa11x0_register_mcp(&assabet_mcp_data); } diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c index d12d0f4..fcadc4c 100644 --- a/arch/arm/mach-sa1100/cerf.c +++ b/arch/arm/mach-sa1100/cerf.c @@ -131,16 +131,6 @@ static void __init cerf_init(void) { platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices)); sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1); - - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - sa11x0_register_mcp(&cerf_mcp_data); } diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index c483912..6b7c74b 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -357,16 +357,6 @@ static void __init collie_init(void) sa11x0_register_mtd(&collie_flash_data, collie_flash_resources, ARRAY_SIZE(collie_flash_resources)); - - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - sa11x0_register_mcp(&collie_mcp_data); sharpsl_save_param(); diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c index e3a28ca..480d2ea 100644 --- a/arch/arm/mach-sa1100/generic.c +++ b/arch/arm/mach-sa1100/generic.c @@ -217,15 +217,10 @@ static struct platform_device sa11x0uart3_device = { static struct resource sa11x0mcp_resources[] = { [0] = { .start = __PREG(Ser4MCCR0), - .end = __PREG(Ser4MCCR0) + 0x1C - 1, + .end = __PREG(Ser4MCCR0) + 0xffff, .flags = IORESOURCE_MEM, }, [1] = { - .start = __PREG(Ser4MCCR1), - .end = __PREG(Ser4MCCR1) + 0x4 - 1, - .flags = IORESOURCE_MEM, - }, - [2] = { .start = IRQ_Ser4MCP, .end = IRQ_Ser4MCP, .flags = IORESOURCE_IRQ, diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c index d117cea..48a8f4e 100644 --- a/arch/arm/mach-sa1100/lart.c +++ b/arch/arm/mach-sa1100/lart.c @@ -29,15 +29,6 @@ static struct mcp_plat_data lart_mcp_data = { static void __init lart_init(void) { - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - sa11x0_register_mcp(&lart_mcp_data); } diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c index 748d344..3807c91 100644 --- a/arch/arm/mach-sa1100/shannon.c +++ b/arch/arm/mach-sa1100/shannon.c @@ -61,16 +61,6 @@ static struct mcp_plat_data shannon_mcp_data = { static void __init shannon_init(void) { sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1); - - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - sa11x0_register_mcp(&shannon_mcp_data); } diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index 458ecec..d9b765c 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -384,16 +384,6 @@ static int __init simpad_init(void) sa11x0_register_mtd(&simpad_flash_data, simpad_flash_resources, ARRAY_SIZE(simpad_flash_resources)); - - /* - * Setup the PPC unit correctly. - */ - PPDR &= ~PPC_RXD4; - PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; - PSDR |= PPC_RXD4; - PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); - sa11x0_register_mcp(&simpad_mcp_data); ret = platform_add_devices(devices, ARRAY_SIZE(devices)); diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 9adc2eb..da4e077 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -27,19 +26,12 @@ #include #include -/* Register offsets */ -#define MCCR0 0x00 -#define MCDR0 0x08 -#define MCDR1 0x0C -#define MCDR2 0x10 -#define MCSR 0x18 -#define MCCR1 0x00 +#include + struct mcp_sa11x0 { - u32 mccr0; - u32 mccr1; - unsigned char *mccr0_base; - unsigned char *mccr1_base; + u32 mccr0; + u32 mccr1; }; #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) @@ -47,25 +39,25 @@ struct mcp_sa11x0 { static void mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) { - struct mcp_sa11x0 *priv = priv(mcp); + unsigned int mccr0; divisor /= 32; - priv->mccr0 &= ~0x00007f00; - priv->mccr0 |= divisor << 8; - __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); + mccr0 = Ser4MCCR0 & ~0x00007f00; + mccr0 |= divisor << 8; + Ser4MCCR0 = mccr0; } static void mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) { - struct mcp_sa11x0 *priv = priv(mcp); + unsigned int mccr0; divisor /= 32; - priv->mccr0 &= ~0x0000007f; - priv->mccr0 |= divisor; - __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); + mccr0 = Ser4MCCR0 & ~0x0000007f; + mccr0 |= divisor; + Ser4MCCR0 = mccr0; } /* @@ -79,16 +71,12 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) { int ret = -ETIME; int i; - u32 mcpreg; - struct mcp_sa11x0 *priv = priv(mcp); - mcpreg = reg << 17 | MCDR2_Wr | (val & 0xffff); - __raw_writel(mcpreg, priv->mccr0_base + MCDR2); + Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - mcpreg = __raw_readl(priv->mccr0_base + MCSR); - if (mcpreg & MCSR_CWC) { + if (Ser4MCSR & MCSR_CWC) { ret = 0; break; } @@ -109,18 +97,13 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) { int ret = -ETIME; int i; - u32 mcpreg; - struct mcp_sa11x0 *priv = priv(mcp); - mcpreg = reg << 17 | MCDR2_Rd; - __raw_writel(mcpreg, priv->mccr0_base + MCDR2); + Ser4MCDR2 = reg << 17 | MCDR2_Rd; for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); - mcpreg = __raw_readl(priv->mccr0_base + MCSR); - if (mcpreg & MCSR_CRC) { - ret = __raw_readl(priv->mccr0_base + MCDR2) - & 0xffff; + if (Ser4MCSR & MCSR_CRC) { + ret = Ser4MCDR2 & 0xffff; break; } } @@ -133,19 +116,13 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) static void mcp_sa11x0_enable(struct mcp *mcp) { - struct mcp_sa11x0 *priv = priv(mcp); - - __raw_writel(-1, priv->mccr0_base + MCSR); - priv->mccr0 |= MCCR0_MCE; - __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); + Ser4MCSR = -1; + Ser4MCCR0 |= MCCR0_MCE; } static void mcp_sa11x0_disable(struct mcp *mcp) { - struct mcp_sa11x0 *priv = priv(mcp); - - priv->mccr0 &= ~MCCR0_MCE; - __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); + Ser4MCCR0 &= ~MCCR0_MCE; } /* @@ -165,9 +142,6 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) struct mcp_plat_data *data = pdev->dev.platform_data; struct mcp *mcp; int ret; - struct mcp_sa11x0 *priv; - struct resource *res_mem0, *res_mem1; - u32 size0, size1; if (!data) return -ENODEV; @@ -175,59 +149,46 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) if (!data->codec) return -ENODEV; - res_mem0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res_mem0) - return -ENODEV; - size0 = res_mem0->end - res_mem0->start + 1; - - res_mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res_mem1) - return -ENODEV; - size1 = res_mem1->end - res_mem1->start + 1; - - if (!request_mem_region(res_mem0->start, size0, "sa11x0-mcp")) + if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) return -EBUSY; - if (!request_mem_region(res_mem1->start, size1, "sa11x0-mcp")) { - ret = -EBUSY; - goto release; - } - mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); if (!mcp) { ret = -ENOMEM; - goto release2; + goto release; } - priv = priv(mcp); - mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; mcp->sclk_rate = data->sclk_rate; - mcp->dma_audio_rd = DDAR_DevAdd(res_mem0->start + MCDR0) - + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev; - mcp->dma_audio_wr = DDAR_DevAdd(res_mem0->start + MCDR0) - + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev; - mcp->dma_telco_rd = DDAR_DevAdd(res_mem0->start + MCDR1) - + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev; - mcp->dma_telco_wr = DDAR_DevAdd(res_mem0->start + MCDR1) - + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev; + mcp->dma_audio_rd = DMA_Ser4MCP0Rd; + mcp->dma_audio_wr = DMA_Ser4MCP0Wr; + mcp->dma_telco_rd = DMA_Ser4MCP1Rd; + mcp->dma_telco_wr = DMA_Ser4MCP1Wr; mcp->codec = data->codec; platform_set_drvdata(pdev, mcp); + if (machine_is_assabet()) { + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + /* * Initialise device. Note that we initially * set the sampling rate to minimum. */ - priv->mccr0_base = ioremap(res_mem0->start, size0); - priv->mccr1_base = ioremap(res_mem1->start, size1); - - __raw_writel(-1, priv->mccr0_base + MCSR); - priv->mccr1 = data->mccr1; - priv->mccr0 = data->mccr0 | 0x7f7f; - __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); - __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1); + Ser4MCSR = -1; + Ser4MCCR1 = data->mccr1; + Ser4MCCR0 = data->mccr0 | 0x7f7f; /* * Calculate the read/write timeout (us) from the bit clock @@ -241,49 +202,32 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) if (ret == 0) goto out; - release2: - release_mem_region(res_mem1->start, size1); release: - release_mem_region(res_mem0->start, size0); + release_mem_region(0x80060000, 0x60); platform_set_drvdata(pdev, NULL); out: return ret; } -static int mcp_sa11x0_remove(struct platform_device *pdev) +static int mcp_sa11x0_remove(struct platform_device *dev) { - struct mcp *mcp = platform_get_drvdata(pdev); - struct mcp_sa11x0 *priv = priv(mcp); - struct resource *res_mem; - u32 size; + struct mcp *mcp = platform_get_drvdata(dev); - platform_set_drvdata(pdev, NULL); + platform_set_drvdata(dev, NULL); mcp_host_unregister(mcp); + release_mem_region(0x80060000, 0x60); - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res_mem) { - size = res_mem->end - res_mem->start + 1; - release_mem_region(res_mem->start, size); - } - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res_mem) { - size = res_mem->end - res_mem->start + 1; - release_mem_region(res_mem->start, size); - } - iounmap(priv->mccr0_base); - iounmap(priv->mccr1_base); return 0; } static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) { struct mcp *mcp = platform_get_drvdata(dev); - struct mcp_sa11x0 *priv = priv(mcp); - u32 mccr0; - mccr0 = priv->mccr0 & ~MCCR0_MCE; - __raw_writel(mccr0, priv->mccr0_base + MCCR0); + priv(mcp)->mccr0 = Ser4MCCR0; + priv(mcp)->mccr1 = Ser4MCCR1; + Ser4MCCR0 &= ~MCCR0_MCE; return 0; } @@ -291,10 +235,9 @@ static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) static int mcp_sa11x0_resume(struct platform_device *dev) { struct mcp *mcp = platform_get_drvdata(dev); - struct mcp_sa11x0 *priv = priv(mcp); - __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0); - __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1); + Ser4MCCR1 = priv(mcp)->mccr1; + Ser4MCCR0 = priv(mcp)->mccr0; return 0; } @@ -311,7 +254,6 @@ static struct platform_driver mcp_sa11x0_driver = { .resume = mcp_sa11x0_resume, .driver = { .name = "sa11x0-mcp", - .owner = THIS_MODULE, }, }; -- cgit v0.10.2 From 65f2e753f1eb09d3a7e2a0d16408a5433b4097b2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Jan 2012 17:38:58 +0000 Subject: Revert "ARM: sa11x0: Implement autoloading of codec and codec pdata for mcp bus." This reverts commit 5dd7bf59e0e8563265b3e5b33276099ef628fcc7. Conflicts: scripts/mod/file2alias.c This change is wrong on many levels. First and foremost, it causes a regression. On boot on Assabet, which this patch gives a codec id of 'ucb1x00', it gives: ucb1x00 ID not found: 1005 0x1005 is a valid ID for the UCB1300 device. Secondly, this patch is way over the top in terms of complexity. The only device which has been seen to be connected with this MCP code is the UCB1x00 (UCB1200, UCB1300 etc) devices, and they all use the same driver. Adding a match table, requiring the codec string to match the hardware ID read out of the ID register, etc is completely over the top when we can just read the hardware ID register. diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index d8aa1c2..0c4b76a 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -202,7 +202,6 @@ static struct irda_platform_data assabet_irda_data = { static struct mcp_plat_data assabet_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .codec = "ucb1x00", }; static void __init assabet_init(void) diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c index fcadc4c..11bb6d0 100644 --- a/arch/arm/mach-sa1100/cerf.c +++ b/arch/arm/mach-sa1100/cerf.c @@ -124,7 +124,6 @@ static void __init cerf_map_io(void) static struct mcp_plat_data cerf_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .codec = "ucb1x00", }; static void __init cerf_init(void) diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index 6b7c74b..b9060e2 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -86,15 +85,10 @@ static struct scoop_pcmcia_config collie_pcmcia_config = { .num_devs = 1, }; -static struct ucb1x00_plat_data collie_ucb1x00_data = { - .gpio_base = COLLIE_TC35143_GPIO_BASE, -}; - static struct mcp_plat_data collie_mcp_data = { .mccr0 = MCCR0_ADM | MCCR0_ExtClk, .sclk_rate = 9216000, - .codec = "ucb1x00", - .codec_pdata = &collie_ucb1x00_data, + .gpio_base = COLLIE_TC35143_GPIO_BASE, }; /* diff --git a/arch/arm/mach-sa1100/include/mach/mcp.h b/arch/arm/mach-sa1100/include/mach/mcp.h index 586cec8..ed1a331 100644 --- a/arch/arm/mach-sa1100/include/mach/mcp.h +++ b/arch/arm/mach-sa1100/include/mach/mcp.h @@ -17,8 +17,6 @@ struct mcp_plat_data { u32 mccr1; unsigned int sclk_rate; int gpio_base; - const char *codec; - void *codec_pdata; }; #endif diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c index 48a8f4e..af4e276 100644 --- a/arch/arm/mach-sa1100/lart.c +++ b/arch/arm/mach-sa1100/lart.c @@ -24,7 +24,6 @@ static struct mcp_plat_data lart_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .codec = "ucb1x00", }; static void __init lart_init(void) diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c index 3807c91..318b2b7 100644 --- a/arch/arm/mach-sa1100/shannon.c +++ b/arch/arm/mach-sa1100/shannon.c @@ -55,7 +55,6 @@ static struct resource shannon_flash_resource = { static struct mcp_plat_data shannon_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .codec = "ucb1x00", }; static void __init shannon_init(void) diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index d9b765c..e17c04d 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -188,15 +187,10 @@ static struct resource simpad_flash_resources [] = { } }; -static struct ucb1x00_plat_data simpad_ucb1x00_data = { - .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, -}; - static struct mcp_plat_data simpad_mcp_data = { .mccr0 = MCCR0_ADM, .sclk_rate = 11981000, - .codec = "ucb1300", - .codec_pdata = &simpad_ucb1x00_data, + .gpio_base = SIMPAD_UCB1X00_GPIO_BASE, }; diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 63be60b..84815f9 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -26,35 +26,9 @@ #define to_mcp(d) container_of(d, struct mcp, attached_device) #define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) -static const struct mcp_device_id *mcp_match_id(const struct mcp_device_id *id, - const char *codec) -{ - while (id->name[0]) { - if (strcmp(codec, id->name) == 0) - return id; - id++; - } - return NULL; -} - -const struct mcp_device_id *mcp_get_device_id(const struct mcp *mcp) -{ - const struct mcp_driver *driver = - to_mcp_driver(mcp->attached_device.driver); - - return mcp_match_id(driver->id_table, mcp->codec); -} -EXPORT_SYMBOL(mcp_get_device_id); - static int mcp_bus_match(struct device *dev, struct device_driver *drv) { - const struct mcp *mcp = to_mcp(dev); - const struct mcp_driver *driver = to_mcp_driver(drv); - - if (driver->id_table) - return !!mcp_match_id(driver->id_table, mcp->codec); - - return 0; + return 1; } static int mcp_bus_probe(struct device *dev) @@ -100,18 +74,9 @@ static int mcp_bus_resume(struct device *dev) return ret; } -static int mcp_bus_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct mcp *mcp = to_mcp(dev); - - add_uevent_var(env, "MODALIAS=%s%s", MCP_MODULE_PREFIX, mcp->codec); - return 0; -} - static struct bus_type mcp_bus_type = { .name = "mcp", .match = mcp_bus_match, - .uevent = mcp_bus_uevent, .probe = mcp_bus_probe, .remove = mcp_bus_remove, .suspend = mcp_bus_suspend, @@ -247,14 +212,9 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size) } EXPORT_SYMBOL(mcp_host_alloc); -int mcp_host_register(struct mcp *mcp, void *pdata) +int mcp_host_register(struct mcp *mcp) { - if (!mcp->codec) - return -EINVAL; - - mcp->attached_device.platform_data = pdata; dev_set_name(&mcp->attached_device, "mcp0"); - request_module("%s%s", MCP_MODULE_PREFIX, mcp->codec); return device_register(&mcp->attached_device); } EXPORT_SYMBOL(mcp_host_register); diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index da4e077..02c53a0 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -146,9 +146,6 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) if (!data) return -ENODEV; - if (!data->codec) - return -ENODEV; - if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) return -EBUSY; @@ -165,7 +162,7 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->dma_audio_wr = DMA_Ser4MCP0Wr; mcp->dma_telco_rd = DMA_Ser4MCP1Rd; mcp->dma_telco_wr = DMA_Ser4MCP1Wr; - mcp->codec = data->codec; + mcp->gpio_base = data->gpio_base; platform_set_drvdata(pdev, mcp); @@ -198,7 +195,7 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; - ret = mcp_host_register(mcp, data->codec_pdata); + ret = mcp_host_register(mcp); if (ret == 0) goto out; diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 91c4f25..b281217 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -36,15 +36,6 @@ static DEFINE_MUTEX(ucb1x00_mutex); static LIST_HEAD(ucb1x00_drivers); static LIST_HEAD(ucb1x00_devices); -static struct mcp_device_id ucb1x00_id[] = { - { "ucb1x00", 0 }, /* auto-detection */ - { "ucb1200", UCB_ID_1200 }, - { "ucb1300", UCB_ID_1300 }, - { "tc35143", UCB_ID_TC35143 }, - { } -}; -MODULE_DEVICE_TABLE(mcp, ucb1x00_id); - /** * ucb1x00_io_set_dir - set IO direction * @ucb: UCB1x00 structure describing chip @@ -536,33 +527,17 @@ static struct class ucb1x00_class = { static int ucb1x00_probe(struct mcp *mcp) { - const struct mcp_device_id *mid; struct ucb1x00 *ucb; struct ucb1x00_driver *drv; - struct ucb1x00_plat_data *pdata; unsigned int id; int ret = -ENODEV; int temp; mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); - mid = mcp_get_device_id(mcp); - if (mid && mid->driver_data) { - if (id != mid->driver_data) { - printk(KERN_WARNING "%s wrong ID %04x found: %04x\n", - mid->name, (unsigned int) mid->driver_data, id); - goto err_disable; - } - } else { - mid = &ucb1x00_id[1]; - while (mid->driver_data) { - if (id == mid->driver_data) - break; - mid++; - } - printk(KERN_WARNING "%s ID not found: %04x\n", - ucb1x00_id[0].name, id); + if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) { + printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); goto err_disable; } @@ -571,28 +546,28 @@ static int ucb1x00_probe(struct mcp *mcp) if (!ucb) goto err_disable; - pdata = mcp->attached_device.platform_data; + ucb->dev.class = &ucb1x00_class; ucb->dev.parent = &mcp->attached_device; - dev_set_name(&ucb->dev, mid->name); + dev_set_name(&ucb->dev, "ucb1x00"); spin_lock_init(&ucb->lock); spin_lock_init(&ucb->io_lock); sema_init(&ucb->adc_sem, 1); - ucb->id = mid; + ucb->id = id; ucb->mcp = mcp; ucb->irq = ucb1x00_detect_irq(ucb); if (ucb->irq == NO_IRQ) { - printk(KERN_ERR "%s: IRQ probe failed\n", mid->name); + printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); ret = -ENODEV; goto err_free; } ucb->gpio.base = -1; - if (pdata && (pdata->gpio_base >= 0)) { + if (mcp->gpio_base != 0) { ucb->gpio.label = dev_name(&ucb->dev); - ucb->gpio.base = pdata->gpio_base; + ucb->gpio.base = mcp->gpio_base; ucb->gpio.ngpio = 10; ucb->gpio.set = ucb1x00_gpio_set; ucb->gpio.get = ucb1x00_gpio_get; @@ -605,10 +580,10 @@ static int ucb1x00_probe(struct mcp *mcp) dev_info(&ucb->dev, "gpio_base not set so no gpiolib support"); ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING, - mid->name, ucb); + "UCB1x00", ucb); if (ret) { - printk(KERN_ERR "%s: unable to grab irq%d: %d\n", - mid->name, ucb->irq, ret); + printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", + ucb->irq, ret); goto err_gpio; } @@ -730,7 +705,6 @@ static struct mcp_driver ucb1x00_driver = { .remove = ucb1x00_remove, .suspend = ucb1x00_suspend, .resume = ucb1x00_resume, - .id_table = ucb1x00_id, }; static int __init ucb1x00_init(void) diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 40ec3c1..38ffbd5 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -382,7 +382,7 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC; idev->name = "Touchscreen panel"; - idev->id.product = ts->ucb->id->driver_data; + idev->id.product = ts->ucb->id; idev->open = ucb1x00_ts_open; idev->close = ucb1x00_ts_close; diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index 1515e64..ee496708 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -10,7 +10,6 @@ #ifndef MCP_H #define MCP_H -#include #include struct mcp_ops; @@ -27,7 +26,7 @@ struct mcp { dma_device_t dma_telco_rd; dma_device_t dma_telco_wr; struct device attached_device; - const char *codec; + int gpio_base; }; struct mcp_ops { @@ -45,11 +44,10 @@ void mcp_reg_write(struct mcp *, unsigned int, unsigned int); unsigned int mcp_reg_read(struct mcp *, unsigned int); void mcp_enable(struct mcp *); void mcp_disable(struct mcp *); -const struct mcp_device_id *mcp_get_device_id(const struct mcp *mcp); #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) struct mcp *mcp_host_alloc(struct device *, size_t); -int mcp_host_register(struct mcp *, void *); +int mcp_host_register(struct mcp *); void mcp_host_unregister(struct mcp *); struct mcp_driver { @@ -58,7 +56,6 @@ struct mcp_driver { void (*remove)(struct mcp *); int (*suspend)(struct mcp *, pm_message_t); int (*resume)(struct mcp *); - const struct mcp_device_id *id_table; }; int mcp_driver_register(struct mcp_driver *); diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index bc19e5f..4321f04 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -104,9 +104,6 @@ #define UCB_MODE_DYN_VFLAG_ENA (1 << 12) #define UCB_MODE_AUD_OFF_CAN (1 << 13) -struct ucb1x00_plat_data { - int gpio_base; -}; struct ucb1x00_irq { void *devid; @@ -119,7 +116,7 @@ struct ucb1x00 { unsigned int irq; struct semaphore adc_sem; spinlock_t io_lock; - const struct mcp_device_id *id; + u16 id; u16 io_dir; u16 io_out; u16 adc_cr; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b29e7f6..83ac071 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -436,17 +436,6 @@ struct spi_device_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; -/* mcp */ - -#define MCP_NAME_SIZE 20 -#define MCP_MODULE_PREFIX "mcp:" - -struct mcp_device_id { - char name[MCP_NAME_SIZE]; - kernel_ulong_t driver_data /* Data private to the driver */ - __attribute__((aligned(sizeof(kernel_ulong_t)))); -}; - /* dmi */ enum dmi_field { DMI_NONE, diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index c0e14b3..e8c9695 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -823,16 +823,6 @@ static int do_spi_entry(const char *filename, struct spi_device_id *id, } ADD_TO_DEVTABLE("spi", struct spi_device_id, do_spi_entry); -/* Looks like: mcp:S */ -static int do_mcp_entry(const char *filename, struct mcp_device_id *id, - char *alias) -{ - sprintf(alias, MCP_MODULE_PREFIX "%s", id->name); - - return 1; -} -ADD_TO_DEVTABLE("mcp", struct mcp_device_id, do_mcp_entry); - static const struct dmifield { const char *prefix; int field; -- cgit v0.10.2 From 98250221691f728b7cad6deed98866f8847e683f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 14 Jan 2012 08:49:46 +0000 Subject: MFD: mcp-core: fix complaints from the genirq layer The genirq layer complains if an interrupt handler returns with interrupts enabled. The UCB1x00 handler does just this, because ucb1x00_enable() calls mcp_enable(), which uses spin_lock_irq() rather than spin_lock_irqsave(). Convert this, and the divisor setting functions to use spin_lock_irqsave(). Signed-off-by: Russell King diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c index 84815f9..86cc3f7 100644 --- a/drivers/mfd/mcp-core.c +++ b/drivers/mfd/mcp-core.c @@ -93,9 +93,11 @@ static struct bus_type mcp_bus_type = { */ void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) { - spin_lock_irq(&mcp->lock); + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); mcp->ops->set_telecom_divisor(mcp, div); - spin_unlock_irq(&mcp->lock); + spin_unlock_irqrestore(&mcp->lock, flags); } EXPORT_SYMBOL(mcp_set_telecom_divisor); @@ -108,9 +110,11 @@ EXPORT_SYMBOL(mcp_set_telecom_divisor); */ void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) { - spin_lock_irq(&mcp->lock); + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); mcp->ops->set_audio_divisor(mcp, div); - spin_unlock_irq(&mcp->lock); + spin_unlock_irqrestore(&mcp->lock, flags); } EXPORT_SYMBOL(mcp_set_audio_divisor); @@ -163,10 +167,11 @@ EXPORT_SYMBOL(mcp_reg_read); */ void mcp_enable(struct mcp *mcp) { - spin_lock_irq(&mcp->lock); + unsigned long flags; + spin_lock_irqsave(&mcp->lock, flags); if (mcp->use_count++ == 0) mcp->ops->enable(mcp); - spin_unlock_irq(&mcp->lock); + spin_unlock_irqrestore(&mcp->lock, flags); } EXPORT_SYMBOL(mcp_enable); -- cgit v0.10.2 From 8f0fc977f58c36e75e205486c1aebb9b8e4263e1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 19 Jan 2012 21:13:47 -0800 Subject: Revert "drm/i915: Work around gen7 BLT ring synchronization issues." This reverts commit 42ff6572e5a4a7414330a4ca91f0335da67deca9. New forcewake voodoo makes this no longer necessary. Acked-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index fa5702c..1ab842c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -805,17 +805,6 @@ ring_add_request(struct intel_ring_buffer *ring, } static bool -gen7_blt_ring_get_irq(struct intel_ring_buffer *ring) -{ - /* The BLT ring on IVB appears to have broken synchronization - * between the seqno write and the interrupt, so that the - * interrupt appears first. Returning false here makes - * i915_wait_request() do a polling loop, instead. - */ - return false; -} - -static bool gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) { struct drm_device *dev = ring->dev; @@ -1600,8 +1589,5 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) *ring = gen6_blt_ring; - if (IS_GEN7(dev)) - ring->irq_get = gen7_blt_ring_get_irq; - return intel_init_ring_buffer(dev, ring); } -- cgit v0.10.2 From 6b35f924b80a0e6d71711e66f5b3c16f427f3d2a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 19 Jan 2012 10:23:22 -0200 Subject: ASoC: mxs: Fix mxs-saif timeout On a mx28evk board the following errors happens on mxs-sgtl5000 probe: [ 0.660000] saif0_clk_set_rate: divider writing timeout [ 0.670000] mxs-sgtl5000: probe of mxs-sgtl5000.0 failed with error -110 [ 0.670000] ALSA device list: [ 0.680000] No soundcards found. This timeout happens because clk_set_rate will result in writing to the DIV bits of register HW_CLKCTRL_SAIF0 with the saif clock gated (CLKGATE bit set to one). MX28 Reference states the following about CLKGATE: "The DIV field can change ONLY when this clock gate bit field is low." So call clk_prepare_enable prior to clk_set_rate to fix this problem. After this change the mxs-saif driver can be correctly probed and audio is functional. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index dccfb37..f204dba 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -124,6 +124,8 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, * * If MCLK is not used, we just set saif clk to 512*fs. */ + clk_prepare_enable(master_saif->clk); + if (master_saif->mclk_in_use) { if (mclk % 32 == 0) { scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; @@ -133,6 +135,7 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, ret = clk_set_rate(master_saif->clk, 384 * rate); } else { /* SAIF MCLK should be either 32x or 48x */ + clk_disable_unprepare(master_saif->clk); return -EINVAL; } } else { @@ -140,6 +143,8 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; } + clk_disable_unprepare(master_saif->clk); + if (ret) return ret; -- cgit v0.10.2 From b67f231ded332461dd31123c4f659c4681223fb1 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Thu, 19 Jan 2012 22:25:36 +0000 Subject: enic: fix compile when CONFIG_PCI_IOV is not enabled reverting back change that access enic->num_vfs outside CONFIG_PCI_IOV Reported-by: Randy Dunlap Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 045c468..ab3f67f 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2273,7 +2273,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, #ifdef CONFIG_PCI_IOV int pos = 0; #endif - int num_pps; + int num_pps = 1; /* Allocate net device structure and initialize. Private * instance data is initialized to zero. @@ -2380,11 +2380,11 @@ static int __devinit enic_probe(struct pci_dev *pdev, goto err_out_vnic_unregister; } enic->priv_flags |= ENIC_SRIOV_ENABLED; + num_pps = enic->num_vfs; } } #endif - num_pps = enic->num_vfs ? enic->num_vfs : 1; /* Allocate structure for port profiles */ enic->pp = kcalloc(num_pps, sizeof(*enic->pp), GFP_KERNEL); if (!enic->pp) { -- cgit v0.10.2 From fc16dcd8c2e1e9bc91ed765957e1f2bbf334253e Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Wed, 18 Jan 2012 17:47:58 +0000 Subject: tcp: fix undo after RTO for BIC This patch fixes BIC so that cwnd reductions made during RTOs can be undone (just as they already can be undone when using the default/Reno behavior). When undoing cwnd reductions, BIC-derived congestion control modules were restoring the cwnd from last_max_cwnd. There were two problems with using last_max_cwnd to restore a cwnd during undo: (a) last_max_cwnd was set to 0 on state transitions into TCP_CA_Loss (by calling the module's reset() functions), so cwnd reductions from RTOs could not be undone. (b) when fast_covergence is enabled (which it is by default) last_max_cwnd does not actually hold the value of snd_cwnd before the loss; instead, it holds a scaled-down version of snd_cwnd. This patch makes the following changes: (1) upon undo, revert snd_cwnd to ca->loss_cwnd, which is already, as the existing comment notes, the "congestion window at last loss" (2) stop forgetting ca->loss_cwnd on TCP_CA_Loss events (3) use ca->last_max_cwnd to check if we're in slow start Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 6187eb4..f45e1c2 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -63,7 +63,6 @@ static inline void bictcp_reset(struct bictcp *ca) { ca->cnt = 0; ca->last_max_cwnd = 0; - ca->loss_cwnd = 0; ca->last_cwnd = 0; ca->last_time = 0; ca->epoch_start = 0; @@ -72,7 +71,11 @@ static inline void bictcp_reset(struct bictcp *ca) static void bictcp_init(struct sock *sk) { - bictcp_reset(inet_csk_ca(sk)); + struct bictcp *ca = inet_csk_ca(sk); + + bictcp_reset(ca); + ca->loss_cwnd = 0; + if (initial_ssthresh) tcp_sk(sk)->snd_ssthresh = initial_ssthresh; } @@ -127,7 +130,7 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) } /* if in slow start or link utilization is very low */ - if (ca->loss_cwnd == 0) { + if (ca->last_max_cwnd == 0) { if (ca->cnt > 20) /* increase cwnd 5% per RTT */ ca->cnt = 20; } @@ -185,7 +188,7 @@ static u32 bictcp_undo_cwnd(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); const struct bictcp *ca = inet_csk_ca(sk); - return max(tp->snd_cwnd, ca->last_max_cwnd); + return max(tp->snd_cwnd, ca->loss_cwnd); } static void bictcp_state(struct sock *sk, u8 new_state) -- cgit v0.10.2 From 5a45f0086a2dcf50db7e6a0bf5be933880f85127 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Wed, 18 Jan 2012 17:47:59 +0000 Subject: tcp: fix undo after RTO for CUBIC This patch fixes CUBIC so that cwnd reductions made during RTOs can be undone (just as they already can be undone when using the default/Reno behavior). When undoing cwnd reductions, BIC-derived congestion control modules were restoring the cwnd from last_max_cwnd. There were two problems with using last_max_cwnd to restore a cwnd during undo: (a) last_max_cwnd was set to 0 on state transitions into TCP_CA_Loss (by calling the module's reset() functions), so cwnd reductions from RTOs could not be undone. (b) when fast_covergence is enabled (which it is by default) last_max_cwnd does not actually hold the value of snd_cwnd before the loss; instead, it holds a scaled-down version of snd_cwnd. This patch makes the following changes: (1) upon undo, revert snd_cwnd to ca->loss_cwnd, which is already, as the existing comment notes, the "congestion window at last loss" (2) stop forgetting ca->loss_cwnd on TCP_CA_Loss events (3) use ca->last_max_cwnd to check if we're in slow start Signed-off-by: Neal Cardwell Acked-by: Stephen Hemminger Acked-by: Sangtae Ha Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index f376b05..a9077f4 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -107,7 +107,6 @@ static inline void bictcp_reset(struct bictcp *ca) { ca->cnt = 0; ca->last_max_cwnd = 0; - ca->loss_cwnd = 0; ca->last_cwnd = 0; ca->last_time = 0; ca->bic_origin_point = 0; @@ -142,7 +141,10 @@ static inline void bictcp_hystart_reset(struct sock *sk) static void bictcp_init(struct sock *sk) { - bictcp_reset(inet_csk_ca(sk)); + struct bictcp *ca = inet_csk_ca(sk); + + bictcp_reset(ca); + ca->loss_cwnd = 0; if (hystart) bictcp_hystart_reset(sk); @@ -275,7 +277,7 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) * The initial growth of cubic function may be too conservative * when the available bandwidth is still unknown. */ - if (ca->loss_cwnd == 0 && ca->cnt > 20) + if (ca->last_max_cwnd == 0 && ca->cnt > 20) ca->cnt = 20; /* increase cwnd 5% per RTT */ /* TCP Friendly */ @@ -342,7 +344,7 @@ static u32 bictcp_undo_cwnd(struct sock *sk) { struct bictcp *ca = inet_csk_ca(sk); - return max(tcp_sk(sk)->snd_cwnd, ca->last_max_cwnd); + return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); } static void bictcp_state(struct sock *sk, u8 new_state) -- cgit v0.10.2 From e615bf5b5519862ab66172f4dec7455d6543a578 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 20 Jan 2012 19:13:24 -0700 Subject: ACPI, APEI: Add 64-bit read/write support for APEI on i386 Base ACPI (CA) currently does not support atomic 64-bit reads and writes (acpi_read() and acpi_write() split 64-bit loads/stores into two 32-bit transfers) yet APEI expects 64-bit transfer capability, even when running on 32-bit systems. This patch implements 64-bit read and write routines for APEI usage. This patch re-factors similar functionality introduced in commit 04c25997c97, bringing it into the ACPI subsystem in preparation for removing ./drivers/acpi/atomicio.[ch]. In the implementation I have replicated acpi_os_read_memory() and acpi_os_write_memory(), creating 64-bit versions for APEI to utilize, as opposed to something more elegant. My thinking is that we should attempt to see if we can get ACPI's CA/OSL changed so that the existing acpi_read() and acpi_write() interfaces are natively 64-bit capable and then subsequently remove the replication. Signed-off-by: Myron Stowe Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index e45350c..e5d53b7 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -596,33 +596,19 @@ int apei_read(u64 *val, struct acpi_generic_address *reg) { int rc; u64 address; - u32 tmp, width = reg->bit_width; acpi_status status; rc = apei_check_gar(reg, &address); if (rc) return rc; - if (width == 64) - width = 32; /* Break into two 32-bit transfers */ - *val = 0; switch(reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: - status = acpi_os_read_memory((acpi_physical_address) - address, &tmp, width); + status = acpi_os_read_memory64((acpi_physical_address) + address, val, reg->bit_width); if (ACPI_FAILURE(status)) return -EIO; - *val = tmp; - - if (reg->bit_width == 64) { - /* Read the top 32 bits */ - status = acpi_os_read_memory((acpi_physical_address) - (address + 4), &tmp, 32); - if (ACPI_FAILURE(status)) - return -EIO; - *val |= ((u64)tmp << 32); - } break; case ACPI_ADR_SPACE_SYSTEM_IO: status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); @@ -642,31 +628,18 @@ int apei_write(u64 val, struct acpi_generic_address *reg) { int rc; u64 address; - u32 width = reg->bit_width; acpi_status status; rc = apei_check_gar(reg, &address); if (rc) return rc; - if (width == 64) - width = 32; /* Break into two 32-bit transfers */ - switch (reg->space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: - status = acpi_os_write_memory((acpi_physical_address) - address, ACPI_LODWORD(val), - width); + status = acpi_os_write_memory64((acpi_physical_address) + address, val, reg->bit_width); if (ACPI_FAILURE(status)) return -EIO; - - if (reg->bit_width == 64) { - status = acpi_os_write_memory((acpi_physical_address) - (address + 4), - ACPI_HIDWORD(val), 32); - if (ACPI_FAILURE(status)) - return -EIO; - } break; case ACPI_ADR_SPACE_SYSTEM_IO: status = acpi_os_write_port(address, val, reg->bit_width); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index fcc12d8..5498a6d 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -710,6 +710,67 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) return AE_OK; } +#ifdef readq +static inline u64 read64(const volatile void __iomem *addr) +{ + return readq(addr); +} +#else +static inline u64 read64(const volatile void __iomem *addr) +{ + u64 l, h; + l = readl(addr); + h = readl(addr+4); + return l | (h << 32); +} +#endif + +acpi_status +acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width) +{ + void __iomem *virt_addr; + unsigned int size = width / 8; + bool unmap = false; + u64 dummy; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + if (!virt_addr) { + rcu_read_unlock(); + virt_addr = acpi_os_ioremap(phys_addr, size); + if (!virt_addr) + return AE_BAD_ADDRESS; + unmap = true; + } + + if (!value) + value = &dummy; + + switch (width) { + case 8: + *(u8 *) value = readb(virt_addr); + break; + case 16: + *(u16 *) value = readw(virt_addr); + break; + case 32: + *(u32 *) value = readl(virt_addr); + break; + case 64: + *(u64 *) value = read64(virt_addr); + break; + default: + BUG(); + } + + if (unmap) + iounmap(virt_addr); + else + rcu_read_unlock(); + + return AE_OK; +} + acpi_status acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) { @@ -749,6 +810,61 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) return AE_OK; } +#ifdef writeq +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writeq(val, addr); +} +#else +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val>>32, addr+4); +} +#endif + +acpi_status +acpi_os_write_memory64(acpi_physical_address phys_addr, u64 value, u32 width) +{ + void __iomem *virt_addr; + unsigned int size = width / 8; + bool unmap = false; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + if (!virt_addr) { + rcu_read_unlock(); + virt_addr = acpi_os_ioremap(phys_addr, size); + if (!virt_addr) + return AE_BAD_ADDRESS; + unmap = true; + } + + switch (width) { + case 8: + writeb(value, virt_addr); + break; + case 16: + writew(value, virt_addr); + break; + case 32: + writel(value, virt_addr); + break; + case 64: + write64(value, virt_addr); + break; + default: + BUG(); + } + + if (unmap) + iounmap(virt_addr); + else + rcu_read_unlock(); + + return AE_OK; +} + acpi_status acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, u64 *value, u32 width) diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 2fe8639..7c9aebe 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -218,9 +218,13 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width); */ acpi_status acpi_os_read_memory(acpi_physical_address address, u32 * value, u32 width); +acpi_status +acpi_os_read_memory64(acpi_physical_address address, u64 *value, u32 width); acpi_status acpi_os_write_memory(acpi_physical_address address, u32 value, u32 width); +acpi_status +acpi_os_write_memory64(acpi_physical_address address, u64 value, u32 width); /* * Platform and hardware-independent PCI configuration space access -- cgit v0.10.2 From ba242d5b1a84bc6611732296517ee40d5a80a4d9 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 20 Jan 2012 19:13:30 -0700 Subject: ACPI, APEI: Add RAM mapping support to ACPI This patch adds support for RAM to ACPI's mapping capabilities in order to support APEI error injection (EINJ) actions. This patch re-factors similar functionality introduced in commit 76da3fb3575, bringing it into osl.c in preparation for removing ./drivers/acpi/atomicio.[ch]. Signed-off-by: Myron Stowe Signed-off-by: Len Brown diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5498a6d..412a1e0 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -321,6 +322,37 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) return NULL; } +#ifndef CONFIG_IA64 +#define should_use_kmap(pfn) page_is_ram(pfn) +#else +/* ioremap will take care of cache attributes */ +#define should_use_kmap(pfn) 0 +#endif + +static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (should_use_kmap(pfn)) { + if (pg_sz > PAGE_SIZE) + return NULL; + return (void __iomem __force *)kmap(pfn_to_page(pfn)); + } else + return acpi_os_ioremap(pg_off, pg_sz); +} + +static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (page_is_ram(pfn)) + kunmap(pfn_to_page(pfn)); + else + iounmap(vaddr); +} + void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { @@ -353,7 +385,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) pg_off = round_down(phys, PAGE_SIZE); pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; - virt = acpi_os_ioremap(pg_off, pg_sz); + virt = acpi_map(pg_off, pg_sz); if (!virt) { mutex_unlock(&acpi_ioremap_lock); kfree(map); @@ -384,7 +416,7 @@ static void acpi_os_map_cleanup(struct acpi_ioremap *map) { if (!map->refcount) { synchronize_rcu(); - iounmap(map->virt); + acpi_unmap(map->phys, map->virt); kfree(map); } } -- cgit v0.10.2 From 1001a3a307ca2425c028fd23b8ad05ad8bb048dd Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 20 Jan 2012 19:13:37 -0700 Subject: ACPI: Remove ./drivers/acpi/atomicio.[ch] With the conversion of atomicio's routines in place (see commits 6f68c91c55e and 700130b41f4), atomicio.[ch] can be removed, replacing the APEI specific pre-mapping capabilities with the more generalized versions that drivers/acpi/osl.c provides. Signed-off-by: Myron Stowe Signed-off-by: Len Brown diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index c07f44f..1567028 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -19,7 +19,6 @@ obj-y += acpi.o \ # All the builtin files are in the "acpi." module_param namespace. acpi-y += osl.o utils.o reboot.o -acpi-y += atomicio.o acpi-y += nvs.o # sleep related files diff --git a/drivers/acpi/atomicio.c b/drivers/acpi/atomicio.c deleted file mode 100644 index d4a5b3d..0000000 --- a/drivers/acpi/atomicio.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * atomicio.c - ACPI IO memory pre-mapping/post-unmapping, then - * accessing in atomic context. - * - * This is used for NMI handler to access IO memory area, because - * ioremap/iounmap can not be used in NMI handler. The IO memory area - * is pre-mapped in process context and accessed in NMI handler. - * - * Copyright (C) 2009-2010, Intel Corp. - * Author: Huang Ying - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ACPI_PFX "ACPI: " - -static LIST_HEAD(acpi_iomaps); -/* - * Used for mutual exclusion between writers of acpi_iomaps list, for - * synchronization between readers and writer, RCU is used. - */ -static DEFINE_SPINLOCK(acpi_iomaps_lock); - -struct acpi_iomap { - struct list_head list; - void __iomem *vaddr; - unsigned long size; - phys_addr_t paddr; - struct kref ref; -}; - -/* acpi_iomaps_lock or RCU read lock must be held before calling */ -static struct acpi_iomap *__acpi_find_iomap(phys_addr_t paddr, - unsigned long size) -{ - struct acpi_iomap *map; - - list_for_each_entry_rcu(map, &acpi_iomaps, list) { - if (map->paddr + map->size >= paddr + size && - map->paddr <= paddr) - return map; - } - return NULL; -} - -/* - * Atomic "ioremap" used by NMI handler, if the specified IO memory - * area is not pre-mapped, NULL will be returned. - * - * acpi_iomaps_lock or RCU read lock must be held before calling - */ -static void __iomem *__acpi_ioremap_fast(phys_addr_t paddr, - unsigned long size) -{ - struct acpi_iomap *map; - - map = __acpi_find_iomap(paddr, size/8); - if (map) - return map->vaddr + (paddr - map->paddr); - else - return NULL; -} - -/* acpi_iomaps_lock must be held before calling */ -static void __iomem *__acpi_try_ioremap(phys_addr_t paddr, - unsigned long size) -{ - struct acpi_iomap *map; - - map = __acpi_find_iomap(paddr, size); - if (map) { - kref_get(&map->ref); - return map->vaddr + (paddr - map->paddr); - } else - return NULL; -} - -#ifndef CONFIG_IA64 -#define should_use_kmap(pfn) page_is_ram(pfn) -#else -/* ioremap will take care of cache attributes */ -#define should_use_kmap(pfn) 0 -#endif - -static void __iomem *acpi_map(phys_addr_t pg_off, unsigned long pg_sz) -{ - unsigned long pfn; - - pfn = pg_off >> PAGE_SHIFT; - if (should_use_kmap(pfn)) { - if (pg_sz > PAGE_SIZE) - return NULL; - return (void __iomem __force *)kmap(pfn_to_page(pfn)); - } else - return ioremap(pg_off, pg_sz); -} - -static void acpi_unmap(phys_addr_t pg_off, void __iomem *vaddr) -{ - unsigned long pfn; - - pfn = pg_off >> PAGE_SHIFT; - if (page_is_ram(pfn)) - kunmap(pfn_to_page(pfn)); - else - iounmap(vaddr); -} - -/* - * Used to pre-map the specified IO memory area. First try to find - * whether the area is already pre-mapped, if it is, increase the - * reference count (in __acpi_try_ioremap) and return; otherwise, do - * the real ioremap, and add the mapping into acpi_iomaps list. - */ -static void __iomem *acpi_pre_map(phys_addr_t paddr, - unsigned long size) -{ - void __iomem *vaddr; - struct acpi_iomap *map; - unsigned long pg_sz, flags; - phys_addr_t pg_off; - - spin_lock_irqsave(&acpi_iomaps_lock, flags); - vaddr = __acpi_try_ioremap(paddr, size); - spin_unlock_irqrestore(&acpi_iomaps_lock, flags); - if (vaddr) - return vaddr; - - pg_off = paddr & PAGE_MASK; - pg_sz = ((paddr + size + PAGE_SIZE - 1) & PAGE_MASK) - pg_off; - vaddr = acpi_map(pg_off, pg_sz); - if (!vaddr) - return NULL; - map = kmalloc(sizeof(*map), GFP_KERNEL); - if (!map) - goto err_unmap; - INIT_LIST_HEAD(&map->list); - map->paddr = pg_off; - map->size = pg_sz; - map->vaddr = vaddr; - kref_init(&map->ref); - - spin_lock_irqsave(&acpi_iomaps_lock, flags); - vaddr = __acpi_try_ioremap(paddr, size); - if (vaddr) { - spin_unlock_irqrestore(&acpi_iomaps_lock, flags); - acpi_unmap(pg_off, map->vaddr); - kfree(map); - return vaddr; - } - list_add_tail_rcu(&map->list, &acpi_iomaps); - spin_unlock_irqrestore(&acpi_iomaps_lock, flags); - - return map->vaddr + (paddr - map->paddr); -err_unmap: - acpi_unmap(pg_off, vaddr); - return NULL; -} - -/* acpi_iomaps_lock must be held before calling */ -static void __acpi_kref_del_iomap(struct kref *ref) -{ - struct acpi_iomap *map; - - map = container_of(ref, struct acpi_iomap, ref); - list_del_rcu(&map->list); -} - -/* - * Used to post-unmap the specified IO memory area. The iounmap is - * done only if the reference count goes zero. - */ -static void acpi_post_unmap(phys_addr_t paddr, unsigned long size) -{ - struct acpi_iomap *map; - unsigned long flags; - int del; - - spin_lock_irqsave(&acpi_iomaps_lock, flags); - map = __acpi_find_iomap(paddr, size); - BUG_ON(!map); - del = kref_put(&map->ref, __acpi_kref_del_iomap); - spin_unlock_irqrestore(&acpi_iomaps_lock, flags); - - if (!del) - return; - - synchronize_rcu(); - acpi_unmap(map->paddr, map->vaddr); - kfree(map); -} - -/* In NMI handler, should set silent = 1 */ -static int acpi_check_gar(struct acpi_generic_address *reg, - u64 *paddr, int silent) -{ - u32 width, space_id; - - width = reg->bit_width; - space_id = reg->space_id; - /* Handle possible alignment issues */ - memcpy(paddr, ®->address, sizeof(*paddr)); - if (!*paddr) { - if (!silent) - pr_warning(FW_BUG ACPI_PFX - "Invalid physical address in GAR [0x%llx/%u/%u]\n", - *paddr, width, space_id); - return -EINVAL; - } - - if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) { - if (!silent) - pr_warning(FW_BUG ACPI_PFX - "Invalid bit width in GAR [0x%llx/%u/%u]\n", - *paddr, width, space_id); - return -EINVAL; - } - - if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && - space_id != ACPI_ADR_SPACE_SYSTEM_IO) { - if (!silent) - pr_warning(FW_BUG ACPI_PFX - "Invalid address space type in GAR [0x%llx/%u/%u]\n", - *paddr, width, space_id); - return -EINVAL; - } - - return 0; -} - -/* Pre-map, working on GAR */ -int acpi_pre_map_gar(struct acpi_generic_address *reg) -{ - u64 paddr; - void __iomem *vaddr; - int rc; - - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) - return 0; - - rc = acpi_check_gar(reg, &paddr, 0); - if (rc) - return rc; - - vaddr = acpi_pre_map(paddr, reg->bit_width / 8); - if (!vaddr) - return -EIO; - - return 0; -} -EXPORT_SYMBOL_GPL(acpi_pre_map_gar); - -/* Post-unmap, working on GAR */ -int acpi_post_unmap_gar(struct acpi_generic_address *reg) -{ - u64 paddr; - int rc; - - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) - return 0; - - rc = acpi_check_gar(reg, &paddr, 0); - if (rc) - return rc; - - acpi_post_unmap(paddr, reg->bit_width / 8); - - return 0; -} -EXPORT_SYMBOL_GPL(acpi_post_unmap_gar); - -#ifdef readq -static inline u64 read64(const volatile void __iomem *addr) -{ - return readq(addr); -} -#else -static inline u64 read64(const volatile void __iomem *addr) -{ - u64 l, h; - l = readl(addr); - h = readl(addr+4); - return l | (h << 32); -} -#endif - -/* - * Can be used in atomic (including NMI) or process context. RCU read - * lock can only be released after the IO memory area accessing. - */ -static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width) -{ - void __iomem *addr; - - rcu_read_lock(); - addr = __acpi_ioremap_fast(paddr, width); - switch (width) { - case 8: - *val = readb(addr); - break; - case 16: - *val = readw(addr); - break; - case 32: - *val = readl(addr); - break; - case 64: - *val = read64(addr); - break; - default: - return -EINVAL; - } - rcu_read_unlock(); - - return 0; -} - -#ifdef writeq -static inline void write64(u64 val, volatile void __iomem *addr) -{ - writeq(val, addr); -} -#else -static inline void write64(u64 val, volatile void __iomem *addr) -{ - writel(val, addr); - writel(val>>32, addr+4); -} -#endif - -static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width) -{ - void __iomem *addr; - - rcu_read_lock(); - addr = __acpi_ioremap_fast(paddr, width); - switch (width) { - case 8: - writeb(val, addr); - break; - case 16: - writew(val, addr); - break; - case 32: - writel(val, addr); - break; - case 64: - write64(val, addr); - break; - default: - return -EINVAL; - } - rcu_read_unlock(); - - return 0; -} - -/* GAR accessing in atomic (including NMI) or process context */ -int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg) -{ - u64 paddr; - int rc; - - rc = acpi_check_gar(reg, &paddr, 1); - if (rc) - return rc; - - *val = 0; - switch (reg->space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - return acpi_atomic_read_mem(paddr, val, reg->bit_width); - case ACPI_ADR_SPACE_SYSTEM_IO: - return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width); - default: - return -EINVAL; - } -} -EXPORT_SYMBOL_GPL(acpi_atomic_read); - -int acpi_atomic_write(u64 val, struct acpi_generic_address *reg) -{ - u64 paddr; - int rc; - - rc = acpi_check_gar(reg, &paddr, 1); - if (rc) - return rc; - - switch (reg->space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - return acpi_atomic_write_mem(paddr, val, reg->bit_width); - case ACPI_ADR_SPACE_SYSTEM_IO: - return acpi_os_write_port(paddr, val, reg->bit_width); - default: - return -EINVAL; - } -} -EXPORT_SYMBOL_GPL(acpi_atomic_write); diff --git a/include/acpi/atomicio.h b/include/acpi/atomicio.h deleted file mode 100644 index 8b9fb4b..0000000 --- a/include/acpi/atomicio.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef ACPI_ATOMIC_IO_H -#define ACPI_ATOMIC_IO_H - -int acpi_pre_map_gar(struct acpi_generic_address *reg); -int acpi_post_unmap_gar(struct acpi_generic_address *reg); - -int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg); -int acpi_atomic_write(u64 val, struct acpi_generic_address *reg); - -#endif -- cgit v0.10.2 From 93f770846e8dedc5d9117bd4ad9d7efd18420627 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Sat, 21 Jan 2012 09:23:56 +0800 Subject: ACPI / PM: Add Sony Vaio VPCCW29FX to nonvs blacklist. Sony Vaio VPCCW29FX does not resume correctly without acpi_sleep=nonvs, so add it to the ACPI sleep blacklist. https://bugzilla.kernel.org/show_bug.cgi?id=34722 Signed-off-by: Lan Tianyu Signed-off-by: Len Brown diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 0a7ed69..ca191ff 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -438,6 +438,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { }, { .callback = init_nvs_nosave, + .ident = "Sony Vaio VPCCW29FX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"), + }, + }, + { + .callback = init_nvs_nosave, .ident = "Averatec AV1020-ED2", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"), -- cgit v0.10.2 From fd45c15f13e754f3c106427e857310f3e0813951 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 20 Jan 2012 10:12:45 +0900 Subject: perf: Don't call release_callchain_buffers() if allocation fails When alloc_callchain_buffers() fails, it frees all of entries before return. In addition, calling the release_callchain_buffers() will cause a NULL pointer dereference since callchain_cpu_entries is not set. Signed-off-by: Namhyung Kim Acked-by: Frederic Weisbecker Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/1327021966-27688-1-git-send-email-namhyung.kim@lge.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c index 057e24b..6581a04 100644 --- a/kernel/events/callchain.c +++ b/kernel/events/callchain.c @@ -115,8 +115,6 @@ int get_callchain_buffers(void) } err = alloc_callchain_buffers(); - if (err) - release_callchain_buffers(); exit: mutex_unlock(&callchain_mutex); -- cgit v0.10.2 From 46cd6a7f680d14f6f80ede9f04aeb70fa83bd266 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 20 Jan 2012 10:12:46 +0900 Subject: perf: Call perf_cgroup_event_time() directly The perf_event_time() will call perf_cgroup_event_time() if @event is a cgroup event. Just do it directly and avoid the extra check.. Signed-off-by: Namhyung Kim Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/1327021966-27688-2-git-send-email-namhyung.kim@lge.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index a8f4ac0..32b48c8 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -815,7 +815,7 @@ static void update_event_times(struct perf_event *event) * here. */ if (is_cgroup_event(event)) - run_end = perf_event_time(event); + run_end = perf_cgroup_event_time(event); else if (ctx->is_active) run_end = ctx->time; else -- cgit v0.10.2 From 2a7f51a3e08cdaeea78d9e101a0079422a55bbc3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 09:28:53 +0000 Subject: MFD: mcp-core: fix mcp_priv() to be more type safe mcp_priv() does unexpected things when passed a void pointer. Make it a typed inline function, which ensures that it works correctly in these cases. Signed-off-by: Russell King diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index ee496708..f88c1cc 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -64,6 +64,9 @@ void mcp_driver_unregister(struct mcp_driver *); #define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device) #define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d) -#define mcp_priv(mcp) ((void *)((mcp)+1)) +static inline void *mcp_priv(struct mcp *mcp) +{ + return mcp + 1; +} #endif -- cgit v0.10.2 From 2e95e51e184bd107380881502ea0f483c4500706 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 18:15:24 +0000 Subject: MFD: ucb1x00-core: fix missing restore of io output data on resume We were not restoring the UCB1x00 gpio output data on resume, resulting in incorrect GPIO output data after a resume. Add the missing register write. Signed-off-by: Russell King diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index b281217..8ebda97 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -687,6 +687,7 @@ static int ucb1x00_resume(struct mcp *mcp) struct ucb1x00 *ucb = mcp_get_drvdata(mcp); struct ucb1x00_dev *dev; + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); mutex_lock(&ucb1x00_mutex); list_for_each_entry(dev, &ucb->devs, dev_node) { -- cgit v0.10.2 From c23bb602af24a635d0894aa7091e184385bf8a9f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 21 Jan 2012 18:21:50 +0000 Subject: MFD: ucb1x00-core: fix gpiolib direction_output handling gpiolib drivers should first set the output data before setting the direction to avoid putting glitches on an output signal. As an additional bonus, we tweak the code to avoid unnecessary register writes to the output and direction registers if they have no need to be updated. Signed-off-by: Russell King diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index 8ebda97..febc90c 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -148,16 +148,22 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset { struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio); unsigned long flags; + unsigned old, mask = 1 << offset; spin_lock_irqsave(&ucb->io_lock, flags); - ucb->io_dir |= (1 << offset); - ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); - + old = ucb->io_out; if (value) - ucb->io_out |= 1 << offset; + ucb->io_out |= mask; else - ucb->io_out &= ~(1 << offset); - ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + ucb->io_out &= ~mask; + + if (old != ucb->io_out) + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + + if (!(ucb->io_dir & mask)) { + ucb->io_dir |= mask; + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + } spin_unlock_irqrestore(&ucb->io_lock, flags); return 0; -- cgit v0.10.2 From aa557875cc6ed78a8c6035dffa354a09d48b16f6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Jul 2011 22:33:13 +0200 Subject: m68k/irq: Remove obsolete IRQ_FLG_* definitions The m68k core irq code stopped honoring these flags during the irq restructuring in 2006. Signed-off-by: Geert Uytterhoeven diff --git a/arch/m68k/include/asm/irq.h b/arch/m68k/include/asm/irq.h index 6198df5..d3a8acd 100644 --- a/arch/m68k/include/asm/irq.h +++ b/arch/m68k/include/asm/irq.h @@ -49,19 +49,6 @@ #define IRQ_USER 8 -/* - * various flags for request_irq() - the Amiga now uses the standard - * mechanism like all other architectures - IRQF_DISABLED and - * IRQF_SHARED are your friends. - */ -#ifndef MACH_AMIGA_ONLY -#define IRQ_FLG_LOCK (0x0001) /* handler is not replaceable */ -#define IRQ_FLG_REPLACE (0x0002) /* replace existing handler */ -#define IRQ_FLG_FAST (0x0004) -#define IRQ_FLG_SLOW (0x0008) -#define IRQ_FLG_STD (0x8000) /* internally used */ -#endif - struct irq_data; struct irq_chip; struct irq_desc; -- cgit v0.10.2 From d9070fc4997e255532f0519709c9326d043501b2 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Mon, 24 Oct 2011 01:11:10 +1100 Subject: macfb: fix black and white modes macfb won't init in black & white modes since fb_alloc_cmap() no longer works for zero cmap length. Fix this and also clean up a few printk's and some stylistic inconsistencies. Signed-off-by: Finn Thain Signed-off-by: Geert Uytterhoeven diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c index 43207cc..fe01add 100644 --- a/drivers/video/macfb.c +++ b/drivers/video/macfb.c @@ -592,12 +592,12 @@ static int __init macfb_init(void) if (!fb_info.screen_base) return -ENODEV; - printk("macfb: framebuffer at 0x%08lx, mapped to 0x%p, size %dk\n", - macfb_fix.smem_start, fb_info.screen_base, - macfb_fix.smem_len / 1024); - printk("macfb: mode is %dx%dx%d, linelength=%d\n", - macfb_defined.xres, macfb_defined.yres, - macfb_defined.bits_per_pixel, macfb_fix.line_length); + pr_info("macfb: framebuffer at 0x%08lx, mapped to 0x%p, size %dk\n", + macfb_fix.smem_start, fb_info.screen_base, + macfb_fix.smem_len / 1024); + pr_info("macfb: mode is %dx%dx%d, linelength=%d\n", + macfb_defined.xres, macfb_defined.yres, + macfb_defined.bits_per_pixel, macfb_fix.line_length); /* Fill in the available video resolution */ macfb_defined.xres_virtual = macfb_defined.xres; @@ -613,14 +613,10 @@ static int __init macfb_init(void) switch (macfb_defined.bits_per_pixel) { case 1: - /* - * XXX: I think this will catch any program that tries - * to do FBIO_PUTCMAP when the visual is monochrome. - */ macfb_defined.red.length = macfb_defined.bits_per_pixel; macfb_defined.green.length = macfb_defined.bits_per_pixel; macfb_defined.blue.length = macfb_defined.bits_per_pixel; - video_cmap_len = 0; + video_cmap_len = 2; macfb_fix.visual = FB_VISUAL_MONO01; break; case 2: @@ -660,11 +656,10 @@ static int __init macfb_init(void) macfb_fix.visual = FB_VISUAL_TRUECOLOR; break; default: - video_cmap_len = 0; - macfb_fix.visual = FB_VISUAL_MONO01; - printk("macfb: unknown or unsupported bit depth: %d\n", + pr_err("macfb: unknown or unsupported bit depth: %d\n", macfb_defined.bits_per_pixel); - break; + err = -EINVAL; + goto fail_unmap; } /* @@ -734,8 +729,8 @@ static int __init macfb_init(void) case MAC_MODEL_Q950: strcpy(macfb_fix.id, "DAFB"); macfb_setpalette = dafb_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; /* @@ -744,8 +739,8 @@ static int __init macfb_init(void) case MAC_MODEL_LCII: strcpy(macfb_fix.id, "V8"); macfb_setpalette = v8_brazil_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; /* @@ -758,8 +753,8 @@ static int __init macfb_init(void) case MAC_MODEL_P600: strcpy(macfb_fix.id, "Brazil"); macfb_setpalette = v8_brazil_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; /* @@ -773,10 +768,10 @@ static int __init macfb_init(void) case MAC_MODEL_P520: case MAC_MODEL_P550: case MAC_MODEL_P460: - macfb_setpalette = v8_brazil_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; strcpy(macfb_fix.id, "Sonora"); + macfb_setpalette = v8_brazil_setpalette; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; /* @@ -786,10 +781,10 @@ static int __init macfb_init(void) */ case MAC_MODEL_IICI: case MAC_MODEL_IISI: - macfb_setpalette = rbv_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; strcpy(macfb_fix.id, "RBV"); + macfb_setpalette = rbv_setpalette; rbv_cmap_regs = ioremap(DAC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; /* @@ -797,10 +792,10 @@ static int __init macfb_init(void) */ case MAC_MODEL_Q840: case MAC_MODEL_C660: - macfb_setpalette = civic_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; strcpy(macfb_fix.id, "Civic"); + macfb_setpalette = civic_setpalette; civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; @@ -809,26 +804,26 @@ static int __init macfb_init(void) * We think this may be like the LC II */ case MAC_MODEL_LC: + strcpy(macfb_fix.id, "LC"); if (vidtest) { macfb_setpalette = v8_brazil_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; } - strcpy(macfb_fix.id, "LC"); break; /* * We think this may be like the LC II */ case MAC_MODEL_CCL: + strcpy(macfb_fix.id, "Color Classic"); if (vidtest) { macfb_setpalette = v8_brazil_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; } - strcpy(macfb_fix.id, "Color Classic"); break; /* @@ -893,10 +888,10 @@ static int __init macfb_init(void) case MAC_MODEL_PB270C: case MAC_MODEL_PB280: case MAC_MODEL_PB280C: - macfb_setpalette = csc_setpalette; - macfb_defined.activate = FB_ACTIVATE_NOW; strcpy(macfb_fix.id, "CSC"); + macfb_setpalette = csc_setpalette; csc_cmap_regs = ioremap(CSC_BASE, 0x1000); + macfb_defined.activate = FB_ACTIVATE_NOW; break; default: @@ -918,8 +913,9 @@ static int __init macfb_init(void) if (err) goto fail_dealloc; - printk("fb%d: %s frame buffer device\n", - fb_info.node, fb_info.fix.id); + pr_info("fb%d: %s frame buffer device\n", + fb_info.node, fb_info.fix.id); + return 0; fail_dealloc: -- cgit v0.10.2 From 37be2c86f0fbd89b66792008767e688c819b7c32 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Mon, 24 Oct 2011 01:11:23 +1100 Subject: mac_scsi: dont enable mac_scsi irq before requesting it Don't enable the SCSI irq when initialising the chip -- the irq has no handler yet. Signed-off-by: Finn Thain Signed-off-by: Geert Uytterhoeven diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c index af3a6af..737d526 100644 --- a/drivers/scsi/mac_scsi.c +++ b/drivers/scsi/mac_scsi.c @@ -340,9 +340,6 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) printk(KERN_INFO "Macintosh SCSI: resetting the SCSI bus..." ); - /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */ - disable_irq(IRQ_MAC_SCSI); - /* get in phase */ NCR5380_write( TARGET_COMMAND_REG, PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) )); @@ -358,9 +355,6 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); ) barrier(); - /* switch on SCSI IRQ again */ - enable_irq(IRQ_MAC_SCSI); - printk(KERN_INFO " done\n" ); } #endif -- cgit v0.10.2 From c808d3d839ab70c87a6c9356c50569c87661378e Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Mon, 24 Oct 2011 01:11:24 +1100 Subject: mac_esp: rename irq Rename the "Mac ESP" irq as "ESP" to be consistent with all the other Mac drivers and ESP drivers. Signed-off-by: Finn Thain Signed-off-by: Geert Uytterhoeven diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c index 4ceeace..70eb1f7 100644 --- a/drivers/scsi/mac_esp.c +++ b/drivers/scsi/mac_esp.c @@ -565,8 +565,7 @@ static int __devinit esp_mac_probe(struct platform_device *dev) esp_chips[dev->id] = esp; mb(); if (esp_chips[!dev->id] == NULL) { - err = request_irq(host->irq, mac_scsi_esp_intr, 0, - "Mac ESP", NULL); + err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL); if (err < 0) { esp_chips[dev->id] = NULL; goto fail_free_priv; -- cgit v0.10.2 From 2a3535069e33d8b416f406c159ce924427315303 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Mon, 9 Jan 2012 15:10:15 +0100 Subject: m68k: Fix assembler constraint to prevent overeager gcc optimisation Passing the address of a variable as an operand to an asm statement doesn't mark the value of this variable as used, so gcc may optimize its initialisation away. Fix this by using the "m" constraint instead. Signed-off-by: Andreas Schwab Signed-off-by: Geert Uytterhoeven Cc: stable@vger.kernel.org diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c index 4203d10..c4ac15c 100644 --- a/arch/m68k/atari/config.c +++ b/arch/m68k/atari/config.c @@ -414,9 +414,9 @@ void __init config_atari(void) * FDC val = 4 -> Supervisor only */ asm volatile ("\n" " .chip 68030\n" - " pmove %0@,%/tt1\n" + " pmove %0,%/tt1\n" " .chip 68k" - : : "a" (&tt1_val)); + : : "m" (tt1_val)); } else { asm volatile ("\n" " .chip 68040\n" @@ -569,10 +569,10 @@ static void atari_reset(void) : "d0"); } else asm volatile ("\n" - " pmove %0@,%%tc\n" + " pmove %0,%%tc\n" " jmp %1@" : /* no outputs */ - : "a" (&tc_val), "a" (reset_addr)); + : "m" (tc_val), "a" (reset_addr)); } diff --git a/arch/m68k/kernel/process_mm.c b/arch/m68k/kernel/process_mm.c index 1bc223a..aa4ffb8 100644 --- a/arch/m68k/kernel/process_mm.c +++ b/arch/m68k/kernel/process_mm.c @@ -189,8 +189,8 @@ void flush_thread(void) current->thread.fs = __USER_DS; if (!FPU_IS_EMU) asm volatile (".chip 68k/68881\n\t" - "frestore %0@\n\t" - ".chip 68k" : : "a" (&zero)); + "frestore %0\n\t" + ".chip 68k" : : "m" (zero)); } /* diff --git a/arch/m68k/kernel/process_no.c b/arch/m68k/kernel/process_no.c index 69c1803..5e1078c 100644 --- a/arch/m68k/kernel/process_no.c +++ b/arch/m68k/kernel/process_no.c @@ -163,8 +163,8 @@ void flush_thread(void) #ifdef CONFIG_FPU if (!FPU_IS_EMU) asm volatile (".chip 68k/68881\n\t" - "frestore %0@\n\t" - ".chip 68k" : : "a" (&zero)); + "frestore %0\n\t" + ".chip 68k" : : "m" (zero)); #endif } diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 89362f2..eb67469 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -552,13 +552,13 @@ static inline void bus_error030 (struct frame *fp) #ifdef DEBUG asm volatile ("ptestr %3,%2@,#7,%0\n\t" - "pmove %%psr,%1@" - : "=a&" (desc) - : "a" (&temp), "a" (addr), "d" (ssw)); + "pmove %%psr,%1" + : "=a&" (desc), "=m" (temp) + : "a" (addr), "d" (ssw)); #else asm volatile ("ptestr %2,%1@,#7\n\t" - "pmove %%psr,%0@" - : : "a" (&temp), "a" (addr), "d" (ssw)); + "pmove %%psr,%0" + : "=m" (temp) : "a" (addr), "d" (ssw)); #endif mmusr = temp; @@ -605,20 +605,18 @@ static inline void bus_error030 (struct frame *fp) !(ssw & RW) ? "write" : "read", addr, fp->ptregs.pc, ssw); asm volatile ("ptestr #1,%1@,#0\n\t" - "pmove %%psr,%0@" - : /* no outputs */ - : "a" (&temp), "a" (addr)); + "pmove %%psr,%0" + : "=m" (temp) + : "a" (addr)); mmusr = temp; printk ("level 0 mmusr is %#x\n", mmusr); #if 0 - asm volatile ("pmove %%tt0,%0@" - : /* no outputs */ - : "a" (&tlong)); + asm volatile ("pmove %%tt0,%0" + : "=m" (tlong)); printk("tt0 is %#lx, ", tlong); - asm volatile ("pmove %%tt1,%0@" - : /* no outputs */ - : "a" (&tlong)); + asm volatile ("pmove %%tt1,%0" + : "=m" (tlong)); printk("tt1 is %#lx\n", tlong); #endif #ifdef DEBUG @@ -668,13 +666,13 @@ static inline void bus_error030 (struct frame *fp) #ifdef DEBUG asm volatile ("ptestr #1,%2@,#7,%0\n\t" - "pmove %%psr,%1@" - : "=a&" (desc) - : "a" (&temp), "a" (addr)); + "pmove %%psr,%1" + : "=a&" (desc), "=m" (temp) + : "a" (addr)); #else asm volatile ("ptestr #1,%1@,#7\n\t" - "pmove %%psr,%0@" - : : "a" (&temp), "a" (addr)); + "pmove %%psr,%0" + : "=m" (temp) : "a" (addr)); #endif mmusr = temp; diff --git a/arch/m68k/mm/cache.c b/arch/m68k/mm/cache.c index 5437fff..5550aa4 100644 --- a/arch/m68k/mm/cache.c +++ b/arch/m68k/mm/cache.c @@ -52,9 +52,9 @@ static unsigned long virt_to_phys_slow(unsigned long vaddr) unsigned long *descaddr; asm volatile ("ptestr %3,%2@,#7,%0\n\t" - "pmove %%psr,%1@" - : "=a&" (descaddr) - : "a" (&mmusr), "a" (vaddr), "d" (get_fs().seg)); + "pmove %%psr,%1" + : "=a&" (descaddr), "=m" (mmusr) + : "a" (vaddr), "d" (get_fs().seg)); if (mmusr & (MMU_I|MMU_B|MMU_L)) return 0; descaddr = phys_to_virt((unsigned long)descaddr); -- cgit v0.10.2 From a14304edcd5e8323205db34b08f709feb5357e64 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 21 Jan 2012 21:48:53 +0000 Subject: ASoC: wm8996: Call _POST_PMU callback for CPVDD We should be allowing a 5ms delay after the charge pump is started in order to ensure it has finished ramping. Signed-off-by: Mark Brown Cc: stable@kernel.org diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 86f5b6b..13aa2bd 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1120,7 +1120,8 @@ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0), -- cgit v0.10.2 From 95120d5d1bc17bdec29085186b6ab3d90e92d6f3 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Sun, 22 Jan 2012 18:57:57 +0100 Subject: Correct bad gpio naming One of the GPIO names in drivers/gpio/gpio-lpc32xx.c was bad. Renaming gpi000 -> gpio00 Signed-off-by: Roland Stigge Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 5b69480..ddfacc5 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -96,7 +96,7 @@ static const char *gpio_p2_names[LPC32XX_GPIO_P2_MAX] = { }; static const char *gpio_p3_names[LPC32XX_GPIO_P3_MAX] = { - "gpi000", "gpio01", "gpio02", "gpio03", + "gpio00", "gpio01", "gpio02", "gpio03", "gpio04", "gpio05" }; -- cgit v0.10.2 From b477ba628a283ba93e631d66907f91df80e82267 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Thu, 19 Jan 2012 09:42:37 +0000 Subject: mlx4_en: clear all eth statistics when port goes up Bug fix: Not all stats fields were cleared. Signed-off-by: Eugenia Emantayev Reviewed-by: Yevgeny Petriln Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 72fa807..be3f415 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -807,37 +807,49 @@ static void mlx4_en_restart(struct work_struct *work) mutex_unlock(&mdev->state_lock); } - -static int mlx4_en_open(struct net_device *dev) +static void mlx4_en_clear_stats(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; int i; - int err = 0; - - mutex_lock(&mdev->state_lock); - - if (!mdev->device_up) { - en_err(priv, "Cannot open - device down/disabled\n"); - err = -EBUSY; - goto out; - } - /* Reset HW statistics and performance counters */ if (mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 1)) en_dbg(HW, priv, "Failed dumping statistics\n"); memset(&priv->stats, 0, sizeof(priv->stats)); memset(&priv->pstats, 0, sizeof(priv->pstats)); + memset(&priv->pkstats, 0, sizeof(priv->pkstats)); + memset(&priv->port_stats, 0, sizeof(priv->port_stats)); for (i = 0; i < priv->tx_ring_num; i++) { priv->tx_ring[i].bytes = 0; priv->tx_ring[i].packets = 0; + priv->tx_ring[i].tx_csum = 0; } for (i = 0; i < priv->rx_ring_num; i++) { priv->rx_ring[i].bytes = 0; priv->rx_ring[i].packets = 0; + priv->rx_ring[i].csum_ok = 0; + priv->rx_ring[i].csum_none = 0; } +} + +static int mlx4_en_open(struct net_device *dev) +{ + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; + int err = 0; + + mutex_lock(&mdev->state_lock); + + if (!mdev->device_up) { + en_err(priv, "Cannot open - device down/disabled\n"); + err = -EBUSY; + goto out; + } + + /* Reset HW statistics and SW counters */ + mlx4_en_clear_stats(dev); err = mlx4_en_start_port(dev); if (err) -- cgit v0.10.2 From 35fb9afbdeef9d5859d9a878d0372907baf119e1 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Thu, 19 Jan 2012 09:44:37 +0000 Subject: mlx4: VF is not allowed to perform dump stats In multifunction mode - DUMP_STATS command is not executed for VFs. Signed-off-by: Eugenia Emantayev Reviewed-by: Yevgeny Petrilin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 88b52e5..1a551d6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -898,6 +898,8 @@ int mlx4_DUMP_ETH_STATS_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + if (slave != dev->caps.function) + return 0; return mlx4_common_dump_eth_stats(dev, slave, vhcr->in_modifier, outbox); } -- cgit v0.10.2 From 93ece0c1a7ace88f10411dbb5643d2aa2fe00ebf Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Thu, 19 Jan 2012 09:45:05 +0000 Subject: mlx4_en: eth statistics modification In native mode display all available staticstics. In SRIOV mode on VF display only SW counters statistics, in SRIOV mode on hypervisor display SW counters and errors (got from FW) statistics. Signed-off-by: Eugenia Emantayev Reviewed-by: Yevgeny Petrilin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 53c6686..70346fd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -183,10 +183,11 @@ static int mlx4_en_set_wol(struct net_device *netdev, static int mlx4_en_get_sset_count(struct net_device *dev, int sset) { struct mlx4_en_priv *priv = netdev_priv(dev); + int bit_count = hweight64(priv->stats_bitmap); switch (sset) { case ETH_SS_STATS: - return NUM_ALL_STATS + + return (priv->stats_bitmap ? bit_count : NUM_ALL_STATS) + (priv->tx_ring_num + priv->rx_ring_num) * 2; case ETH_SS_TEST: return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags @@ -201,14 +202,34 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, { struct mlx4_en_priv *priv = netdev_priv(dev); int index = 0; - int i; + int i, j = 0; spin_lock_bh(&priv->stats_lock); - for (i = 0; i < NUM_MAIN_STATS; i++) - data[index++] = ((unsigned long *) &priv->stats)[i]; - for (i = 0; i < NUM_PORT_STATS; i++) - data[index++] = ((unsigned long *) &priv->port_stats)[i]; + if (!(priv->stats_bitmap)) { + for (i = 0; i < NUM_MAIN_STATS; i++) + data[index++] = + ((unsigned long *) &priv->stats)[i]; + for (i = 0; i < NUM_PORT_STATS; i++) + data[index++] = + ((unsigned long *) &priv->port_stats)[i]; + for (i = 0; i < NUM_PKT_STATS; i++) + data[index++] = + ((unsigned long *) &priv->pkstats)[i]; + } else { + for (i = 0; i < NUM_MAIN_STATS; i++) { + if ((priv->stats_bitmap >> j) & 1) + data[index++] = + ((unsigned long *) &priv->stats)[i]; + j++; + } + for (i = 0; i < NUM_PORT_STATS; i++) { + if ((priv->stats_bitmap >> j) & 1) + data[index++] = + ((unsigned long *) &priv->port_stats)[i]; + j++; + } + } for (i = 0; i < priv->tx_ring_num; i++) { data[index++] = priv->tx_ring[i].packets; data[index++] = priv->tx_ring[i].bytes; @@ -217,8 +238,6 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, data[index++] = priv->rx_ring[i].packets; data[index++] = priv->rx_ring[i].bytes; } - for (i = 0; i < NUM_PKT_STATS; i++) - data[index++] = ((unsigned long *) &priv->pkstats)[i]; spin_unlock_bh(&priv->stats_lock); } @@ -247,11 +266,29 @@ static void mlx4_en_get_strings(struct net_device *dev, case ETH_SS_STATS: /* Add main counters */ - for (i = 0; i < NUM_MAIN_STATS; i++) - strcpy(data + (index++) * ETH_GSTRING_LEN, main_strings[i]); - for (i = 0; i< NUM_PORT_STATS; i++) - strcpy(data + (index++) * ETH_GSTRING_LEN, - main_strings[i + NUM_MAIN_STATS]); + if (!priv->stats_bitmap) { + for (i = 0; i < NUM_MAIN_STATS; i++) + strcpy(data + (index++) * ETH_GSTRING_LEN, + main_strings[i]); + for (i = 0; i < NUM_PORT_STATS; i++) + strcpy(data + (index++) * ETH_GSTRING_LEN, + main_strings[i + + NUM_MAIN_STATS]); + for (i = 0; i < NUM_PKT_STATS; i++) + strcpy(data + (index++) * ETH_GSTRING_LEN, + main_strings[i + + NUM_MAIN_STATS + + NUM_PORT_STATS]); + } else + for (i = 0; i < NUM_MAIN_STATS + NUM_PORT_STATS; i++) { + if ((priv->stats_bitmap >> i) & 1) { + strcpy(data + + (index++) * ETH_GSTRING_LEN, + main_strings[i]); + } + if (!(priv->stats_bitmap >> i)) + break; + } for (i = 0; i < priv->tx_ring_num; i++) { sprintf(data + (index++) * ETH_GSTRING_LEN, "tx%d_packets", i); @@ -264,9 +301,6 @@ static void mlx4_en_get_strings(struct net_device *dev, sprintf(data + (index++) * ETH_GSTRING_LEN, "rx%d_bytes", i); } - for (i = 0; i< NUM_PKT_STATS; i++) - strcpy(data + (index++) * ETH_GSTRING_LEN, - main_strings[i + NUM_MAIN_STATS + NUM_PORT_STATS]); break; } } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index be3f415..467ae58 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -702,6 +702,8 @@ int mlx4_en_start_port(struct net_device *dev) /* Schedule multicast task to populate multicast list */ queue_work(mdev->workqueue, &priv->mcast_task); + mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); + priv->port_up = true; netif_tx_start_all_queues(dev); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index f4d189a..35f0884 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -476,6 +476,7 @@ struct mlx4_en_priv { struct mlx4_en_perf_stats pstats; struct mlx4_en_pkt_stats pkstats; struct mlx4_en_port_stats port_stats; + u64 stats_bitmap; char *mc_addrs; int mc_addrs_cnt; struct mlx4_en_stat_out_mbox hw_stats; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 1a551d6..f44ae55 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -44,6 +44,11 @@ #define MLX4_VLAN_VALID (1u << 31) #define MLX4_VLAN_MASK 0xfff +#define MLX4_STATS_TRAFFIC_COUNTERS_MASK 0xfULL +#define MLX4_STATS_TRAFFIC_DROPS_MASK 0xc0ULL +#define MLX4_STATS_ERROR_COUNTERS_MASK 0x1ffc30ULL +#define MLX4_STATS_PORT_COUNTERS_MASK 0x1fe00000ULL + void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table) { int i; @@ -903,3 +908,19 @@ int mlx4_DUMP_ETH_STATS_wrapper(struct mlx4_dev *dev, int slave, return mlx4_common_dump_eth_stats(dev, slave, vhcr->in_modifier, outbox); } + +void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap) +{ + if (!mlx4_is_mfunc(dev)) { + *stats_bitmap = 0; + return; + } + + *stats_bitmap = (MLX4_STATS_TRAFFIC_COUNTERS_MASK | + MLX4_STATS_TRAFFIC_DROPS_MASK | + MLX4_STATS_PORT_COUNTERS_MASK); + + if (mlx4_is_master(dev)) + *stats_bitmap |= MLX4_STATS_ERROR_COUNTERS_MASK; +} +EXPORT_SYMBOL(mlx4_set_stats_bitmap); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 5c4fe8e..aea6190 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -621,6 +621,7 @@ void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac); int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac); int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn); void mlx4_put_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int qpn); +void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index); -- cgit v0.10.2 From eb41049f2f5eee1525fe1c699e6b1f03c8c51532 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 19 Jan 2012 09:45:19 +0000 Subject: mlx4_core: removed function index from vf. The Virtual Functions should not be aware their function number. Signed-off-by: Marcel Apfelbaum Reviewed-by: Jack Morgenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 475f9d6..7e64033 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -96,7 +96,7 @@ void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type) static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, int cq_num) { - return mlx4_cmd(dev, mailbox->dma | dev->caps.function, cq_num, 0, + return mlx4_cmd(dev, mailbox->dma, cq_num, 0, MLX4_CMD_SW2HW_CQ, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } @@ -111,7 +111,7 @@ static int mlx4_MODIFY_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox static int mlx4_HW2SW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, int cq_num) { - return mlx4_cmd_box(dev, dev->caps.function, mailbox ? mailbox->dma : 0, + return mlx4_cmd_box(dev, 0, mailbox ? mailbox->dma : 0, cq_num, mailbox ? 0 : 1, MLX4_CMD_HW2SW_CQ, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 1e9b55e..e163de6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -546,7 +546,7 @@ static int mlx4_MAP_EQ(struct mlx4_dev *dev, u64 event_mask, int unmap, static int mlx4_SW2HW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, int eq_num) { - return mlx4_cmd(dev, mailbox->dma | dev->caps.function, eq_num, 0, + return mlx4_cmd(dev, mailbox->dma, eq_num, 0, MLX4_CMD_SW2HW_EQ, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } @@ -554,7 +554,7 @@ static int mlx4_SW2HW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, static int mlx4_HW2SW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, int eq_num) { - return mlx4_cmd_box(dev, dev->caps.function, mailbox->dma, eq_num, + return mlx4_cmd_box(dev, 0, mailbox->dma, eq_num, 0, MLX4_CMD_HW2SW_EQ, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index a424a19..8a21e10 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -158,7 +158,6 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, #define QUERY_FUNC_CAP_FLAGS_OFFSET 0x0 #define QUERY_FUNC_CAP_NUM_PORTS_OFFSET 0x1 -#define QUERY_FUNC_CAP_FUNCTION_OFFSET 0x3 #define QUERY_FUNC_CAP_PF_BHVR_OFFSET 0x4 #define QUERY_FUNC_CAP_QP_QUOTA_OFFSET 0x10 #define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET 0x14 @@ -182,9 +181,6 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, field = 1 << 7; /* enable only ethernet interface */ MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FLAGS_OFFSET); - field = slave; - MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FUNCTION_OFFSET); - field = dev->caps.num_ports; MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_NUM_PORTS_OFFSET); @@ -249,9 +245,6 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, struct mlx4_func_cap *func_cap) goto out; } - MLX4_GET(field, outbox, QUERY_FUNC_CAP_FUNCTION_OFFSET); - func_cap->function = field; - MLX4_GET(field, outbox, QUERY_FUNC_CAP_NUM_PORTS_OFFSET); func_cap->num_ports = field; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 119e0cc..e1a5fa5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -119,7 +119,6 @@ struct mlx4_dev_cap { }; struct mlx4_func_cap { - u8 function; u8 num_ports; u8 flags; u32 pf_context_behaviour; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 6bb62c5..91c8bc9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -471,7 +471,6 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -ENOSYS; } - dev->caps.function = func_cap.function; dev->caps.num_ports = func_cap.num_ports; dev->caps.num_qps = func_cap.qp_quota; dev->caps.num_srqs = func_cap.srq_quota; diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 01df556..8deeef9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -291,7 +291,7 @@ static u32 key_to_hw_index(u32 key) static int mlx4_SW2HW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, int mpt_index) { - return mlx4_cmd(dev, mailbox->dma | dev->caps.function , mpt_index, + return mlx4_cmd(dev, mailbox->dma, mpt_index, 0, MLX4_CMD_SW2HW_MPT, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); } diff --git a/drivers/net/ethernet/mellanox/mlx4/pd.c b/drivers/net/ethernet/mellanox/mlx4/pd.c index 5c9a54d..db4746d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/pd.c +++ b/drivers/net/ethernet/mellanox/mlx4/pd.c @@ -52,8 +52,7 @@ int mlx4_pd_alloc(struct mlx4_dev *dev, u32 *pdn) *pdn = mlx4_bitmap_alloc(&priv->pd_bitmap); if (*pdn == -1) return -ENOMEM; - if (mlx4_is_mfunc(dev)) - *pdn |= (dev->caps.function + 1) << NOT_MASKED_PD_BITS; + return 0; } EXPORT_SYMBOL_GPL(mlx4_pd_alloc); diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 6b03ac8..738f950 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -162,7 +162,7 @@ static int __mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, ((struct mlx4_qp_context *) (mailbox->buf + 8))->local_qpn = cpu_to_be32(qp->qpn); - ret = mlx4_cmd(dev, mailbox->dma | dev->caps.function, + ret = mlx4_cmd(dev, mailbox->dma, qp->qpn | (!!sqd_event << 31), new_state == MLX4_QP_STATE_RST ? 2 : 0, op[cur_state][new_state], MLX4_CMD_TIME_CLASS_C, native); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index ed20751..a30cf19 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1561,11 +1561,6 @@ static int mr_get_mtt_size(struct mlx4_mpt_entry *mpt) return be32_to_cpu(mpt->mtt_sz); } -static int mr_get_pdn(struct mlx4_mpt_entry *mpt) -{ - return be32_to_cpu(mpt->pd_flags) & 0xffffff; -} - static int qp_get_mtt_addr(struct mlx4_qp_context *qpc) { return be32_to_cpu(qpc->mtt_base_addr_l) & 0xfffffff8; @@ -1602,16 +1597,6 @@ static int qp_get_mtt_size(struct mlx4_qp_context *qpc) return total_pages; } -static int qp_get_pdn(struct mlx4_qp_context *qpc) -{ - return be32_to_cpu(qpc->pd) & 0xffffff; -} - -static int pdn2slave(int pdn) -{ - return (pdn >> NOT_MASKED_PD_BITS) - 1; -} - static int check_mtt_range(struct mlx4_dev *dev, int slave, int start, int size, struct res_mtt *mtt) { @@ -1656,11 +1641,6 @@ int mlx4_SW2HW_MPT_wrapper(struct mlx4_dev *dev, int slave, mpt->mtt = mtt; } - if (pdn2slave(mr_get_pdn(inbox->buf)) != slave) { - err = -EPERM; - goto ex_put; - } - err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd); if (err) goto ex_put; @@ -1792,11 +1772,6 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave, if (err) goto ex_put_mtt; - if (pdn2slave(qp_get_pdn(qpc)) != slave) { - err = -EPERM; - goto ex_put_mtt; - } - err = get_res(dev, slave, rcqn, RES_CQ, &rcq); if (err) goto ex_put_mtt; @@ -2289,11 +2264,6 @@ ex_put: return err; } -static int srq_get_pdn(struct mlx4_srq_context *srqc) -{ - return be32_to_cpu(srqc->pd) & 0xffffff; -} - static int srq_get_mtt_size(struct mlx4_srq_context *srqc) { int log_srq_size = (be32_to_cpu(srqc->state_logsize_srqn) >> 24) & 0xf; @@ -2333,11 +2303,6 @@ int mlx4_SW2HW_SRQ_wrapper(struct mlx4_dev *dev, int slave, if (err) goto ex_put_mtt; - if (pdn2slave(srq_get_pdn(srqc)) != slave) { - err = -EPERM; - goto ex_put_mtt; - } - err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd); if (err) goto ex_put_mtt; diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index 2823fff..feda6c0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -67,7 +67,7 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type) static int mlx4_SW2HW_SRQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, int srq_num) { - return mlx4_cmd(dev, mailbox->dma | dev->caps.function, srq_num, 0, + return mlx4_cmd(dev, mailbox->dma, srq_num, 0, MLX4_CMD_SW2HW_SRQ, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } -- cgit v0.10.2 From 9fd7a1e147bd41fb999a58da8a8826b9f3415384 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 19 Jan 2012 09:45:31 +0000 Subject: mlx4_core: Fix mtt profile issue Num mtts from profile is really the number of mtt segments. Thus, in make profile, to get the proper number of MTT entries, must multiply num_mtts by mtts per segment. Signed-off-by: Marcel Apfelbaum Reviewed-by: Jack Morgenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 91c8bc9..678558b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -108,7 +108,7 @@ static struct mlx4_profile default_profile = { .num_cq = 1 << 16, .num_mcg = 1 << 13, .num_mpt = 1 << 19, - .num_mtt = 1 << 20, + .num_mtt = 1 << 20, /* It is really num mtt segements */ }; static int log_num_mac = 7; diff --git a/drivers/net/ethernet/mellanox/mlx4/profile.c b/drivers/net/ethernet/mellanox/mlx4/profile.c index 66f91ca..1129677 100644 --- a/drivers/net/ethernet/mellanox/mlx4/profile.c +++ b/drivers/net/ethernet/mellanox/mlx4/profile.c @@ -110,7 +110,7 @@ u64 mlx4_make_profile(struct mlx4_dev *dev, profile[MLX4_RES_EQ].num = min_t(unsigned, dev_cap->max_eqs, MAX_MSIX); profile[MLX4_RES_DMPT].num = request->num_mpt; profile[MLX4_RES_CMPT].num = MLX4_NUM_CMPTS; - profile[MLX4_RES_MTT].num = request->num_mtt; + profile[MLX4_RES_MTT].num = request->num_mtt * (1 << log_mtts_per_seg); profile[MLX4_RES_MCG].num = request->num_mcg; for (i = 0; i < MLX4_RES_NUM; ++i) { -- cgit v0.10.2 From 803143fbda719106017309d551d082e4b4e7c8c3 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 19 Jan 2012 09:45:46 +0000 Subject: mlx4_core: map async events to arbitrary slave eqs Slave async events were mapped to single eq. This patch fixes this issue, so the slaves can map the async events to any eq. Signed-off-by: Marcel Apfelbaum Reviewed-by: Jack Morgenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 978f593..405e6ac 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1247,6 +1247,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, u32 reply; u32 slave_status = 0; u8 is_going_down = 0; + int i; slave_state[slave].comm_toggle ^= 1; reply = (u32) slave_state[slave].comm_toggle << 31; @@ -1258,6 +1259,10 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, if (cmd == MLX4_COMM_CMD_RESET) { mlx4_warn(dev, "Received reset from slave:%d\n", slave); slave_state[slave].active = false; + for (i = 0; i < MLX4_EVENT_TYPES_NUM; ++i) { + slave_state[slave].event_eq[i].eqn = -1; + slave_state[slave].event_eq[i].token = 0; + } /*check if we are in the middle of FLR process, if so return "retry" status to the slave*/ if (MLX4_COMM_CMD_FLR == slave_state[slave].last_cmd) { @@ -1452,7 +1457,7 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_slave_state *s_state; - int i, err, port; + int i, j, err, port; priv->mfunc.vhcr = dma_alloc_coherent(&(dev->pdev->dev), PAGE_SIZE, &priv->mfunc.vhcr_dma, @@ -1485,6 +1490,8 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) for (i = 0; i < dev->num_slaves; ++i) { s_state = &priv->mfunc.master.slave_state[i]; s_state->last_cmd = MLX4_COMM_CMD_RESET; + for (j = 0; j < MLX4_EVENT_TYPES_NUM; ++j) + s_state->event_eq[j].eqn = -1; __raw_writel((__force u32) 0, &priv->mfunc.comm[i].slave_write); __raw_writel((__force u32) 0, diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index e163de6..55d7bd4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -513,25 +513,22 @@ int mlx4_MAP_EQ_wrapper(struct mlx4_dev *dev, int slave, { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_slave_event_eq_info *event_eq = - &priv->mfunc.master.slave_state[slave].event_eq; + priv->mfunc.master.slave_state[slave].event_eq; u32 in_modifier = vhcr->in_modifier; u32 eqn = in_modifier & 0x1FF; u64 in_param = vhcr->in_param; int err = 0; + int i; if (slave == dev->caps.function) err = mlx4_cmd(dev, in_param, (in_modifier & 0x80000000) | eqn, 0, MLX4_CMD_MAP_EQ, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); - if (!err) { - if (in_modifier >> 31) { - /* unmap */ - event_eq->event_type &= ~in_param; - } else { - event_eq->eqn = eqn; - event_eq->event_type = in_param; - } - } + if (!err) + for (i = 0; i < MLX4_EVENT_TYPES_NUM; ++i) + if (in_param & (1LL << i)) + event_eq[i].eqn = in_modifier >> 31 ? -1 : eqn; + return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index a80121a..c92269f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -388,9 +388,8 @@ struct mlx4_slave_eqe { }; struct mlx4_slave_event_eq_info { - u32 eqn; + int eqn; u16 token; - u64 event_type; }; struct mlx4_profile { @@ -449,6 +448,8 @@ struct mlx4_steer_index { struct list_head duplicates; }; +#define MLX4_EVENT_TYPES_NUM 64 + struct mlx4_slave_state { u8 comm_toggle; u8 last_cmd; @@ -461,7 +462,8 @@ struct mlx4_slave_state { struct mlx4_slave_eqe eq[MLX4_MFUNC_MAX_EQES]; struct list_head mcast_filters[MLX4_MAX_PORTS + 1]; struct mlx4_vlan_fltr *vlan_filter[MLX4_MAX_PORTS + 1]; - struct mlx4_slave_event_eq_info event_eq; + /* event type to eq number lookup */ + struct mlx4_slave_event_eq_info event_eq[MLX4_EVENT_TYPES_NUM]; u16 eq_pi; u16 eq_ci; spinlock_t lock; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index a30cf19..dcd819b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2023,10 +2023,10 @@ int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) if (!priv->mfunc.master.slave_state) return -EINVAL; - event_eq = &priv->mfunc.master.slave_state[slave].event_eq; + event_eq = &priv->mfunc.master.slave_state[slave].event_eq[eqe->type]; /* Create the event only if the slave is registered */ - if ((event_eq->event_type & (1 << eqe->type)) == 0) + if (event_eq->eqn < 0) return 0; mutex_lock(&priv->mfunc.master.gen_eqe_mutex[slave]); -- cgit v0.10.2 From a42b4799c683723e8c464de4026af085b2ebd5fa Mon Sep 17 00:00:00 2001 From: Vijay Subramanian Date: Thu, 19 Jan 2012 10:20:59 +0000 Subject: netem: Fix off-by-one bug in reordering With netem reordering, a gap of N is supposed to reorder every Nth packet with given reorder probability. However, the code currently skips N packets and reorders every (N+1)th packet. Signed-off-by: Vijay Subramanian Signed-off-by: David S. Miller diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index e7e1d0b..2776012 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -419,7 +419,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) cb = netem_skb_cb(skb); if (q->gap == 0 || /* not doing reordering */ - q->counter < q->gap || /* inside last reordering gap */ + q->counter < q->gap - 1 || /* inside last reordering gap */ q->reorder < get_crandom(&q->reorder_cor)) { psched_time_t now; psched_tdiff_t delay; -- cgit v0.10.2 From 19f9ad789396fe5b4fe0302344b3acfa07cb6772 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 19 Jan 2012 14:35:25 +0000 Subject: skge: don't assert carrier until link is up Skge device would assert carrier (link up) as soon as network device open was called, rather than waiting until PHY has detected link. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 18a87a5..f580f05 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -2576,6 +2576,7 @@ static int skge_up(struct net_device *dev) } /* Initialize MAC */ + netif_carrier_off(dev); spin_lock_bh(&hw->phy_lock); if (is_genesis(hw)) genesis_mac_init(hw, port); -- cgit v0.10.2 From d0249e44432aa0ffcf710b64449b8eaa3722547e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 19 Jan 2012 14:37:18 +0000 Subject: skge: check for PCI dma mapping errors Driver should check for mapping errors. Machines with limited DMA maps may return an error when a PCI map is requested (not an issue on standard x86). Also use upper/lower 32 bits macros for clarity. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index f580f05..299c33b 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -931,17 +931,20 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base) } /* Allocate and setup a new buffer for receiving */ -static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, - struct sk_buff *skb, unsigned int bufsize) +static int skge_rx_setup(struct pci_dev *pdev, + struct skge_element *e, + struct sk_buff *skb, unsigned int bufsize) { struct skge_rx_desc *rd = e->desc; - u64 map; + dma_addr_t map; - map = pci_map_single(skge->hw->pdev, skb->data, bufsize, + map = pci_map_single(pdev, skb->data, bufsize, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(pdev, map)) + goto mapping_error; - rd->dma_lo = map; - rd->dma_hi = map >> 32; + rd->dma_lo = lower_32_bits(map); + rd->dma_hi = upper_32_bits(map); e->skb = skb; rd->csum1_start = ETH_HLEN; rd->csum2_start = ETH_HLEN; @@ -953,6 +956,13 @@ static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize; dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, bufsize); + return 0; + +mapping_error: + if (net_ratelimit()) + dev_warn(&pdev->dev, "%s: rx mapping error\n", + skb->dev->name); + return -EIO; } /* Resume receiving using existing skb, @@ -1014,7 +1024,11 @@ static int skge_rx_fill(struct net_device *dev) return -ENOMEM; skb_reserve(skb, NET_IP_ALIGN); - skge_rx_setup(skge, e, skb, skge->rx_buf_size); + if (skge_rx_setup(skge->hw->pdev, e, skb, skge->rx_buf_size)) { + kfree_skb(skb); + return -ENOMEM; + } + } while ((e = e->next) != ring->start); ring->to_clean = ring->start; @@ -2729,7 +2743,7 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, struct skge_tx_desc *td; int i; u32 control, len; - u64 map; + dma_addr_t map; if (skb_padto(skb, ETH_ZLEN)) return NETDEV_TX_OK; @@ -2743,11 +2757,14 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, e->skb = skb; len = skb_headlen(skb); map = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(hw->pdev, map)) + goto mapping_error; + dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, len); - td->dma_lo = map; - td->dma_hi = map >> 32; + td->dma_lo = lower_32_bits(map); + td->dma_hi = upper_32_bits(map); if (skb->ip_summed == CHECKSUM_PARTIAL) { const int offset = skb_checksum_start_offset(skb); @@ -2778,14 +2795,16 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, map = skb_frag_dma_map(&hw->pdev->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); + if (dma_mapping_error(&hw->pdev->dev, map)) + goto mapping_unwind; e = e->next; e->skb = skb; tf = e->desc; BUG_ON(tf->control & BMU_OWN); - tf->dma_lo = map; - tf->dma_hi = (u64) map >> 32; + tf->dma_lo = lower_32_bits(map); + tf->dma_hi = upper_32_bits(map); dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, skb_frag_size(frag)); @@ -2813,6 +2832,28 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, } return NETDEV_TX_OK; + +mapping_unwind: + /* unroll any pages that were already mapped. */ + if (e != skge->tx_ring.to_use) { + struct skge_element *u; + + for (u = skge->tx_ring.to_use->next; u != e; u = u->next) + pci_unmap_page(hw->pdev, dma_unmap_addr(u, mapaddr), + dma_unmap_len(u, maplen), + PCI_DMA_TODEVICE); + e = skge->tx_ring.to_use; + } + /* undo the mapping for the skb header */ + pci_unmap_single(hw->pdev, dma_unmap_addr(e, mapaddr), + dma_unmap_len(e, maplen), + PCI_DMA_TODEVICE); +mapping_error: + /* mapping error causes error message and packet to be discarded. */ + if (net_ratelimit()) + dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; } @@ -3060,13 +3101,17 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, if (!nskb) goto resubmit; + if (unlikely(skge_rx_setup(skge->hw->pdev, e, nskb, skge->rx_buf_size))) { + dev_kfree_skb(nskb); + goto resubmit; + } + pci_unmap_single(skge->hw->pdev, dma_unmap_addr(e, mapaddr), dma_unmap_len(e, maplen), PCI_DMA_FROMDEVICE); skb = e->skb; prefetch(skb->data); - skge_rx_setup(skge, e, nskb, skge->rx_buf_size); } skb_put(skb, len); -- cgit v0.10.2 From 974c12360dfe6ab01201fe9e708e7755c413f8b6 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Thu, 19 Jan 2012 14:42:21 +0000 Subject: tcp: detect loss above high_seq in recovery Correctly implement a loss detection heuristic: New sequences (above high_seq) sent during the fast recovery are deemed lost when higher sequences are SACKed. Current code does not catch these losses, because tcp_mark_head_lost() does not check packets beyond high_seq. The fix is straight-forward by checking packets until the highest sacked packet. In addition, all the FLAG_DATA_LOST logic are in-effective and redundant and can be removed. Update the loss heuristic comments. The algorithm above is documented as heuristic B, but it is redundant too because heuristic A already covers B. Note that this change only marks some forward-retransmitted packets LOST. It does NOT forbid TCP performing further CWR on new losses. A potential follow-up patch under preparation is to perform another CWR on "new" losses such as 1) sequence above high_seq is lost (by resetting high_seq to snd_nxt) 2) retransmission is lost. Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller diff --git a/include/linux/snmp.h b/include/linux/snmp.h index e16557a..c1241c42 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -192,7 +192,6 @@ enum LINUX_MIB_TCPPARTIALUNDO, /* TCPPartialUndo */ LINUX_MIB_TCPDSACKUNDO, /* TCPDSACKUndo */ LINUX_MIB_TCPLOSSUNDO, /* TCPLossUndo */ - LINUX_MIB_TCPLOSS, /* TCPLoss */ LINUX_MIB_TCPLOSTRETRANSMIT, /* TCPLostRetransmit */ LINUX_MIB_TCPRENOFAILURES, /* TCPRenoFailures */ LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 3569d8e..6afc807 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -216,7 +216,6 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPPartialUndo", LINUX_MIB_TCPPARTIALUNDO), SNMP_MIB_ITEM("TCPDSACKUndo", LINUX_MIB_TCPDSACKUNDO), SNMP_MIB_ITEM("TCPLossUndo", LINUX_MIB_TCPLOSSUNDO), - SNMP_MIB_ITEM("TCPLoss", LINUX_MIB_TCPLOSS), SNMP_MIB_ITEM("TCPLostRetransmit", LINUX_MIB_TCPLOSTRETRANSMIT), SNMP_MIB_ITEM("TCPRenoFailures", LINUX_MIB_TCPRENOFAILURES), SNMP_MIB_ITEM("TCPSackFailures", LINUX_MIB_TCPSACKFAILURES), diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2877c3e..976034f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -105,7 +105,6 @@ int sysctl_tcp_abc __read_mostly; #define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ #define FLAG_DATA_SACKED 0x20 /* New SACK. */ #define FLAG_ECE 0x40 /* ECE in this ACK */ -#define FLAG_DATA_LOST 0x80 /* SACK detected data lossage. */ #define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ #define FLAG_ONLY_ORIG_SACKED 0x200 /* SACKs only non-rexmit sent before RTO */ #define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ @@ -1040,13 +1039,11 @@ static void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, * These 6 states form finite state machine, controlled by the following events: * 1. New ACK (+SACK) arrives. (tcp_sacktag_write_queue()) * 2. Retransmission. (tcp_retransmit_skb(), tcp_xmit_retransmit_queue()) - * 3. Loss detection event of one of three flavors: + * 3. Loss detection event of two flavors: * A. Scoreboard estimator decided the packet is lost. * A'. Reno "three dupacks" marks head of queue lost. - * A''. Its FACK modfication, head until snd.fack is lost. - * B. SACK arrives sacking data transmitted after never retransmitted - * hole was sent out. - * C. SACK arrives sacking SND.NXT at the moment, when the + * A''. Its FACK modification, head until snd.fack is lost. + * B. SACK arrives sacking SND.NXT at the moment, when the * segment was retransmitted. * 4. D-SACK added new rule: D-SACK changes any tag to S. * @@ -1153,7 +1150,7 @@ static int tcp_is_sackblock_valid(struct tcp_sock *tp, int is_dsack, } /* Check for lost retransmit. This superb idea is borrowed from "ratehalving". - * Event "C". Later note: FACK people cheated me again 8), we have to account + * Event "B". Later note: FACK people cheated me again 8), we have to account * for reordering! Ugly, but should help. * * Search retransmitted skbs from write_queue that were sent when snd_nxt was @@ -1844,10 +1841,6 @@ tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb, if (found_dup_sack && ((i + 1) == first_sack_index)) next_dup = &sp[i + 1]; - /* Event "B" in the comment above. */ - if (after(end_seq, tp->high_seq)) - state.flag |= FLAG_DATA_LOST; - /* Skip too early cached blocks */ while (tcp_sack_cache_ok(tp, cache) && !before(start_seq, cache->end_seq)) @@ -2515,8 +2508,11 @@ static void tcp_timeout_skbs(struct sock *sk) tcp_verify_left_out(tp); } -/* Mark head of queue up as lost. With RFC3517 SACK, the packets is - * is against sacked "cnt", otherwise it's against facked "cnt" +/* Detect loss in event "A" above by marking head of queue up as lost. + * For FACK or non-SACK(Reno) senders, the first "packets" number of segments + * are considered lost. For RFC3517 SACK, a segment is considered lost if it + * has at least tp->reordering SACKed seqments above it; "packets" refers to + * the maximum SACKed segments to pass before reaching this limit. */ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) { @@ -2525,6 +2521,8 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) int cnt, oldcnt; int err; unsigned int mss; + /* Use SACK to deduce losses of new sequences sent during recovery */ + const u32 loss_high = tcp_is_sack(tp) ? tp->snd_nxt : tp->high_seq; WARN_ON(packets > tp->packets_out); if (tp->lost_skb_hint) { @@ -2546,7 +2544,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) tp->lost_skb_hint = skb; tp->lost_cnt_hint = cnt; - if (after(TCP_SKB_CB(skb)->end_seq, tp->high_seq)) + if (after(TCP_SKB_CB(skb)->end_seq, loss_high)) break; oldcnt = cnt; @@ -3033,19 +3031,10 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, if (tcp_check_sack_reneging(sk, flag)) return; - /* C. Process data loss notification, provided it is valid. */ - if (tcp_is_fack(tp) && (flag & FLAG_DATA_LOST) && - before(tp->snd_una, tp->high_seq) && - icsk->icsk_ca_state != TCP_CA_Open && - tp->fackets_out > tp->reordering) { - tcp_mark_head_lost(sk, tp->fackets_out - tp->reordering, 0); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSS); - } - - /* D. Check consistency of the current state. */ + /* C. Check consistency of the current state. */ tcp_verify_left_out(tp); - /* E. Check state exit conditions. State can be terminated + /* D. Check state exit conditions. State can be terminated * when high_seq is ACKed. */ if (icsk->icsk_ca_state == TCP_CA_Open) { WARN_ON(tp->retrans_out != 0); @@ -3077,7 +3066,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, } } - /* F. Process state. */ + /* E. Process state. */ switch (icsk->icsk_ca_state) { case TCP_CA_Recovery: if (!(flag & FLAG_SND_UNA_ADVANCED)) { -- cgit v0.10.2 From bf0813bd282f6ded0a5efca3db238287f7a3dbe8 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 19 Jan 2012 15:40:06 +0000 Subject: pktgen: Fix unsigned function that is returning negative vals Every call to num_args() immediately checks the return value for less than zero, as it will return -EFAULT for a failed get_user() call. So it makes no sense for the function to be declared as an unsigned long. Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 65f80c7..4d8ce93 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -767,8 +767,8 @@ done: return i; } -static unsigned long num_arg(const char __user * user_buffer, - unsigned long maxlen, unsigned long *num) +static long num_arg(const char __user *user_buffer, unsigned long maxlen, + unsigned long *num) { int i; *num = 0; -- cgit v0.10.2 From df505eb804d5221c3164ebecd1286cb7fc7f49ba Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Thu, 19 Jan 2012 20:34:04 +0000 Subject: be2net: create RSS rings even in multi-channel configs Currently RSS rings are not created in a multi-channel config. RSS rings can be created on one (out of four) interfaces per port in a multi-channel config. Doing this insulates the driver from a FW bug wherin multi-channel config is wrongly reported even when not enabled. This also helps performance in a multi-channel config, as one interface per port gets RSS rings. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a6bcdb5..e703d64 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1786,8 +1786,7 @@ static void be_rx_queues_destroy(struct be_adapter *adapter) static u32 be_num_rxqs_want(struct be_adapter *adapter) { if ((adapter->function_caps & BE_FUNCTION_CAPS_RSS) && - !sriov_enabled(adapter) && be_physfn(adapter) && - !be_is_mc(adapter)) { + !sriov_enabled(adapter) && be_physfn(adapter)) { return 1 + MAX_RSS_QS; /* one default non-RSS queue */ } else { dev_warn(&adapter->pdev->dev, -- cgit v0.10.2 From 8a622e71f58ec9f092fc99eacae0e6cf14f6e742 Mon Sep 17 00:00:00 2001 From: shawnlu Date: Fri, 20 Jan 2012 12:22:04 +0000 Subject: tcp: md5: using remote adress for md5 lookup in rst packet md5 key is added in socket through remote address. remote address should be used in finding md5 key when sending out reset packet. Signed-off-by: shawnlu Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1eb4ad5..337ba4c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -631,7 +631,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len = sizeof(rep.th); #ifdef CONFIG_TCP_MD5SIG - key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr) : NULL; + key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL; if (key) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 906c7ca..3edd05a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1083,7 +1083,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_TCP_MD5SIG if (sk) - key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr); + key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr); #endif if (th->ack) -- cgit v0.10.2 From af58f1d62853cd883c1fa26556fb470f05878f21 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 09:03:04 +0000 Subject: kernel-doc: fix new warning in net/phy/mdio_bus.c Fix new kernel-doc warning: Warning(drivers/net/phy/mdio_bus.c:49): No description found for parameter 'size' Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 88cc5db..8985cc6 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -38,12 +38,11 @@ /** * mdiobus_alloc_size - allocate a mii_bus structure + * @size: extra amount of memory to allocate for private storage. + * If non-zero, then bus->priv is points to that memory. * * Description: called by a bus driver to allocate an mii_bus * structure to fill in. - * - * 'size' is an an extra amount of memory to allocate for private storage. - * If non-zero, then bus->priv is points to that memory. */ struct mii_bus *mdiobus_alloc_size(size_t size) { -- cgit v0.10.2 From 1a3bc369ba547c11ca8b3ed079d7584f27499e70 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 09:03:10 +0000 Subject: kernel-doc: fix new warning in net/sock.h Fix new kernel-doc warning: Warning(include/net/sock.h:372): No description found for parameter 'sk_cgrp_prioidx' Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index bb972d2..97fc0ad 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -226,6 +226,7 @@ struct cg_proto; * @sk_ack_backlog: current listen backlog * @sk_max_ack_backlog: listen backlog set in listen() * @sk_priority: %SO_PRIORITY setting + * @sk_cgrp_prioidx: socket group's priority map index * @sk_type: socket type (%SOCK_STREAM, etc) * @sk_protocol: which protocol this socket belongs in this network family * @sk_peer_pid: &struct pid for this socket's peer -- cgit v0.10.2 From 376be5ff8a6a36efadd131860cf26841f366d44c Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Fri, 20 Jan 2012 04:57:14 +0000 Subject: net: fix socket memcg build with !CONFIG_NET There is still a build bug with the sock memcg code, that triggers with !CONFIG_NET, that survived my series of randconfig builds. Signed-off-by: Glauber Costa Reported-by: Randy Dunlap CC: Hiroyouki Kamezawa Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index 97fc0ad..0e7a9b0 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -922,7 +922,7 @@ inline void sk_refcnt_debug_release(const struct sock *sk) #define sk_refcnt_debug_release(sk) do { } while (0) #endif /* SOCK_REFCNT_DEBUG */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#if defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) && defined(CONFIG_NET) extern struct jump_label_key memcg_socket_limit_enabled; static inline struct cg_proto *parent_cg_proto(struct proto *proto, struct cg_proto *cg_proto) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 3dbff4d..c3688df 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -379,7 +379,7 @@ static void mem_cgroup_put(struct mem_cgroup *memcg); static bool mem_cgroup_is_root(struct mem_cgroup *memcg); void sock_update_memcg(struct sock *sk) { - if (static_branch(&memcg_socket_limit_enabled)) { + if (mem_cgroup_sockets_enabled) { struct mem_cgroup *memcg; BUG_ON(!sk->sk_prot->proto_cgroup); @@ -411,7 +411,7 @@ EXPORT_SYMBOL(sock_update_memcg); void sock_release_memcg(struct sock *sk) { - if (static_branch(&memcg_socket_limit_enabled) && sk->sk_cgrp) { + if (mem_cgroup_sockets_enabled && sk->sk_cgrp) { struct mem_cgroup *memcg; WARN_ON(!sk->sk_cgrp->memcg); memcg = sk->sk_cgrp->memcg; -- cgit v0.10.2 From 8cfd14ad1eb52e44cb1fe7b47a68126e45e04026 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Fri, 20 Jan 2012 04:57:15 +0000 Subject: cgroup: make sure memcg margin is 0 when over limit For the memcg sock code, we'll need to register allocations that are temporarily over limit. Let's make sure that margin is 0 in this case. I am keeping this as a separate patch, so that if any weirdness interaction appears in the future, we can now exactly what caused it. Suggested by Johannes Weiner Signed-off-by: Glauber Costa CC: KAMEZAWA Hiroyuki CC: Johannes Weiner CC: Michal Hocko CC: Tejun Heo CC: Li Zefan Acked-by: Tejun Heo Signed-off-by: David S. Miller diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index c9d625c..d06d014 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -142,7 +142,10 @@ static inline unsigned long long res_counter_margin(struct res_counter *cnt) unsigned long flags; spin_lock_irqsave(&cnt->lock, flags); - margin = cnt->limit - cnt->usage; + if (cnt->limit > cnt->usage) + margin = cnt->limit - cnt->usage; + else + margin = 0; spin_unlock_irqrestore(&cnt->lock, flags); return margin; } -- cgit v0.10.2 From 0e90b31f4ba77027a7c21cbfc66404df0851ca21 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Fri, 20 Jan 2012 04:57:16 +0000 Subject: net: introduce res_counter_charge_nofail() for socket allocations There is a case in __sk_mem_schedule(), where an allocation is beyond the maximum, but yet we are allowed to proceed. It happens under the following condition: sk->sk_wmem_queued + size >= sk->sk_sndbuf The network code won't revert the allocation in this case, meaning that at some point later it'll try to do it. Since this is never communicated to the underlying res_counter code, there is an inbalance in res_counter uncharge operation. I see two ways of fixing this: 1) storing the information about those allocations somewhere in memcg, and then deducting from that first, before we start draining the res_counter, 2) providing a slightly different allocation function for the res_counter, that matches the original behavior of the network code more closely. I decided to go for #2 here, believing it to be more elegant, since #1 would require us to do basically that, but in a more obscure way. Signed-off-by: Glauber Costa Cc: KAMEZAWA Hiroyuki Cc: Johannes Weiner Cc: Michal Hocko CC: Tejun Heo CC: Li Zefan CC: Laurent Chavey Acked-by: Tejun Heo Signed-off-by: David S. Miller diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index d06d014..da81af0 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -109,12 +109,18 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent); * * returns 0 on success and <0 if the counter->usage will exceed the * counter->limit _locked call expects the counter->lock to be taken + * + * charge_nofail works the same, except that it charges the resource + * counter unconditionally, and returns < 0 if the after the current + * charge we are over limit. */ int __must_check res_counter_charge_locked(struct res_counter *counter, unsigned long val); int __must_check res_counter_charge(struct res_counter *counter, unsigned long val, struct res_counter **limit_fail_at); +int __must_check res_counter_charge_nofail(struct res_counter *counter, + unsigned long val, struct res_counter **limit_fail_at); /* * uncharge - tell that some portion of the resource is released diff --git a/include/net/sock.h b/include/net/sock.h index 0e7a9b0..4c69ac1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1008,9 +1008,8 @@ static inline void memcg_memory_allocated_add(struct cg_proto *prot, struct res_counter *fail; int ret; - ret = res_counter_charge(prot->memory_allocated, - amt << PAGE_SHIFT, &fail); - + ret = res_counter_charge_nofail(prot->memory_allocated, + amt << PAGE_SHIFT, &fail); if (ret < 0) *parent_status = OVER_LIMIT; } @@ -1054,12 +1053,11 @@ sk_memory_allocated_add(struct sock *sk, int amt, int *parent_status) } static inline void -sk_memory_allocated_sub(struct sock *sk, int amt, int parent_status) +sk_memory_allocated_sub(struct sock *sk, int amt) { struct proto *prot = sk->sk_prot; - if (mem_cgroup_sockets_enabled && sk->sk_cgrp && - parent_status != OVER_LIMIT) /* Otherwise was uncharged already */ + if (mem_cgroup_sockets_enabled && sk->sk_cgrp) memcg_memory_allocated_sub(sk->sk_cgrp, amt); atomic_long_sub(amt, prot->memory_allocated); diff --git a/kernel/res_counter.c b/kernel/res_counter.c index 6d269cc..d508363 100644 --- a/kernel/res_counter.c +++ b/kernel/res_counter.c @@ -66,6 +66,31 @@ done: return ret; } +int res_counter_charge_nofail(struct res_counter *counter, unsigned long val, + struct res_counter **limit_fail_at) +{ + int ret, r; + unsigned long flags; + struct res_counter *c; + + r = ret = 0; + *limit_fail_at = NULL; + local_irq_save(flags); + for (c = counter; c != NULL; c = c->parent) { + spin_lock(&c->lock); + r = res_counter_charge_locked(c, val); + if (r) + c->usage += val; + spin_unlock(&c->lock); + if (r < 0 && ret == 0) { + *limit_fail_at = c; + ret = r; + } + } + local_irq_restore(flags); + + return ret; +} void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) { if (WARN_ON(counter->usage < val)) diff --git a/net/core/sock.c b/net/core/sock.c index 5c5af998..3e81fd2 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1827,7 +1827,7 @@ suppress_allocation: /* Alas. Undo changes. */ sk->sk_forward_alloc -= amt * SK_MEM_QUANTUM; - sk_memory_allocated_sub(sk, amt, parent_status); + sk_memory_allocated_sub(sk, amt); return 0; } @@ -1840,7 +1840,7 @@ EXPORT_SYMBOL(__sk_mem_schedule); void __sk_mem_reclaim(struct sock *sk) { sk_memory_allocated_sub(sk, - sk->sk_forward_alloc >> SK_MEM_QUANTUM_SHIFT, 0); + sk->sk_forward_alloc >> SK_MEM_QUANTUM_SHIFT); sk->sk_forward_alloc &= SK_MEM_QUANTUM - 1; if (sk_under_memory_pressure(sk) && -- cgit v0.10.2 From b1cc16b8e643096adb92bbcb76c6c4c564141c40 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 22 Jan 2012 14:45:14 -0500 Subject: bluetooth: hci: Fix type of "enable_hs" to bool. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: net/bluetooth/hci_core.c: In function ‘__check_enable_hs’: net/bluetooth/hci_core.c:2587:1: warning: return from incompatible pointer type [enabled by default] Signed-off-by: David S. Miller diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5b2fed5..00596e8 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1388,6 +1388,6 @@ struct hci_inquiry_req { }; #define IREQ_CACHE_FLUSH 0x0001 -extern int enable_hs; +extern bool enable_hs; #endif /* __HCI_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 845da3e..9de9371 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -55,7 +55,7 @@ #define AUTO_OFF_TIMEOUT 2000 -int enable_hs; +bool enable_hs; static void hci_rx_work(struct work_struct *work); static void hci_cmd_work(struct work_struct *work); -- cgit v0.10.2 From 0af5e4c36e70cfd4ae96d3704a425c414f530f2a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jan 2012 20:58:55 +0000 Subject: MFD: ucb1x00-ts: fix resume failure If the ucb1x00 touchscreen is resumed while the touchscreen is being touched, the main thread stops responding. This occurs because two things happen: 1. When we suspended, we were woken up, and executed the loop. Finding that the touchscreen was not pressed, we prepare to schedule for a maximum timeout, before being stopped in try_to_freeze(). 2. an irq occurs, we disable the irq, and mark it as disabled, and wake the thread. This wake occurs while the thread is still within __refrigerator() 3. The thread is unfrozen, and __refrigerator() sets the threads state back to INTERRUPTIBLE. We then drop into schedule_timeout() with an infinite timeout and the IRQ disabled. This prevents any further screen touches activating the thread. Fix this by using kthread_freezable_should_stop() which handles the freezing issues for us outside of the hotspot where the task state matters. Include a flag to ignore the touchscreen until it is released to avoid sending unintended data to the application. Signed-off-by: Russell King diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 38ffbd5..63a3cbd 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -47,7 +47,6 @@ struct ucb1x00_ts { u16 x_res; u16 y_res; - unsigned int restart:1; unsigned int adcsync:1; }; @@ -207,15 +206,17 @@ static int ucb1x00_thread(void *_ts) { struct ucb1x00_ts *ts = _ts; DECLARE_WAITQUEUE(wait, current); + bool frozen, ignore = false; int valid = 0; set_freezable(); add_wait_queue(&ts->irq_wait, &wait); - while (!kthread_should_stop()) { + while (!kthread_freezable_should_stop(&frozen)) { unsigned int x, y, p; signed long timeout; - ts->restart = 0; + if (frozen) + ignore = true; ucb1x00_adc_enable(ts->ucb); @@ -258,7 +259,7 @@ static int ucb1x00_thread(void *_ts) * space. We therefore leave it to user space * to do any filtering they please. */ - if (!ts->restart) { + if (!ignore) { ucb1x00_ts_evt_add(ts, p, x, y); valid = 1; } @@ -267,8 +268,6 @@ static int ucb1x00_thread(void *_ts) timeout = HZ / 100; } - try_to_freeze(); - schedule_timeout(timeout); } @@ -340,26 +339,6 @@ static void ucb1x00_ts_close(struct input_dev *idev) ucb1x00_disable(ts->ucb); } -#ifdef CONFIG_PM -static int ucb1x00_ts_resume(struct ucb1x00_dev *dev) -{ - struct ucb1x00_ts *ts = dev->priv; - - if (ts->rtask != NULL) { - /* - * Restart the TS thread to ensure the - * TS interrupt mode is set up again - * after sleep. - */ - ts->restart = 1; - wake_up(&ts->irq_wait); - } - return 0; -} -#else -#define ucb1x00_ts_resume NULL -#endif - /* * Initialisation. @@ -425,7 +404,6 @@ static void ucb1x00_ts_remove(struct ucb1x00_dev *dev) static struct ucb1x00_driver ucb1x00_ts_driver = { .add = ucb1x00_ts_add, .remove = ucb1x00_ts_remove, - .resume = ucb1x00_ts_resume, }; static int __init ucb1x00_ts_init(void) -- cgit v0.10.2 From e9c688a3272fd4b659228f3880de8109a94540e2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 22 Jan 2012 14:31:15 -0700 Subject: driver core: remove drivers/base/sys.c and include/linux/sysdev.h Now that all users of 'struct sysdev' are removed from the kernel, we can safely remove the .h and .c files for this code, to ensure that no one accidentally starts to use it again. Many thanks for Kay who did all the hard work here on making this happen. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 2c8272d..610f999 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,6 +1,6 @@ # Makefile for the Linux device tree -obj-y := core.o sys.o bus.o dd.o syscore.o \ +obj-y := core.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ diff --git a/drivers/base/sys.c b/drivers/base/sys.c deleted file mode 100644 index 409f5ce..0000000 --- a/drivers/base/sys.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc) - * - * Copyright (c) 2002-3 Patrick Mochel - * 2002-3 Open Source Development Lab - * - * This file is released under the GPLv2 - * - * This exports a 'system' bus type. - * By default, a 'sys' bus gets added to the root of the system. There will - * always be core system devices. Devices can use sysdev_register() to - * add themselves as children of the system bus. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base.h" - -#define to_sysdev(k) container_of(k, struct sys_device, kobj) -#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr) - - -static ssize_t -sysdev_show(struct kobject *kobj, struct attribute *attr, char *buffer) -{ - struct sys_device *sysdev = to_sysdev(kobj); - struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr); - - if (sysdev_attr->show) - return sysdev_attr->show(sysdev, sysdev_attr, buffer); - return -EIO; -} - - -static ssize_t -sysdev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct sys_device *sysdev = to_sysdev(kobj); - struct sysdev_attribute *sysdev_attr = to_sysdev_attr(attr); - - if (sysdev_attr->store) - return sysdev_attr->store(sysdev, sysdev_attr, buffer, count); - return -EIO; -} - -static const struct sysfs_ops sysfs_ops = { - .show = sysdev_show, - .store = sysdev_store, -}; - -static struct kobj_type ktype_sysdev = { - .sysfs_ops = &sysfs_ops, -}; - - -int sysdev_create_file(struct sys_device *s, struct sysdev_attribute *a) -{ - return sysfs_create_file(&s->kobj, &a->attr); -} - - -void sysdev_remove_file(struct sys_device *s, struct sysdev_attribute *a) -{ - sysfs_remove_file(&s->kobj, &a->attr); -} - -EXPORT_SYMBOL_GPL(sysdev_create_file); -EXPORT_SYMBOL_GPL(sysdev_remove_file); - -#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj) -#define to_sysdev_class_attr(a) container_of(a, \ - struct sysdev_class_attribute, attr) - -static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct sysdev_class *class = to_sysdev_class(kobj); - struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr); - - if (class_attr->show) - return class_attr->show(class, class_attr, buffer); - return -EIO; -} - -static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct sysdev_class *class = to_sysdev_class(kobj); - struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr); - - if (class_attr->store) - return class_attr->store(class, class_attr, buffer, count); - return -EIO; -} - -static const struct sysfs_ops sysfs_class_ops = { - .show = sysdev_class_show, - .store = sysdev_class_store, -}; - -static struct kobj_type ktype_sysdev_class = { - .sysfs_ops = &sysfs_class_ops, -}; - -int sysdev_class_create_file(struct sysdev_class *c, - struct sysdev_class_attribute *a) -{ - return sysfs_create_file(&c->kset.kobj, &a->attr); -} -EXPORT_SYMBOL_GPL(sysdev_class_create_file); - -void sysdev_class_remove_file(struct sysdev_class *c, - struct sysdev_class_attribute *a) -{ - sysfs_remove_file(&c->kset.kobj, &a->attr); -} -EXPORT_SYMBOL_GPL(sysdev_class_remove_file); - -extern struct kset *system_kset; - -int sysdev_class_register(struct sysdev_class *cls) -{ - int retval; - - pr_debug("Registering sysdev class '%s'\n", cls->name); - - INIT_LIST_HEAD(&cls->drivers); - memset(&cls->kset.kobj, 0x00, sizeof(struct kobject)); - cls->kset.kobj.parent = &system_kset->kobj; - cls->kset.kobj.ktype = &ktype_sysdev_class; - cls->kset.kobj.kset = system_kset; - - retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name); - if (retval) - return retval; - - retval = kset_register(&cls->kset); - if (!retval && cls->attrs) - retval = sysfs_create_files(&cls->kset.kobj, - (const struct attribute **)cls->attrs); - return retval; -} - -void sysdev_class_unregister(struct sysdev_class *cls) -{ - pr_debug("Unregistering sysdev class '%s'\n", - kobject_name(&cls->kset.kobj)); - if (cls->attrs) - sysfs_remove_files(&cls->kset.kobj, - (const struct attribute **)cls->attrs); - kset_unregister(&cls->kset); -} - -EXPORT_SYMBOL_GPL(sysdev_class_register); -EXPORT_SYMBOL_GPL(sysdev_class_unregister); - -static DEFINE_MUTEX(sysdev_drivers_lock); - -/* - * @dev != NULL means that we're unwinding because some drv->add() - * failed for some reason. You need to grab sysdev_drivers_lock before - * calling this. - */ -static void __sysdev_driver_remove(struct sysdev_class *cls, - struct sysdev_driver *drv, - struct sys_device *from_dev) -{ - struct sys_device *dev = from_dev; - - list_del_init(&drv->entry); - if (!cls) - return; - - if (!drv->remove) - goto kset_put; - - if (dev) - list_for_each_entry_continue_reverse(dev, &cls->kset.list, - kobj.entry) - drv->remove(dev); - else - list_for_each_entry(dev, &cls->kset.list, kobj.entry) - drv->remove(dev); - -kset_put: - kset_put(&cls->kset); -} - -/** - * sysdev_driver_register - Register auxiliary driver - * @cls: Device class driver belongs to. - * @drv: Driver. - * - * @drv is inserted into @cls->drivers to be - * called on each operation on devices of that class. The refcount - * of @cls is incremented. - */ -int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) -{ - struct sys_device *dev = NULL; - int err = 0; - - if (!cls) { - WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!\n", - __func__); - return -EINVAL; - } - - /* Check whether this driver has already been added to a class. */ - if (drv->entry.next && !list_empty(&drv->entry)) - WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already" - " been registered to a class, something is wrong, but " - "will forge on!\n", cls->name, drv); - - mutex_lock(&sysdev_drivers_lock); - if (cls && kset_get(&cls->kset)) { - list_add_tail(&drv->entry, &cls->drivers); - - /* If devices of this class already exist, tell the driver */ - if (drv->add) { - list_for_each_entry(dev, &cls->kset.list, kobj.entry) { - err = drv->add(dev); - if (err) - goto unwind; - } - } - } else { - err = -EINVAL; - WARN(1, KERN_ERR "%s: invalid device class\n", __func__); - } - - goto unlock; - -unwind: - __sysdev_driver_remove(cls, drv, dev); - -unlock: - mutex_unlock(&sysdev_drivers_lock); - return err; -} - -/** - * sysdev_driver_unregister - Remove an auxiliary driver. - * @cls: Class driver belongs to. - * @drv: Driver. - */ -void sysdev_driver_unregister(struct sysdev_class *cls, - struct sysdev_driver *drv) -{ - mutex_lock(&sysdev_drivers_lock); - __sysdev_driver_remove(cls, drv, NULL); - mutex_unlock(&sysdev_drivers_lock); -} -EXPORT_SYMBOL_GPL(sysdev_driver_register); -EXPORT_SYMBOL_GPL(sysdev_driver_unregister); - -/** - * sysdev_register - add a system device to the tree - * @sysdev: device in question - * - */ -int sysdev_register(struct sys_device *sysdev) -{ - int error; - struct sysdev_class *cls = sysdev->cls; - - if (!cls) - return -EINVAL; - - pr_debug("Registering sys device of class '%s'\n", - kobject_name(&cls->kset.kobj)); - - /* initialize the kobject to 0, in case it had previously been used */ - memset(&sysdev->kobj, 0x00, sizeof(struct kobject)); - - /* Make sure the kset is set */ - sysdev->kobj.kset = &cls->kset; - - /* Register the object */ - error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL, - "%s%d", kobject_name(&cls->kset.kobj), - sysdev->id); - - if (!error) { - struct sysdev_driver *drv; - - pr_debug("Registering sys device '%s'\n", - kobject_name(&sysdev->kobj)); - - mutex_lock(&sysdev_drivers_lock); - /* Generic notification is implicit, because it's that - * code that should have called us. - */ - - /* Notify class auxiliary drivers */ - list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->add) - drv->add(sysdev); - } - mutex_unlock(&sysdev_drivers_lock); - kobject_uevent(&sysdev->kobj, KOBJ_ADD); - } - - return error; -} - -void sysdev_unregister(struct sys_device *sysdev) -{ - struct sysdev_driver *drv; - - mutex_lock(&sysdev_drivers_lock); - list_for_each_entry(drv, &sysdev->cls->drivers, entry) { - if (drv->remove) - drv->remove(sysdev); - } - mutex_unlock(&sysdev_drivers_lock); - - kobject_put(&sysdev->kobj); -} - -EXPORT_SYMBOL_GPL(sysdev_register); -EXPORT_SYMBOL_GPL(sysdev_unregister); - -#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr) - -ssize_t sysdev_store_ulong(struct sys_device *sysdev, - struct sysdev_attribute *attr, - const char *buf, size_t size) -{ - struct sysdev_ext_attribute *ea = to_ext_attr(attr); - char *end; - unsigned long new = simple_strtoul(buf, &end, 0); - if (end == buf) - return -EINVAL; - *(unsigned long *)(ea->var) = new; - /* Always return full write size even if we didn't consume all */ - return size; -} -EXPORT_SYMBOL_GPL(sysdev_store_ulong); - -ssize_t sysdev_show_ulong(struct sys_device *sysdev, - struct sysdev_attribute *attr, - char *buf) -{ - struct sysdev_ext_attribute *ea = to_ext_attr(attr); - return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var)); -} -EXPORT_SYMBOL_GPL(sysdev_show_ulong); - -ssize_t sysdev_store_int(struct sys_device *sysdev, - struct sysdev_attribute *attr, - const char *buf, size_t size) -{ - struct sysdev_ext_attribute *ea = to_ext_attr(attr); - char *end; - long new = simple_strtol(buf, &end, 0); - if (end == buf || new > INT_MAX || new < INT_MIN) - return -EINVAL; - *(int *)(ea->var) = new; - /* Always return full write size even if we didn't consume all */ - return size; -} -EXPORT_SYMBOL_GPL(sysdev_store_int); - -ssize_t sysdev_show_int(struct sys_device *sysdev, - struct sysdev_attribute *attr, - char *buf) -{ - struct sysdev_ext_attribute *ea = to_ext_attr(attr); - return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var)); -} -EXPORT_SYMBOL_GPL(sysdev_show_int); - diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h deleted file mode 100644 index 20f63d3..0000000 --- a/include/linux/sysdev.h +++ /dev/null @@ -1,164 +0,0 @@ -/** - * System devices follow a slightly different driver model. - * They don't need to do dynammic driver binding, can't be probed, - * and don't reside on any type of peripheral bus. - * So, we represent and treat them a little differently. - * - * We still have a notion of a driver for a system device, because we still - * want to perform basic operations on these devices. - * - * We also support auxiliary drivers binding to devices of a certain class. - * - * This allows configurable drivers to register themselves for devices of - * a certain type. And, it allows class definitions to reside in generic - * code while arch-specific code can register specific drivers. - * - * Auxiliary drivers registered with a NULL cls are registered as drivers - * for all system devices, and get notification calls for each device. - */ - - -#ifndef _SYSDEV_H_ -#define _SYSDEV_H_ - -#include -#include - - -struct sys_device; -struct sysdev_class_attribute; - -struct sysdev_class { - const char *name; - struct list_head drivers; - struct sysdev_class_attribute **attrs; - struct kset kset; -}; - -struct sysdev_class_attribute { - struct attribute attr; - ssize_t (*show)(struct sysdev_class *, struct sysdev_class_attribute *, - char *); - ssize_t (*store)(struct sysdev_class *, struct sysdev_class_attribute *, - const char *, size_t); -}; - -#define _SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ -{ \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -} - -#define SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ - struct sysdev_class_attribute attr_##_name = \ - _SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) - - -extern int sysdev_class_register(struct sysdev_class *); -extern void sysdev_class_unregister(struct sysdev_class *); - -extern int sysdev_class_create_file(struct sysdev_class *, - struct sysdev_class_attribute *); -extern void sysdev_class_remove_file(struct sysdev_class *, - struct sysdev_class_attribute *); -/** - * Auxiliary system device drivers. - */ - -struct sysdev_driver { - struct list_head entry; - int (*add)(struct sys_device *); - int (*remove)(struct sys_device *); -}; - - -extern int sysdev_driver_register(struct sysdev_class *, struct sysdev_driver *); -extern void sysdev_driver_unregister(struct sysdev_class *, struct sysdev_driver *); - - -/** - * sys_devices can be simplified a lot from regular devices, because they're - * simply not as versatile. - */ - -struct sys_device { - u32 id; - struct sysdev_class * cls; - struct kobject kobj; -}; - -extern int sysdev_register(struct sys_device *); -extern void sysdev_unregister(struct sys_device *); - - -struct sysdev_attribute { - struct attribute attr; - ssize_t (*show)(struct sys_device *, struct sysdev_attribute *, char *); - ssize_t (*store)(struct sys_device *, struct sysdev_attribute *, - const char *, size_t); -}; - - -#define _SYSDEV_ATTR(_name, _mode, _show, _store) \ -{ \ - .attr = { .name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -} - -#define SYSDEV_ATTR(_name, _mode, _show, _store) \ - struct sysdev_attribute attr_##_name = \ - _SYSDEV_ATTR(_name, _mode, _show, _store); - -extern int sysdev_create_file(struct sys_device *, struct sysdev_attribute *); -extern void sysdev_remove_file(struct sys_device *, struct sysdev_attribute *); - -/* Create/remove NULL terminated attribute list */ -static inline int -sysdev_create_files(struct sys_device *d, struct sysdev_attribute **a) -{ - return sysfs_create_files(&d->kobj, (const struct attribute **)a); -} - -static inline void -sysdev_remove_files(struct sys_device *d, struct sysdev_attribute **a) -{ - return sysfs_remove_files(&d->kobj, (const struct attribute **)a); -} - -struct sysdev_ext_attribute { - struct sysdev_attribute attr; - void *var; -}; - -/* - * Support for simple variable sysdev attributes. - * The pointer to the variable is stored in a sysdev_ext_attribute - */ - -/* Add more types as needed */ - -extern ssize_t sysdev_show_ulong(struct sys_device *, struct sysdev_attribute *, - char *); -extern ssize_t sysdev_store_ulong(struct sys_device *, - struct sysdev_attribute *, const char *, size_t); -extern ssize_t sysdev_show_int(struct sys_device *, struct sysdev_attribute *, - char *); -extern ssize_t sysdev_store_int(struct sys_device *, - struct sysdev_attribute *, const char *, size_t); - -#define _SYSDEV_ULONG_ATTR(_name, _mode, _var) \ - { _SYSDEV_ATTR(_name, _mode, sysdev_show_ulong, sysdev_store_ulong), \ - &(_var) } -#define SYSDEV_ULONG_ATTR(_name, _mode, _var) \ - struct sysdev_ext_attribute attr_##_name = \ - _SYSDEV_ULONG_ATTR(_name, _mode, _var); -#define _SYSDEV_INT_ATTR(_name, _mode, _var) \ - { _SYSDEV_ATTR(_name, _mode, sysdev_show_int, sysdev_store_int), \ - &(_var) } -#define SYSDEV_INT_ATTR(_name, _mode, _var) \ - struct sysdev_ext_attribute attr_##_name = \ - _SYSDEV_INT_ATTR(_name, _mode, _var); - -#endif /* _SYSDEV_H_ */ -- cgit v0.10.2 From f80400a26a2e8bff541de12834a1134358bb6642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Sun, 22 Jan 2012 00:20:40 +0000 Subject: ethtool: allow ETHTOOL_GSSET_INFO for users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow ETHTOOL_GSSET_INFO ethtool ioctl() for unprivileged users. ETHTOOL_GSTRINGS is already allowed, but is unusable without this one. Signed-off-by: MichaÅ‚ MirosÅ‚aw Acked-by: Ben Hutchings Signed-off-by: David S. Miller diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 921aa2b..369b418 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1311,6 +1311,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GRXCSUM: case ETHTOOL_GTXCSUM: case ETHTOOL_GSG: + case ETHTOOL_GSSET_INFO: case ETHTOOL_GSTRINGS: case ETHTOOL_GTSO: case ETHTOOL_GPERMADDR: -- cgit v0.10.2 From 2d58d7ea9164da59d0ea82fdf80e3ababe52d58c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 4 Nov 2011 10:31:04 +0100 Subject: thermal: Rename generate_netlink_event It doesn't seem right for the thermal subsystem to export a symbol named generate_netlink_event. This function is thermal-specific and its name should reflect that fact. Rename it to thermal_generate_netlink_event. Signed-off-by: Jean Delvare Acked-by: Rafael J. Wysocki Acked-by: R.Durgadoss Signed-off-by: Len Brown diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index b61e46f..1733ab9 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -284,7 +284,7 @@ method, the sys I/F structure will be built like this: The framework includes a simple notification mechanism, in the form of a netlink event. Netlink socket initialization is done during the _init_ of the framework. Drivers which intend to use the notification mechanism -just need to call generate_netlink_event() with two arguments viz +just need to call thermal_generate_netlink_event() with two arguments viz (originator, event). Typically the originator will be an integer assigned to a thermal_zone_device when it registers itself with the framework. The event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL, diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index dd9a574..220ce7e 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1304,7 +1304,7 @@ static struct genl_multicast_group thermal_event_mcgrp = { .name = THERMAL_GENL_MCAST_GROUP_NAME, }; -int generate_netlink_event(u32 orig, enum events event) +int thermal_generate_netlink_event(u32 orig, enum events event) { struct sk_buff *skb; struct nlattr *attr; @@ -1363,7 +1363,7 @@ int generate_netlink_event(u32 orig, enum events event) return result; } -EXPORT_SYMBOL(generate_netlink_event); +EXPORT_SYMBOL(thermal_generate_netlink_event); static int genetlink_init(void) { diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 47b4a27..796f1ff 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -152,9 +152,9 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, void thermal_cooling_device_unregister(struct thermal_cooling_device *); #ifdef CONFIG_NET -extern int generate_netlink_event(u32 orig, enum events event); +extern int thermal_generate_netlink_event(u32 orig, enum events event); #else -static inline int generate_netlink_event(u32 orig, enum events event) +static inline int thermal_generate_netlink_event(u32 orig, enum events event) { return 0; } -- cgit v0.10.2 From f0d5375e3c7b5d7f128af03c5271c328faeb3ae7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 11:55:54 +0100 Subject: ARM: 7289/1: vmlinux.lds.S: do not hardcode cacheline size as 32 bytes The linker script assumes a cacheline size of 32 bytes when aligning the .data..cacheline_aligned and .data..percpu sections. This patch updates the script to use L1_CACHE_BYTES, which should be set to 64 on platforms that require it. Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index f76e755..1077e4f 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -181,7 +182,7 @@ SECTIONS } #endif - PERCPU_SECTION(32) + PERCPU_SECTION(L1_CACHE_BYTES) #ifdef CONFIG_XIP_KERNEL __data_loc = ALIGN(4); /* location in binary */ @@ -212,8 +213,8 @@ SECTIONS #endif NOSAVE_DATA - CACHELINE_ALIGNED_DATA(32) - READ_MOSTLY_DATA(32) + CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES) + READ_MOSTLY_DATA(L1_CACHE_BYTES) /* * The exception fixup table (might need resorting at runtime) -- cgit v0.10.2 From 972da06470519b6eaef9776a586e2353f089de9c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 12:01:09 +0100 Subject: ARM: 7290/1: vmlinux.lds.S: align the exception fixup table to a 4-byte boundary The exception fixup table is currently aligned to a 32-byte boundary. Whilst this won't cause any problems, the exception_table_entry structures contain only a pair of unsigned longs, so 4-byte alignment is all that is required. If the table was walked from start to end, cacheline alignment may bring some performance benefits, but since a binary search is used, the access pattern is random and will not benefit from a stricter alignment. Acked-by: Nicolas Pitre Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 1077e4f..1e19691 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -219,7 +219,7 @@ SECTIONS /* * The exception fixup table (might need resorting at runtime) */ - . = ALIGN(32); + . = ALIGN(4); __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) -- cgit v0.10.2 From a092f2b15399bb4d1aa4e83cffe775f0c946f323 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 12:01:10 +0100 Subject: ARM: 7291/1: cache: assume 64-byte L1 cachelines for ARMv7 CPUs To ensure correct alignment of cacheline-aligned data, the maximum cacheline size needs to be known at compile time. Since Cortex-A8 and Cortex-A15 have 64-byte cachelines (and it is likely that there will be future ARMv7 implementations with the same line size) then it makes sense to assume that CPU_V7 implies a 64-byte L1 cacheline size. For CPUs with smaller caches, this will result in some harmless padding but will help with single zImage work and avoid hitting subtle bugs with misaligned data structures. Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bb68e65..a48aecc 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -825,7 +825,6 @@ config ARCH_S5PC100 select HAVE_CLK select CLKDEV_LOOKUP select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 select ARCH_USES_GETTIMEOFFSET select HAVE_S3C2410_I2C if I2C select HAVE_S3C_RTC if RTC_CLASS @@ -842,7 +841,6 @@ config ARCH_S5PV210 select HAVE_CLK select CLKDEV_LOOKUP select CLKSRC_MMIO - select ARM_L1_CACHE_SHIFT_6 select ARCH_HAS_CPUFREQ select GENERIC_CLOCKEVENTS select HAVE_SCHED_CLOCK diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig index af0c212..9cf4c3c 100644 --- a/arch/arm/mach-mx5/Kconfig +++ b/arch/arm/mach-mx5/Kconfig @@ -15,7 +15,6 @@ config ARCH_MX53 config SOC_IMX50 bool select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 select MXC_TZIC select ARCH_MXC_IOMUX_V3 select ARCH_MXC_AUDMUX_V2 @@ -25,7 +24,6 @@ config SOC_IMX50 config SOC_IMX51 bool select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 select MXC_TZIC select ARCH_MXC_IOMUX_V3 select ARCH_MXC_AUDMUX_V2 @@ -35,7 +33,6 @@ config SOC_IMX51 config SOC_IMX53 bool select CPU_V7 - select ARM_L1_CACHE_SHIFT_6 select MXC_TZIC select ARCH_MXC_IOMUX_V3 select ARCH_MX53 diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index a8ba7b9..41e6612 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -33,7 +33,6 @@ config ARCH_OMAP3 default y select CPU_V7 select USB_ARCH_HAS_EHCI - select ARM_L1_CACHE_SHIFT_6 if !ARCH_OMAP4 select ARCH_HAS_OPP select PM_OPP if PM select ARM_CPU_SUSPEND if PM diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 4cefb57..1a3ca24 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -882,6 +882,7 @@ config CACHE_XSC3L2 config ARM_L1_CACHE_SHIFT_6 bool + default y if CPU_V7 help Setting ARM L1 cache line size to 64 Bytes. -- cgit v0.10.2 From eb50439b92b6298bf209a982f295ba9c0f7cb30b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 12:01:12 +0100 Subject: ARM: 7293/1: logical_cpu_map: decouple CPU mapping from SMP It turns out that the logical CPU mapping is useful even when !CONFIG_SMP for manipulation of devices like interrupt and power controllers when running a UP kernel on a CPU other than 0. This can happen when kexecing a UP image from an SMP kernel. In the future, multi-cluster systems running AMP configurations will require something similar for mapping cluster IDs, so it makes sense to decouple this logic in preparation for this support. Acked-by: Yang Bai Acked-by: Marc Zyngier Reported-by: Joerg Roedel Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index b2dc2dd..c47d619 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -352,11 +353,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic) unsigned int gic_irqs = gic->gic_irqs; struct irq_domain *domain = &gic->domain; void __iomem *base = gic_data_dist_base(gic); - u32 cpu = 0; - -#ifdef CONFIG_SMP - cpu = cpu_logical_map(smp_processor_id()); -#endif + u32 cpu = cpu_logical_map(smp_processor_id()); cpumask = 1 << cpu; cpumask |= cpumask << 8; diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 1e5717a..ae29293 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -71,12 +71,6 @@ extern void platform_secondary_init(unsigned int cpu); extern void platform_smp_prepare_cpus(unsigned int); /* - * Logical CPU mapping. - */ -extern int __cpu_logical_map[NR_CPUS]; -#define cpu_logical_map(cpu) __cpu_logical_map[cpu] - -/* * Initial data for bringing up a secondary CPU. */ struct secondary_data { diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h index f24c1b9..558d6c8 100644 --- a/arch/arm/include/asm/smp_plat.h +++ b/arch/arm/include/asm/smp_plat.h @@ -43,4 +43,10 @@ static inline int cache_ops_need_broadcast(void) } #endif +/* + * Logical CPU mapping. + */ +extern int __cpu_logical_map[]; +#define cpu_logical_map(cpu) __cpu_logical_map[cpu] + #endif diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index ab70c91..a255c39 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -426,6 +426,20 @@ void cpu_init(void) : "r14"); } +int __cpu_logical_map[NR_CPUS]; + +void __init smp_setup_processor_id(void) +{ + int i; + u32 cpu = is_smp() ? read_cpuid_mpidr() & 0xff : 0; + + cpu_logical_map(0) = cpu; + for (i = 1; i < NR_CPUS; ++i) + cpu_logical_map(i) = i == cpu ? 0 : i; + + printk(KERN_INFO "Booting Linux on physical CPU %d\n", cpu); +} + static void __init setup_processor(void) { struct proc_info_list *list; diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 26cdc49..cdeb727 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -233,20 +233,6 @@ void __ref cpu_die(void) } #endif /* CONFIG_HOTPLUG_CPU */ -int __cpu_logical_map[NR_CPUS]; - -void __init smp_setup_processor_id(void) -{ - int i; - u32 cpu = is_smp() ? read_cpuid_mpidr() & 0xff : 0; - - cpu_logical_map(0) = cpu; - for (i = 1; i < NR_CPUS; ++i) - cpu_logical_map(i) = i == cpu ? 0 : i; - - printk(KERN_INFO "Booting Linux on physical CPU %d\n", cpu); -} - /* * Called by both boot and secondaries to move global data into * per-processor storage. diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c index da70e7e..dd1ad55 100644 --- a/arch/arm/mach-exynos/hotplug.c +++ b/arch/arm/mach-exynos/hotplug.c @@ -16,6 +16,7 @@ #include #include +#include #include diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 683aec7..0f2035a 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -23,6 +23,7 @@ #include #include +#include #include #include diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index 7afbe1e..8394d51 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -72,9 +73,7 @@ static void __init highbank_map_io(void) void highbank_set_cpu_jump(int cpu, void *jump_addr) { -#ifdef CONFIG_SMP cpu = cpu_logical_map(cpu); -#endif writel(virt_to_phys(jump_addr), HB_JUMP_TABLE_VIRT(cpu)); __cpuc_flush_dcache_area(HB_JUMP_TABLE_VIRT(cpu), 16); outer_clean_range(HB_JUMP_TABLE_PHYS(cpu), diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c index 29bd124..e15f155 100644 --- a/arch/arm/mach-imx/src.c +++ b/arch/arm/mach-imx/src.c @@ -15,6 +15,7 @@ #include #include #include +#include #define SRC_SCR 0x000 #define SRC_GPR1 0x020 @@ -24,10 +25,6 @@ static void __iomem *src_base; -#ifndef CONFIG_SMP -#define cpu_logical_map(cpu) 0 -#endif - void imx_enable_cpu(int cpu, bool enable) { u32 mask, val; diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c index 41c252d..a446fc1 100644 --- a/arch/arm/mach-msm/hotplug.c +++ b/arch/arm/mach-msm/hotplug.c @@ -11,6 +11,7 @@ #include #include +#include extern volatile int pen_release; diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c index 0b3e357..db0117e 100644 --- a/arch/arm/mach-msm/platsmp.c +++ b/arch/arm/mach-msm/platsmp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include diff --git a/arch/arm/mach-realview/hotplug.c b/arch/arm/mach-realview/hotplug.c index ac1aed2..eb55f05 100644 --- a/arch/arm/mach-realview/hotplug.c +++ b/arch/arm/mach-realview/hotplug.c @@ -13,6 +13,7 @@ #include #include +#include extern volatile int pen_release; diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c index cc97ef8..4fe2e9e 100644 --- a/arch/arm/mach-shmobile/smp-r8a7779.c +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c index be1ade7..0d159d6 100644 --- a/arch/arm/mach-shmobile/smp-sh73a0.c +++ b/arch/arm/mach-shmobile/smp-sh73a0.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c index 572015e..c76f0f4 100644 --- a/arch/arm/mach-ux500/hotplug.c +++ b/arch/arm/mach-ux500/hotplug.c @@ -13,6 +13,7 @@ #include #include +#include extern volatile int pen_release; diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index a19e398..d2058ef 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/arch/arm/mach-vexpress/hotplug.c b/arch/arm/mach-vexpress/hotplug.c index 813ee08..3034a4d 100644 --- a/arch/arm/mach-vexpress/hotplug.c +++ b/arch/arm/mach-vexpress/hotplug.c @@ -13,6 +13,7 @@ #include #include +#include #include extern volatile int pen_release; diff --git a/arch/arm/plat-versatile/platsmp.c b/arch/arm/plat-versatile/platsmp.c index 92f18d3..49c7db4 100644 --- a/arch/arm/plat-versatile/platsmp.c +++ b/arch/arm/plat-versatile/platsmp.c @@ -16,6 +16,7 @@ #include #include +#include #include /* -- cgit v0.10.2 From 868dbf905245a524496a0535982ed21ad3be5585 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 12:01:14 +0100 Subject: ARM: 7295/1: cortex-a7: move proc_info out of !CONFIG_ARM_LPAE block The merging of commits 1b6ba46b ("ARM: LPAE: MMU setup for the 3-level page table format") and b4244738 ("ARM: 7202/1: Add Cortex-A7 proc info") during the merge window ended up putting the Cortex-A7 proc_info into a code block guarded by !CONFIG_ARM_LPAE. This makes Cortex-A7 platforms unbootable when LPAE is enabled. This patch moves the proc_info structure for Cortex-A7 outside of the guarded block. Cc: Pawel Moll Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 7e9b5bf..b155974 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -330,16 +330,6 @@ __v7_ca5mp_proc_info: .size __v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info /* - * ARM Ltd. Cortex A7 processor. - */ - .type __v7_ca7mp_proc_info, #object -__v7_ca7mp_proc_info: - .long 0x410fc070 - .long 0xff0ffff0 - __v7_proc __v7_ca7mp_setup, hwcaps = HWCAP_IDIV - .size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info - - /* * ARM Ltd. Cortex A9 processor. */ .type __v7_ca9mp_proc_info, #object @@ -351,6 +341,16 @@ __v7_ca9mp_proc_info: #endif /* CONFIG_ARM_LPAE */ /* + * ARM Ltd. Cortex A7 processor. + */ + .type __v7_ca7mp_proc_info, #object +__v7_ca7mp_proc_info: + .long 0x410fc070 + .long 0xff0ffff0 + __v7_proc __v7_ca7mp_setup, hwcaps = HWCAP_IDIV + .size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info + + /* * ARM Ltd. Cortex A15 processor. */ .type __v7_ca15mp_proc_info, #object -- cgit v0.10.2 From 612539e81f655f6ac73c7af1da8701c1ee618aee Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 12:10:18 +0100 Subject: ARM: 7296/1: proc-v7.S: remove HARVARD_CACHE preprocessor guards On v7, we use the same cache maintenance instructions for data lines as for unified lines. This was not the case for v6, where HARVARD_CACHE was defined to indicate the L1 cache topology. This patch removes the erroneous compile-time check for HARVARD_CACHE in proc-v7.S, ensuring that we perform I-side invalidation at boot. Reported-and-Acked-by: Shawn Guo Cc: stable Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index b155974..0404ccb 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -148,10 +148,6 @@ ENDPROC(cpu_v7_do_resume) * Initialise TLB, Caches, and MMU state ready to switch the MMU * on. Return in r0 the new CP15 C1 control register setting. * - * We automatically detect if we have a Harvard cache, and use the - * Harvard cache control instructions insead of the unified cache - * control instructions. - * * This should be able to cover all ARMv7 cores. * * It is assumed that: @@ -251,9 +247,7 @@ __v7_setup: #endif 3: mov r10, #0 -#ifdef HARVARD_CACHE mcr p15, 0, r10, c7, c5, 0 @ I+BTB cache invalidate -#endif dsb #ifdef CONFIG_MMU mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs -- cgit v0.10.2 From c214455f3205fa20819da6d67a8b20609ff786e7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 20 Jan 2012 12:24:47 +0100 Subject: ARM: 7297/1: smp_twd: make sure timer is stopped before registering it On secondary CPUs, the Timer Control Register is not reset to a sane value before the timer is registered, and the TRM doesn't seem to indicate any reset value either. In some cases, the kernel will take an interrupt too early, depending on what junk was present in the registers at reset time. The fix is to set the Timer Control Register to 0 before registering the clock_event_device and enabling the interrupt. Problem seen on VE (Cortex A5) and Tegra. Signed-off-by: Marc Zyngier Signed-off-by: Russell King diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index c8e9385..4285daa 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -252,6 +252,8 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) else twd_calibrate_rate(); + __raw_writel(0, twd_base + TWD_TIMER_CONTROL); + clk->name = "local_timer"; clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; -- cgit v0.10.2 From b3945bcbc3f9856f4b5452079bfc2b9738040a37 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 20 Jan 2012 07:54:51 +0100 Subject: ARM: 7288/1: mach-sa1100: add missing module_init() call The Jornada SSP driver is supposed to be initialized by a module_init() call, but it was missed at some merge point. Since the driver mostly pass calls through it magically works anyway, but needs to be rectified. Cc: Kristoffer Ericson Signed-off-by: Linus Walleij Signed-off-by: Russell King diff --git a/arch/arm/mach-sa1100/jornada720_ssp.c b/arch/arm/mach-sa1100/jornada720_ssp.c index f50b00b..b412fc0 100644 --- a/arch/arm/mach-sa1100/jornada720_ssp.c +++ b/arch/arm/mach-sa1100/jornada720_ssp.c @@ -198,3 +198,5 @@ static int __init jornada_ssp_init(void) { return platform_driver_register(&jornadassp_driver); } + +module_init(jornada_ssp_init); -- cgit v0.10.2 From 4e087a7a1f3884750790bda580e22e9eccd5f4fa Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 14 Jan 2012 21:20:37 +0100 Subject: gpu, drm, sis: Don't return uninitialized variable from sis_driver_load() In sis_driver_load(), the only use of 'ret' is as the return value from the function, unfortunately it is never initialized, so the function just returns garbage when it succeeds. To fix that, remove the variable and just return 0 directly on success. Signed-off-by: Jesper Juhl Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 06da063..573220c 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -40,7 +40,6 @@ static struct pci_device_id pciidlist[] = { static int sis_driver_load(struct drm_device *dev, unsigned long chipset) { drm_sis_private_t *dev_priv; - int ret; dev_priv = kzalloc(sizeof(drm_sis_private_t), GFP_KERNEL); if (dev_priv == NULL) @@ -50,7 +49,7 @@ static int sis_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->chipset = chipset; idr_init(&dev->object_name_idr); - return ret; + return 0; } static int sis_driver_unload(struct drm_device *dev) -- cgit v0.10.2 From 44517c44496062180a6376cc704b33129441ce60 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Sun, 15 Jan 2012 08:51:12 -0500 Subject: drm/radeon/kms: Add an MSI quirk for Dell RS690 Interrupts only work with MSIs. https://bugs.freedesktop.org/show_bug.cgi?id=37679 Reported-by: Dmitry Podgorny Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index be38921..66d5fe1 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -138,6 +138,12 @@ static bool radeon_msi_ok(struct radeon_device *rdev) /* Dell RS690 only seems to work with MSIs. */ if ((rdev->pdev->device == 0x791f) && (rdev->pdev->subsystem_vendor == 0x1028) && + (rdev->pdev->subsystem_device == 0x01fc)) + return true; + + /* Dell RS690 only seems to work with MSIs. */ + if ((rdev->pdev->device == 0x791f) && + (rdev->pdev->subsystem_vendor == 0x1028) && (rdev->pdev->subsystem_device == 0x01fd)) return true; -- cgit v0.10.2 From 11ef3f1f8780b64425a4cadbf42a46aa2e36895f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Jan 2012 14:47:43 -0500 Subject: drm/radeon/kms: add some missing semaphore init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Deucher Reviewed-by: Michel Dänzer Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 636660f..ae09fe8 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1455,6 +1455,7 @@ int evergreen_cp_resume(struct radeon_device *rdev) #endif WREG32(CP_RB_CNTL, tmp); WREG32(CP_SEM_WAIT_TIMER, 0x0); + WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); /* Set the write pointer delay */ WREG32(CP_RB_WPTR_DELAY, 0); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index b502216..74713d4 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -108,6 +108,7 @@ #define CP_RB_WPTR_ADDR_HI 0xC11C #define CP_RB_WPTR_DELAY 0x8704 #define CP_SEM_WAIT_TIMER 0x85BC +#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 #define CP_DEBUG 0xC1FC diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 3211372..db09065 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1219,6 +1219,7 @@ int cayman_cp_resume(struct radeon_device *rdev) RREG32(GRBM_SOFT_RESET); WREG32(CP_SEM_WAIT_TIMER, 0x0); + WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); /* Set the write pointer delay */ WREG32(CP_RB_WPTR_DELAY, 0); diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index f9df2a6..9a7f3b6 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -222,6 +222,7 @@ #define SCRATCH_UMSK 0x8540 #define SCRATCH_ADDR 0x8544 #define CP_SEM_WAIT_TIMER 0x85BC +#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8 #define CP_COHER_CNTL2 0x85E8 #define CP_ME_CNTL 0x86D8 #define CP_ME_HALT (1 << 28) -- cgit v0.10.2 From af1be04901e27ce669b4ecde1c953d5c939498f5 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 18 Jan 2012 14:03:11 +0100 Subject: iommu/amd: Work around broken IVRS tables On some systems the IVRS table does not contain all PCI devices present in the system. In case a device not present in the IVRS table is translated by the IOMMU no DMA is possible from that device by default. This patch fixes this by removing the DTE entry for every PCI device present in the system and not covered by IVRS. Cc: stable@vger.kernel.org # >= 3.0 Signed-off-by: Joerg Roedel diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index cce1f03..f75e060 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2863,6 +2863,9 @@ static unsigned device_dma_ops_init(void) for_each_pci_dev(pdev) { if (!check_device(&pdev->dev)) { + + iommu_ignore_device(&pdev->dev); + unhandled += 1; continue; } -- cgit v0.10.2 From 52409aa6a0e96337da137c069856298f4dd825a0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Jan 2012 17:10:24 +0100 Subject: ALSA: hda - Fix buffer-alignment regression with Nvidia HDMI The commit 2ae66c26550cd94b0e2606a9275eb0ab7070ad0e ALSA: hda: option to enable arbitrary buffer/period sizes introduced a regression on machines with Intel controller and Nvidia HDMI. The reason is that the driver modifies the global variable align_buffer_size when an Intel controller is found, and the Nvidia HDMI controller is probed after Intel although Nvidia chips require the aligned buffers. This patch fixes the problem by moving the flag into the local struct so that it's not affected by other controllers. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42567 Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fb35474..95dfb68 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -469,6 +469,7 @@ struct azx { unsigned int irq_pending_warned :1; unsigned int probing :1; /* codec probing phase */ unsigned int snoop:1; + unsigned int align_buffer_size:1; /* for debugging */ unsigned int last_cmd[AZX_MAX_CODECS]; @@ -1690,7 +1691,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rates = hinfo->rates; snd_pcm_limit_hw_rates(runtime); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - if (align_buffer_size) + if (chip->align_buffer_size) /* constrain buffer sizes to be multiple of 128 bytes. This is more efficient in terms of memory access but isn't required by the HDA spec and @@ -2773,8 +2774,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, } /* disable buffer size rounding to 128-byte multiples if supported */ + chip->align_buffer_size = align_buffer_size; if (chip->driver_caps & AZX_DCAPS_BUFSIZE) - align_buffer_size = 0; + chip->align_buffer_size = 0; /* allow 64bit DMA address if supported by H/W */ if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) -- cgit v0.10.2 From 29c5fbbcfefba5225a6783683c46c39e10877703 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 23 Jan 2012 16:39:55 +0100 Subject: ALSA: HDA: Use model=auto for Thinkpad T510 The user reports that model=auto works fine for him. Using model=auto bring in new features such as jack detection notification to userspace. Alsa info is available at http://paste.ubuntu.com/805351/ Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 8a32a69..a7a5733 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3027,7 +3027,7 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), - SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T510", CXT5066_AUTO), SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), -- cgit v0.10.2 From 6536e3123e5d3371a6f52e32a3d0694bcc987702 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 20 Jan 2012 14:33:53 -0800 Subject: mm: fix warnings regarding enum migrate_mode sparc64 allmodconfig: In file included from include/linux/compat.h:15, from /usr/src/25/arch/sparc/include/asm/siginfo.h:19, from include/linux/signal.h:5, from include/linux/sched.h:73, from arch/sparc/kernel/asm-offsets.c:13: include/linux/fs.h:618: warning: parameter has incomplete type It seems that my sparc64 compiler (gcc-3.4.5) doesn't like the forward declaration of enums. Fix this by moving the "enum migrate_mode" definition into its own header file. Acked-by: Mel Gorman Cc: Rik van Riel Cc: Andrea Arcangeli Cc: Minchan Kim Cc: Dave Jones Cc: Jan Kara Cc: Andy Isaacson Cc: Nai Xia Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/fs.h b/include/linux/fs.h index 0244082..4b3a41f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -10,6 +10,7 @@ #include #include #include +#include /* * It's silly to have NR_OPEN bigger than NR_FILE, but you can change @@ -526,7 +527,6 @@ enum positive_aop_returns { struct page; struct address_space; struct writeback_control; -enum migrate_mode; struct iov_iter { const struct iovec *iov; diff --git a/include/linux/migrate.h b/include/linux/migrate.h index eaf8674..05ed282 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -3,22 +3,10 @@ #include #include +#include typedef struct page *new_page_t(struct page *, unsigned long private, int **); -/* - * MIGRATE_ASYNC means never block - * MIGRATE_SYNC_LIGHT in the current implementation means to allow blocking - * on most operations but not ->writepage as the potential stall time - * is too significant - * MIGRATE_SYNC will block when migrating pages - */ -enum migrate_mode { - MIGRATE_ASYNC, - MIGRATE_SYNC_LIGHT, - MIGRATE_SYNC, -}; - #ifdef CONFIG_MIGRATION #define PAGE_MIGRATION 1 diff --git a/include/linux/migrate_mode.h b/include/linux/migrate_mode.h new file mode 100644 index 0000000..ebf3d89 --- /dev/null +++ b/include/linux/migrate_mode.h @@ -0,0 +1,16 @@ +#ifndef MIGRATE_MODE_H_INCLUDED +#define MIGRATE_MODE_H_INCLUDED +/* + * MIGRATE_ASYNC means never block + * MIGRATE_SYNC_LIGHT in the current implementation means to allow blocking + * on most operations but not ->writepage as the potential stall time + * is too significant + * MIGRATE_SYNC will block when migrating pages + */ +enum migrate_mode { + MIGRATE_ASYNC, + MIGRATE_SYNC_LIGHT, + MIGRATE_SYNC, +}; + +#endif /* MIGRATE_MODE_H_INCLUDED */ -- cgit v0.10.2 From 687875fb7de4a95223af20ee024282fa9099f860 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Fri, 20 Jan 2012 14:33:55 -0800 Subject: mm: fix NULL ptr dereference in __count_immobile_pages Fix the following NULL ptr dereference caused by cat /sys/devices/system/memory/memory0/removable Pid: 13979, comm: sed Not tainted 3.0.13-0.5-default #1 IBM BladeCenter LS21 -[7971PAM]-/Server Blade RIP: __count_immobile_pages+0x4/0x100 Process sed (pid: 13979, threadinfo ffff880221c36000, task ffff88022e788480) Call Trace: is_pageblock_removable_nolock+0x34/0x40 is_mem_section_removable+0x74/0xf0 show_mem_removable+0x41/0x70 sysfs_read_file+0xfe/0x1c0 vfs_read+0xc7/0x130 sys_read+0x53/0xa0 system_call_fastpath+0x16/0x1b We are crashing because we are trying to dereference NULL zone which came from pfn=0 (struct page ffffea0000000000). According to the boot log this page is marked reserved: e820 update range: 0000000000000000 - 0000000000010000 (usable) ==> (reserved) and early_node_map confirms that: early_node_map[3] active PFN ranges 1: 0x00000010 -> 0x0000009c 1: 0x00000100 -> 0x000bffa3 1: 0x00100000 -> 0x00240000 The problem is that memory_present works in PAGE_SECTION_MASK aligned blocks so the reserved range sneaks into the the section as well. This also means that free_area_init_node will not take care of those reserved pages and they stay uninitialized. When we try to read the removable status we walk through all available sections and hope that the zone is valid for all pages in the section. But this is not true in this case as the zone and nid are not initialized. We have only one node in this particular case and it is marked as node=1 (rather than 0) and that made the problem visible because page_to_nid will return 0 and there are no zones on the node. Let's check that the zone is valid and that the given pfn falls into its boundaries and mark the section not removable. This might cause some false positives, probably, but we do not have any sane way to find out whether the page is reserved by the platform or it is just not used for whatever other reasons. Signed-off-by: Michal Hocko Acked-by: Mel Gorman Cc: KAMEZAWA Hiroyuki Cc: Andrea Arcangeli Cc: David Rientjes Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0027d8f..21c272d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5414,6 +5414,17 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count) bool is_pageblock_removable_nolock(struct page *page) { struct zone *zone = page_zone(page); + unsigned long pfn = page_to_pfn(page); + + /* + * We have to be careful here because we are iterating over memory + * sections which are not zone aware so we might end up outside of + * the zone but still within the section. + */ + if (!zone || zone->zone_start_pfn > pfn || + zone->zone_start_pfn + zone->spanned_pages <= pfn) + return false; + return __count_immobile_pages(zone, page, 0); } -- cgit v0.10.2 From 656a070629adfe23c12768e35ddf91feab469ff7 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Fri, 20 Jan 2012 14:33:58 -0800 Subject: mm: __count_immobile_pages(): make sure the node is online page_zone() requires an online node otherwise we are accessing NULL NODE_DATA. This is not an issue at the moment because node_zones are located at the structure beginning but this might change in the future so better be careful about that. Signed-off-by: Michal Hocko Signed-off-by: KAMEZAWA Hiroyuki Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 21c272d..d2186ec 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5413,15 +5413,22 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count) bool is_pageblock_removable_nolock(struct page *page) { - struct zone *zone = page_zone(page); - unsigned long pfn = page_to_pfn(page); + struct zone *zone; + unsigned long pfn; /* * We have to be careful here because we are iterating over memory * sections which are not zone aware so we might end up outside of * the zone but still within the section. + * We have to take care about the node as well. If the node is offline + * its NODE_DATA will be NULL - see page_zone. */ - if (!zone || zone->zone_start_pfn > pfn || + if (!node_online(page_to_nid(page))) + return false; + + zone = page_zone(page); + pfn = page_to_pfn(page); + if (zone->zone_start_pfn > pfn || zone->zone_start_pfn + zone->spanned_pages <= pfn) return false; -- cgit v0.10.2 From e9a4593cc5e36c6d47c87b439cb41c2568e7395f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 20 Jan 2012 14:33:59 -0800 Subject: leds: add led driver for Bachmann's ot200 Add support for leds on Bachmann's ot200 visualisation device. The device has three leds on the back panel (led_err, led_init and led_run) and can handle up to seven leds on the front panel. The driver was written by Linutronix on behalf of Bachmann electronic GmbH. It incorporates feedback from Lars-Peter Clausen [akpm@linux-foundation.org: add dependency on HAS_IOMEM] Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Christian Gmeiner Cc: Lars-Peter Clausen Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index c957c34..9ca28fc 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -403,6 +403,13 @@ config LEDS_MAX8997 This option enables support for on-chip LED drivers on MAXIM MAX8997 PMIC. +config LEDS_OT200 + tristate "LED support for the Bachmann OT200" + depends on LEDS_CLASS && HAS_IOMEM + help + This option enables support for the LEDs on the Bachmann OT200. + Say Y to enable LEDs on the Bachmann OT200. + config LEDS_TRIGGERS bool "LED Trigger support" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index b8a9723..1fc6875 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o +obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c new file mode 100644 index 0000000..c464682 --- /dev/null +++ b/drivers/leds/leds-ot200.c @@ -0,0 +1,171 @@ +/* + * Bachmann ot200 leds driver. + * + * Author: Sebastian Andrzej Siewior + * Christian Gmeiner + * + * License: GPL as published by the FSF. + */ + +#include +#include +#include +#include +#include +#include +#include + + +struct ot200_led { + struct led_classdev cdev; + const char *name; + unsigned long port; + u8 mask; +}; + +/* + * The device has three leds on the back panel (led_err, led_init and led_run) + * and can handle up to seven leds on the front panel. + */ + +static struct ot200_led leds[] = { + { + .name = "led_run", + .port = 0x5a, + .mask = BIT(0), + }, + { + .name = "led_init", + .port = 0x5a, + .mask = BIT(1), + }, + { + .name = "led_err", + .port = 0x5a, + .mask = BIT(2), + }, + { + .name = "led_1", + .port = 0x49, + .mask = BIT(7), + }, + { + .name = "led_2", + .port = 0x49, + .mask = BIT(6), + }, + { + .name = "led_3", + .port = 0x49, + .mask = BIT(5), + }, + { + .name = "led_4", + .port = 0x49, + .mask = BIT(4), + }, + { + .name = "led_5", + .port = 0x49, + .mask = BIT(3), + }, + { + .name = "led_6", + .port = 0x49, + .mask = BIT(2), + }, + { + .name = "led_7", + .port = 0x49, + .mask = BIT(1), + } +}; + +static DEFINE_SPINLOCK(value_lock); + +/* + * we need to store the current led states, as it is not + * possible to read the current led state via inb(). + */ +static u8 leds_back; +static u8 leds_front; + +static void ot200_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct ot200_led *led = container_of(led_cdev, struct ot200_led, cdev); + u8 *val; + unsigned long flags; + + spin_lock_irqsave(&value_lock, flags); + + if (led->port == 0x49) + val = &leds_front; + else if (led->port == 0x5a) + val = &leds_back; + else + BUG(); + + if (value == LED_OFF) + *val &= ~led->mask; + else + *val |= led->mask; + + outb(*val, led->port); + spin_unlock_irqrestore(&value_lock, flags); +} + +static int __devinit ot200_led_probe(struct platform_device *pdev) +{ + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(leds); i++) { + + leds[i].cdev.name = leds[i].name; + leds[i].cdev.brightness_set = ot200_led_brightness_set; + + ret = led_classdev_register(&pdev->dev, &leds[i].cdev); + if (ret < 0) + goto err; + } + + leds_front = 0; /* turn off all front leds */ + leds_back = BIT(1); /* turn on init led */ + outb(leds_front, 0x49); + outb(leds_back, 0x5a); + + return 0; + +err: + for (i = i - 1; i >= 0; i--) + led_classdev_unregister(&leds[i].cdev); + + return ret; +} + +static int __devexit ot200_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(leds); i++) + led_classdev_unregister(&leds[i].cdev); + + return 0; +} + +static struct platform_driver ot200_led_driver = { + .probe = ot200_led_probe, + .remove = __devexit_p(ot200_led_remove), + .driver = { + .name = "leds-ot200", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(ot200_led_driver); + +MODULE_AUTHOR("Sebastian A. Siewior "); +MODULE_DESCRIPTION("ot200 LED driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:leds-ot200"); -- cgit v0.10.2 From 2a4e64b8f6bcbf23ddd375b78342051ae8862284 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Fri, 20 Jan 2012 14:34:01 -0800 Subject: ipc/mqueue: simplify reading msgqueue limit Because the current task is being used to get the limit, we can simply use rlimit() instead of task_rlimit(). Signed-off-by: Davidlohr Bueso Acked-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 9b7c8ab..86ee272 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -128,7 +128,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb, if (S_ISREG(mode)) { struct mqueue_inode_info *info; - struct task_struct *p = current; unsigned long mq_bytes, mq_msg_tblsz; inode->i_fop = &mqueue_file_operations; @@ -159,7 +158,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb, spin_lock(&mq_lock); if (u->mq_bytes + mq_bytes < u->mq_bytes || - u->mq_bytes + mq_bytes > task_rlimit(p, RLIMIT_MSGQUEUE)) { + u->mq_bytes + mq_bytes > rlimit(RLIMIT_MSGQUEUE)) { spin_unlock(&mq_lock); /* mqueue_evict_inode() releases info->messages */ ret = -EMFILE; -- cgit v0.10.2 From d496aab567e7e52b3e974c9192a5de6e77dce32c Mon Sep 17 00:00:00 2001 From: Ananth N Mavinakayanahalli Date: Fri, 20 Jan 2012 14:34:04 -0800 Subject: kprobes: initialize before using a hlist Commit ef53d9c5e ("kprobes: improve kretprobe scalability with hashed locking") introduced a bug where we can potentially leak kretprobe_instances since we initialize a hlist head after having used it. Initialize the hlist head before using it. Reported by: Jim Keniston Acked-by: Jim Keniston Signed-off-by: Ananth N Mavinakayanahalli Acked-by: Masami Hiramatsu Cc: Srinivasa D S Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 95dd721..29f5b65 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1077,6 +1077,7 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) /* Early boot. kretprobe_table_locks not yet initialized. */ return; + INIT_HLIST_HEAD(&empty_rp); hash = hash_ptr(tk, KPROBE_HASH_BITS); head = &kretprobe_inst_table[hash]; kretprobe_table_lock(hash, &flags); @@ -1085,7 +1086,6 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) recycle_rp_inst(ri, &empty_rp); } kretprobe_table_unlock(hash, &flags); - INIT_HLIST_HEAD(&empty_rp); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); -- cgit v0.10.2 From 36c3e75907c8cb515fad260190ca1beb7e53df96 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 20 Jan 2012 14:34:05 -0800 Subject: drivers/video/backlight/adp88x0_bl.c: fix bit testing logic We need to write new value if the bit mask fields of new value is not equal to old value. It does not make sense to write new value only when all the bit_mask bits are zero. Signed-off-by: Axel Lin Cc: Michael Hennerich Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 66bc74d..378276c 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -146,7 +146,7 @@ static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask ret = adp8860_read(client, reg, ®_val); - if (!ret && ((reg_val & bit_mask) == 0)) { + if (!ret && ((reg_val & bit_mask) != bit_mask)) { reg_val |= bit_mask; ret = adp8860_write(client, reg, reg_val); } diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index 6c68a68..6735059 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -160,7 +160,7 @@ static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask ret = adp8870_read(client, reg, ®_val); - if (!ret && ((reg_val & bit_mask) == 0)) { + if (!ret && ((reg_val & bit_mask) != bit_mask)) { reg_val |= bit_mask; ret = adp8870_write(client, reg, reg_val); } -- cgit v0.10.2 From d59d9ebaacba32b63f24d53b1463519b445b4683 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 20 Jan 2012 14:34:06 -0800 Subject: drivers/video/backlight/l4f00242t03.c: return proper error in l4f00242t03_probe if regulator_get() fails Signed-off-by: Axel Lin Acked-by: Alberto Panizzo Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 4f5d1c4..27d1d7a 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -190,6 +190,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) priv->io_reg = regulator_get(&spi->dev, "vdd"); if (IS_ERR(priv->io_reg)) { + ret = PTR_ERR(priv->io_reg); dev_err(&spi->dev, "%s: Unable to get the IO regulator\n", __func__); goto err3; @@ -197,6 +198,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi) priv->core_reg = regulator_get(&spi->dev, "vcore"); if (IS_ERR(priv->core_reg)) { + ret = PTR_ERR(priv->core_reg); dev_err(&spi->dev, "%s: Unable to get the core regulator\n", __func__); goto err4; -- cgit v0.10.2 From 85e72aa5384b1a614563ad63257ded0e91d1a620 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 20 Jan 2012 14:34:09 -0800 Subject: proc: clear_refs: do not clear reserved pages /proc/pid/clear_refs is used to clear the Referenced and YOUNG bits for pages and corresponding page table entries of the task with PID pid, which includes any special mappings inserted into the page tables in order to provide things like vDSOs and user helper functions. On ARM this causes a problem because the vectors page is mapped as a global mapping and since ec706dab ("ARM: add a vma entry for the user accessible vector page"), a VMA is also inserted into each task for this page to aid unwinding through signals and syscall restarts. Since the vectors page is required for handling faults, clearing the YOUNG bit (and subsequently writing a faulting pte) means that we lose the vectors page *globally* and cannot fault it back in. This results in a system deadlock on the next exception. To see this problem in action, just run: $ echo 1 > /proc/self/clear_refs on an ARM platform (as any user) and watch your system hang. I think this has been the case since 2.6.37 This patch avoids clearing the aforementioned bits for reserved pages, therefore leaving the vectors page intact on ARM. Since reserved pages are not candidates for swap, this change should not have any impact on the usefulness of clear_refs. Signed-off-by: Will Deacon Reported-by: Moussa Ba Acked-by: Hugh Dickins Cc: David Rientjes Cc: Russell King Acked-by: Nicolas Pitre Cc: Matt Mackall Cc: [2.6.37+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index e418c5a..7dcd2a2 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -518,6 +518,9 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, if (!page) continue; + if (PageReserved(page)) + continue; + /* Clear accessed and referenced bits. */ ptep_test_and_clear_young(vma, addr, pte); ClearPageReferenced(page); -- cgit v0.10.2 From 6568d4a9c9ff16d6c4f0b14dfea567806ce579e4 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Fri, 20 Jan 2012 14:34:12 -0800 Subject: mm: memcg: update the correct soft limit tree during migration end_migration() passes the old page instead of the new page to commit the charge. This page descriptor is not used for committing itself, though, since we also pass the (correct) page_cgroup descriptor. But it's used to find the soft limit tree through the page's zone, so the soft limit tree of the old page's zone is updated instead of that of the new page's, which might get slightly out of date until the next charge reaches the ratelimit point. This glitch has been present since 5564e88 ("memcg: condense page_cgroup-to-page lookup points"). This fixes a bug that I introduced in 2.6.38. It's benign enough (to my knowledge) that we probably don't want this for stable. Reported-by: Hugh Dickins Signed-off-by: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Acked-by: Kirill A. Shutemov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 3dbff4d..4baddba 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3247,7 +3247,7 @@ int mem_cgroup_prepare_migration(struct page *page, ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; else ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM; - __mem_cgroup_commit_charge(memcg, page, 1, pc, ctype); + __mem_cgroup_commit_charge(memcg, newpage, 1, pc, ctype); return ret; } -- cgit v0.10.2 From 409eb8c2611b4310947a150af988111f7f52ab15 Mon Sep 17 00:00:00 2001 From: Hillf Danton Date: Fri, 20 Jan 2012 14:34:13 -0800 Subject: mm/hugetlb.c: undo change to page mapcount in fault handler Page mapcount should be updated only if we are sure that the page ends up in the page table otherwise we would leak if we couldn't COW due to reservations or if idx is out of bounds. Signed-off-by: Hillf Danton Reviewed-by: Michal Hocko Acked-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/hugetlb.c b/mm/hugetlb.c index ea8c3a4..5f34bd8 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2508,6 +2508,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, { struct hstate *h = hstate_vma(vma); int ret = VM_FAULT_SIGBUS; + int anon_rmap = 0; pgoff_t idx; unsigned long size; struct page *page; @@ -2562,14 +2563,13 @@ retry: spin_lock(&inode->i_lock); inode->i_blocks += blocks_per_huge_page(h); spin_unlock(&inode->i_lock); - page_dup_rmap(page); } else { lock_page(page); if (unlikely(anon_vma_prepare(vma))) { ret = VM_FAULT_OOM; goto backout_unlocked; } - hugepage_add_new_anon_rmap(page, vma, address); + anon_rmap = 1; } } else { /* @@ -2582,7 +2582,6 @@ retry: VM_FAULT_SET_HINDEX(h - hstates); goto backout_unlocked; } - page_dup_rmap(page); } /* @@ -2606,6 +2605,10 @@ retry: if (!huge_pte_none(huge_ptep_get(ptep))) goto backout; + if (anon_rmap) + hugepage_add_new_anon_rmap(page, vma, address); + else + page_dup_rmap(page); new_pte = make_huge_pte(vma, page, ((vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_SHARED))); set_huge_pte_at(mm, address, ptep, new_pte); -- cgit v0.10.2 From cb78edfdcef5259ac9e9088bd63810d21299928d Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Fri, 20 Jan 2012 14:34:16 -0800 Subject: kdump: define KEXEC_NOTE_BYTES arch specific for s390x kdump only allocates memory for the prstatus ELF note. For s390x, besides of prstatus multiple ELF notes for various different register types are stored. Therefore the currently allocated memory is not sufficient. With this patch the KEXEC_NOTE_BYTES macro can be defined by architecture code and for s390x it is set to the correct size now. Signed-off-by: Michael Holzheu Cc: "Eric W. Biederman" Cc: Vivek Goyal Cc: Martin Schwidefsky Cc: Heiko Carstens Reviewed-by: Simon Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/s390/include/asm/kexec.h b/arch/s390/include/asm/kexec.h index cf4e47b..3f30dac 100644 --- a/arch/s390/include/asm/kexec.h +++ b/arch/s390/include/asm/kexec.h @@ -42,6 +42,24 @@ /* The native architecture */ #define KEXEC_ARCH KEXEC_ARCH_S390 +/* + * Size for s390x ELF notes per CPU + * + * Seven notes plus zero note at the end: prstatus, fpregset, timer, + * tod_cmp, tod_reg, control regs, and prefix + */ +#define KEXEC_NOTE_BYTES \ + (ALIGN(sizeof(struct elf_note), 4) * 8 + \ + ALIGN(sizeof("CORE"), 4) * 7 + \ + ALIGN(sizeof(struct elf_prstatus), 4) + \ + ALIGN(sizeof(elf_fpregset_t), 4) + \ + ALIGN(sizeof(u64), 4) + \ + ALIGN(sizeof(u64), 4) + \ + ALIGN(sizeof(u32), 4) + \ + ALIGN(sizeof(u64) * 16, 4) + \ + ALIGN(sizeof(u32), 4) \ + ) + /* Provide a dummy definition to avoid build failures. */ static inline void crash_setup_regs(struct pt_regs *newregs, struct pt_regs *oldregs) { } diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 2fa0901..0d7d6a1 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -50,9 +50,11 @@ * note header. For kdump, the code in vmcore.c runs in the context * of the second kernel to combine them into one note. */ +#ifndef KEXEC_NOTE_BYTES #define KEXEC_NOTE_BYTES ( (KEXEC_NOTE_HEAD_BYTES * 2) + \ KEXEC_CORE_NOTE_NAME_BYTES + \ KEXEC_CORE_NOTE_DESC_BYTES ) +#endif /* * This structure is used to hold the arguments that are used when loading -- cgit v0.10.2 From 85046579bde15e532983438f86b36856e358f417 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Fri, 20 Jan 2012 14:34:19 -0800 Subject: SHM_UNLOCK: fix long unpreemptible section scan_mapping_unevictable_pages() is used to make SysV SHM_LOCKed pages evictable again once the shared memory is unlocked. It does this with pagevec_lookup()s across the whole object (which might occupy most of memory), and takes 300ms to unlock 7GB here. A cond_resched() every PAGEVEC_SIZE pages would be good. However, KOSAKI-san points out that this is called under shmem.c's info->lock, and it's also under shm.c's shm_lock(), both spinlocks. There is no strong reason for that: we need to take these pages off the unevictable list soonish, but those locks are not required for it. So move the call to scan_mapping_unevictable_pages() from shmem.c's unlock handling up to shm.c's unlock handling. Remove the recently added barrier, not needed now we have spin_unlock() before the scan. Use get_file(), with subsequent fput(), to make sure we have a reference to mapping throughout scan_mapping_unevictable_pages(): that's something that was previously guaranteed by the shm_lock(). Remove shmctl's lru_add_drain_all(): we don't fault in pages at SHM_LOCK time, and we lazily discover them to be Unevictable later, so it serves no purpose for SHM_LOCK; and serves no purpose for SHM_UNLOCK, since pages still on pagevec are not marked Unevictable. The original code avoided redundant rescans by checking VM_LOCKED flag at its level: now avoid them by checking shp's SHM_LOCKED. The original code called scan_mapping_unevictable_pages() on a locked area at shm_destroy() time: perhaps we once had accounting cross-checks which required that, but not now, so skip the overhead and just let inode eviction deal with them. Put check_move_unevictable_page() and scan_mapping_unevictable_pages() under CONFIG_SHMEM (with stub for the TINY case when ramfs is used), more as comment than to save space; comment them used for SHM_UNLOCK. Signed-off-by: Hugh Dickins Reviewed-by: KOSAKI Motohiro Cc: Minchan Kim Cc: Rik van Riel Cc: Shaohua Li Cc: Eric Dumazet Cc: Johannes Weiner Cc: Michel Lespinasse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/ipc/shm.c b/ipc/shm.c index 02ecf2c..854ab58 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -870,9 +870,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) case SHM_LOCK: case SHM_UNLOCK: { - struct file *uninitialized_var(shm_file); - - lru_add_drain_all(); /* drain pagevecs to lru lists */ + struct file *shm_file; shp = shm_lock_check(ns, shmid); if (IS_ERR(shp)) { @@ -895,22 +893,31 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) err = security_shm_shmctl(shp, cmd); if (err) goto out_unlock; - - if(cmd==SHM_LOCK) { + + shm_file = shp->shm_file; + if (is_file_hugepages(shm_file)) + goto out_unlock; + + if (cmd == SHM_LOCK) { struct user_struct *user = current_user(); - if (!is_file_hugepages(shp->shm_file)) { - err = shmem_lock(shp->shm_file, 1, user); - if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){ - shp->shm_perm.mode |= SHM_LOCKED; - shp->mlock_user = user; - } + err = shmem_lock(shm_file, 1, user); + if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { + shp->shm_perm.mode |= SHM_LOCKED; + shp->mlock_user = user; } - } else if (!is_file_hugepages(shp->shm_file)) { - shmem_lock(shp->shm_file, 0, shp->mlock_user); - shp->shm_perm.mode &= ~SHM_LOCKED; - shp->mlock_user = NULL; + goto out_unlock; } + + /* SHM_UNLOCK */ + if (!(shp->shm_perm.mode & SHM_LOCKED)) + goto out_unlock; + shmem_lock(shm_file, 0, shp->mlock_user); + shp->shm_perm.mode &= ~SHM_LOCKED; + shp->mlock_user = NULL; + get_file(shm_file); shm_unlock(shp); + scan_mapping_unevictable_pages(shm_file->f_mapping); + fput(shm_file); goto out; } case IPC_RMID: diff --git a/mm/shmem.c b/mm/shmem.c index feead19..4aaa53a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1068,13 +1068,6 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user) user_shm_unlock(inode->i_size, user); info->flags &= ~VM_LOCKED; mapping_clear_unevictable(file->f_mapping); - /* - * Ensure that a racing putback_lru_page() can see - * the pages of this mapping are evictable when we - * skip them due to !PageLRU during the scan. - */ - smp_mb__after_clear_bit(); - scan_mapping_unevictable_pages(file->f_mapping); } retval = 0; diff --git a/mm/vmscan.c b/mm/vmscan.c index 2880396..e097c10 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3499,6 +3499,7 @@ int page_evictable(struct page *page, struct vm_area_struct *vma) return 1; } +#ifdef CONFIG_SHMEM /** * check_move_unevictable_page - check page for evictability and move to appropriate zone lru list * @page: page to check evictability and move to appropriate lru list @@ -3509,6 +3510,8 @@ int page_evictable(struct page *page, struct vm_area_struct *vma) * * Restrictions: zone->lru_lock must be held, page must be on LRU and must * have PageUnevictable set. + * + * This function is only used for SysV IPC SHM_UNLOCK. */ static void check_move_unevictable_page(struct page *page, struct zone *zone) { @@ -3545,6 +3548,8 @@ retry: * * Scan all pages in mapping. Check unevictable pages for * evictability and move them to the appropriate zone lru list. + * + * This function is only used for SysV IPC SHM_UNLOCK. */ void scan_mapping_unevictable_pages(struct address_space *mapping) { @@ -3590,9 +3595,14 @@ void scan_mapping_unevictable_pages(struct address_space *mapping) pagevec_release(&pvec); count_vm_events(UNEVICTABLE_PGSCANNED, pg_scanned); + cond_resched(); } - } +#else +void scan_mapping_unevictable_pages(struct address_space *mapping) +{ +} +#endif /* CONFIG_SHMEM */ static void warn_scan_unevictable_pages(void) { -- cgit v0.10.2 From 245132643e1cfcd145bbc86a716c1818371fcb93 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Fri, 20 Jan 2012 14:34:21 -0800 Subject: SHM_UNLOCK: fix Unevictable pages stranded after swap Commit cc39c6a9bbde ("mm: account skipped entries to avoid looping in find_get_pages") correctly fixed an infinite loop; but left a problem that find_get_pages() on shmem would return 0 (appearing to callers to mean end of tree) when it meets a run of nr_pages swap entries. The only uses of find_get_pages() on shmem are via pagevec_lookup(), called from invalidate_mapping_pages(), and from shmctl SHM_UNLOCK's scan_mapping_unevictable_pages(). The first is already commented, and not worth worrying about; but the second can leave pages on the Unevictable list after an unusual sequence of swapping and locking. Fix that by using shmem_find_get_pages_and_swap() (then ignoring the swap) instead of pagevec_lookup(). But I don't want to contaminate vmscan.c with shmem internals, nor shmem.c with LRU locking. So move scan_mapping_unevictable_pages() into shmem.c, renaming it shmem_unlock_mapping(); and rename check_move_unevictable_page() to check_move_unevictable_pages(), looping down an array of pages, oftentimes under the same lock. Leave out the "rotate unevictable list" block: that's a leftover from when this was used for /proc/sys/vm/scan_unevictable_pages, whose flawed handling involved looking at pages at tail of LRU. Was there significance to the sequence first ClearPageUnevictable, then test page_evictable, then SetPageUnevictable here? I think not, we're under LRU lock, and have no barriers between those. Signed-off-by: Hugh Dickins Reviewed-by: KOSAKI Motohiro Cc: Minchan Kim Cc: Rik van Riel Cc: Shaohua Li Cc: Eric Dumazet Cc: Johannes Weiner Cc: Michel Lespinasse Cc: [back to 3.1 but will need respins] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index e4c711c..79ab255 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -48,6 +48,7 @@ extern struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); extern int shmem_zero_setup(struct vm_area_struct *); extern int shmem_lock(struct file *file, int lock, struct user_struct *user); +extern void shmem_unlock_mapping(struct address_space *mapping); extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end); diff --git a/include/linux/swap.h b/include/linux/swap.h index 06061a7..3e60228 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -273,7 +273,7 @@ static inline int zone_reclaim(struct zone *z, gfp_t mask, unsigned int order) #endif extern int page_evictable(struct page *page, struct vm_area_struct *vma); -extern void scan_mapping_unevictable_pages(struct address_space *); +extern void check_move_unevictable_pages(struct page **, int nr_pages); extern unsigned long scan_unevictable_pages; extern int scan_unevictable_handler(struct ctl_table *, int, diff --git a/ipc/shm.c b/ipc/shm.c index 854ab58..b76be5b 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -916,7 +916,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) shp->mlock_user = NULL; get_file(shm_file); shm_unlock(shp); - scan_mapping_unevictable_pages(shm_file->f_mapping); + shmem_unlock_mapping(shm_file->f_mapping); fput(shm_file); goto out; } diff --git a/mm/shmem.c b/mm/shmem.c index 4aaa53a..269d049 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -379,7 +379,7 @@ static int shmem_free_swap(struct address_space *mapping, /* * Pagevec may contain swap entries, so shuffle up pages before releasing. */ -static void shmem_pagevec_release(struct pagevec *pvec) +static void shmem_deswap_pagevec(struct pagevec *pvec) { int i, j; @@ -389,7 +389,36 @@ static void shmem_pagevec_release(struct pagevec *pvec) pvec->pages[j++] = page; } pvec->nr = j; - pagevec_release(pvec); +} + +/* + * SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists. + */ +void shmem_unlock_mapping(struct address_space *mapping) +{ + struct pagevec pvec; + pgoff_t indices[PAGEVEC_SIZE]; + pgoff_t index = 0; + + pagevec_init(&pvec, 0); + /* + * Minor point, but we might as well stop if someone else SHM_LOCKs it. + */ + while (!mapping_unevictable(mapping)) { + /* + * Avoid pagevec_lookup(): find_get_pages() returns 0 as if it + * has finished, if it hits a row of PAGEVEC_SIZE swap entries. + */ + pvec.nr = shmem_find_get_pages_and_swap(mapping, index, + PAGEVEC_SIZE, pvec.pages, indices); + if (!pvec.nr) + break; + index = indices[pvec.nr - 1] + 1; + shmem_deswap_pagevec(&pvec); + check_move_unevictable_pages(pvec.pages, pvec.nr); + pagevec_release(&pvec); + cond_resched(); + } } /* @@ -440,7 +469,8 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) } unlock_page(page); } - shmem_pagevec_release(&pvec); + shmem_deswap_pagevec(&pvec); + pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); index++; @@ -470,7 +500,8 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) continue; } if (index == start && indices[0] > end) { - shmem_pagevec_release(&pvec); + shmem_deswap_pagevec(&pvec); + pagevec_release(&pvec); break; } mem_cgroup_uncharge_start(); @@ -494,7 +525,8 @@ void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) } unlock_page(page); } - shmem_pagevec_release(&pvec); + shmem_deswap_pagevec(&pvec); + pagevec_release(&pvec); mem_cgroup_uncharge_end(); index++; } @@ -2438,6 +2470,10 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user) return 0; } +void shmem_unlock_mapping(struct address_space *mapping) +{ +} + void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend) { truncate_inode_pages_range(inode->i_mapping, lstart, lend); diff --git a/mm/vmscan.c b/mm/vmscan.c index e097c10..c52b235 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -26,7 +26,6 @@ #include /* for try_to_release_page(), buffer_heads_over_limit */ #include -#include #include #include #include @@ -661,7 +660,7 @@ redo: * When racing with an mlock or AS_UNEVICTABLE clearing * (page is unlocked) make sure that if the other thread * does not observe our setting of PG_lru and fails - * isolation/check_move_unevictable_page, + * isolation/check_move_unevictable_pages, * we see PG_mlocked/AS_UNEVICTABLE cleared below and move * the page back to the evictable list. * @@ -3501,107 +3500,58 @@ int page_evictable(struct page *page, struct vm_area_struct *vma) #ifdef CONFIG_SHMEM /** - * check_move_unevictable_page - check page for evictability and move to appropriate zone lru list - * @page: page to check evictability and move to appropriate lru list - * @zone: zone page is in + * check_move_unevictable_pages - check pages for evictability and move to appropriate zone lru list + * @pages: array of pages to check + * @nr_pages: number of pages to check * - * Checks a page for evictability and moves the page to the appropriate - * zone lru list. - * - * Restrictions: zone->lru_lock must be held, page must be on LRU and must - * have PageUnevictable set. + * Checks pages for evictability and moves them to the appropriate lru list. * * This function is only used for SysV IPC SHM_UNLOCK. */ -static void check_move_unevictable_page(struct page *page, struct zone *zone) +void check_move_unevictable_pages(struct page **pages, int nr_pages) { struct lruvec *lruvec; + struct zone *zone = NULL; + int pgscanned = 0; + int pgrescued = 0; + int i; - VM_BUG_ON(PageActive(page)); -retry: - ClearPageUnevictable(page); - if (page_evictable(page, NULL)) { - enum lru_list l = page_lru_base_type(page); - - __dec_zone_state(zone, NR_UNEVICTABLE); - lruvec = mem_cgroup_lru_move_lists(zone, page, - LRU_UNEVICTABLE, l); - list_move(&page->lru, &lruvec->lists[l]); - __inc_zone_state(zone, NR_INACTIVE_ANON + l); - __count_vm_event(UNEVICTABLE_PGRESCUED); - } else { - /* - * rotate unevictable list - */ - SetPageUnevictable(page); - lruvec = mem_cgroup_lru_move_lists(zone, page, LRU_UNEVICTABLE, - LRU_UNEVICTABLE); - list_move(&page->lru, &lruvec->lists[LRU_UNEVICTABLE]); - if (page_evictable(page, NULL)) - goto retry; - } -} - -/** - * scan_mapping_unevictable_pages - scan an address space for evictable pages - * @mapping: struct address_space to scan for evictable pages - * - * Scan all pages in mapping. Check unevictable pages for - * evictability and move them to the appropriate zone lru list. - * - * This function is only used for SysV IPC SHM_UNLOCK. - */ -void scan_mapping_unevictable_pages(struct address_space *mapping) -{ - pgoff_t next = 0; - pgoff_t end = (i_size_read(mapping->host) + PAGE_CACHE_SIZE - 1) >> - PAGE_CACHE_SHIFT; - struct zone *zone; - struct pagevec pvec; - - if (mapping->nrpages == 0) - return; - - pagevec_init(&pvec, 0); - while (next < end && - pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { - int i; - int pg_scanned = 0; - - zone = NULL; - - for (i = 0; i < pagevec_count(&pvec); i++) { - struct page *page = pvec.pages[i]; - pgoff_t page_index = page->index; - struct zone *pagezone = page_zone(page); + for (i = 0; i < nr_pages; i++) { + struct page *page = pages[i]; + struct zone *pagezone; - pg_scanned++; - if (page_index > next) - next = page_index; - next++; + pgscanned++; + pagezone = page_zone(page); + if (pagezone != zone) { + if (zone) + spin_unlock_irq(&zone->lru_lock); + zone = pagezone; + spin_lock_irq(&zone->lru_lock); + } - if (pagezone != zone) { - if (zone) - spin_unlock_irq(&zone->lru_lock); - zone = pagezone; - spin_lock_irq(&zone->lru_lock); - } + if (!PageLRU(page) || !PageUnevictable(page)) + continue; - if (PageLRU(page) && PageUnevictable(page)) - check_move_unevictable_page(page, zone); + if (page_evictable(page, NULL)) { + enum lru_list lru = page_lru_base_type(page); + + VM_BUG_ON(PageActive(page)); + ClearPageUnevictable(page); + __dec_zone_state(zone, NR_UNEVICTABLE); + lruvec = mem_cgroup_lru_move_lists(zone, page, + LRU_UNEVICTABLE, lru); + list_move(&page->lru, &lruvec->lists[lru]); + __inc_zone_state(zone, NR_INACTIVE_ANON + lru); + pgrescued++; } - if (zone) - spin_unlock_irq(&zone->lru_lock); - pagevec_release(&pvec); + } - count_vm_events(UNEVICTABLE_PGSCANNED, pg_scanned); - cond_resched(); + if (zone) { + __count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued); + __count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned); + spin_unlock_irq(&zone->lru_lock); } } -#else -void scan_mapping_unevictable_pages(struct address_space *mapping) -{ -} #endif /* CONFIG_SHMEM */ static void warn_scan_unevictable_pages(void) -- cgit v0.10.2 From 9f9f1acd713d69fae2af286fbeedc6c8963411c6 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Fri, 20 Jan 2012 14:34:24 -0800 Subject: mm: fix rss count leakage during migration Memory migration fills a pte with a migration entry and it doesn't update the rss counters. Then it replaces the migration entry with the new page (or the old one if migration failed). But between these two passes this pte can be unmaped, or a task can fork a child and it will get a copy of this migration entry. Nobody accounts for this in the rss counters. This patch properly adjust rss counters for migration entries in zap_pte_range() and copy_one_pte(). Thus we avoid extra atomic operations on the migration fast-path. Signed-off-by: Konstantin Khlebnikov Cc: Hugh Dickins Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/memory.c b/mm/memory.c index 5e30583..fa2f04e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -878,15 +878,24 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm, } if (likely(!non_swap_entry(entry))) rss[MM_SWAPENTS]++; - else if (is_write_migration_entry(entry) && - is_cow_mapping(vm_flags)) { - /* - * COW mappings require pages in both parent - * and child to be set to read. - */ - make_migration_entry_read(&entry); - pte = swp_entry_to_pte(entry); - set_pte_at(src_mm, addr, src_pte, pte); + else if (is_migration_entry(entry)) { + page = migration_entry_to_page(entry); + + if (PageAnon(page)) + rss[MM_ANONPAGES]++; + else + rss[MM_FILEPAGES]++; + + if (is_write_migration_entry(entry) && + is_cow_mapping(vm_flags)) { + /* + * COW mappings require pages in both + * parent and child to be set to read. + */ + make_migration_entry_read(&entry); + pte = swp_entry_to_pte(entry); + set_pte_at(src_mm, addr, src_pte, pte); + } } } goto out_set_pte; @@ -1191,6 +1200,16 @@ again: if (!non_swap_entry(entry)) rss[MM_SWAPENTS]--; + else if (is_migration_entry(entry)) { + struct page *page; + + page = migration_entry_to_page(entry); + + if (PageAnon(page)) + rss[MM_ANONPAGES]--; + else + rss[MM_FILEPAGES]--; + } if (unlikely(!free_swap_and_cache(entry))) print_bad_pte(vma, addr, ptent, NULL); } -- cgit v0.10.2 From c25a785d6647984505fa165b5cd84cfc9a95970b Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Fri, 20 Jan 2012 14:34:27 -0800 Subject: score: fix off-by-one index into syscall table If the provided system call number is equal to __NR_syscalls, the current check will pass and a function pointer just after the system call table may be called, since sys_call_table is an array with total size __NR_syscalls. Whether or not this is a security bug depends on what the compiler puts immediately after the system call table. It's likely that this won't do anything bad because there is an additional NULL check on the syscall entry, but if there happens to be a non-NULL value immediately after the system call table, this may result in local privilege escalation. Signed-off-by: Dan Rosenberg Cc: Cc: Chen Liqin Cc: Lennox Wu Cc: Eugene Teo Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/score/kernel/entry.S b/arch/score/kernel/entry.S index 577abba..83bb960 100644 --- a/arch/score/kernel/entry.S +++ b/arch/score/kernel/entry.S @@ -408,7 +408,7 @@ ENTRY(handle_sys) sw r9, [r0, PT_EPC] cmpi.c r27, __NR_syscalls # check syscall number - bgtu illegal_syscall + bgeu illegal_syscall slli r8, r27, 2 # get syscall routine la r11, sys_call_table -- cgit v0.10.2 From 70c95b00b87f85ce5a15b7899aaa862eacd2364a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 10:31:54 -0800 Subject: scripts/kernel-doc: fix fatal error caused by cfg80211.h include/net/cfg80211.h uses __must_check in functions that have kernel-doc notation. This was confusing scripts/kernel-doc, so have scripts/kernel-doc ignore "__must_check". Error(include/net/cfg80211.h:2702): cannot understand prototype: 'struct cfg80211_bss * __must_check cfg80211_inform_bss(...) Signed-off-by: Randy Dunlap Cc: Johannes Berg Signed-off-by: Linus Torvalds diff --git a/scripts/kernel-doc b/scripts/kernel-doc index d793001..9b0c0b8 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -5,7 +5,7 @@ use strict; ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## ## Copyright (C) 2000, 1 Tim Waugh ## ## Copyright (C) 2001 Simon Huggins ## -## Copyright (C) 2005-2010 Randy Dunlap ## +## Copyright (C) 2005-2012 Randy Dunlap ## ## ## ## #define enhancements by Armin Kuster ## ## Copyright (c) 2000 MontaVista Software, Inc. ## @@ -1785,6 +1785,7 @@ sub dump_function($$) { $prototype =~ s/__devinit +//; $prototype =~ s/__init +//; $prototype =~ s/__init_or_module +//; + $prototype =~ s/__must_check +//; $prototype =~ s/^#\s*define\s+//; #ak added $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//; -- cgit v0.10.2 From 42ae610c1a820ddecb80943d4ccfc936f7772535 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:24 -0800 Subject: kernel-doc: fix new warnings in auditsc.c Fix new kernel-doc warnings in auditsc.c: Warning(kernel/auditsc.c:1875): No description found for parameter 'success' Warning(kernel/auditsc.c:1875): No description found for parameter 'return_code' Warning(kernel/auditsc.c:1875): Excess function parameter 'pt_regs' description in '__audit_syscall_exit' Signed-off-by: Randy Dunlap Cc: Al Viro Cc: Eric Paris Signed-off-by: Linus Torvalds diff --git a/kernel/auditsc.c b/kernel/auditsc.c index caaea6e..af1de0f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1863,11 +1863,12 @@ void __audit_syscall_entry(int arch, int major, /** * audit_syscall_exit - deallocate audit context after a system call - * @pt_regs: syscall registers + * @success: success value of the syscall + * @return_code: return value of the syscall * * Tear down after system call. If the audit context has been marked as * auditable (either because of the AUDIT_RECORD_CONTEXT state from - * filtering, or because some other part of the kernel write an audit + * filtering, or because some other part of the kernel wrote an audit * message), then write out the syscall information. In call cases, * free the names stored from getname(). */ -- cgit v0.10.2 From 78d79559f2af1e77034436326aa20f2654074e4c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:28 -0800 Subject: kernel-doc: fix new warnings in driver-core Fix new kernel-doc warnings: Warning(drivers/base/bus.c:925): No description found for parameter 'key' Warning(drivers/base/bus.c:1241): No description found for parameter 'subsys' Warning(drivers/base/bus.c:1241): No description found for parameter 'groups' Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 99dc592..40fb122 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -915,9 +915,10 @@ static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /** * __bus_register - register a driver-core subsystem - * @bus: bus. + * @bus: bus to register + * @key: lockdep class key * - * Once we have that, we registered the bus with the kobject + * Once we have that, we register the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the subsystem. */ @@ -1220,8 +1221,8 @@ static void system_root_device_release(struct device *dev) } /** * subsys_system_register - register a subsystem at /sys/devices/system/ - * @subsys - system subsystem - * @groups - default attributes for the root device + * @subsys: system subsystem + * @groups: default attributes for the root device * * All 'system' subsystems have a /sys/devices/system/ root device * with the name of the subsystem. The root device can carry subsystem- -- cgit v0.10.2 From 6e9292c588894bd39eb2d093013f0aee558ddf0e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:35 -0800 Subject: kernel-doc: fix new warnings in pci Fix new kernel-doc warnings: Warning(drivers/pci/pci.c:2811): No description found for parameter 'dev' Warning(drivers/pci/pci.c:2811): Excess function parameter 'pdev' description in 'pci_intx_mask_supported' Warning(drivers/pci/pci.c:2894): No description found for parameter 'dev' Warning(drivers/pci/pci.c:2894): Excess function parameter 'pdev' description in 'pci_check_and_mask_intx' Warning(drivers/pci/pci.c:2908): No description found for parameter 'dev' Warning(drivers/pci/pci.c:2908): Excess function parameter 'pdev' description in 'pci_check_and_unmask_intx' Signed-off-by: Randy Dunlap Cc: Jesse Barnes Signed-off-by: Linus Torvalds diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 97fff78..af295bb 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2802,7 +2802,7 @@ pci_intx(struct pci_dev *pdev, int enable) /** * pci_intx_mask_supported - probe for INTx masking support - * @pdev: the PCI device to operate on + * @dev: the PCI device to operate on * * Check if the device dev support INTx masking via the config space * command word. @@ -2884,7 +2884,7 @@ done: /** * pci_check_and_mask_intx - mask INTx on pending interrupt - * @pdev: the PCI device to operate on + * @dev: the PCI device to operate on * * Check if the device dev has its INTx line asserted, mask it and * return true in that case. False is returned if not interrupt was @@ -2898,7 +2898,7 @@ EXPORT_SYMBOL_GPL(pci_check_and_mask_intx); /** * pci_check_and_mask_intx - unmask INTx of no interrupt is pending - * @pdev: the PCI device to operate on + * @dev: the PCI device to operate on * * Check if the device dev has its INTx line asserted, unmask it if not * and return true. False is returned and the mask remains active if -- cgit v0.10.2 From 5bc75a886353fa5f386c5ce49a93da1756006d8f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:38 -0800 Subject: kernel-doc: fix new warning in regulator core Fix new kernel-doc warning: Warning(drivers/regulator/core.c:2741): No description found for parameter 'of_node' Signed-off-by: Randy Dunlap Cc: Liam Girdwood Cc: Mark Brown Signed-off-by: Linus Torvalds diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ca86f39..b1fa25d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2731,6 +2731,7 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) * @dev: struct device for the regulator * @init_data: platform provided init data, passed through by driver * @driver_data: private regulator data + * @of_node: target open firmware device structure (may be NULL) * * Called by regulator drivers to register a regulator. * Returns 0 on success. -- cgit v0.10.2 From b5763accd3b5fc131ee06e26ce56e63ae0322c9b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:42 -0800 Subject: kernel-doc: fix new warnings in debugfs Fix new kernel-doc warnings: Warning(fs/debugfs/file.c:556): No description found for parameter 'nregs' Warning(fs/debugfs/file.c:556): Excess function parameter 'mregs' description in 'debugfs_print_regs32' Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index f65d445..ef023ee 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -540,7 +540,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_blob); * debugfs_print_regs32 - use seq_print to describe a set of registers * @s: the seq_file structure being used to generate output * @regs: an array if struct debugfs_reg32 structures - * @mregs: the length of the above array + * @nregs: the length of the above array * @base: the base address to be used in reading the registers * @prefix: a string to be prefixed to every output line * -- cgit v0.10.2 From 2eda013f4894bc200124f791a56c4defb613a0cc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:51 -0800 Subject: kernel-doc: fix new warnings in device.h Fix new kernel-doc warnings: Warning(include/linux/device.h:299): No description found for parameter 'name' Warning(include/linux/device.h:299): No description found for parameter 'subsys' Warning(include/linux/device.h:299): No description found for parameter 'node' Warning(include/linux/device.h:299): No description found for parameter 'add_dev' Warning(include/linux/device.h:299): No description found for parameter 'remove_dev' Warning(include/linux/device.h:685): No description found for parameter 'id' Warning(include/linux/device.h:1009): No description found for parameter '__driver' Warning(include/linux/device.h:1009): No description found for parameter '__register' Warning(include/linux/device.h:1009): No description found for parameter '__unregister' Signed-off-by: Randy Dunlap Cc: Lars-Peter Clausen Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/include/linux/device.h b/include/linux/device.h index 5b3adb8..b63fb39 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -279,11 +279,11 @@ struct device *driver_find_device(struct device_driver *drv, /** * struct subsys_interface - interfaces to device functions - * @name name of the device function - * @subsystem subsytem of the devices to attach to - * @node the list of functions registered at the subsystem - * @add device hookup to device function handler - * @remove device hookup to device function handler + * @name: name of the device function + * @subsys: subsytem of the devices to attach to + * @node: the list of functions registered at the subsystem + * @add_dev: device hookup to device function handler + * @remove_dev: device hookup to device function handler * * Simple interfaces attached to a subsystem. Multiple interfaces can * attach to a subsystem and its devices. Unlike drivers, they do not @@ -612,6 +612,7 @@ struct device_dma_parameters { * @archdata: For arch-specific additions. * @of_node: Associated device tree node. * @devt: For creating the sysfs "dev". + * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. * @devres_head: The resources list of the device. * @knode_class: The node used to add the device to the class list. @@ -1003,6 +1004,10 @@ extern long sysfs_deprecated; * Each module may only use this macro once, and calling it replaces * module_init() and module_exit(). * + * @__driver: driver name + * @__register: register function for this driver type + * @__unregister: unregister function for this driver type + * * Use this macro to construct bus specific macros for registering * drivers, and do not use it on its own. */ -- cgit v0.10.2 From 4d922612df8bd1202a1f51d95b78aca3d67302cd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:56 -0800 Subject: kernel-doc: fix new warning in usb.h Fix new kernel-doc warning: Warning(include/linux/usb.h:1251): No description found for parameter 'num_mapped_sgs' Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/include/linux/usb.h b/include/linux/usb.h index 27a4e16..69d8457 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1073,6 +1073,7 @@ typedef void (*usb_complete_t)(struct urb *); * which the host controller driver should use in preference to the * transfer_buffer. * @sg: scatter gather buffer list + * @num_mapped_sgs: (internal) number of mapped sg entries * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may * be broken up into chunks according to the current maximum packet -- cgit v0.10.2 From 2f6c76aa5f0fb53059730c628ac59ef62a14ed7a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:00 -0800 Subject: kernel-doc: fix new warnings in cfg80211.h Fix new kernel-doc warnings: Warning(include/net/cfg80211.h:1165): No description found for parameter 'channel_type' Warning(include/net/cfg80211.h:2090): No description found for parameter 'probe_resp_offload' Signed-off-by: Randy Dunlap Cc: Johannes Berg Cc: linux-wireless@vger.kernel.org Signed-off-by: Linus Torvalds diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 15f4be7..a067d30 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1140,6 +1140,7 @@ struct cfg80211_disassoc_request { * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not * search for IBSSs with a different BSSID. * @channel: The channel to use if no IBSS can be found to join. + * @channel_type: channel type (HT mode) * @channel_fixed: The channel should be fixed -- do not search for * IBSSs to join on other channels. * @ie: information element(s) to include in the beacon @@ -1978,6 +1979,11 @@ struct wiphy_wowlan_support { * configured as RX antennas. Antenna configuration commands will be * rejected unless this or @available_antennas_tx is set. * + * @probe_resp_offload: + * Bitmap of supported protocols for probe response offloading. + * See &enum nl80211_probe_resp_offload_support_attr. Only valid + * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set. + * * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation * may request, if implemented. * -- cgit v0.10.2 From fa757281a08799fd6c0f7ec6f111d1cd66afc97b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:13 -0800 Subject: kernel-doc: fix kernel-doc warnings in sched Fix new kernel-doc notation warnings: Warning(include/linux/sched.h:2094): No description found for parameter 'p' Warning(include/linux/sched.h:2094): Excess function parameter 'tsk' description in 'is_idle_task' Warning(kernel/sched/cpupri.c:139): No description found for parameter 'newpri' Warning(kernel/sched/cpupri.c:139): Excess function parameter 'pri' description in 'cpupri_set' Warning(kernel/sched/cpupri.c:208): Excess function parameter 'bootmem' description in 'cpupri_init' Signed-off-by: Randy Dunlap Cc: Ingo Molnar Cc: Peter Zijlstra Signed-off-by: Linus Torvalds diff --git a/include/linux/sched.h b/include/linux/sched.h index 4032ec1..513f524 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2088,7 +2088,7 @@ extern int sched_setscheduler_nocheck(struct task_struct *, int, extern struct task_struct *idle_task(int cpu); /** * is_idle_task - is the specified task an idle task? - * @tsk: the task in question. + * @p: the task in question. */ static inline bool is_idle_task(struct task_struct *p) { diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c index b0d798e..d72586f 100644 --- a/kernel/sched/cpupri.c +++ b/kernel/sched/cpupri.c @@ -129,7 +129,7 @@ int cpupri_find(struct cpupri *cp, struct task_struct *p, * cpupri_set - update the cpu priority setting * @cp: The cpupri context * @cpu: The target cpu - * @pri: The priority (INVALID-RT99) to assign to this CPU + * @newpri: The priority (INVALID-RT99) to assign to this CPU * * Note: Assumes cpu_rq(cpu)->lock is locked * @@ -200,7 +200,6 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri) /** * cpupri_init - initialize the cpupri structure * @cp: The cpupri context - * @bootmem: true if allocations need to use bootmem * * Returns: -ENOMEM if memory fails. */ -- cgit v0.10.2 From 73e4a987390aacce102d7d36095a2dd63665f032 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:16 -0800 Subject: docbook: don't use serial_core.h in device-drivers book Fix new kernel-doc warning. This file no longer contains kernel-doc comments. Warning(include/linux/serial_core.h): no structured comments found Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index b638e50..b330b32 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -216,7 +216,6 @@ X!Isound/sound_firmware.c 16x50 UART Driver -!Iinclude/linux/serial_core.h !Edrivers/tty/serial/serial_core.c !Edrivers/tty/serial/8250.c -- cgit v0.10.2 From 4e35d28751752859b9985074e02320393b6f1c33 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:20 -0800 Subject: docbook: change iomap source filename in deviceiobook Fix warning since kernel-doc comments were moved to another source file. Warning(lib/iomap.c): no structured comments found Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds diff --git a/Documentation/DocBook/deviceiobook.tmpl b/Documentation/DocBook/deviceiobook.tmpl index c1ed6a4..54199a0 100644 --- a/Documentation/DocBook/deviceiobook.tmpl +++ b/Documentation/DocBook/deviceiobook.tmpl @@ -317,7 +317,7 @@ CPU B: spin_unlock_irqrestore(&dev_lock, flags) Public Functions Provided !Iarch/x86/include/asm/io.h -!Elib/iomap.c +!Elib/pci_iomap.c -- cgit v0.10.2 From b4d20859362fde976bb2fa53eb51b798cdba1afc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:24 -0800 Subject: docbook: fix sched source file names in device-drivers book Fix warning since kernel scheduler functions and kernel-doc notation were moved to other files. docproc: lnx-33-rc1/kernel/sched.c: No such file or directory Signed-off-by: Randy Dunlap Cc: Ingo Molnar Cc: Peter Zijlstra Signed-off-by: Linus Torvalds diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index b330b32..2f7fd43 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -50,7 +50,9 @@ Delaying, scheduling, and timer routines !Iinclude/linux/sched.h -!Ekernel/sched.c +!Ekernel/sched/core.c +!Ikernel/sched/cpupri.c +!Ikernel/sched/fair.c !Iinclude/linux/completion.h !Ekernel/timer.c -- cgit v0.10.2 From b4ead019afc201f71c39cd0dfcaafed4a97b3dd2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Jan 2012 18:23:36 +0100 Subject: ALSA: hda - Fix silent outputs from docking-station jacks of Dell laptops The recent change of the power-widget handling for IDT codecs caused the silent output from the docking-station line-out jack. This was partially fixed by the commit f2cbba7602383cd9cdd21f0a5d0b8bd1aad47b33 "ALSA: hda - Fix the lost power-setup of seconary pins after PM resume". But the line-out on the docking-station is still silent when booted with the jack plugged even by this fix. The remainig bug is that the power-widget is set off in stac92xx_init() because the pins in cfg->line_out_pins[] aren't checked there properly but only hp_pins[] are checked in is_nid_hp_pin(). This patch fixes the problem by checking both HP and line-out pins and leaving the power-map correctly. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42637 Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 1a26dbc..336cfcd 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4163,13 +4163,15 @@ static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, return 1; } -static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) { int i; for (i = 0; i < cfg->hp_outs; i++) if (cfg->hp_pins[i] == nid) return 1; /* nid is a HP-Out */ - + for (i = 0; i < cfg->line_outs; i++) + if (cfg->line_out_pins[i] == nid) + return 1; /* nid is a line-Out */ return 0; /* nid is not a HP-Out */ }; @@ -4375,7 +4377,7 @@ static int stac92xx_init(struct hda_codec *codec) continue; } - if (is_nid_hp_pin(cfg, nid)) + if (is_nid_out_jack_pin(cfg, nid)) continue; /* already has an unsol event */ pinctl = snd_hda_codec_read(codec, nid, 0, -- cgit v0.10.2 From 4ec7ac1203bcf21f5e3d977c9818b1a56c9ef40d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Jan 2012 05:38:59 +0000 Subject: macvlan: fix a possible use after free Commit bc416d9768 (macvlan: handle fragmented multicast frames) added a possible use after free in macvlan_handle_frame(), since ip_check_defrag() uses pskb_may_pull() : skb header can be reallocated. Signed-off-by: Eric Dumazet Cc: Ben Greear Signed-off-by: David S. Miller diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index f2f820c..9ea9921 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -173,6 +173,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN); if (!skb) return RX_HANDLER_CONSUMED; + eth = eth_hdr(skb); src = macvlan_hash_lookup(port, eth->h_source); if (!src) /* frame comes from an external address */ -- cgit v0.10.2 From 460a25cdaef1a2b6b8e14e371d868aa91b0e72e8 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Mon, 23 Jan 2012 07:31:51 +0000 Subject: bnx2x: credit-leakage fixup on vlan_mac_del_all Upon insertion of elements into the execution queue, it is validated that there are enough credits to support additional vlan-macs, and the credits are consumed. However, when removing a pending command in `bnx2x_vland_mac_del_all' the consumed credits are not released, which might cause leakage and eventually the inability to add new vlan-macs in certain scenarios. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 5ac6160..cb6339c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -50,6 +50,7 @@ static inline void bnx2x_exe_queue_init(struct bnx2x *bp, int exe_len, union bnx2x_qable_obj *owner, exe_q_validate validate, + exe_q_remove remove, exe_q_optimize optimize, exe_q_execute exec, exe_q_get get) @@ -66,6 +67,7 @@ static inline void bnx2x_exe_queue_init(struct bnx2x *bp, /* Owner specific callbacks */ o->validate = validate; + o->remove = remove; o->optimize = optimize; o->execute = exec; o->get = get; @@ -1340,6 +1342,35 @@ static int bnx2x_validate_vlan_mac(struct bnx2x *bp, } } +static int bnx2x_remove_vlan_mac(struct bnx2x *bp, + union bnx2x_qable_obj *qo, + struct bnx2x_exeq_elem *elem) +{ + int rc = 0; + + /* If consumption wasn't required, nothing to do */ + if (test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, + &elem->cmd_data.vlan_mac.vlan_mac_flags)) + return 0; + + switch (elem->cmd_data.vlan_mac.cmd) { + case BNX2X_VLAN_MAC_ADD: + case BNX2X_VLAN_MAC_MOVE: + rc = qo->vlan_mac.put_credit(&qo->vlan_mac); + break; + case BNX2X_VLAN_MAC_DEL: + rc = qo->vlan_mac.get_credit(&qo->vlan_mac); + break; + default: + return -EINVAL; + } + + if (rc != true) + return -EINVAL; + + return 0; +} + /** * bnx2x_wait_vlan_mac - passivly wait for 5 seconds until all work completes. * @@ -1801,8 +1832,14 @@ static int bnx2x_vlan_mac_del_all(struct bnx2x *bp, list_for_each_entry_safe(exeq_pos, exeq_pos_n, &exeq->exe_queue, link) { if (exeq_pos->cmd_data.vlan_mac.vlan_mac_flags == - *vlan_mac_flags) + *vlan_mac_flags) { + rc = exeq->remove(bp, exeq->owner, exeq_pos); + if (rc) { + BNX2X_ERR("Failed to remove command\n"); + return rc; + } list_del(&exeq_pos->link); + } } spin_unlock_bh(&exeq->lock); @@ -1908,6 +1945,7 @@ void bnx2x_init_mac_obj(struct bnx2x *bp, bnx2x_exe_queue_init(bp, &mac_obj->exe_queue, 1, qable_obj, bnx2x_validate_vlan_mac, + bnx2x_remove_vlan_mac, bnx2x_optimize_vlan_mac, bnx2x_execute_vlan_mac, bnx2x_exeq_get_mac); @@ -1924,6 +1962,7 @@ void bnx2x_init_mac_obj(struct bnx2x *bp, bnx2x_exe_queue_init(bp, &mac_obj->exe_queue, CLASSIFY_RULES_COUNT, qable_obj, bnx2x_validate_vlan_mac, + bnx2x_remove_vlan_mac, bnx2x_optimize_vlan_mac, bnx2x_execute_vlan_mac, bnx2x_exeq_get_mac); @@ -1963,6 +2002,7 @@ void bnx2x_init_vlan_obj(struct bnx2x *bp, bnx2x_exe_queue_init(bp, &vlan_obj->exe_queue, CLASSIFY_RULES_COUNT, qable_obj, bnx2x_validate_vlan_mac, + bnx2x_remove_vlan_mac, bnx2x_optimize_vlan_mac, bnx2x_execute_vlan_mac, bnx2x_exeq_get_vlan); @@ -2009,6 +2049,7 @@ void bnx2x_init_vlan_mac_obj(struct bnx2x *bp, bnx2x_exe_queue_init(bp, &vlan_mac_obj->exe_queue, 1, qable_obj, bnx2x_validate_vlan_mac, + bnx2x_remove_vlan_mac, bnx2x_optimize_vlan_mac, bnx2x_execute_vlan_mac, bnx2x_exeq_get_vlan_mac); @@ -2025,6 +2066,7 @@ void bnx2x_init_vlan_mac_obj(struct bnx2x *bp, &vlan_mac_obj->exe_queue, CLASSIFY_RULES_COUNT, qable_obj, bnx2x_validate_vlan_mac, + bnx2x_remove_vlan_mac, bnx2x_optimize_vlan_mac, bnx2x_execute_vlan_mac, bnx2x_exeq_get_vlan_mac); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index 992308f..66da39f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -161,6 +161,10 @@ typedef int (*exe_q_validate)(struct bnx2x *bp, union bnx2x_qable_obj *o, struct bnx2x_exeq_elem *elem); +typedef int (*exe_q_remove)(struct bnx2x *bp, + union bnx2x_qable_obj *o, + struct bnx2x_exeq_elem *elem); + /** * @return positive is entry was optimized, 0 - if not, negative * in case of an error. @@ -203,11 +207,18 @@ struct bnx2x_exe_queue_obj { */ exe_q_validate validate; + /** + * Called before removing pending commands, cleaning allocated + * resources (e.g., credits from validate) + */ + exe_q_remove remove; /** * This will try to cancel the current pending commands list * considering the new command. * + * Returns the number of optimized commands or a negative error code + * * Must run under exe_queue->lock */ exe_q_optimize optimize; -- cgit v0.10.2 From d5e836329bd836d24b168004827532426cad2f39 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Mon, 23 Jan 2012 07:31:52 +0000 Subject: bnx2x: fixed ethtool statistics for MF modes Previosuly, in MF modes `ethtool -S' lacked some of the statistics which appeared in non-MF modes. This has been fixed. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index f99c6e3..7d32e00 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2121,18 +2121,16 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset) case ETH_SS_STATS: if (is_multi(bp)) { num_stats = bnx2x_num_stat_queues(bp) * - BNX2X_NUM_Q_STATS; - if (!IS_MF_MODE_STAT(bp)) - num_stats += BNX2X_NUM_STATS; - } else { - if (IS_MF_MODE_STAT(bp)) { - num_stats = 0; - for (i = 0; i < BNX2X_NUM_STATS; i++) - if (IS_FUNC_STAT(i)) - num_stats++; - } else - num_stats = BNX2X_NUM_STATS; - } + BNX2X_NUM_Q_STATS; + } else + num_stats = 0; + if (IS_MF_MODE_STAT(bp)) { + for (i = 0; i < BNX2X_NUM_STATS; i++) + if (IS_FUNC_STAT(i)) + num_stats++; + } else + num_stats += BNX2X_NUM_STATS; + return num_stats; case ETH_SS_TEST: @@ -2151,8 +2149,8 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) switch (stringset) { case ETH_SS_STATS: + k = 0; if (is_multi(bp)) { - k = 0; for_each_eth_queue(bp, i) { memset(queue_name, 0, sizeof(queue_name)); sprintf(queue_name, "%d", i); @@ -2163,20 +2161,17 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) queue_name); k += BNX2X_NUM_Q_STATS; } - if (IS_MF_MODE_STAT(bp)) - break; - for (j = 0; j < BNX2X_NUM_STATS; j++) - strcpy(buf + (k + j)*ETH_GSTRING_LEN, - bnx2x_stats_arr[j].string); - } else { - for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) { - if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i)) - continue; - strcpy(buf + j*ETH_GSTRING_LEN, - bnx2x_stats_arr[i].string); - j++; - } } + + + for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) { + if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i)) + continue; + strcpy(buf + (k + j)*ETH_GSTRING_LEN, + bnx2x_stats_arr[i].string); + j++; + } + break; case ETH_SS_TEST: @@ -2190,10 +2185,9 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); u32 *hw_stats, *offset; - int i, j, k; + int i, j, k = 0; if (is_multi(bp)) { - k = 0; for_each_eth_queue(bp, i) { hw_stats = (u32 *)&bp->fp[i].eth_q_stats; for (j = 0; j < BNX2X_NUM_Q_STATS; j++) { @@ -2214,46 +2208,28 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, } k += BNX2X_NUM_Q_STATS; } - if (IS_MF_MODE_STAT(bp)) - return; - hw_stats = (u32 *)&bp->eth_stats; - for (j = 0; j < BNX2X_NUM_STATS; j++) { - if (bnx2x_stats_arr[j].size == 0) { - /* skip this counter */ - buf[k + j] = 0; - continue; - } - offset = (hw_stats + bnx2x_stats_arr[j].offset); - if (bnx2x_stats_arr[j].size == 4) { - /* 4-byte counter */ - buf[k + j] = (u64) *offset; - continue; - } - /* 8-byte counter */ - buf[k + j] = HILO_U64(*offset, *(offset + 1)); + } + + hw_stats = (u32 *)&bp->eth_stats; + for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) { + if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i)) + continue; + if (bnx2x_stats_arr[i].size == 0) { + /* skip this counter */ + buf[k + j] = 0; + j++; + continue; } - } else { - hw_stats = (u32 *)&bp->eth_stats; - for (i = 0, j = 0; i < BNX2X_NUM_STATS; i++) { - if (IS_MF_MODE_STAT(bp) && IS_PORT_STAT(i)) - continue; - if (bnx2x_stats_arr[i].size == 0) { - /* skip this counter */ - buf[j] = 0; - j++; - continue; - } - offset = (hw_stats + bnx2x_stats_arr[i].offset); - if (bnx2x_stats_arr[i].size == 4) { - /* 4-byte counter */ - buf[j] = (u64) *offset; - j++; - continue; - } - /* 8-byte counter */ - buf[j] = HILO_U64(*offset, *(offset + 1)); + offset = (hw_stats + bnx2x_stats_arr[i].offset); + if (bnx2x_stats_arr[i].size == 4) { + /* 4-byte counter */ + buf[k + j] = (u64) *offset; j++; + continue; } + /* 8-byte counter */ + buf[k + j] = HILO_U64(*offset, *(offset + 1)); + j++; } } -- cgit v0.10.2 From b0700b1e6b9556aa99bb9bf6ad6d830ae38344c7 Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Mon, 23 Jan 2012 07:31:53 +0000 Subject: bnx2x: fix Big-Endianess in ethtool -t Signed-off-by: Dmitry Kravkov Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 7d32e00..31a8b38 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1738,7 +1738,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode) struct bnx2x_fp_txdata *txdata = &fp_tx->txdata[0]; u16 tx_start_idx, tx_idx; u16 rx_start_idx, rx_idx; - u16 pkt_prod, bd_prod, rx_comp_cons; + u16 pkt_prod, bd_prod; struct sw_tx_bd *tx_buf; struct eth_tx_start_bd *tx_start_bd; struct eth_tx_parse_bd_e1x *pbd_e1x = NULL; @@ -1873,8 +1873,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode) if (rx_idx != rx_start_idx + num_pkts) goto test_loopback_exit; - rx_comp_cons = le16_to_cpu(fp_rx->rx_comp_cons); - cqe = &fp_rx->rx_comp_ring[RCQ_BD(rx_comp_cons)]; + cqe = &fp_rx->rx_comp_ring[RCQ_BD(fp_rx->rx_comp_cons)]; cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE; if (!CQE_TYPE_FAST(cqe_fp_type) || (cqe_fp_flags & ETH_RX_ERROR_FALGS)) -- cgit v0.10.2 From 1fdf155158886514c82e5401ab7b1264beb375bf Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Mon, 23 Jan 2012 07:31:54 +0000 Subject: bnx2x: allow user to change ring size in ISCSI SD mode Signed-off-by: Dmitry Kravkov Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 2b731b2..03f3935 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3117,7 +3117,7 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index) int rx_ring_size = 0; #ifdef BCM_CNIC - if (IS_MF_ISCSI_SD(bp)) { + if (!bp->rx_ring_size && IS_MF_ISCSI_SD(bp)) { rx_ring_size = MIN_RX_SIZE_NONTPA; bp->rx_ring_size = rx_ring_size; } else -- cgit v0.10.2 From 65087cfee50595185f6bbf3d78272eeb34186d2b Mon Sep 17 00:00:00 2001 From: Ariel Elior Date: Mon, 23 Jan 2012 07:31:55 +0000 Subject: bnx2x: handle CHIP_REVISION during init_one The macro `CHIP_IS_E1x' requires `bp' to be initialized. As `bp' is not yet initialized during this phase of `bnx2x_init_dev', it accessed uninitialized fields in the struct. Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index ffeaaa9..f4c2fe5 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -10536,6 +10536,9 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev, { struct bnx2x *bp; int rc; + bool chip_is_e1x = (board_type == BCM57710 || + board_type == BCM57711 || + board_type == BCM57711E); SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); @@ -10624,7 +10627,7 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev, REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0, 0); REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0, 0); - if (CHIP_IS_E1x(bp)) { + if (chip_is_e1x) { REG_WR(bp, PXP2_REG_PGL_ADDR_88_F1, 0); REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F1, 0); REG_WR(bp, PXP2_REG_PGL_ADDR_90_F1, 0); @@ -10635,9 +10638,7 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev, * Enable internal target-read (in case we are probed after PF FLR). * Must be done prior to any BAR read access. Only for 57712 and up */ - if (board_type != BCM57710 && - board_type != BCM57711 && - board_type != BCM57711E) + if (!chip_is_e1x) REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1); /* Reset the load counter */ -- cgit v0.10.2 From 44151acb9f13563e40d40d14c3e5c11ce21b59e1 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Mon, 23 Jan 2012 07:31:56 +0000 Subject: bnx2x: fix compilation error with SOE in fw_dump Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index f4c2fe5..1e3f978 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -941,7 +941,7 @@ void bnx2x_panic_dump(struct bnx2x *bp) struct sw_rx_bd *sw_bd = &fp->rx_buf_ring[j]; BNX2X_ERR("fp%d: rx_bd[%x]=[%x:%x] sw_bd=[%p]\n", - i, j, rx_bd[1], rx_bd[0], sw_bd->skb); + i, j, rx_bd[1], rx_bd[0], sw_bd->data); } start = RX_SGE(fp->rx_sge_prod); -- cgit v0.10.2 From 302476c99863fe6d08eed6145e37322892ab7f55 Mon Sep 17 00:00:00 2001 From: Paulius Zaleckas Date: Mon, 23 Jan 2012 01:16:35 +0000 Subject: mv643xx_eth: Add Rx Discard and Rx Overrun statistics These statistics helped me a lot while searching who is losing packets in my setup. I added these stats to MIB group since they are very similar, but just in other registers. I have tested this patch on 88F6281 SoC. Signed-off-by: Paulius Zaleckas Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 9c049d2..9edecfa 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -136,6 +136,8 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define INT_MASK 0x0068 #define INT_MASK_EXT 0x006c #define TX_FIFO_URGENT_THRESHOLD 0x0074 +#define RX_DISCARD_FRAME_CNT 0x0084 +#define RX_OVERRUN_FRAME_CNT 0x0088 #define TXQ_FIX_PRIO_CONF_MOVED 0x00dc #define TX_BW_RATE_MOVED 0x00e0 #define TX_BW_MTU_MOVED 0x00e8 @@ -334,6 +336,9 @@ struct mib_counters { u32 bad_crc_event; u32 collision; u32 late_collision; + /* Non MIB hardware counters */ + u32 rx_discard; + u32 rx_overrun; }; struct lro_counters { @@ -1225,6 +1230,10 @@ static void mib_counters_clear(struct mv643xx_eth_private *mp) for (i = 0; i < 0x80; i += 4) mib_read(mp, i); + + /* Clear non MIB hw counters also */ + rdlp(mp, RX_DISCARD_FRAME_CNT); + rdlp(mp, RX_OVERRUN_FRAME_CNT); } static void mib_counters_update(struct mv643xx_eth_private *mp) @@ -1262,6 +1271,9 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) p->bad_crc_event += mib_read(mp, 0x74); p->collision += mib_read(mp, 0x78); p->late_collision += mib_read(mp, 0x7c); + /* Non MIB hardware counters */ + p->rx_discard += rdlp(mp, RX_DISCARD_FRAME_CNT); + p->rx_overrun += rdlp(mp, RX_OVERRUN_FRAME_CNT); spin_unlock_bh(&mp->mib_counters_lock); mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ); @@ -1413,6 +1425,8 @@ static const struct mv643xx_eth_stats mv643xx_eth_stats[] = { MIBSTAT(bad_crc_event), MIBSTAT(collision), MIBSTAT(late_collision), + MIBSTAT(rx_discard), + MIBSTAT(rx_overrun), LROSTAT(lro_aggregated), LROSTAT(lro_flushed), LROSTAT(lro_no_desc), -- cgit v0.10.2 From da057fb7d272c7e7609465a54bcac8ec8072ead5 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sun, 22 Jan 2012 09:40:40 +0000 Subject: skge: add byte queue limit support This also changes the cleanup logic slightly to aggregate completed notifications for multiple packets. Signed-off-by: Stephen Hemminger Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 299c33b..edb9bda 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -2817,6 +2817,8 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, td->control = BMU_OWN | BMU_SW | BMU_STF | control | len; wmb(); + netdev_sent_queue(dev, skb->len); + skge_write8(hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_START); netif_printk(skge, tx_queued, KERN_DEBUG, skge->netdev, @@ -2858,11 +2860,9 @@ mapping_error: /* Free resources associated with this reing element */ -static void skge_tx_free(struct skge_port *skge, struct skge_element *e, - u32 control) +static inline void skge_tx_unmap(struct pci_dev *pdev, struct skge_element *e, + u32 control) { - struct pci_dev *pdev = skge->hw->pdev; - /* skb header vs. fragment */ if (control & BMU_STF) pci_unmap_single(pdev, dma_unmap_addr(e, mapaddr), @@ -2872,13 +2872,6 @@ static void skge_tx_free(struct skge_port *skge, struct skge_element *e, pci_unmap_page(pdev, dma_unmap_addr(e, mapaddr), dma_unmap_len(e, maplen), PCI_DMA_TODEVICE); - - if (control & BMU_EOF) { - netif_printk(skge, tx_done, KERN_DEBUG, skge->netdev, - "tx done slot %td\n", e - skge->tx_ring.start); - - dev_kfree_skb(e->skb); - } } /* Free all buffers in transmit ring */ @@ -2889,10 +2882,15 @@ static void skge_tx_clean(struct net_device *dev) for (e = skge->tx_ring.to_clean; e != skge->tx_ring.to_use; e = e->next) { struct skge_tx_desc *td = e->desc; - skge_tx_free(skge, e, td->control); + + skge_tx_unmap(skge->hw->pdev, e, td->control); + + if (td->control & BMU_EOF) + dev_kfree_skb(e->skb); td->control = 0; } + netdev_reset_queue(dev); skge->tx_ring.to_clean = e; } @@ -3157,6 +3155,7 @@ static void skge_tx_done(struct net_device *dev) struct skge_port *skge = netdev_priv(dev); struct skge_ring *ring = &skge->tx_ring; struct skge_element *e; + unsigned int bytes_compl = 0, pkts_compl = 0; skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F); @@ -3166,8 +3165,20 @@ static void skge_tx_done(struct net_device *dev) if (control & BMU_OWN) break; - skge_tx_free(skge, e, control); + skge_tx_unmap(skge->hw->pdev, e, control); + + if (control & BMU_EOF) { + netif_printk(skge, tx_done, KERN_DEBUG, skge->netdev, + "tx done slot %td\n", + e - skge->tx_ring.start); + + pkts_compl++; + bytes_compl += e->skb->len; + + dev_kfree_skb(e->skb); + } } + netdev_completed_queue(dev, pkts_compl, bytes_compl); skge->tx_ring.to_clean = e; /* Can run lockless until we need to synchronize to restart queue. */ -- cgit v0.10.2 From a5a1195559f2e20bd975f58e50f53ebe84d5cca6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Jan 2012 01:22:09 +0000 Subject: tg3: fix ipv6 header length computation tg3_start_xmit() makes the wrong assumption for TSOV6 that skb->head doesnt include any payload data. if (skb_is_gso_v6(skb)) hdr_len = skb_headlen(skb) - ETH_HLEN; This is not true anymore after commit f07d960df3 (tcp: avoid frag allocation for small frames) We should instead use : skb_transport_offset(skb) + tcp_hdrlen(skb) Its also true for IPv4 Signed-off-by: Eric Dumazet CC: Matt Carlson CC: Michael Chan Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index d529af9..a1f2e0f 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6667,14 +6667,9 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) iph = ip_hdr(skb); tcp_opt_len = tcp_optlen(skb); - if (skb_is_gso_v6(skb)) { - hdr_len = skb_headlen(skb) - ETH_HLEN; - } else { - u32 ip_tcp_len; - - ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); - hdr_len = ip_tcp_len + tcp_opt_len; + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb) - ETH_HLEN; + if (!skb_is_gso_v6(skb)) { iph->check = 0; iph->tot_len = htons(mss + hdr_len); } -- cgit v0.10.2 From d07a74a546981a09ba490936645fbf0d1340b96c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 25 Aug 2011 16:13:55 -0700 Subject: dmaengine: fix missing 'cnt' in ?: in dmatest Hi, On the latest tree my compiler has started giving the warning: drivers/dma/dmatest.c:575:28: warning: the omitted middle operand in ?: will always be ?true?, suggest explicit middle operand [-Wparentheses] The following patch fixes the missing middle clause with the same fix that Nicolas Ferre used in the similar clauses. (There seems to have been a race between him fixing that and the extra clause going in a little later). I don't actually know the dmatest code/structures, nor do I own any hardware to test it on (assuming it needs a DMA engine); but this patch builds, the existing code is almost certainly wrong and the fix is the same as the corresponding lines above it. (WTH is x=y?:z legal C anyway?) Signed-off-by: Dr. David Alan Gilbert Reported-by: Dan Carpenter Reported-by: Paul Gortmaker Acked-by: Nicolas Ferre Signed-off-by: Dan Williams diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 2b8661b..24225f0 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -599,7 +599,7 @@ static int dmatest_add_channel(struct dma_chan *chan) } if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { cnt = dmatest_add_threads(dtc, DMA_PQ); - thread_count += cnt > 0 ?: 0; + thread_count += cnt > 0 ? cnt : 0; } pr_info("dmatest: Started %u threads using %s\n", -- cgit v0.10.2 From edd664bbba53f771d4a6d4559ed6e1ff48b46406 Mon Sep 17 00:00:00 2001 From: Chris Healy Date: Sun, 22 Jan 2012 21:20:54 +0000 Subject: dsa: Add reporting of silicon revision for Marvell 88E6123/88E6161/88E6165 switches. Add reporting of silicon revision during the probe function for Marvell 88E6123/88E6161/88E6165 switches. Signed-off-by: Chris Healy Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index c0a458f..6f23c95 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c @@ -20,12 +20,25 @@ static char *mv88e6123_61_65_probe(struct mii_bus *bus, int sw_addr) ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); if (ret >= 0) { - ret &= 0xfff0; - if (ret == 0x1210) + if (ret == 0x1212) + return "Marvell 88E6123 (A1)"; + if (ret == 0x1213) + return "Marvell 88E6123 (A2)"; + if ((ret & 0xfff0) == 0x1210) return "Marvell 88E6123"; - if (ret == 0x1610) + + if (ret == 0x1612) + return "Marvell 88E6161 (A1)"; + if (ret == 0x1613) + return "Marvell 88E6161 (A2)"; + if ((ret & 0xfff0) == 0x1610) return "Marvell 88E6161"; - if (ret == 0x1650) + + if (ret == 0x1652) + return "Marvell 88E6165 (A1)"; + if (ret == 0x1653) + return "Marvell 88e6165 (A2)"; + if ((ret & 0xfff0) == 0x1650) return "Marvell 88E6165"; } -- cgit v0.10.2 From 9bf31efa84c898a0cf294bacdfe8edcac24e6318 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 18 Jan 2012 13:57:33 -0300 Subject: [media] cxd2820r: fix dvb_frontend_ops Fix bug introduced by multi-frontend to single-frontend change. * Add missing DVB-C caps * Change frontend name as single frontend does all the standards Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index caae7f7..5fe591d 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -562,7 +562,7 @@ static const struct dvb_frontend_ops cxd2820r_ops = { .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, /* default: DVB-T/T2 */ .info = { - .name = "Sony CXD2820R (DVB-T/T2)", + .name = "Sony CXD2820R", .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | @@ -572,7 +572,9 @@ static const struct dvb_frontend_ops cxd2820r_ops = { FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | -- cgit v0.10.2 From c2bbbe7b5e79974c5ed1c828690731f6f5106bee Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 19 Jan 2012 14:46:43 -0300 Subject: [media] cxd2820r: remove unused parameter from cxd2820r_attach Fix bug introduced by multi-frontend to single-frontend change. This parameter is no longer used after multi-frontend to single-frontend change. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index ecc3add..7b77c7b 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -887,8 +887,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) /* attach demod */ adap->fe_adap[state->fe_id].fe = dvb_attach(cxd2820r_attach, - &anysee_cxd2820r_config, &adap->dev->i2c_adap, - NULL); + &anysee_cxd2820r_config, &adap->dev->i2c_adap); state->has_ci = true; diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h index cf0f546..5aa306e 100644 --- a/drivers/media/dvb/frontends/cxd2820r.h +++ b/drivers/media/dvb/frontends/cxd2820r.h @@ -77,14 +77,12 @@ struct cxd2820r_config { (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE)) extern struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, - struct i2c_adapter *i2c, - struct dvb_frontend *fe + struct i2c_adapter *i2c ); #else static inline struct dvb_frontend *cxd2820r_attach( const struct cxd2820r_config *config, - struct i2c_adapter *i2c, - struct dvb_frontend *fe + struct i2c_adapter *i2c ) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index 5fe591d..bdfa207 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -604,8 +604,7 @@ static const struct dvb_frontend_ops cxd2820r_ops = { }; struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, - struct i2c_adapter *i2c, - struct dvb_frontend *fe) + struct i2c_adapter *i2c) { struct cxd2820r_priv *priv = NULL; int ret; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 9449423..aabbf48 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -853,8 +853,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM28174_BOARD_PCTV_290E: dvb->fe[0] = dvb_attach(cxd2820r_attach, &em28xx_cxd2820r_config, - &dev->i2c_adap, - NULL); + &dev->i2c_adap); if (dvb->fe[0]) { /* FE 0 attach tuner */ if (!dvb_attach(tda18271_attach, -- cgit v0.10.2 From 46de20a78ae4b122b79fc02633e9a6c3d539ecad Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 20 Jan 2012 17:39:17 -0300 Subject: [media] anysee: fix CI init No more error that error seen when device is plugged: dvb_ca adapter 0: Invalid PC card inserted :( Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index 7b77c7b..fdee856 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -1188,6 +1188,14 @@ static int anysee_ci_init(struct dvb_usb_device *d) if (ret) return ret; + ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 2)|(0 << 1)|(0 << 0), 0x07); + if (ret) + return ret; + + ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 2)|(1 << 1)|(1 << 0), 0x07); + if (ret) + return ret; + ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1); if (ret) return ret; -- cgit v0.10.2 From 72565224609a23a60d10fcdf42f87a2fa8f7b16d Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 20 Jan 2012 19:48:28 -0300 Subject: [media] cxd2820r: sleep on DVB-T/T2 delivery system switch Fix bug introduced by multi-frontend to single-frontend change. It is safer to put DVB-T parts sleeping when auto-switching to DVB-T2 and vice versa. That was original behaviour. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index bdfa207..5c7c2aa 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -482,10 +482,19 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) /* switch between DVB-T and DVB-T2 when tune fails */ if (priv->last_tune_failed) { - if (priv->delivery_system == SYS_DVBT) + if (priv->delivery_system == SYS_DVBT) { + ret = cxd2820r_sleep_t(fe); + if (ret) + goto error; + c->delivery_system = SYS_DVBT2; - else if (priv->delivery_system == SYS_DVBT2) + } else if (priv->delivery_system == SYS_DVBT2) { + ret = cxd2820r_sleep_t2(fe); + if (ret) + goto error; + c->delivery_system = SYS_DVBT; + } } /* set frontend */ -- cgit v0.10.2 From 03652e0ad4b140523ec5ef7fec8d2b3c7218447b Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 11 Jan 2012 00:58:29 -0300 Subject: [media] V4L: atmel-isi: add clk_prepare()/clk_unprepare() functions Signed-off-by: Josh Wu Acked-by: Nicolas Ferre Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index 9fe4519..ec3f6a0 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -922,7 +922,9 @@ static int __devexit atmel_isi_remove(struct platform_device *pdev) isi->fb_descriptors_phys); iounmap(isi->regs); + clk_unprepare(isi->mck); clk_put(isi->mck); + clk_unprepare(isi->pclk); clk_put(isi->pclk); kfree(isi); @@ -955,6 +957,10 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev) if (IS_ERR(pclk)) return PTR_ERR(pclk); + ret = clk_prepare(pclk); + if (ret) + goto err_clk_prepare_pclk; + isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL); if (!isi) { ret = -ENOMEM; @@ -978,6 +984,10 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev) goto err_clk_get; } + ret = clk_prepare(isi->mck); + if (ret) + goto err_clk_prepare_mck; + /* Set ISI_MCK's frequency, it should be faster than pixel clock */ ret = clk_set_rate(isi->mck, pdata->mck_hz); if (ret < 0) @@ -1059,10 +1069,14 @@ err_alloc_ctx: isi->fb_descriptors_phys); err_alloc_descriptors: err_set_mck_rate: + clk_unprepare(isi->mck); +err_clk_prepare_mck: clk_put(isi->mck); err_clk_get: kfree(isi); err_alloc_isi: + clk_unprepare(pclk); +err_clk_prepare_pclk: clk_put(pclk); return ret; -- cgit v0.10.2 From c79eba92406acc4898adcd1689fc21a6aa91ed0b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 23 Jan 2012 13:15:22 -0200 Subject: [media] cinergyT2-fe: Fix bandwdith settings Changeset 7830bbaff9f mangled the bandwidth field for CinergyT2. Properly fill it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c index 8a57ed8..1efc028 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c @@ -276,14 +276,15 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) param.flags = 0; switch (fep->bandwidth_hz) { + default: case 8000000: - param.bandwidth = 0; + param.bandwidth = 8; break; case 7000000: - param.bandwidth = 1; + param.bandwidth = 7; break; case 6000000: - param.bandwidth = 2; + param.bandwidth = 6; break; } -- cgit v0.10.2 From e4c89a508f4385a0cd8681c2749a2cd2fa476e40 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 23 Jan 2012 21:59:08 +0100 Subject: PM / Sleep: Fix read_unlock_usermodehelper() call. Commit b298d289 "PM / Sleep: Fix freezer failures due to racy usermodehelper_is_disabled()" added read_unlock_usermodehelper() but read_unlock_usermodehelper() is called without read_lock_usermodehelper() when kmalloc() failed. Signed-off-by: Tetsuo Handa Acked-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 26ab358..6c9387d 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -525,8 +525,7 @@ static int _request_firmware(const struct firmware **firmware_p, if (!firmware) { dev_err(device, "%s: kmalloc(struct firmware) failed\n", __func__); - retval = -ENOMEM; - goto out; + return -ENOMEM; } if (fw_get_builtin_firmware(firmware, name)) { -- cgit v0.10.2 From 7edf1a4f27f44588d69cbde955651990090eb25d Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 23 Jan 2012 21:15:48 +0100 Subject: ASoC: wm8958: Use correct format string in dev_err() call To print a value of type size_t one should use %zd, not %d. Signed-off-by: Jesper Juhl Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 8d4ea43..40ac888 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -55,7 +55,7 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name, return 0; if (fw->size < 32) { - dev_err(codec->dev, "%s: firmware too short (%d bytes)\n", + dev_err(codec->dev, "%s: firmware too short (%zd bytes)\n", name, fw->size); goto err; } -- cgit v0.10.2 From 875ad3f8e7dff6bc1d053e5bfe73d8e8d2e6ae67 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 23 Jan 2012 12:49:36 -0500 Subject: SUNRPC: Fix machine creds in generic_create_cred and generic_match - generic_create_cred needs to copy the '.principal' field. - generic_match needs to ignore the groups and match on the '.principal' field. This fixes an Oops that was introduced by commit 68c9715 (SUNRPC: Clean up the RPCSEC_GSS service ticket requests) Reported-by: J. Bruce Fields Signed-off-by: Trond Myklebust Tested-by: J. Bruce Fields diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 1426ec3..75762f3 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -92,6 +92,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) if (gcred->acred.group_info != NULL) get_group_info(gcred->acred.group_info); gcred->acred.machine_cred = acred->machine_cred; + gcred->acred.principal = acred->principal; dprintk("RPC: allocated %s cred %p for uid %d gid %d\n", gcred->acred.machine_cred ? "machine" : "generic", @@ -123,6 +124,17 @@ generic_destroy_cred(struct rpc_cred *cred) call_rcu(&cred->cr_rcu, generic_free_cred_callback); } +static int +machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flags) +{ + if (!gcred->acred.machine_cred || + gcred->acred.principal != acred->principal || + gcred->acred.uid != acred->uid || + gcred->acred.gid != acred->gid) + return 0; + return 1; +} + /* * Match credentials against current process creds. */ @@ -132,9 +144,12 @@ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base); int i; + if (acred->machine_cred) + return machine_cred_match(acred, gcred, flags); + if (gcred->acred.uid != acred->uid || gcred->acred.gid != acred->gid || - gcred->acred.machine_cred != acred->machine_cred) + gcred->acred.machine_cred != 0) goto out_nomatch; /* Optimisation in the case where pointers are identical... */ -- cgit v0.10.2 From 4c40aed869a200a621b53bcb491c5ee8a34ef5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Mon, 9 Jan 2012 16:49:44 +0100 Subject: ACPI, APEI, EINJ Allow empty Trigger Error Action Table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the ACPI spec [1] section 18.6.4 the TRIGGER_ERROR action table can consists of zero elements. [1] Advanced Configuration and Power Interface Specification Revision 5.0, December 6, 2011 http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf Signed-off-by: Niklas Söderlund Acked-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 5b898d4..bb20b03 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -276,7 +276,7 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger)) return -EINVAL; if (trigger_tab->table_size > PAGE_SIZE || - trigger_tab->table_size <= trigger_tab->header_size) + trigger_tab->table_size < trigger_tab->header_size) return -EINVAL; if (trigger_tab->entry_count != (trigger_tab->table_size - trigger_tab->header_size) / @@ -340,6 +340,11 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, "The trigger error action table is invalid\n"); goto out_rel_header; } + + /* No action structures in the TRIGGER_ERROR table, nothing to do */ + if (!trigger_tab->entry_count) + goto out_rel_header; + rc = -EIO; table_size = trigger_tab->table_size; r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), -- cgit v0.10.2 From 29924b9f8f39e37275cff93740835d28b9e6fb36 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 20 Jan 2012 10:57:14 +0300 Subject: ACPI, APEI, EINJ, cleanup 0 vs NULL confusion This function is returning pointers. Sparse complains here: drivers/acpi/apei/einj.c:262:32: warning: Using plain integer as NULL pointer Signed-off-by: Dan Carpenter Acked-by: Huang Ying Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index bb20b03..c89b0e5 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -259,15 +259,15 @@ static void *einj_get_parameter_address(void) v4param = ioremap(paddrv4, sizeof(*v4param)); if (!v4param) - return 0; + return NULL; if (readq(&v4param->reserved1) || readq(&v4param->reserved2)) { iounmap(v4param); - return 0; + return NULL; } return v4param; } - return 0; + return NULL; } /* do sanity check to trigger table */ -- cgit v0.10.2 From 459413db33d6c99fc13d60f88899fb72d2530ed3 Mon Sep 17 00:00:00 2001 From: "Luck, Tony" Date: Mon, 23 Jan 2012 15:27:56 -0800 Subject: Use acpi_os_map_memory() instead of ioremap() in einj driver ioremap() has become more picky and is now spitting out console messages like: ioremap error for 0xbddbd000-0xbddbe000, requested 0x10, got 0x0 when loading the einj driver. What we are trying to so here is map a couple of data structures that the EINJ table points to. Perhaps acpi_os_map_memory() is a better tool for this? Most importantly it works, but as a side benefit it maps the structures into kernel virtual space so we can access them with normal C memory dereferences, so instead of using: writel(param1, &v5param->apicid); we can use the more natural: v5param->apicid = param1; Signed-off-by: Tony Luck Signed-off-by: Len Brown diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index c89b0e5..4ca087d 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -141,21 +141,6 @@ static DEFINE_MUTEX(einj_mutex); static void *einj_param; -#ifndef readq -static inline __u64 readq(volatile void __iomem *addr) -{ - return ((__u64)readl(addr+4) << 32) + readl(addr); -} -#endif - -#ifndef writeq -static inline void writeq(__u64 val, volatile void __iomem *addr) -{ - writel(val, addr); - writel(val >> 32, addr+4); -} -#endif - static void einj_exec_ctx_init(struct apei_exec_context *ctx) { apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), @@ -204,22 +189,21 @@ static int einj_timedout(u64 *t) static void check_vendor_extension(u64 paddr, struct set_error_type_with_address *v5param) { - int offset = readl(&v5param->vendor_extension); + int offset = v5param->vendor_extension; struct vendor_error_type_extension *v; u32 sbdf; if (!offset) return; - v = ioremap(paddr + offset, sizeof(*v)); + v = acpi_os_map_memory(paddr + offset, sizeof(*v)); if (!v) return; - sbdf = readl(&v->pcie_sbdf); + sbdf = v->pcie_sbdf; sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", sbdf >> 24, (sbdf >> 16) & 0xff, (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, - readw(&v->vendor_id), readw(&v->device_id), - readb(&v->rev_id)); - iounmap(v); + v->vendor_id, v->device_id, v->rev_id); + acpi_os_unmap_memory(v, sizeof(*v)); } static void *einj_get_parameter_address(void) @@ -247,7 +231,7 @@ static void *einj_get_parameter_address(void) if (paddrv5) { struct set_error_type_with_address *v5param; - v5param = ioremap(paddrv5, sizeof(*v5param)); + v5param = acpi_os_map_memory(paddrv5, sizeof(*v5param)); if (v5param) { acpi5 = 1; check_vendor_extension(paddrv5, v5param); @@ -257,11 +241,11 @@ static void *einj_get_parameter_address(void) if (paddrv4) { struct einj_parameter *v4param; - v4param = ioremap(paddrv4, sizeof(*v4param)); + v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param)); if (!v4param) return NULL; - if (readq(&v4param->reserved1) || readq(&v4param->reserved2)) { - iounmap(v4param); + if (v4param->reserved1 || v4param->reserved2) { + acpi_os_unmap_memory(v4param, sizeof(*v4param)); return NULL; } return v4param; @@ -440,41 +424,41 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) if (acpi5) { struct set_error_type_with_address *v5param = einj_param; - writel(type, &v5param->type); + v5param->type = type; if (type & 0x80000000) { switch (vendor_flags) { case SETWA_FLAGS_APICID: - writel(param1, &v5param->apicid); + v5param->apicid = param1; break; case SETWA_FLAGS_MEM: - writeq(param1, &v5param->memory_address); - writeq(param2, &v5param->memory_address_range); + v5param->memory_address = param1; + v5param->memory_address_range = param2; break; case SETWA_FLAGS_PCIE_SBDF: - writel(param1, &v5param->pcie_sbdf); + v5param->pcie_sbdf = param1; break; } - writel(vendor_flags, &v5param->flags); + v5param->flags = vendor_flags; } else { switch (type) { case ACPI_EINJ_PROCESSOR_CORRECTABLE: case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: case ACPI_EINJ_PROCESSOR_FATAL: - writel(param1, &v5param->apicid); - writel(SETWA_FLAGS_APICID, &v5param->flags); + v5param->apicid = param1; + v5param->flags = SETWA_FLAGS_APICID; break; case ACPI_EINJ_MEMORY_CORRECTABLE: case ACPI_EINJ_MEMORY_UNCORRECTABLE: case ACPI_EINJ_MEMORY_FATAL: - writeq(param1, &v5param->memory_address); - writeq(param2, &v5param->memory_address_range); - writel(SETWA_FLAGS_MEM, &v5param->flags); + v5param->memory_address = param1; + v5param->memory_address_range = param2; + v5param->flags = SETWA_FLAGS_MEM; break; case ACPI_EINJ_PCIX_CORRECTABLE: case ACPI_EINJ_PCIX_UNCORRECTABLE: case ACPI_EINJ_PCIX_FATAL: - writel(param1, &v5param->pcie_sbdf); - writel(SETWA_FLAGS_PCIE_SBDF, &v5param->flags); + v5param->pcie_sbdf = param1; + v5param->flags = SETWA_FLAGS_PCIE_SBDF; break; } } @@ -484,8 +468,8 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) return rc; if (einj_param) { struct einj_parameter *v4param = einj_param; - writeq(param1, &v4param->param1); - writeq(param2, &v4param->param2); + v4param->param1 = param1; + v4param->param2 = param2; } } rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); @@ -736,8 +720,13 @@ static int __init einj_init(void) return 0; err_unmap: - if (einj_param) - iounmap(einj_param); + if (einj_param) { + acpi_size size = (acpi5) ? + sizeof(struct set_error_type_with_address) : + sizeof(struct einj_parameter); + + acpi_os_unmap_memory(einj_param, size); + } apei_exec_post_unmap_gars(&ctx); err_release: apei_resources_release(&einj_resources); @@ -753,8 +742,13 @@ static void __exit einj_exit(void) { struct apei_exec_context ctx; - if (einj_param) - iounmap(einj_param); + if (einj_param) { + acpi_size size = (acpi5) ? + sizeof(struct set_error_type_with_address) : + sizeof(struct einj_parameter); + + acpi_os_unmap_memory(einj_param, size); + } einj_exec_ctx_init(&ctx); apei_exec_post_unmap_gars(&ctx); apei_resources_release(&einj_resources); -- cgit v0.10.2 From c1aab02dac690af7ff634d8e1cb3be6a04387eef Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 24 Jan 2012 11:41:32 +1100 Subject: migrate_mode.h is not exported to user mode so move its include into fs.h inside the __KERNEL__ protection. Signed-off-by: Stephen Rothwell Signed-off-by: Linus Torvalds diff --git a/include/linux/fs.h b/include/linux/fs.h index 4b3a41f..386da09 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -10,7 +10,6 @@ #include #include #include -#include /* * It's silly to have NR_OPEN bigger than NR_FILE, but you can change @@ -397,6 +396,7 @@ struct inodes_stat_t { #include #include #include +#include #include -- cgit v0.10.2 From f02432571ad52fcfb7e7c676d81f902a0351a8af Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Thu, 22 Dec 2011 14:03:12 +0000 Subject: ARM: tegra: dma: fix buildbreak for !CONFIG_TEGRA_SYSTEM_DMA There's no need to keep the DMA_REQ_SEL defines inside the ifdef. Fixes the following build break with CONFIG_TEGRA_SYSTEM_DMA=n: arch/arm/mach-tegra/devices.c:645: error: 'TEGRA_DMA_REQ_SEL_I2S_1' undeclared here (not in a function) arch/arm/mach-tegra/devices.c:663: error: 'TEGRA_DMA_REQ_SEL_I2S2_1' undeclared here (not in a function) arch/arm/mach-tegra/devices.c:663: error: initializer element is not constant arch/arm/mach-tegra/devices.c:663: error: (near initialization for 'i2s_resource2[1].start') arch/arm/mach-tegra/devices.c:664: error: initializer element is not constant arch/arm/mach-tegra/devices.c:664: error: (near initialization for 'i2s_resource2[1].end') Signed-off-by: Olof Johansson Acked-by: Stephen Warren diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h index d0132e8..3c9339058 100644 --- a/arch/arm/mach-tegra/include/mach/dma.h +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -23,11 +23,6 @@ #include -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - -struct tegra_dma_req; -struct tegra_dma_channel; - #define TEGRA_DMA_REQ_SEL_CNTR 0 #define TEGRA_DMA_REQ_SEL_I2S_2 1 #define TEGRA_DMA_REQ_SEL_I2S_1 2 @@ -56,6 +51,11 @@ struct tegra_dma_channel; #define TEGRA_DMA_REQ_SEL_OWR 25 #define TEGRA_DMA_REQ_SEL_INVALID 31 +#if defined(CONFIG_TEGRA_SYSTEM_DMA) + +struct tegra_dma_req; +struct tegra_dma_channel; + enum tegra_dma_mode { TEGRA_DMA_SHARED = 1, TEGRA_DMA_MODE_CONTINOUS = 2, -- cgit v0.10.2 From c83f1d7e71625801c72f4013291194e09b6f0a6e Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 23 Jan 2012 22:28:44 +0100 Subject: ASoC: wm2000: Fix use-after-free - don't release_firmware() twice on error In wm2000_i2c_probe(), if we take the true branch in " ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0); if (ret != 0) goto err_fw; " then we'll release_firmware(fw) at the 'err_fw' label. But we've already done that just a few lines above. That's a use-after-free bug. This patch restructures the code so that we always call release_firmware(fw) before leaving the function, but only ever call it once. This means that we have to initialize 'fw' to NULL since some paths may now end up calling it without having called request_firmware(), but since request_firmware() deals gracefully with NULL pointers, we are fine if we just NULL initialize it. Signed-off-by: Jesper Juhl Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index c288090..a75c376 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -733,8 +733,9 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, struct wm2000_priv *wm2000; struct wm2000_platform_data *pdata; const char *filename; - const struct firmware *fw; - int reg, ret; + const struct firmware *fw = NULL; + int ret; + int reg; u16 id; wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv), @@ -751,7 +752,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(wm2000->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto err; + goto out; } /* Verify that this is a WM2000 */ @@ -763,7 +764,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, if (id != 0x2000) { dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id); ret = -ENODEV; - goto err_regmap; + goto out_regmap_exit; } reg = wm2000_read(i2c, WM2000_REG_REVISON); @@ -782,7 +783,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, ret = request_firmware(&fw, filename, &i2c->dev); if (ret != 0) { dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret); - goto err_regmap; + goto out_regmap_exit; } /* Pre-cook the concatenation of the register address onto the image */ @@ -793,15 +794,13 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, if (wm2000->anc_download == NULL) { dev_err(&i2c->dev, "Out of memory\n"); ret = -ENOMEM; - goto err_fw; + goto out_regmap_exit; } wm2000->anc_download[0] = 0x80; wm2000->anc_download[1] = 0x00; memcpy(wm2000->anc_download + 2, fw->data, fw->size); - release_firmware(fw); - wm2000->anc_eng_ena = 1; wm2000->anc_active = 1; wm2000->spk_ena = 1; @@ -809,18 +808,14 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, wm2000_reset(wm2000); - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, - NULL, 0); - if (ret != 0) - goto err_fw; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0); + if (!ret) + goto out; - return 0; - -err_fw: - release_firmware(fw); -err_regmap: +out_regmap_exit: regmap_exit(wm2000->regmap); -err: +out: + release_firmware(fw); return ret; } -- cgit v0.10.2 From 68d8a781575d7be490f97eb2c403fb13b083da6a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 29 Dec 2011 06:32:29 +0200 Subject: usb: dwc3: ep0: tidy up Pending Request handling The way our code was written, we should never have a DWC3_EP_PENDING_REQUEST flag set out of a Data Phase and the code in __dwc3_gadget_ep0_queue() did not reflect that situation properly. Tidy up that case to avoid any possible mistakes when starting requests for IRQs which are long gone. Cc: stable@vger.kernel.org Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 2f51de5..74a3828 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -149,20 +149,14 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, direction = !!(dep->flags & DWC3_EP0_DIR_IN); - if (dwc->ep0state == EP0_STATUS_PHASE) { - type = dwc->three_stage_setup - ? DWC3_TRBCTL_CONTROL_STATUS3 - : DWC3_TRBCTL_CONTROL_STATUS2; - } else if (dwc->ep0state == EP0_DATA_PHASE) { - type = DWC3_TRBCTL_CONTROL_DATA; - } else { - /* should never happen */ - WARN_ON(1); + if (dwc->ep0state != EP0_DATA_PHASE) { + dev_WARN(dwc->dev, "Unexpected pending request\n"); return 0; } ret = dwc3_ep0_start_trans(dwc, direction, - req->request.dma, req->request.length, type); + req->request.dma, req->request.length, + DWC3_TRBCTL_CONTROL_DATA); dep->flags &= ~(DWC3_EP_PENDING_REQUEST | DWC3_EP0_DIR_IN); } else if (dwc->delayed_status) { -- cgit v0.10.2 From c1084a56da255ef5385c0f587e16fdc225a5460f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 21 Dec 2011 10:19:38 +0200 Subject: usb: otg: kill langwell_otg driver The way this driver was added by f0ae849 (usb: Add Intel Langwell USB OTG Transceiver Driver) never even compiled together with langwell_udc, and that's the only way for it to be useful. Signed-off-by: Alexander Shishkin Cc: stable@vger.kernel.org # v2.6.31+ Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Cc: Alan Cox Cc: linux-usb@vger.kernel.org Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 2a25955..9105c28 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -86,20 +86,6 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. -config USB_LANGWELL_OTG - tristate "Intel Langwell USB OTG dual-role support" - depends on USB && PCI && INTEL_SCU_IPC - select USB_OTG - select USB_OTG_UTILS - help - Say Y here if you want to build Intel Langwell USB OTG - transciever driver in kernel. This driver implements role - switch between EHCI host driver and Langwell USB OTG - client driver. - - To compile this driver as a module, choose M here: the - module will be called langwell_otg. - config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" depends on (USB || USB_GADGET) && ARCH_MSM diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index b2c5a95..41aa509 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o -obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c deleted file mode 100644 index f08f784..0000000 --- a/drivers/usb/otg/langwell_otg.c +++ /dev/null @@ -1,2347 +0,0 @@ -/* - * Intel Langwell USB OTG transceiver driver - * Copyright (C) 2008 - 2010, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -/* This driver helps to switch Langwell OTG controller function between host - * and peripheral. It works with EHCI driver and Langwell client controller - * driver together. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" -#define DRIVER_VERSION "July 10, 2010" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Henry Yuan , Hao Wu "); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); - -static const char driver_name[] = "langwell_otg"; - -static int langwell_otg_probe(struct pci_dev *pdev, - const struct pci_device_id *id); -static void langwell_otg_remove(struct pci_dev *pdev); -static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); -static int langwell_otg_resume(struct pci_dev *pdev); - -static int langwell_otg_set_host(struct otg_transceiver *otg, - struct usb_bus *host); -static int langwell_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget); -static int langwell_otg_start_srp(struct otg_transceiver *otg); - -static const struct pci_device_id pci_ids[] = {{ - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x8086, - .device = 0x0811, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { /* end: all zeroes */ } -}; - -static struct pci_driver otg_pci_driver = { - .name = (char *) driver_name, - .id_table = pci_ids, - - .probe = langwell_otg_probe, - .remove = langwell_otg_remove, - - .suspend = langwell_otg_suspend, - .resume = langwell_otg_resume, -}; - -/* HSM timers */ -static inline struct langwell_otg_timer *otg_timer_initializer -(void (*function)(unsigned long), unsigned long expires, unsigned long data) -{ - struct langwell_otg_timer *timer; - timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); - if (timer == NULL) - return timer; - - timer->function = function; - timer->expires = expires; - timer->data = data; - return timer; -} - -static struct langwell_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr, - *b_se0_srp_tmr, *b_srp_init_tmr; - -static struct list_head active_timers; - -static struct langwell_otg *the_transceiver; - -/* host/client notify transceiver when event affects HNP state */ -void langwell_update_transceiver(void) -{ - struct langwell_otg *lnw = the_transceiver; - - dev_dbg(lnw->dev, "transceiver is updated\n"); - - if (!lnw->qwork) - return ; - - queue_work(lnw->qwork, &lnw->work); -} -EXPORT_SYMBOL(langwell_update_transceiver); - -static int langwell_otg_set_host(struct otg_transceiver *otg, - struct usb_bus *host) -{ - otg->host = host; - - return 0; -} - -static int langwell_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - - return 0; -} - -static int langwell_otg_set_power(struct otg_transceiver *otg, - unsigned mA) -{ - return 0; -} - -/* A-device drives vbus, controlled through IPC commands */ -static int langwell_otg_set_vbus(struct otg_transceiver *otg, bool enabled) -{ - struct langwell_otg *lnw = the_transceiver; - u8 sub_id; - - dev_dbg(lnw->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off"); - - if (enabled) - sub_id = 0x8; /* Turn on the VBus */ - else - sub_id = 0x9; /* Turn off the VBus */ - - if (intel_scu_ipc_simple_command(0xef, sub_id)) { - dev_dbg(lnw->dev, "Failed to set Vbus via IPC commands\n"); - return -EBUSY; - } - - dev_dbg(lnw->dev, "%s --->\n", __func__); - - return 0; -} - -/* charge vbus or discharge vbus through a resistor to ground */ -static void langwell_otg_chrg_vbus(int on) -{ - struct langwell_otg *lnw = the_transceiver; - u32 val; - - val = readl(lnw->iotg.base + CI_OTGSC); - - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, - lnw->iotg.base + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, - lnw->iotg.base + CI_OTGSC); -} - -/* Start SRP */ -static int langwell_otg_start_srp(struct otg_transceiver *otg) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 val; - - dev_dbg(lnw->dev, "%s --->\n", __func__); - - val = readl(iotg->base + CI_OTGSC); - - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, - iotg->base + CI_OTGSC); - - /* Check if the data plus is finished or not */ - msleep(8); - val = readl(iotg->base + CI_OTGSC); - if (val & (OTGSC_HADP | OTGSC_DP)) - dev_dbg(lnw->dev, "DataLine SRP Error\n"); - - /* Disable interrupt - b_sess_vld */ - val = readl(iotg->base + CI_OTGSC); - val &= (~(OTGSC_BSVIE | OTGSC_BSEIE)); - writel(val, iotg->base + CI_OTGSC); - - /* Start VBus SRP, drive vbus to generate VBus pulse */ - iotg->otg.set_vbus(&iotg->otg, true); - msleep(15); - iotg->otg.set_vbus(&iotg->otg, false); - - /* Enable interrupt - b_sess_vld*/ - val = readl(iotg->base + CI_OTGSC); - dev_dbg(lnw->dev, "after VBUS pulse otgsc = %x\n", val); - - val |= (OTGSC_BSVIE | OTGSC_BSEIE); - writel(val, iotg->base + CI_OTGSC); - - /* If Vbus is valid, then update the hsm */ - if (val & OTGSC_BSV) { - dev_dbg(lnw->dev, "no b_sess_vld interrupt\n"); - - lnw->iotg.hsm.b_sess_vld = 1; - langwell_update_transceiver(); - } - - dev_dbg(lnw->dev, "%s <---\n", __func__); - return 0; -} - -/* stop SOF via bus_suspend */ -static void langwell_otg_loc_sof(int on) -{ - struct langwell_otg *lnw = the_transceiver; - struct usb_hcd *hcd; - int err; - - dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "suspend" : "resume"); - - hcd = bus_to_hcd(lnw->iotg.otg.host); - if (on) - err = hcd->driver->bus_resume(hcd); - else - err = hcd->driver->bus_suspend(hcd); - - if (err) - dev_dbg(lnw->dev, "Fail to resume/suspend USB bus - %d\n", err); - - dev_dbg(lnw->dev, "%s <---\n", __func__); -} - -static int langwell_otg_check_otgsc(void) -{ - struct langwell_otg *lnw = the_transceiver; - u32 otgsc, usbcfg; - - dev_dbg(lnw->dev, "check sync OTGSC and USBCFG registers\n"); - - otgsc = readl(lnw->iotg.base + CI_OTGSC); - usbcfg = readl(lnw->usbcfg); - - dev_dbg(lnw->dev, "OTGSC = %08x, USBCFG = %08x\n", - otgsc, usbcfg); - dev_dbg(lnw->dev, "OTGSC_AVV = %d\n", !!(otgsc & OTGSC_AVV)); - dev_dbg(lnw->dev, "USBCFG.VBUSVAL = %d\n", - !!(usbcfg & USBCFG_VBUSVAL)); - dev_dbg(lnw->dev, "OTGSC_ASV = %d\n", !!(otgsc & OTGSC_ASV)); - dev_dbg(lnw->dev, "USBCFG.AVALID = %d\n", - !!(usbcfg & USBCFG_AVALID)); - dev_dbg(lnw->dev, "OTGSC_BSV = %d\n", !!(otgsc & OTGSC_BSV)); - dev_dbg(lnw->dev, "USBCFG.BVALID = %d\n", - !!(usbcfg & USBCFG_BVALID)); - dev_dbg(lnw->dev, "OTGSC_BSE = %d\n", !!(otgsc & OTGSC_BSE)); - dev_dbg(lnw->dev, "USBCFG.SESEND = %d\n", - !!(usbcfg & USBCFG_SESEND)); - - /* Check USBCFG VBusValid/AValid/BValid/SessEnd */ - if (!!(otgsc & OTGSC_AVV) ^ !!(usbcfg & USBCFG_VBUSVAL)) { - dev_dbg(lnw->dev, "OTGSC.AVV != USBCFG.VBUSVAL\n"); - goto err; - } - if (!!(otgsc & OTGSC_ASV) ^ !!(usbcfg & USBCFG_AVALID)) { - dev_dbg(lnw->dev, "OTGSC.ASV != USBCFG.AVALID\n"); - goto err; - } - if (!!(otgsc & OTGSC_BSV) ^ !!(usbcfg & USBCFG_BVALID)) { - dev_dbg(lnw->dev, "OTGSC.BSV != USBCFG.BVALID\n"); - goto err; - } - if (!!(otgsc & OTGSC_BSE) ^ !!(usbcfg & USBCFG_SESEND)) { - dev_dbg(lnw->dev, "OTGSC.BSE != USBCFG.SESSEN\n"); - goto err; - } - - dev_dbg(lnw->dev, "OTGSC and USBCFG are synced\n"); - - return 0; - -err: - dev_warn(lnw->dev, "OTGSC isn't equal to USBCFG\n"); - return -EPIPE; -} - - -static void langwell_otg_phy_low_power(int on) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u8 val, phcd; - int retval; - - dev_dbg(lnw->dev, "%s ---> %s mode\n", - __func__, on ? "Low power" : "Normal"); - - phcd = 0x40; - - val = readb(iotg->base + CI_HOSTPC1 + 2); - - if (on) { - /* Due to hardware issue, after set PHCD, sync will failed - * between USBCFG and OTGSC, so before set PHCD, check if - * sync is in process now. If the answer is "yes", then do - * not touch PHCD bit */ - retval = langwell_otg_check_otgsc(); - if (retval) { - dev_dbg(lnw->dev, "Skip PHCD programming..\n"); - return ; - } - - writeb(val | phcd, iotg->base + CI_HOSTPC1 + 2); - } else - writeb(val & ~phcd, iotg->base + CI_HOSTPC1 + 2); - - dev_dbg(lnw->dev, "%s <--- done\n", __func__); -} - -/* After drv vbus, add 5 ms delay to set PHCD */ -static void langwell_otg_phy_low_power_wait(int on) -{ - struct langwell_otg *lnw = the_transceiver; - - dev_dbg(lnw->dev, "add 5ms delay before programing PHCD\n"); - - mdelay(5); - langwell_otg_phy_low_power(on); -} - -/* Enable/Disable OTG interrupt */ -static void langwell_otg_intr(int on) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 val; - - dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); - - val = readl(iotg->base + CI_OTGSC); - - /* OTGSC_INT_MASK doesn't contains 1msInt */ - if (on) { - val = val | (OTGSC_INT_MASK); - writel(val, iotg->base + CI_OTGSC); - } else { - val = val & ~(OTGSC_INT_MASK); - writel(val, iotg->base + CI_OTGSC); - } - - dev_dbg(lnw->dev, "%s <---\n", __func__); -} - -/* set HAAR: Hardware Assist Auto-Reset */ -static void langwell_otg_HAAR(int on) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 val; - - dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); - - val = readl(iotg->base + CI_OTGSC); - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, - iotg->base + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, - iotg->base + CI_OTGSC); - - dev_dbg(lnw->dev, "%s <---\n", __func__); -} - -/* set HABA: Hardware Assist B-Disconnect to A-Connect */ -static void langwell_otg_HABA(int on) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 val; - - dev_dbg(lnw->dev, "%s ---> %s\n", __func__, on ? "on" : "off"); - - val = readl(iotg->base + CI_OTGSC); - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, - iotg->base + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, - iotg->base + CI_OTGSC); - - dev_dbg(lnw->dev, "%s <---\n", __func__); -} - -static int langwell_otg_check_se0_srp(int on) -{ - struct langwell_otg *lnw = the_transceiver; - int delay_time = TB_SE0_SRP * 10; - u32 val; - - dev_dbg(lnw->dev, "%s --->\n", __func__); - - do { - udelay(100); - if (!delay_time--) - break; - val = readl(lnw->iotg.base + CI_PORTSC1); - val &= PORTSC_LS; - } while (!val); - - dev_dbg(lnw->dev, "%s <---\n", __func__); - return val; -} - -/* The timeout callback function to set time out bit */ -static void set_tmout(unsigned long indicator) -{ - *(int *)indicator = 1; -} - -void langwell_otg_nsf_msg(unsigned long indicator) -{ - struct langwell_otg *lnw = the_transceiver; - - switch (indicator) { - case 2: - case 4: - case 6: - case 7: - dev_warn(lnw->dev, - "OTG:NSF-%lu - deivce not responding\n", indicator); - break; - case 3: - dev_warn(lnw->dev, - "OTG:NSF-%lu - deivce not supported\n", indicator); - break; - default: - dev_warn(lnw->dev, "Do not have this kind of NSF\n"); - break; - } -} - -/* Initialize timers */ -static int langwell_otg_init_timers(struct otg_hsm *hsm) -{ - /* HSM used timers */ - a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, - (unsigned long)&hsm->a_wait_vrise_tmout); - if (a_wait_vrise_tmr == NULL) - return -ENOMEM; - a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, - (unsigned long)&hsm->a_aidl_bdis_tmout); - if (a_aidl_bdis_tmr == NULL) - return -ENOMEM; - b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, - (unsigned long)&hsm->b_se0_srp); - if (b_se0_srp_tmr == NULL) - return -ENOMEM; - b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT, - (unsigned long)&hsm->b_srp_init_tmout); - if (b_srp_init_tmr == NULL) - return -ENOMEM; - - return 0; -} - -/* Free timers */ -static void langwell_otg_free_timers(void) -{ - kfree(a_wait_vrise_tmr); - kfree(a_aidl_bdis_tmr); - kfree(b_se0_srp_tmr); - kfree(b_srp_init_tmr); -} - -/* The timeout callback function to set time out bit */ -static void langwell_otg_timer_fn(unsigned long indicator) -{ - struct langwell_otg *lnw = the_transceiver; - - *(int *)indicator = 1; - - dev_dbg(lnw->dev, "kernel timer - timeout\n"); - - langwell_update_transceiver(); -} - -/* kernel timer used instead of HW based interrupt */ -static void langwell_otg_add_ktimer(enum langwell_otg_timer_type timers) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - unsigned long j = jiffies; - unsigned long data, time; - - switch (timers) { - case TA_WAIT_VRISE_TMR: - iotg->hsm.a_wait_vrise_tmout = 0; - data = (unsigned long)&iotg->hsm.a_wait_vrise_tmout; - time = TA_WAIT_VRISE; - break; - case TA_WAIT_BCON_TMR: - iotg->hsm.a_wait_bcon_tmout = 0; - data = (unsigned long)&iotg->hsm.a_wait_bcon_tmout; - time = TA_WAIT_BCON; - break; - case TA_AIDL_BDIS_TMR: - iotg->hsm.a_aidl_bdis_tmout = 0; - data = (unsigned long)&iotg->hsm.a_aidl_bdis_tmout; - time = TA_AIDL_BDIS; - break; - case TB_ASE0_BRST_TMR: - iotg->hsm.b_ase0_brst_tmout = 0; - data = (unsigned long)&iotg->hsm.b_ase0_brst_tmout; - time = TB_ASE0_BRST; - break; - case TB_SRP_INIT_TMR: - iotg->hsm.b_srp_init_tmout = 0; - data = (unsigned long)&iotg->hsm.b_srp_init_tmout; - time = TB_SRP_INIT; - break; - case TB_SRP_FAIL_TMR: - iotg->hsm.b_srp_fail_tmout = 0; - data = (unsigned long)&iotg->hsm.b_srp_fail_tmout; - time = TB_SRP_FAIL; - break; - case TB_BUS_SUSPEND_TMR: - iotg->hsm.b_bus_suspend_tmout = 0; - data = (unsigned long)&iotg->hsm.b_bus_suspend_tmout; - time = TB_BUS_SUSPEND; - break; - default: - dev_dbg(lnw->dev, "unknown timer, cannot enable it\n"); - return; - } - - lnw->hsm_timer.data = data; - lnw->hsm_timer.function = langwell_otg_timer_fn; - lnw->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */ - - add_timer(&lnw->hsm_timer); - - dev_dbg(lnw->dev, "add timer successfully\n"); -} - -/* Add timer to timer list */ -static void langwell_otg_add_timer(void *gtimer) -{ - struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; - struct langwell_otg_timer *tmp_timer; - struct intel_mid_otg_xceiv *iotg = &the_transceiver->iotg; - u32 val32; - - /* Check if the timer is already in the active list, - * if so update timer count - */ - list_for_each_entry(tmp_timer, &active_timers, list) - if (tmp_timer == timer) { - timer->count = timer->expires; - return; - } - timer->count = timer->expires; - - if (list_empty(&active_timers)) { - val32 = readl(iotg->base + CI_OTGSC); - writel(val32 | OTGSC_1MSE, iotg->base + CI_OTGSC); - } - - list_add_tail(&timer->list, &active_timers); -} - -/* Remove timer from the timer list; clear timeout status */ -static void langwell_otg_del_timer(void *gtimer) -{ - struct langwell_otg *lnw = the_transceiver; - struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; - struct langwell_otg_timer *tmp_timer, *del_tmp; - u32 val32; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) - if (tmp_timer == timer) - list_del(&timer->list); - - if (list_empty(&active_timers)) { - val32 = readl(lnw->iotg.base + CI_OTGSC); - writel(val32 & ~OTGSC_1MSE, lnw->iotg.base + CI_OTGSC); - } -} - -/* Reduce timer count by 1, and find timeout conditions.*/ -static int langwell_otg_tick_timer(u32 *int_sts) -{ - struct langwell_otg *lnw = the_transceiver; - struct langwell_otg_timer *tmp_timer, *del_tmp; - int expired = 0; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { - tmp_timer->count--; - /* check if timer expires */ - if (!tmp_timer->count) { - list_del(&tmp_timer->list); - tmp_timer->function(tmp_timer->data); - expired = 1; - } - } - - if (list_empty(&active_timers)) { - dev_dbg(lnw->dev, "tick timer: disable 1ms int\n"); - *int_sts = *int_sts & ~OTGSC_1MSE; - } - return expired; -} - -static void reset_otg(void) -{ - struct langwell_otg *lnw = the_transceiver; - int delay_time = 1000; - u32 val; - - dev_dbg(lnw->dev, "reseting OTG controller ...\n"); - val = readl(lnw->iotg.base + CI_USBCMD); - writel(val | USBCMD_RST, lnw->iotg.base + CI_USBCMD); - do { - udelay(100); - if (!delay_time--) - dev_dbg(lnw->dev, "reset timeout\n"); - val = readl(lnw->iotg.base + CI_USBCMD); - val &= USBCMD_RST; - } while (val != 0); - dev_dbg(lnw->dev, "reset done.\n"); -} - -static void set_host_mode(void) -{ - struct langwell_otg *lnw = the_transceiver; - u32 val; - - reset_otg(); - val = readl(lnw->iotg.base + CI_USBMODE); - val = (val & (~USBMODE_CM)) | USBMODE_HOST; - writel(val, lnw->iotg.base + CI_USBMODE); -} - -static void set_client_mode(void) -{ - struct langwell_otg *lnw = the_transceiver; - u32 val; - - reset_otg(); - val = readl(lnw->iotg.base + CI_USBMODE); - val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; - writel(val, lnw->iotg.base + CI_USBMODE); -} - -static void init_hsm(void) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 val32; - - /* read OTGSC after reset */ - val32 = readl(lnw->iotg.base + CI_OTGSC); - dev_dbg(lnw->dev, "%s: OTGSC init value = 0x%x\n", __func__, val32); - - /* set init state */ - if (val32 & OTGSC_ID) { - iotg->hsm.id = 1; - iotg->otg.default_a = 0; - set_client_mode(); - iotg->otg.state = OTG_STATE_B_IDLE; - } else { - iotg->hsm.id = 0; - iotg->otg.default_a = 1; - set_host_mode(); - iotg->otg.state = OTG_STATE_A_IDLE; - } - - /* set session indicator */ - if (val32 & OTGSC_BSE) - iotg->hsm.b_sess_end = 1; - if (val32 & OTGSC_BSV) - iotg->hsm.b_sess_vld = 1; - if (val32 & OTGSC_ASV) - iotg->hsm.a_sess_vld = 1; - if (val32 & OTGSC_AVV) - iotg->hsm.a_vbus_vld = 1; - - /* defautly power the bus */ - iotg->hsm.a_bus_req = 1; - iotg->hsm.a_bus_drop = 0; - /* defautly don't request bus as B device */ - iotg->hsm.b_bus_req = 0; - /* no system error */ - iotg->hsm.a_clr_err = 0; - - langwell_otg_phy_low_power_wait(1); -} - -static void update_hsm(void) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 val32; - - /* read OTGSC */ - val32 = readl(lnw->iotg.base + CI_OTGSC); - dev_dbg(lnw->dev, "%s: OTGSC value = 0x%x\n", __func__, val32); - - iotg->hsm.id = !!(val32 & OTGSC_ID); - iotg->hsm.b_sess_end = !!(val32 & OTGSC_BSE); - iotg->hsm.b_sess_vld = !!(val32 & OTGSC_BSV); - iotg->hsm.a_sess_vld = !!(val32 & OTGSC_ASV); - iotg->hsm.a_vbus_vld = !!(val32 & OTGSC_AVV); -} - -static irqreturn_t otg_dummy_irq(int irq, void *_dev) -{ - struct langwell_otg *lnw = the_transceiver; - void __iomem *reg_base = _dev; - u32 val; - u32 int_mask = 0; - - val = readl(reg_base + CI_USBMODE); - if ((val & USBMODE_CM) != USBMODE_DEVICE) - return IRQ_NONE; - - val = readl(reg_base + CI_USBSTS); - int_mask = val & INTR_DUMMY_MASK; - - if (int_mask == 0) - return IRQ_NONE; - - /* clear hsm.b_conn here since host driver can't detect it - * otg_dummy_irq called means B-disconnect happened. - */ - if (lnw->iotg.hsm.b_conn) { - lnw->iotg.hsm.b_conn = 0; - if (spin_trylock(&lnw->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&lnw->wq_lock); - } - } - - /* Clear interrupts */ - writel(int_mask, reg_base + CI_USBSTS); - return IRQ_HANDLED; -} - -static irqreturn_t otg_irq(int irq, void *_dev) -{ - struct langwell_otg *lnw = _dev; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - u32 int_sts, int_en; - u32 int_mask = 0; - int flag = 0; - - int_sts = readl(lnw->iotg.base + CI_OTGSC); - int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; - int_mask = int_sts & int_en; - if (int_mask == 0) - return IRQ_NONE; - - if (int_mask & OTGSC_IDIS) { - dev_dbg(lnw->dev, "%s: id change int\n", __func__); - iotg->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; - dev_dbg(lnw->dev, "id = %d\n", iotg->hsm.id); - flag = 1; - } - if (int_mask & OTGSC_DPIS) { - dev_dbg(lnw->dev, "%s: data pulse int\n", __func__); - iotg->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; - dev_dbg(lnw->dev, "data pulse = %d\n", iotg->hsm.a_srp_det); - flag = 1; - } - if (int_mask & OTGSC_BSEIS) { - dev_dbg(lnw->dev, "%s: b session end int\n", __func__); - iotg->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; - dev_dbg(lnw->dev, "b_sess_end = %d\n", iotg->hsm.b_sess_end); - flag = 1; - } - if (int_mask & OTGSC_BSVIS) { - dev_dbg(lnw->dev, "%s: b session valid int\n", __func__); - iotg->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; - dev_dbg(lnw->dev, "b_sess_vld = %d\n", iotg->hsm.b_sess_end); - flag = 1; - } - if (int_mask & OTGSC_ASVIS) { - dev_dbg(lnw->dev, "%s: a session valid int\n", __func__); - iotg->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; - dev_dbg(lnw->dev, "a_sess_vld = %d\n", iotg->hsm.a_sess_vld); - flag = 1; - } - if (int_mask & OTGSC_AVVIS) { - dev_dbg(lnw->dev, "%s: a vbus valid int\n", __func__); - iotg->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; - dev_dbg(lnw->dev, "a_vbus_vld = %d\n", iotg->hsm.a_vbus_vld); - flag = 1; - } - - if (int_mask & OTGSC_1MSS) { - /* need to schedule otg_work if any timer is expired */ - if (langwell_otg_tick_timer(&int_sts)) - flag = 1; - } - - writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, - lnw->iotg.base + CI_OTGSC); - if (flag) - langwell_update_transceiver(); - - return IRQ_HANDLED; -} - -static int langwell_otg_iotg_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = data; - int flag = 0; - - if (iotg == NULL) - return NOTIFY_BAD; - - if (lnw == NULL) - return NOTIFY_BAD; - - switch (action) { - case MID_OTG_NOTIFY_CONNECT: - dev_dbg(lnw->dev, "Lnw OTG Notify Connect Event\n"); - if (iotg->otg.default_a == 1) - iotg->hsm.b_conn = 1; - else - iotg->hsm.a_conn = 1; - flag = 1; - break; - case MID_OTG_NOTIFY_DISCONN: - dev_dbg(lnw->dev, "Lnw OTG Notify Disconnect Event\n"); - if (iotg->otg.default_a == 1) - iotg->hsm.b_conn = 0; - else - iotg->hsm.a_conn = 0; - flag = 1; - break; - case MID_OTG_NOTIFY_HSUSPEND: - dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus suspend Event\n"); - if (iotg->otg.default_a == 1) - iotg->hsm.a_suspend_req = 1; - else - iotg->hsm.b_bus_req = 0; - flag = 1; - break; - case MID_OTG_NOTIFY_HRESUME: - dev_dbg(lnw->dev, "Lnw OTG Notify Host Bus resume Event\n"); - if (iotg->otg.default_a == 1) - iotg->hsm.b_bus_resume = 1; - flag = 1; - break; - case MID_OTG_NOTIFY_CSUSPEND: - dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus suspend Event\n"); - if (iotg->otg.default_a == 1) { - if (iotg->hsm.b_bus_suspend_vld == 2) { - iotg->hsm.b_bus_suspend = 1; - iotg->hsm.b_bus_suspend_vld = 0; - flag = 1; - } else { - iotg->hsm.b_bus_suspend_vld++; - flag = 0; - } - } else { - if (iotg->hsm.a_bus_suspend == 0) { - iotg->hsm.a_bus_suspend = 1; - flag = 1; - } - } - break; - case MID_OTG_NOTIFY_CRESUME: - dev_dbg(lnw->dev, "Lnw OTG Notify Client Bus resume Event\n"); - if (iotg->otg.default_a == 0) - iotg->hsm.a_bus_suspend = 0; - flag = 0; - break; - case MID_OTG_NOTIFY_HOSTADD: - dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver Add\n"); - flag = 1; - break; - case MID_OTG_NOTIFY_HOSTREMOVE: - dev_dbg(lnw->dev, "Lnw OTG Nofity Host Driver remove\n"); - flag = 1; - break; - case MID_OTG_NOTIFY_CLIENTADD: - dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver Add\n"); - flag = 1; - break; - case MID_OTG_NOTIFY_CLIENTREMOVE: - dev_dbg(lnw->dev, "Lnw OTG Nofity Client Driver remove\n"); - flag = 1; - break; - default: - dev_dbg(lnw->dev, "Lnw OTG Nofity unknown notify message\n"); - return NOTIFY_DONE; - } - - if (flag) - langwell_update_transceiver(); - - return NOTIFY_OK; -} - -static void langwell_otg_work(struct work_struct *work) -{ - struct langwell_otg *lnw; - struct intel_mid_otg_xceiv *iotg; - int retval; - struct pci_dev *pdev; - - lnw = container_of(work, struct langwell_otg, work); - iotg = &lnw->iotg; - pdev = to_pci_dev(lnw->dev); - - dev_dbg(lnw->dev, "%s: old state = %s\n", __func__, - otg_state_string(iotg->otg.state)); - - switch (iotg->otg.state) { - case OTG_STATE_UNDEFINED: - case OTG_STATE_B_IDLE: - if (!iotg->hsm.id) { - langwell_otg_del_timer(b_srp_init_tmr); - del_timer_sync(&lnw->hsm_timer); - - iotg->otg.default_a = 1; - iotg->hsm.a_srp_det = 0; - - langwell_otg_chrg_vbus(0); - set_host_mode(); - langwell_otg_phy_low_power(1); - - iotg->otg.state = OTG_STATE_A_IDLE; - langwell_update_transceiver(); - } else if (iotg->hsm.b_sess_vld) { - langwell_otg_del_timer(b_srp_init_tmr); - del_timer_sync(&lnw->hsm_timer); - iotg->hsm.b_sess_end = 0; - iotg->hsm.a_bus_suspend = 0; - langwell_otg_chrg_vbus(0); - - if (lnw->iotg.start_peripheral) { - lnw->iotg.start_peripheral(&lnw->iotg); - iotg->otg.state = OTG_STATE_B_PERIPHERAL; - } else - dev_dbg(lnw->dev, "client driver not loaded\n"); - - } else if (iotg->hsm.b_srp_init_tmout) { - iotg->hsm.b_srp_init_tmout = 0; - dev_warn(lnw->dev, "SRP init timeout\n"); - } else if (iotg->hsm.b_srp_fail_tmout) { - iotg->hsm.b_srp_fail_tmout = 0; - iotg->hsm.b_bus_req = 0; - - /* No silence failure */ - langwell_otg_nsf_msg(6); - } else if (iotg->hsm.b_bus_req && iotg->hsm.b_sess_end) { - del_timer_sync(&lnw->hsm_timer); - /* workaround for b_se0_srp detection */ - retval = langwell_otg_check_se0_srp(0); - if (retval) { - iotg->hsm.b_bus_req = 0; - dev_dbg(lnw->dev, "LS isn't SE0, try later\n"); - } else { - /* clear the PHCD before start srp */ - langwell_otg_phy_low_power(0); - - /* Start SRP */ - langwell_otg_add_timer(b_srp_init_tmr); - iotg->otg.start_srp(&iotg->otg); - langwell_otg_del_timer(b_srp_init_tmr); - langwell_otg_add_ktimer(TB_SRP_FAIL_TMR); - - /* reset PHY low power mode here */ - langwell_otg_phy_low_power_wait(1); - } - } - break; - case OTG_STATE_B_SRP_INIT: - if (!iotg->hsm.id) { - iotg->otg.default_a = 1; - iotg->hsm.a_srp_det = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - langwell_otg_chrg_vbus(0); - set_host_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_A_IDLE; - langwell_update_transceiver(); - } else if (iotg->hsm.b_sess_vld) { - langwell_otg_chrg_vbus(0); - if (lnw->iotg.start_peripheral) { - lnw->iotg.start_peripheral(&lnw->iotg); - iotg->otg.state = OTG_STATE_B_PERIPHERAL; - } else - dev_dbg(lnw->dev, "client driver not loaded\n"); - } - break; - case OTG_STATE_B_PERIPHERAL: - if (!iotg->hsm.id) { - iotg->otg.default_a = 1; - iotg->hsm.a_srp_det = 0; - - langwell_otg_chrg_vbus(0); - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - set_host_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_A_IDLE; - langwell_update_transceiver(); - } else if (!iotg->hsm.b_sess_vld) { - iotg->hsm.b_hnp_enable = 0; - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - iotg->otg.state = OTG_STATE_B_IDLE; - } else if (iotg->hsm.b_bus_req && iotg->otg.gadget && - iotg->otg.gadget->b_hnp_enable && - iotg->hsm.a_bus_suspend) { - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - langwell_otg_HAAR(1); - iotg->hsm.a_conn = 0; - - if (lnw->iotg.start_host) { - lnw->iotg.start_host(&lnw->iotg); - iotg->otg.state = OTG_STATE_B_WAIT_ACON; - } else - dev_dbg(lnw->dev, - "host driver not loaded.\n"); - - iotg->hsm.a_bus_resume = 0; - langwell_otg_add_ktimer(TB_ASE0_BRST_TMR); - } - break; - - case OTG_STATE_B_WAIT_ACON: - if (!iotg->hsm.id) { - /* delete hsm timer for b_ase0_brst_tmr */ - del_timer_sync(&lnw->hsm_timer); - - iotg->otg.default_a = 1; - iotg->hsm.a_srp_det = 0; - - langwell_otg_chrg_vbus(0); - - langwell_otg_HAAR(0); - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - set_host_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_A_IDLE; - langwell_update_transceiver(); - } else if (!iotg->hsm.b_sess_vld) { - /* delete hsm timer for b_ase0_brst_tmr */ - del_timer_sync(&lnw->hsm_timer); - - iotg->hsm.b_hnp_enable = 0; - iotg->hsm.b_bus_req = 0; - - langwell_otg_chrg_vbus(0); - langwell_otg_HAAR(0); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - set_client_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_B_IDLE; - } else if (iotg->hsm.a_conn) { - /* delete hsm timer for b_ase0_brst_tmr */ - del_timer_sync(&lnw->hsm_timer); - - langwell_otg_HAAR(0); - iotg->otg.state = OTG_STATE_B_HOST; - langwell_update_transceiver(); - } else if (iotg->hsm.a_bus_resume || - iotg->hsm.b_ase0_brst_tmout) { - /* delete hsm timer for b_ase0_brst_tmr */ - del_timer_sync(&lnw->hsm_timer); - - langwell_otg_HAAR(0); - langwell_otg_nsf_msg(7); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - iotg->hsm.a_bus_suspend = 0; - iotg->hsm.b_bus_req = 0; - - if (lnw->iotg.start_peripheral) - lnw->iotg.start_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver not loaded.\n"); - - iotg->otg.state = OTG_STATE_B_PERIPHERAL; - } - break; - - case OTG_STATE_B_HOST: - if (!iotg->hsm.id) { - iotg->otg.default_a = 1; - iotg->hsm.a_srp_det = 0; - - langwell_otg_chrg_vbus(0); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - set_host_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_A_IDLE; - langwell_update_transceiver(); - } else if (!iotg->hsm.b_sess_vld) { - iotg->hsm.b_hnp_enable = 0; - iotg->hsm.b_bus_req = 0; - - langwell_otg_chrg_vbus(0); - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - set_client_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_B_IDLE; - } else if ((!iotg->hsm.b_bus_req) || - (!iotg->hsm.a_conn)) { - iotg->hsm.b_bus_req = 0; - langwell_otg_loc_sof(0); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - iotg->hsm.a_bus_suspend = 0; - - if (lnw->iotg.start_peripheral) - lnw->iotg.start_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver not loaded.\n"); - - iotg->otg.state = OTG_STATE_B_PERIPHERAL; - } - break; - - case OTG_STATE_A_IDLE: - iotg->otg.default_a = 1; - if (iotg->hsm.id) { - iotg->otg.default_a = 0; - iotg->hsm.b_bus_req = 0; - iotg->hsm.vbus_srp_up = 0; - - langwell_otg_chrg_vbus(0); - set_client_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (!iotg->hsm.a_bus_drop && - (iotg->hsm.a_srp_det || iotg->hsm.a_bus_req)) { - langwell_otg_phy_low_power(0); - - /* Turn on VBus */ - iotg->otg.set_vbus(&iotg->otg, true); - - iotg->hsm.vbus_srp_up = 0; - iotg->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - iotg->otg.state = OTG_STATE_A_WAIT_VRISE; - langwell_update_transceiver(); - } else if (!iotg->hsm.a_bus_drop && iotg->hsm.a_sess_vld) { - iotg->hsm.vbus_srp_up = 1; - } else if (!iotg->hsm.a_sess_vld && iotg->hsm.vbus_srp_up) { - msleep(10); - langwell_otg_phy_low_power(0); - - /* Turn on VBus */ - iotg->otg.set_vbus(&iotg->otg, true); - iotg->hsm.a_srp_det = 1; - iotg->hsm.vbus_srp_up = 0; - iotg->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - iotg->otg.state = OTG_STATE_A_WAIT_VRISE; - langwell_update_transceiver(); - } else if (!iotg->hsm.a_sess_vld && - !iotg->hsm.vbus_srp_up) { - langwell_otg_phy_low_power(1); - } - break; - case OTG_STATE_A_WAIT_VRISE: - if (iotg->hsm.id) { - langwell_otg_del_timer(a_wait_vrise_tmr); - iotg->hsm.b_bus_req = 0; - iotg->otg.default_a = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - set_client_mode(); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_B_IDLE; - } else if (iotg->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_wait_vrise_tmr); - iotg->hsm.b_conn = 0; - if (lnw->iotg.start_host) - lnw->iotg.start_host(&lnw->iotg); - else { - dev_dbg(lnw->dev, "host driver not loaded.\n"); - break; - } - - langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); - iotg->otg.state = OTG_STATE_A_WAIT_BCON; - } else if (iotg->hsm.a_wait_vrise_tmout) { - iotg->hsm.b_conn = 0; - if (iotg->hsm.a_vbus_vld) { - if (lnw->iotg.start_host) - lnw->iotg.start_host(&lnw->iotg); - else { - dev_dbg(lnw->dev, - "host driver not loaded.\n"); - break; - } - langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); - iotg->otg.state = OTG_STATE_A_WAIT_BCON; - } else { - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_A_VBUS_ERR; - } - } - break; - case OTG_STATE_A_WAIT_BCON: - if (iotg->hsm.id) { - /* delete hsm timer for a_wait_bcon_tmr */ - del_timer_sync(&lnw->hsm_timer); - - iotg->otg.default_a = 0; - iotg->hsm.b_bus_req = 0; - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - set_client_mode(); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (!iotg->hsm.a_vbus_vld) { - /* delete hsm timer for a_wait_bcon_tmr */ - del_timer_sync(&lnw->hsm_timer); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (iotg->hsm.a_bus_drop || - (iotg->hsm.a_wait_bcon_tmout && - !iotg->hsm.a_bus_req)) { - /* delete hsm timer for a_wait_bcon_tmr */ - del_timer_sync(&lnw->hsm_timer); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (iotg->hsm.b_conn) { - /* delete hsm timer for a_wait_bcon_tmr */ - del_timer_sync(&lnw->hsm_timer); - - iotg->hsm.a_suspend_req = 0; - iotg->otg.state = OTG_STATE_A_HOST; - if (iotg->hsm.a_srp_det && iotg->otg.host && - !iotg->otg.host->b_hnp_enable) { - /* SRP capable peripheral-only device */ - iotg->hsm.a_bus_req = 1; - iotg->hsm.a_srp_det = 0; - } else if (!iotg->hsm.a_bus_req && iotg->otg.host && - iotg->otg.host->b_hnp_enable) { - /* It is not safe enough to do a fast - * transition from A_WAIT_BCON to - * A_SUSPEND */ - msleep(10000); - if (iotg->hsm.a_bus_req) - break; - - if (request_irq(pdev->irq, - otg_dummy_irq, IRQF_SHARED, - driver_name, iotg->base) != 0) { - dev_dbg(lnw->dev, - "request interrupt %d fail\n", - pdev->irq); - } - - langwell_otg_HABA(1); - iotg->hsm.b_bus_resume = 0; - iotg->hsm.a_aidl_bdis_tmout = 0; - - langwell_otg_loc_sof(0); - /* clear PHCD to enable HW timer */ - langwell_otg_phy_low_power(0); - langwell_otg_add_timer(a_aidl_bdis_tmr); - iotg->otg.state = OTG_STATE_A_SUSPEND; - } else if (!iotg->hsm.a_bus_req && iotg->otg.host && - !iotg->otg.host->b_hnp_enable) { - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_WAIT_VFALL; - } - } - break; - case OTG_STATE_A_HOST: - if (iotg->hsm.id) { - iotg->otg.default_a = 0; - iotg->hsm.b_bus_req = 0; - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - set_client_mode(); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (iotg->hsm.a_bus_drop || - (iotg->otg.host && - !iotg->otg.host->b_hnp_enable && - !iotg->hsm.a_bus_req)) { - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (!iotg->hsm.a_vbus_vld) { - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (iotg->otg.host && - iotg->otg.host->b_hnp_enable && - !iotg->hsm.a_bus_req) { - /* Set HABA to enable hardware assistance to signal - * A-connect after receiver B-disconnect. Hardware - * will then set client mode and enable URE, SLE and - * PCE after the assistance. otg_dummy_irq is used to - * clean these ints when client driver is not resumed. - */ - if (request_irq(pdev->irq, otg_dummy_irq, IRQF_SHARED, - driver_name, iotg->base) != 0) { - dev_dbg(lnw->dev, - "request interrupt %d failed\n", - pdev->irq); - } - - /* set HABA */ - langwell_otg_HABA(1); - iotg->hsm.b_bus_resume = 0; - iotg->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_loc_sof(0); - /* clear PHCD to enable HW timer */ - langwell_otg_phy_low_power(0); - langwell_otg_add_timer(a_aidl_bdis_tmr); - iotg->otg.state = OTG_STATE_A_SUSPEND; - } else if (!iotg->hsm.b_conn || !iotg->hsm.a_bus_req) { - langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); - iotg->otg.state = OTG_STATE_A_WAIT_BCON; - } - break; - case OTG_STATE_A_SUSPEND: - if (iotg->hsm.id) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(pdev->irq, iotg->base); - iotg->otg.default_a = 0; - iotg->hsm.b_bus_req = 0; - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - set_client_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (iotg->hsm.a_bus_req || - iotg->hsm.b_bus_resume) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(pdev->irq, iotg->base); - iotg->hsm.a_suspend_req = 0; - langwell_otg_loc_sof(1); - iotg->otg.state = OTG_STATE_A_HOST; - } else if (iotg->hsm.a_aidl_bdis_tmout || - iotg->hsm.a_bus_drop) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(pdev->irq, iotg->base); - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (!iotg->hsm.b_conn && iotg->otg.host && - iotg->otg.host->b_hnp_enable) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(pdev->irq, iotg->base); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - iotg->hsm.b_bus_suspend = 0; - iotg->hsm.b_bus_suspend_vld = 0; - - /* msleep(200); */ - if (lnw->iotg.start_peripheral) - lnw->iotg.start_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver not loaded.\n"); - - langwell_otg_add_ktimer(TB_BUS_SUSPEND_TMR); - iotg->otg.state = OTG_STATE_A_PERIPHERAL; - break; - } else if (!iotg->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(pdev->irq, iotg->base); - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_A_VBUS_ERR; - } - break; - case OTG_STATE_A_PERIPHERAL: - if (iotg->hsm.id) { - /* delete hsm timer for b_bus_suspend_tmr */ - del_timer_sync(&lnw->hsm_timer); - iotg->otg.default_a = 0; - iotg->hsm.b_bus_req = 0; - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - set_client_mode(); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (!iotg->hsm.a_vbus_vld) { - /* delete hsm timer for b_bus_suspend_tmr */ - del_timer_sync(&lnw->hsm_timer); - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - langwell_otg_phy_low_power_wait(1); - iotg->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (iotg->hsm.a_bus_drop) { - /* delete hsm timer for b_bus_suspend_tmr */ - del_timer_sync(&lnw->hsm_timer); - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (iotg->hsm.b_bus_suspend) { - /* delete hsm timer for b_bus_suspend_tmr */ - del_timer_sync(&lnw->hsm_timer); - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - if (lnw->iotg.start_host) - lnw->iotg.start_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver not loaded.\n"); - langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); - iotg->otg.state = OTG_STATE_A_WAIT_BCON; - } else if (iotg->hsm.b_bus_suspend_tmout) { - u32 val; - val = readl(lnw->iotg.base + CI_PORTSC1); - if (!(val & PORTSC_SUSP)) - break; - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(lnw->dev, - "client driver has been removed.\n"); - - if (lnw->iotg.start_host) - lnw->iotg.start_host(&lnw->iotg); - else - dev_dbg(lnw->dev, - "host driver not loaded.\n"); - langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); - iotg->otg.state = OTG_STATE_A_WAIT_BCON; - } - break; - case OTG_STATE_A_VBUS_ERR: - if (iotg->hsm.id) { - iotg->otg.default_a = 0; - iotg->hsm.a_clr_err = 0; - iotg->hsm.a_srp_det = 0; - set_client_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (iotg->hsm.a_clr_err) { - iotg->hsm.a_clr_err = 0; - iotg->hsm.a_srp_det = 0; - reset_otg(); - init_hsm(); - if (iotg->otg.state == OTG_STATE_A_IDLE) - langwell_update_transceiver(); - } else { - /* FW will clear PHCD bit when any VBus - * event detected. Reset PHCD to 1 again */ - langwell_otg_phy_low_power(1); - } - break; - case OTG_STATE_A_WAIT_VFALL: - if (iotg->hsm.id) { - iotg->otg.default_a = 0; - set_client_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_B_IDLE; - langwell_update_transceiver(); - } else if (iotg->hsm.a_bus_req) { - - /* Turn on VBus */ - iotg->otg.set_vbus(&iotg->otg, true); - iotg->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - iotg->otg.state = OTG_STATE_A_WAIT_VRISE; - } else if (!iotg->hsm.a_sess_vld) { - iotg->hsm.a_srp_det = 0; - set_host_mode(); - langwell_otg_phy_low_power(1); - iotg->otg.state = OTG_STATE_A_IDLE; - } - break; - default: - ; - } - - dev_dbg(lnw->dev, "%s: new state = %s\n", __func__, - otg_state_string(iotg->otg.state)); -} - -static ssize_t -show_registers(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *lnw = the_transceiver; - char *next; - unsigned size, t; - - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, - "\n" - "USBCMD = 0x%08x\n" - "USBSTS = 0x%08x\n" - "USBINTR = 0x%08x\n" - "ASYNCLISTADDR = 0x%08x\n" - "PORTSC1 = 0x%08x\n" - "HOSTPC1 = 0x%08x\n" - "OTGSC = 0x%08x\n" - "USBMODE = 0x%08x\n", - readl(lnw->iotg.base + 0x30), - readl(lnw->iotg.base + 0x34), - readl(lnw->iotg.base + 0x38), - readl(lnw->iotg.base + 0x48), - readl(lnw->iotg.base + 0x74), - readl(lnw->iotg.base + 0xb4), - readl(lnw->iotg.base + 0xf4), - readl(lnw->iotg.base + 0xf8) - ); - size -= t; - next += t; - - return PAGE_SIZE - size; -} -static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); - -static ssize_t -show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - char *next; - unsigned size, t; - - next = buf; - size = PAGE_SIZE; - - if (iotg->otg.host) - iotg->hsm.a_set_b_hnp_en = iotg->otg.host->b_hnp_enable; - - if (iotg->otg.gadget) - iotg->hsm.b_hnp_enable = iotg->otg.gadget->b_hnp_enable; - - t = scnprintf(next, size, - "\n" - "current state = %s\n" - "a_bus_resume = \t%d\n" - "a_bus_suspend = \t%d\n" - "a_conn = \t%d\n" - "a_sess_vld = \t%d\n" - "a_srp_det = \t%d\n" - "a_vbus_vld = \t%d\n" - "b_bus_resume = \t%d\n" - "b_bus_suspend = \t%d\n" - "b_conn = \t%d\n" - "b_se0_srp = \t%d\n" - "b_sess_end = \t%d\n" - "b_sess_vld = \t%d\n" - "id = \t%d\n" - "a_set_b_hnp_en = \t%d\n" - "b_srp_done = \t%d\n" - "b_hnp_enable = \t%d\n" - "a_wait_vrise_tmout = \t%d\n" - "a_wait_bcon_tmout = \t%d\n" - "a_aidl_bdis_tmout = \t%d\n" - "b_ase0_brst_tmout = \t%d\n" - "a_bus_drop = \t%d\n" - "a_bus_req = \t%d\n" - "a_clr_err = \t%d\n" - "a_suspend_req = \t%d\n" - "b_bus_req = \t%d\n" - "b_bus_suspend_tmout = \t%d\n" - "b_bus_suspend_vld = \t%d\n", - otg_state_string(iotg->otg.state), - iotg->hsm.a_bus_resume, - iotg->hsm.a_bus_suspend, - iotg->hsm.a_conn, - iotg->hsm.a_sess_vld, - iotg->hsm.a_srp_det, - iotg->hsm.a_vbus_vld, - iotg->hsm.b_bus_resume, - iotg->hsm.b_bus_suspend, - iotg->hsm.b_conn, - iotg->hsm.b_se0_srp, - iotg->hsm.b_sess_end, - iotg->hsm.b_sess_vld, - iotg->hsm.id, - iotg->hsm.a_set_b_hnp_en, - iotg->hsm.b_srp_done, - iotg->hsm.b_hnp_enable, - iotg->hsm.a_wait_vrise_tmout, - iotg->hsm.a_wait_bcon_tmout, - iotg->hsm.a_aidl_bdis_tmout, - iotg->hsm.b_ase0_brst_tmout, - iotg->hsm.a_bus_drop, - iotg->hsm.a_bus_req, - iotg->hsm.a_clr_err, - iotg->hsm.a_suspend_req, - iotg->hsm.b_bus_req, - iotg->hsm.b_bus_suspend_tmout, - iotg->hsm.b_bus_suspend_vld - ); - size -= t; - next += t; - - return PAGE_SIZE - size; -} -static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); - -static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *lnw = the_transceiver; - char *next; - unsigned size, t; - - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_req); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - - if (!iotg->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '0') { - iotg->hsm.a_bus_req = 0; - dev_dbg(lnw->dev, "User request: a_bus_req = 0\n"); - } else if (buf[0] == '1') { - /* If a_bus_drop is TRUE, a_bus_req can't be set */ - if (iotg->hsm.a_bus_drop) - return -1; - iotg->hsm.a_bus_req = 1; - dev_dbg(lnw->dev, "User request: a_bus_req = 1\n"); - } - if (spin_trylock(&lnw->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&lnw->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req); - -static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *lnw = the_transceiver; - char *next; - unsigned size, t; - - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", lnw->iotg.hsm.a_bus_drop); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - - if (!iotg->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '0') { - iotg->hsm.a_bus_drop = 0; - dev_dbg(lnw->dev, "User request: a_bus_drop = 0\n"); - } else if (buf[0] == '1') { - iotg->hsm.a_bus_drop = 1; - iotg->hsm.a_bus_req = 0; - dev_dbg(lnw->dev, "User request: a_bus_drop = 1\n"); - dev_dbg(lnw->dev, "User request: and a_bus_req = 0\n"); - } - if (spin_trylock(&lnw->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&lnw->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, set_a_bus_drop); - -static ssize_t -get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *lnw = the_transceiver; - char *next; - unsigned size, t; - - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", lnw->iotg.hsm.b_bus_req); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_b_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - - if (iotg->otg.default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '0') { - iotg->hsm.b_bus_req = 0; - dev_dbg(lnw->dev, "User request: b_bus_req = 0\n"); - } else if (buf[0] == '1') { - iotg->hsm.b_bus_req = 1; - dev_dbg(lnw->dev, "User request: b_bus_req = 1\n"); - } - if (spin_trylock(&lnw->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&lnw->wq_lock); - } - return count; -} -static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req); - -static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - - if (!iotg->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '1') { - iotg->hsm.a_clr_err = 1; - dev_dbg(lnw->dev, "User request: a_clr_err = 1\n"); - } - if (spin_trylock(&lnw->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&lnw->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); - -static struct attribute *inputs_attrs[] = { - &dev_attr_a_bus_req.attr, - &dev_attr_a_bus_drop.attr, - &dev_attr_b_bus_req.attr, - &dev_attr_a_clr_err.attr, - NULL, -}; - -static struct attribute_group debug_dev_attr_group = { - .name = "inputs", - .attrs = inputs_attrs, -}; - -static int langwell_otg_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - unsigned long resource, len; - void __iomem *base = NULL; - int retval; - u32 val32; - struct langwell_otg *lnw; - char qname[] = "langwell_otg_queue"; - - retval = 0; - dev_dbg(&pdev->dev, "\notg controller is detected.\n"); - if (pci_enable_device(pdev) < 0) { - retval = -ENODEV; - goto done; - } - - lnw = kzalloc(sizeof *lnw, GFP_KERNEL); - if (lnw == NULL) { - retval = -ENOMEM; - goto done; - } - the_transceiver = lnw; - - /* control register: BAR 0 */ - resource = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - if (!request_mem_region(resource, len, driver_name)) { - retval = -EBUSY; - goto err; - } - lnw->region = 1; - - base = ioremap_nocache(resource, len); - if (base == NULL) { - retval = -EFAULT; - goto err; - } - lnw->iotg.base = base; - - if (!request_mem_region(USBCFG_ADDR, USBCFG_LEN, driver_name)) { - retval = -EBUSY; - goto err; - } - lnw->cfg_region = 1; - - /* For the SCCB.USBCFG register */ - base = ioremap_nocache(USBCFG_ADDR, USBCFG_LEN); - if (base == NULL) { - retval = -EFAULT; - goto err; - } - lnw->usbcfg = base; - - if (!pdev->irq) { - dev_dbg(&pdev->dev, "No IRQ.\n"); - retval = -ENODEV; - goto err; - } - - lnw->qwork = create_singlethread_workqueue(qname); - if (!lnw->qwork) { - dev_dbg(&pdev->dev, "cannot create workqueue %s\n", qname); - retval = -ENOMEM; - goto err; - } - INIT_WORK(&lnw->work, langwell_otg_work); - - /* OTG common part */ - lnw->dev = &pdev->dev; - lnw->iotg.otg.dev = lnw->dev; - lnw->iotg.otg.label = driver_name; - lnw->iotg.otg.set_host = langwell_otg_set_host; - lnw->iotg.otg.set_peripheral = langwell_otg_set_peripheral; - lnw->iotg.otg.set_power = langwell_otg_set_power; - lnw->iotg.otg.set_vbus = langwell_otg_set_vbus; - lnw->iotg.otg.start_srp = langwell_otg_start_srp; - lnw->iotg.otg.state = OTG_STATE_UNDEFINED; - - if (otg_set_transceiver(&lnw->iotg.otg)) { - dev_dbg(lnw->dev, "can't set transceiver\n"); - retval = -EBUSY; - goto err; - } - - reset_otg(); - init_hsm(); - - spin_lock_init(&lnw->lock); - spin_lock_init(&lnw->wq_lock); - INIT_LIST_HEAD(&active_timers); - retval = langwell_otg_init_timers(&lnw->iotg.hsm); - if (retval) { - dev_dbg(&pdev->dev, "Failed to init timers\n"); - goto err; - } - - init_timer(&lnw->hsm_timer); - ATOMIC_INIT_NOTIFIER_HEAD(&lnw->iotg.iotg_notifier); - - lnw->iotg_notifier.notifier_call = langwell_otg_iotg_notify; - - retval = intel_mid_otg_register_notifier(&lnw->iotg, - &lnw->iotg_notifier); - if (retval) { - dev_dbg(lnw->dev, "Failed to register notifier\n"); - goto err; - } - - if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, lnw) != 0) { - dev_dbg(lnw->dev, "request interrupt %d failed\n", pdev->irq); - retval = -EBUSY; - goto err; - } - - /* enable OTGSC int */ - val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | - OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; - writel(val32, lnw->iotg.base + CI_OTGSC); - - retval = device_create_file(&pdev->dev, &dev_attr_registers); - if (retval < 0) { - dev_dbg(lnw->dev, - "Can't register sysfs attribute: %d\n", retval); - goto err; - } - - retval = device_create_file(&pdev->dev, &dev_attr_hsm); - if (retval < 0) { - dev_dbg(lnw->dev, "Can't hsm sysfs attribute: %d\n", retval); - goto err; - } - - retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); - if (retval < 0) { - dev_dbg(lnw->dev, - "Can't register sysfs attr group: %d\n", retval); - goto err; - } - - if (lnw->iotg.otg.state == OTG_STATE_A_IDLE) - langwell_update_transceiver(); - - return 0; - -err: - if (the_transceiver) - langwell_otg_remove(pdev); -done: - return retval; -} - -static void langwell_otg_remove(struct pci_dev *pdev) -{ - struct langwell_otg *lnw = the_transceiver; - - if (lnw->qwork) { - flush_workqueue(lnw->qwork); - destroy_workqueue(lnw->qwork); - } - intel_mid_otg_unregister_notifier(&lnw->iotg, &lnw->iotg_notifier); - langwell_otg_free_timers(); - - /* disable OTGSC interrupt as OTGSC doesn't change in reset */ - writel(0, lnw->iotg.base + CI_OTGSC); - - if (pdev->irq) - free_irq(pdev->irq, lnw); - if (lnw->usbcfg) - iounmap(lnw->usbcfg); - if (lnw->cfg_region) - release_mem_region(USBCFG_ADDR, USBCFG_LEN); - if (lnw->iotg.base) - iounmap(lnw->iotg.base); - if (lnw->region) - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - - otg_set_transceiver(NULL); - pci_disable_device(pdev); - sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); - device_remove_file(&pdev->dev, &dev_attr_hsm); - device_remove_file(&pdev->dev, &dev_attr_registers); - kfree(lnw); - lnw = NULL; -} - -static void transceiver_suspend(struct pci_dev *pdev) -{ - pci_save_state(pdev); - pci_set_power_state(pdev, PCI_D3hot); - langwell_otg_phy_low_power(1); -} - -static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) -{ - struct langwell_otg *lnw = the_transceiver; - struct intel_mid_otg_xceiv *iotg = &lnw->iotg; - int ret = 0; - - /* Disbale OTG interrupts */ - langwell_otg_intr(0); - - if (pdev->irq) - free_irq(pdev->irq, lnw); - - /* Prevent more otg_work */ - flush_workqueue(lnw->qwork); - destroy_workqueue(lnw->qwork); - lnw->qwork = NULL; - - /* start actions */ - switch (iotg->otg.state) { - case OTG_STATE_A_WAIT_VFALL: - iotg->otg.state = OTG_STATE_A_IDLE; - case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_VBUS_ERR: - transceiver_suspend(pdev); - break; - case OTG_STATE_A_WAIT_VRISE: - langwell_otg_del_timer(a_wait_vrise_tmr); - iotg->hsm.a_srp_det = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_A_WAIT_BCON: - del_timer_sync(&lnw->hsm_timer); - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(&pdev->dev, "host driver has been removed.\n"); - - iotg->hsm.a_srp_det = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_A_HOST: - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(&pdev->dev, "host driver has been removed.\n"); - - iotg->hsm.a_srp_det = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - - iotg->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_A_SUSPEND: - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(lnw->dev, "host driver has been removed.\n"); - iotg->hsm.a_srp_det = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_A_PERIPHERAL: - del_timer_sync(&lnw->hsm_timer); - - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(&pdev->dev, - "client driver has been removed.\n"); - iotg->hsm.a_srp_det = 0; - - /* Turn off VBus */ - iotg->otg.set_vbus(&iotg->otg, false); - iotg->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_B_HOST: - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(&pdev->dev, "host driver has been removed.\n"); - iotg->hsm.b_bus_req = 0; - iotg->otg.state = OTG_STATE_B_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_B_PERIPHERAL: - if (lnw->iotg.stop_peripheral) - lnw->iotg.stop_peripheral(&lnw->iotg); - else - dev_dbg(&pdev->dev, - "client driver has been removed.\n"); - iotg->otg.state = OTG_STATE_B_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_B_WAIT_ACON: - /* delete hsm timer for b_ase0_brst_tmr */ - del_timer_sync(&lnw->hsm_timer); - - langwell_otg_HAAR(0); - - if (lnw->iotg.stop_host) - lnw->iotg.stop_host(&lnw->iotg); - else - dev_dbg(&pdev->dev, "host driver has been removed.\n"); - iotg->hsm.b_bus_req = 0; - iotg->otg.state = OTG_STATE_B_IDLE; - transceiver_suspend(pdev); - break; - default: - dev_dbg(lnw->dev, "error state before suspend\n"); - break; - } - - return ret; -} - -static void transceiver_resume(struct pci_dev *pdev) -{ - pci_restore_state(pdev); - pci_set_power_state(pdev, PCI_D0); -} - -static int langwell_otg_resume(struct pci_dev *pdev) -{ - struct langwell_otg *lnw = the_transceiver; - int ret = 0; - - transceiver_resume(pdev); - - lnw->qwork = create_singlethread_workqueue("langwell_otg_queue"); - if (!lnw->qwork) { - dev_dbg(&pdev->dev, "cannot create langwell otg workqueuen"); - ret = -ENOMEM; - goto error; - } - - if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, lnw) != 0) { - dev_dbg(&pdev->dev, "request interrupt %d failed\n", pdev->irq); - ret = -EBUSY; - goto error; - } - - /* enable OTG interrupts */ - langwell_otg_intr(1); - - update_hsm(); - - langwell_update_transceiver(); - - return ret; -error: - langwell_otg_intr(0); - transceiver_suspend(pdev); - return ret; -} - -static int __init langwell_otg_init(void) -{ - return pci_register_driver(&otg_pci_driver); -} -module_init(langwell_otg_init); - -static void __exit langwell_otg_cleanup(void) -{ - pci_unregister_driver(&otg_pci_driver); -} -module_exit(langwell_otg_cleanup); diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h deleted file mode 100644 index 51f17b1..0000000 --- a/include/linux/usb/langwell_otg.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Intel Langwell USB OTG transceiver driver - * Copyright (C) 2008 - 2010, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef __LANGWELL_OTG_H -#define __LANGWELL_OTG_H - -#include - -#define CI_USBCMD 0x30 -# define USBCMD_RST BIT(1) -# define USBCMD_RS BIT(0) -#define CI_USBSTS 0x34 -# define USBSTS_SLI BIT(8) -# define USBSTS_URI BIT(6) -# define USBSTS_PCI BIT(2) -#define CI_PORTSC1 0x74 -# define PORTSC_PP BIT(12) -# define PORTSC_LS (BIT(11) | BIT(10)) -# define PORTSC_SUSP BIT(7) -# define PORTSC_CCS BIT(0) -#define CI_HOSTPC1 0xb4 -# define HOSTPC1_PHCD BIT(22) -#define CI_OTGSC 0xf4 -# define OTGSC_DPIE BIT(30) -# define OTGSC_1MSE BIT(29) -# define OTGSC_BSEIE BIT(28) -# define OTGSC_BSVIE BIT(27) -# define OTGSC_ASVIE BIT(26) -# define OTGSC_AVVIE BIT(25) -# define OTGSC_IDIE BIT(24) -# define OTGSC_DPIS BIT(22) -# define OTGSC_1MSS BIT(21) -# define OTGSC_BSEIS BIT(20) -# define OTGSC_BSVIS BIT(19) -# define OTGSC_ASVIS BIT(18) -# define OTGSC_AVVIS BIT(17) -# define OTGSC_IDIS BIT(16) -# define OTGSC_DPS BIT(14) -# define OTGSC_1MST BIT(13) -# define OTGSC_BSE BIT(12) -# define OTGSC_BSV BIT(11) -# define OTGSC_ASV BIT(10) -# define OTGSC_AVV BIT(9) -# define OTGSC_ID BIT(8) -# define OTGSC_HABA BIT(7) -# define OTGSC_HADP BIT(6) -# define OTGSC_IDPU BIT(5) -# define OTGSC_DP BIT(4) -# define OTGSC_OT BIT(3) -# define OTGSC_HAAR BIT(2) -# define OTGSC_VC BIT(1) -# define OTGSC_VD BIT(0) -# define OTGSC_INTEN_MASK (0x7f << 24) -# define OTGSC_INT_MASK (0x5f << 24) -# define OTGSC_INTSTS_MASK (0x7f << 16) -#define CI_USBMODE 0xf8 -# define USBMODE_CM (BIT(1) | BIT(0)) -# define USBMODE_IDLE 0 -# define USBMODE_DEVICE 0x2 -# define USBMODE_HOST 0x3 -#define USBCFG_ADDR 0xff10801c -#define USBCFG_LEN 4 -# define USBCFG_VBUSVAL BIT(14) -# define USBCFG_AVALID BIT(13) -# define USBCFG_BVALID BIT(12) -# define USBCFG_SESEND BIT(11) - -#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) - -enum langwell_otg_timer_type { - TA_WAIT_VRISE_TMR, - TA_WAIT_BCON_TMR, - TA_AIDL_BDIS_TMR, - TB_ASE0_BRST_TMR, - TB_SE0_SRP_TMR, - TB_SRP_INIT_TMR, - TB_SRP_FAIL_TMR, - TB_BUS_SUSPEND_TMR -}; - -#define TA_WAIT_VRISE 100 -#define TA_WAIT_BCON 30000 -#define TA_AIDL_BDIS 15000 -#define TB_ASE0_BRST 5000 -#define TB_SE0_SRP 2 -#define TB_SRP_INIT 100 -#define TB_SRP_FAIL 5500 -#define TB_BUS_SUSPEND 500 - -struct langwell_otg_timer { - unsigned long expires; /* Number of count increase to timeout */ - unsigned long count; /* Tick counter */ - void (*function)(unsigned long); /* Timeout function */ - unsigned long data; /* Data passed to function */ - struct list_head list; -}; - -struct langwell_otg { - struct intel_mid_otg_xceiv iotg; - struct device *dev; - - void __iomem *usbcfg; /* SCCBUSB config Reg */ - - unsigned region; - unsigned cfg_region; - - struct work_struct work; - struct workqueue_struct *qwork; - struct timer_list hsm_timer; - - spinlock_t lock; - spinlock_t wq_lock; - - struct notifier_block iotg_notifier; -}; - -static inline -struct langwell_otg *mid_xceiv_to_lnw(struct intel_mid_otg_xceiv *iotg) -{ - return container_of(iotg, struct langwell_otg, iotg); -} - -#endif /* __LANGWELL_OTG_H__ */ -- cgit v0.10.2 From 28bd6222544d7559edf9ff487172e45ce46e2578 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 21 Dec 2011 10:19:39 +0200 Subject: usb: gadget: langwell: drop langwell_otg support Since there is no working (or even compilable) OTG_TRANSCEIVER support for this driver, remove the dead code which depends on it at compile time. Signed-off-by: Alexander Shishkin Cc: stable@vger.kernel.org # v2.6.31+ Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index fa0fcc1..34e3bf8 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -11,11 +11,6 @@ /* #undef DEBUG */ /* #undef VERBOSE_DEBUG */ -#if defined(CONFIG_USB_LANGWELL_OTG) -#define OTG_TRANSCEIVER -#endif - - #include #include #include @@ -2315,13 +2310,9 @@ static void handle_setup_packet(struct langwell_udc *dev, if (!gadget_is_otg(&dev->gadget)) break; - else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) { + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) dev->gadget.b_hnp_enable = 1; -#ifdef OTG_TRANSCEIVER - if (!dev->lotg->otg.default_a) - dev->lotg->hsm.b_hnp_enable = 1; -#endif - } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) + else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) dev->gadget.a_hnp_support = 1; else if (setup->bRequest == USB_DEVICE_A_ALT_HNP_SUPPORT) @@ -2752,12 +2743,6 @@ static void handle_usb_reset(struct langwell_udc *dev) dev->usb_state = USB_STATE_ATTACHED; } -#ifdef OTG_TRANSCEIVER - /* refer to USB OTG 6.6.2.3 b_hnp_en is cleared */ - if (!dev->lotg->otg.default_a) - dev->lotg->hsm.b_hnp_enable = 0; -#endif - dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__); } @@ -2770,29 +2755,6 @@ static void handle_bus_suspend(struct langwell_udc *dev) dev->resume_state = dev->usb_state; dev->usb_state = USB_STATE_SUSPENDED; -#ifdef OTG_TRANSCEIVER - if (dev->lotg->otg.default_a) { - if (dev->lotg->hsm.b_bus_suspend_vld == 1) { - dev->lotg->hsm.b_bus_suspend = 1; - /* notify transceiver the state changes */ - if (spin_trylock(&dev->lotg->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&dev->lotg->wq_lock); - } - } - dev->lotg->hsm.b_bus_suspend_vld++; - } else { - if (!dev->lotg->hsm.a_bus_suspend) { - dev->lotg->hsm.a_bus_suspend = 1; - /* notify transceiver the state changes */ - if (spin_trylock(&dev->lotg->wq_lock)) { - langwell_update_transceiver(); - spin_unlock(&dev->lotg->wq_lock); - } - } - } -#endif - /* report suspend to the driver */ if (dev->driver) { if (dev->driver->suspend) { @@ -2823,11 +2785,6 @@ static void handle_bus_resume(struct langwell_udc *dev) if (dev->pdev->device != 0x0829) langwell_phy_low_power(dev, 0); -#ifdef OTG_TRANSCEIVER - if (dev->lotg->otg.default_a == 0) - dev->lotg->hsm.a_bus_suspend = 0; -#endif - /* report resume to the driver */ if (dev->driver) { if (dev->driver->resume) { @@ -3020,7 +2977,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) dev->done = &done; -#ifndef OTG_TRANSCEIVER /* free dTD dma_pool and dQH */ if (dev->dtd_pool) dma_pool_destroy(dev->dtd_pool); @@ -3032,7 +2988,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) /* release SRAM caching */ if (dev->has_sram && dev->got_sram) sram_deinit(dev); -#endif if (dev->status_req) { kfree(dev->status_req->req.buf); @@ -3045,7 +3000,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) if (dev->got_irq) free_irq(pdev->irq, dev); -#ifndef OTG_TRANSCEIVER if (dev->cap_regs) iounmap(dev->cap_regs); @@ -3055,13 +3009,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) if (dev->enabled) pci_disable_device(pdev); -#else - if (dev->transceiver) { - otg_put_transceiver(dev->transceiver); - dev->transceiver = NULL; - dev->lotg = NULL; - } -#endif dev->cap_regs = NULL; @@ -3072,9 +3019,7 @@ static void langwell_udc_remove(struct pci_dev *pdev) device_remove_file(&pdev->dev, &dev_attr_langwell_udc); device_remove_file(&pdev->dev, &dev_attr_remote_wakeup); -#ifndef OTG_TRANSCEIVER pci_set_drvdata(pdev, NULL); -#endif /* free dev, wait for the release() finished */ wait_for_completion(&done); @@ -3089,9 +3034,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct langwell_udc *dev; -#ifndef OTG_TRANSCEIVER unsigned long resource, len; -#endif void __iomem *base = NULL; size_t size; int retval; @@ -3109,16 +3052,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->pdev = pdev; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); -#ifdef OTG_TRANSCEIVER - /* PCI device is already enabled by otg_transceiver driver */ - dev->enabled = 1; - - /* mem region and register base */ - dev->region = 1; - dev->transceiver = otg_get_transceiver(); - dev->lotg = otg_to_langwell(dev->transceiver); - base = dev->lotg->regs; -#else pci_set_drvdata(pdev, dev); /* now all the pci goodies ... */ @@ -3139,7 +3072,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->region = 1; base = ioremap_nocache(resource, len); -#endif if (base == NULL) { dev_err(&dev->pdev->dev, "can't map memory\n"); retval = -EFAULT; @@ -3163,7 +3095,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->got_sram = 0; dev_vdbg(&dev->pdev->dev, "dev->has_sram: %d\n", dev->has_sram); -#ifndef OTG_TRANSCEIVER /* enable SRAM caching if detected */ if (dev->has_sram && !dev->got_sram) sram_init(dev); @@ -3182,7 +3113,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, goto error; } dev->got_irq = 1; -#endif /* set stopped bit */ dev->stopped = 1; @@ -3257,10 +3187,8 @@ static int langwell_udc_probe(struct pci_dev *pdev, dev->remote_wakeup = 0; dev->dev_status = 1 << USB_DEVICE_SELF_POWERED; -#ifndef OTG_TRANSCEIVER /* reset device controller */ langwell_udc_reset(dev); -#endif /* initialize gadget structure */ dev->gadget.ops = &langwell_ops; /* usb_gadget_ops */ @@ -3268,9 +3196,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, INIT_LIST_HEAD(&dev->gadget.ep_list); /* ep_list */ dev->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ dev->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ -#ifdef OTG_TRANSCEIVER - dev->gadget.is_otg = 1; /* support otg mode */ -#endif /* the "gadget" abstracts/virtualizes the controller */ dev_set_name(&dev->gadget.dev, "gadget"); @@ -3282,10 +3207,8 @@ static int langwell_udc_probe(struct pci_dev *pdev, /* controller endpoints reinit */ eps_reinit(dev); -#ifndef OTG_TRANSCEIVER /* reset ep0 dQH and endptctrl */ ep0_reset(dev); -#endif /* create dTD dma_pool resource */ dev->dtd_pool = dma_pool_create("langwell_dtd", @@ -3525,22 +3448,14 @@ static struct pci_driver langwell_pci_driver = { static int __init init(void) { -#ifdef OTG_TRANSCEIVER - return langwell_register_peripheral(&langwell_pci_driver); -#else return pci_register_driver(&langwell_pci_driver); -#endif } module_init(init); static void __exit cleanup(void) { -#ifdef OTG_TRANSCEIVER - return langwell_unregister_peripheral(&langwell_pci_driver); -#else pci_unregister_driver(&langwell_pci_driver); -#endif } module_exit(cleanup); diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h index ef79e24..d6e78ac 100644 --- a/drivers/usb/gadget/langwell_udc.h +++ b/drivers/usb/gadget/langwell_udc.h @@ -8,7 +8,6 @@ */ #include -#include /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 37fd37108449d574da11aa9055c5c8afb39ff226 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 21 Dec 2011 10:19:40 +0200 Subject: usb: gadget: langwell: don't call gadget's disconnect() UDC core will call disconnect() and unbind() for us upon the gadget removal, so we should not do it ourselves. Otherwise, a composite gadget will explode, for example. Others might too. This was introduced during conversion to new style gadget in 2c7f0989 (usb: gadget: langwell: convert to new style). Signed-off-by: Alexander Shishkin Cc: stable@vger.kernel.org # v3.2 Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 34e3bf8..e2293c1 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -1517,8 +1517,7 @@ static void langwell_udc_stop(struct langwell_udc *dev) /* stop all USB activities */ -static void stop_activity(struct langwell_udc *dev, - struct usb_gadget_driver *driver) +static void stop_activity(struct langwell_udc *dev) { struct langwell_ep *ep; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -1530,9 +1529,9 @@ static void stop_activity(struct langwell_udc *dev, } /* report disconnect; the driver is already quiesced */ - if (driver) { + if (dev->driver) { spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); + dev->driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } @@ -1920,11 +1919,10 @@ static int langwell_stop(struct usb_gadget *g, /* stop all usb activities */ dev->gadget.speed = USB_SPEED_UNKNOWN; - stop_activity(dev, driver); - spin_unlock_irqrestore(&dev->lock, flags); - dev->gadget.dev.driver = NULL; dev->driver = NULL; + stop_activity(dev); + spin_unlock_irqrestore(&dev->lock, flags); device_remove_file(&dev->pdev->dev, &dev_attr_function); @@ -2724,7 +2722,7 @@ static void handle_usb_reset(struct langwell_udc *dev) dev->bus_reset = 1; /* reset all the queues, stop all USB activities */ - stop_activity(dev, dev->driver); + stop_activity(dev); dev->usb_state = USB_STATE_DEFAULT; } else { dev_vdbg(&dev->pdev->dev, "device controller reset\n"); @@ -2732,7 +2730,7 @@ static void handle_usb_reset(struct langwell_udc *dev) langwell_udc_reset(dev); /* reset all the queues, stop all USB activities */ - stop_activity(dev, dev->driver); + stop_activity(dev); /* reset ep0 dQH and endptctrl */ ep0_reset(dev); @@ -3290,7 +3288,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) spin_lock_irq(&dev->lock); /* stop all usb activities */ - stop_activity(dev, dev->driver); + stop_activity(dev); spin_unlock_irq(&dev->lock); /* free dTD dma_pool and dQH */ -- cgit v0.10.2 From 006896fc612f11bf0624db7814a75d0d5410855f Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Wed, 28 Dec 2011 12:02:57 +0530 Subject: usb: musb: davinci: fix build breakage Commit 0020afb369859472a461ef4af6410732e929d402 (ARM: mach-davinci: remove mach/memory.h) removed mach/memory.h for DaVinci which broke DaVinci MUSB build. mach/memory.h is not actually needed in davinci.c, so remove it. While at it, also remove some more machine specific inclulde files which are not needed for build. Tested on DM644x EVM using USB card reader. Cc: stable@vger.kernel.org # v3.2 Signed-off-by: Sekhar Nori Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index f9a3f62..7c569f5 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -33,9 +33,6 @@ #include #include -#include -#include -#include #include #include -- cgit v0.10.2 From 1a0955fed11363bea66742fffc6f8ad1e6800a6d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 10 Jan 2012 17:29:29 +0200 Subject: usb: dwc3: ep0: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 34c60a7 (usb: dwc3: ep0: tidy up Pending Request handling) introduced a compile warning by leaving an unused variable. This patch fixes that warning: drivers/usb/dwc3/ep0.c: In function ‘__dwc3_gadget_ep0_queue’: drivers/usb/dwc3/ep0.c:129:8: warning: unused variable ‘type’ [-Wunused-variable] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 74a3828..c8df1dd 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -126,7 +126,6 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; - u32 type; int ret = 0; req->request.actual = 0; -- cgit v0.10.2 From a85016390135d577c457876d0e905095600751de Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 4 Jan 2012 15:18:27 +0800 Subject: usb: gadget: storage: endian fix Fix some endian issues for storage gadgets. Cc: stable@vger.kernel.org Signed-off-by: Andiry Xu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index c7f291a..85ea14e 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -598,16 +598,16 @@ static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = { | USB_5GBPS_OPERATION), .bFunctionalitySupport = USB_LOW_SPEED_OPERATION, .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT, - .bU2DevExitLat = USB_DEFAULT_U2_DEV_EXIT_LAT, + .bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT), }; static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = { .bLength = USB_DT_BOS_SIZE, .bDescriptorType = USB_DT_BOS, - .wTotalLength = USB_DT_BOS_SIZE + .wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE + USB_DT_USB_EXT_CAP_SIZE - + USB_DT_USB_SS_CAP_SIZE, + + USB_DT_USB_SS_CAP_SIZE), .bNumDeviceCaps = 2, }; -- cgit v0.10.2 From 9e878a6bfa9e1cf70cf77caeca60a0465d77954b Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Mon, 16 Jan 2012 13:24:38 -0800 Subject: usb: gadget: SS Isoc endpoints use comp_desc->bMaxBurst too SuperSpeed Isoc endpoints also use the bMaxBurst value from the companion descriptor. See section 9.6.7 in the USB 3.0 spec. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a95de6a..baaebf2 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -175,13 +175,12 @@ ep_found: _ep->comp_desc = comp_desc; if (g->speed == USB_SPEED_SUPER) { switch (usb_endpoint_type(_ep->desc)) { - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_INT: - _ep->maxburst = comp_desc->bMaxBurst; - break; case USB_ENDPOINT_XFER_ISOC: /* mult: bits 1:0 of bmAttributes */ _ep->mult = comp_desc->bmAttributes & 0x3; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + _ep->maxburst = comp_desc->bMaxBurst; break; default: /* Do nothing for control endpoints */ -- cgit v0.10.2 From 7983bc74fc0bc91f026c7ba0654b08073d843657 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 16 Jan 2012 22:42:10 +0100 Subject: usb: renesas: silence uninitialized variable report in usbhsg_recip_run_handle() In drivers/usb/renesas_usbhs/mod_gadget.c::usbhsg_recip_run_handle() the Coverity Prevent checker currently flags a warning about possibly uninitialized use of 'ret' i usbhsg_recip_run_handle(). It does this since it assumes we take one of the non-default branches in the switch and then subsequently take the false branch in the 'if (func)' case below. This exact scenario will never happen, but Coverity can't see that for some reason. This patch initializes 'ret' to '0' when it is declared which should shut up this report and won't really hurt - so why not? At least then it's clear that 'ret' is always initialized.. Signed-off-by: Jesper Juhl Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 528691d5f..7542aa9 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -425,7 +425,7 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv, struct usbhs_pipe *pipe; int recip = ctrl->bRequestType & USB_RECIP_MASK; int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; - int ret; + int ret = 0; int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep, struct usb_ctrlrequest *ctrl); char *msg; -- cgit v0.10.2 From a37670b1c0f5dee021e451130653936742233457 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 13 Jan 2012 15:19:06 -0200 Subject: drivers: usb: otg: Fix dependencies for some OTG drivers Fix the following build warning: warning: (USB_LANGWELL_OTG && FSL_USB2_OTG && USB_MV_OTG) selects USB_OTG which has unmet direct dependencies (USB_SUPPORT && USB && EXPERIMENTAL && USB_SUSPEND) Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 9105c28..76d6293 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -110,7 +110,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 + depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help @@ -118,7 +118,7 @@ config FSL_USB2_OTG config USB_MV_OTG tristate "Marvell USB OTG support" - depends on USB_MV_UDC + depends on USB_MV_UDC && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help -- cgit v0.10.2 From 118d63f7f84cd400ba537f5d318c035c95c6776d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 11 Jan 2012 13:39:07 +0800 Subject: usb: gadget: fsl_udc: fix the usage of udc->max_ep The max_ep is the number of endpoint * 2. But in dtd_complete_irq, it does again * 2, it will deference wrong memory after scanning max_ep - 1. The another similar problem is at USB_REQ_SET_FEATURE (the pipe number should be 0 and max_ep - 1). Signed-off-by: Peter Chen Signed-off-by: Matthieu castet Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d7ea6c0..b04712f 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1430,7 +1430,7 @@ static void setup_received_irq(struct fsl_udc *udc, int pipe = get_pipe_by_windex(wIndex); struct fsl_ep *ep; - if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) + if (wValue != 0 || wLength != 0 || pipe >= udc->max_ep) break; ep = get_ep_by_pipe(udc, pipe); @@ -1673,7 +1673,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) if (!bit_pos) return; - for (i = 0; i < udc->max_ep * 2; i++) { + for (i = 0; i < udc->max_ep; i++) { ep_num = i >> 1; direction = i % 2; -- cgit v0.10.2 From c74c930082fd407e3b9e503d855d78777a8e5a84 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 21:42:44 +0100 Subject: usb: gadget: check for streams only for SS udcs Currently the UASP gadget fails to bind on an UDC which does not provide stream support. This is true for all udc in tree except for dummy and dwc3 since they don't support SuperSpeed. There is no need to test for the availability of stream support on those UDCs because we will never even try to use them. I think it is sane to assume that StreamSupport is always available on SuperSpeed since it is one of the key features. The host side will only allocate on SS so this part is also fine. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 753aa06..e0e6375 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -126,7 +126,7 @@ ep_matches ( * descriptor and see if the EP matches it */ if (usb_endpoint_xfer_bulk(desc)) { - if (ep_comp) { + if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) { num_req_streams = ep_comp->bmAttributes & 0x1f; if (num_req_streams > ep->max_streams) return 0; -- cgit v0.10.2 From 4b5203f1883e2dd49273e9f91235c36a0708aad1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 12 Jan 2012 16:09:15 -0200 Subject: usb: gadget: f_mass_storage: Use "bool" instead of "int" in fsg_module_parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following build warnings: CC [M] drivers/usb/gadget/acm_ms.o drivers/usb/gadget/acm_ms.c: In function ‘__check_ro’: drivers/usb/gadget/acm_ms.c:119: warning: return from incompatible pointer type drivers/usb/gadget/acm_ms.c: In function ‘__check_removable’: drivers/usb/gadget/acm_ms.c:119: warning: return from incompatible pointer type drivers/usb/gadget/acm_ms.c: In function ‘__check_cdrom’: drivers/usb/gadget/acm_ms.c:119: warning: return from incompatible pointer type drivers/usb/gadget/acm_ms.c: In function ‘__check_nofua’: drivers/usb/gadget/acm_ms.c:119: warning: return from incompatible pointer type drivers/usb/gadget/acm_ms.c: In function ‘__check_stall’: drivers/usb/gadget/acm_ms.c:119: warning: return from incompatible pointer type CC [M] drivers/usb/gadget/mass_storage.o drivers/usb/gadget/mass_storage.c: In function ‘__check_ro’: drivers/usb/gadget/mass_storage.c:94: warning: return from incompatible pointer type drivers/usb/gadget/mass_storage.c: In function ‘__check_removable’: drivers/usb/gadget/mass_storage.c:94: warning: return from incompatible pointer type drivers/usb/gadget/mass_storage.c: In function ‘__check_cdrom’: drivers/usb/gadget/mass_storage.c:94: warning: return from incompatible pointer type drivers/usb/gadget/mass_storage.c: In function ‘__check_nofua’: drivers/usb/gadget/mass_storage.c:94: warning: return from incompatible pointer type drivers/usb/gadget/mass_storage.c: In function ‘__check_stall’: drivers/usb/gadget/mass_storage.c:94: warning: return from incompatible pointer type Declare the fsg_module_parameters fields as "bool" so that they can match the types passed in FSG_MODULE_PARAM_ARRAY macro. Since commit 493c90ef (module_param: check that bool parameters really are bool.), moduleparam.h was changed in a way that the "bool" parameter type now really requires "bool" type and no longer allows "unsigned int". Signed-off-by: Fabio Estevam Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 6353eca..ee8ceec 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3123,15 +3123,15 @@ fsg_add(struct usb_composite_dev *cdev, struct usb_configuration *c, struct fsg_module_parameters { char *file[FSG_MAX_LUNS]; - int ro[FSG_MAX_LUNS]; - int removable[FSG_MAX_LUNS]; - int cdrom[FSG_MAX_LUNS]; - int nofua[FSG_MAX_LUNS]; + bool ro[FSG_MAX_LUNS]; + bool removable[FSG_MAX_LUNS]; + bool cdrom[FSG_MAX_LUNS]; + bool nofua[FSG_MAX_LUNS]; unsigned int file_count, ro_count, removable_count, cdrom_count; unsigned int nofua_count; unsigned int luns; /* nluns */ - int stall; /* can_stall */ + bool stall; /* can_stall */ }; #define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ -- cgit v0.10.2 From 24307caef4950e42e7875a901856ed8816c4679c Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Thu, 12 Jan 2012 15:22:45 +0200 Subject: usb: musb: fix shutdown while usb gadget is in use If we shutdown without stopping the gadget first or removing the cable, gadget manages to configure itself again: root@pandora /root# poweroff The system is going down NOW! Requesting system poweroff [ 47.714385] musb-hm halted. [ 48.120697] gadget: suspend [ 48.123748] gadget: reset config [ 48.127227] gadget: ecm deactivated [ 48.130981] usb0: gether_disconnect [ 48.281799] gadget: high-speed config #1: CDC Ethernet (ECM) [ 48.287872] gadget: init ecm [ 48.290985] gadget: notify connect false [ 48.295288] gadget: notify speed 425984000 This is not only unwanted, it's also happening on half-unitialized state, after musb_shutdown() has returned, which sometimes causes hardware to fail to work after reboot. Let's better properly stop gadget on shutdown too. This patch moves musb_gadget_cleanup out of musb_free(), which has 2 callsites: probe error path and musb_remove. On probe error path it was superflous since musb_gadget_cleanup is called explicitly there, and musb_remove() calls musb_shutdown(), so cleanup will get called as before. Signed-off-by: Grazvydas Ignotas Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 56cf024..3d11cf64 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -981,6 +981,9 @@ static void musb_shutdown(struct platform_device *pdev) unsigned long flags; pm_runtime_get_sync(musb->controller); + + musb_gadget_cleanup(musb); + spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); @@ -1827,8 +1830,6 @@ static void musb_free(struct musb *musb) sysfs_remove_group(&musb->controller->kobj, &musb_attr_group); #endif - musb_gadget_cleanup(musb); - if (musb->nIrq >= 0) { if (musb->irq_wake) disable_irq_wake(musb->nIrq); -- cgit v0.10.2 From c09d6b51d78f5ad33417dbac9b479bd6709f9f25 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 24 Jan 2012 12:44:34 +0100 Subject: usb: dwc3: unmap the proper number of sg entries num_sgs contains the number of sgs assigned by the gadget. num_mapped_sgs contains the number of mapped sgs which may differ from the gadget's values. For dma_unmap_sg() we have to provide the value which was returned by dma_map_sg(). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a696bde..064b6e2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -101,7 +101,7 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) if (req->request.num_mapped_sgs) { req->request.dma = DMA_ADDR_INVALID; dma_unmap_sg(dwc->dev, req->request.sg, - req->request.num_sgs, + req->request.num_mapped_sgs, req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); -- cgit v0.10.2 From 4d20bb1d5fe1afbdbff951c06cd3d3654fa5ceed Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Tue, 17 Jan 2012 11:41:47 +0800 Subject: ALSA: ymfpci - Don't create invalid PCM & mixers when AC97 doesn't support - check SDAC bit of AC97 primary codec when create "rear" device 3, "4ch" device 2 and "4ch Duplication" switch as the card need a four channels AC97 codec to support surround40. Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index e57b89e8..94ab728 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -286,17 +286,22 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci, snd_card_free(card); return err; } - if ((err = snd_ymfpci_pcm_4ch(chip, 2, NULL)) < 0) { + err = snd_ymfpci_mixer(chip, rear_switch[dev]); + if (err < 0) { snd_card_free(card); return err; } - if ((err = snd_ymfpci_pcm2(chip, 3, NULL)) < 0) { - snd_card_free(card); - return err; - } - if ((err = snd_ymfpci_mixer(chip, rear_switch[dev])) < 0) { - snd_card_free(card); - return err; + if (chip->ac97->ext_id & AC97_EI_SDAC) { + err = snd_ymfpci_pcm_4ch(chip, 2, NULL); + if (err < 0) { + snd_card_free(card); + return err; + } + err = snd_ymfpci_pcm2(chip, 3, NULL); + if (err < 0) { + snd_card_free(card); + return err; + } } if ((err = snd_ymfpci_timer(chip, 0)) < 0) { snd_card_free(card); diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 03ee4e3..12a9a2b 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1614,6 +1614,14 @@ static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_e return change; } +static struct snd_kcontrol_new snd_ymfpci_dup4ch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "4ch Duplication", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ymfpci_info_dup4ch, + .get = snd_ymfpci_get_dup4ch, + .put = snd_ymfpci_put_dup4ch, +}; static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = { { @@ -1642,13 +1650,6 @@ YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL), YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL, 0), YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL, 0), YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4), -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "4ch Duplication", - .info = snd_ymfpci_info_dup4ch, - .get = snd_ymfpci_get_dup4ch, - .put = snd_ymfpci_put_dup4ch, -}, }; @@ -1838,6 +1839,12 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch) if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) return err; } + if (chip->ac97->ext_id & AC97_EI_SDAC) { + kctl = snd_ctl_new1(&snd_ymfpci_dup4ch, chip); + err = snd_ctl_add(chip->card, kctl); + if (err < 0) + return err; + } /* add S/PDIF control */ if (snd_BUG_ON(!chip->pcm_spdif)) -- cgit v0.10.2 From 769fab2a41da4bd3c59eee38f47d6d5405738fe0 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 23 Jan 2012 21:02:57 +0100 Subject: ALSA: Fix memory leak on error in snd_compr_set_params() If copy_from_user() does not return 0 we'll leak the memory we allocated for 'params' when that variable goes out of scope. Also a small CodingStyle cleanup: Use braces on both branches of if/else when one branch needs it. Signed-off-by: Jesper Juhl Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index dac3633..a68aed7 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -441,19 +441,22 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) params = kmalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; - if (copy_from_user(params, (void __user *)arg, sizeof(*params))) - return -EFAULT; + if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { + retval = -EFAULT; + goto out; + } retval = snd_compr_allocate_buffer(stream, params); if (retval) { - kfree(params); - return -ENOMEM; + retval = -ENOMEM; + goto out; } retval = stream->ops->set_params(stream, params); if (retval) goto out; stream->runtime->state = SNDRV_PCM_STATE_SETUP; - } else + } else { return -EPERM; + } out: kfree(params); return retval; -- cgit v0.10.2 From 803ab977618eae2b292cda0a97eed75f42250ddf Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 24 Jan 2012 11:39:22 +0300 Subject: cifs: NULL dereference on allocation failure We should just return directly here, the goto causes a NULL dereference. Signed-off-by: Dan Carpenter Reviewed-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 986709a..026d646 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3857,10 +3857,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) struct smb_vol *vol_info; vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); - if (vol_info == NULL) { - tcon = ERR_PTR(-ENOMEM); - goto out; - } + if (vol_info == NULL) + return ERR_PTR(-ENOMEM); vol_info->local_nls = cifs_sb->local_nls; vol_info->linux_uid = fsuid; -- cgit v0.10.2 From 7a7546b377bdaa25ac77f33d9433c59f259b9688 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 23 Jan 2012 19:32:25 +0000 Subject: x86: xen: size struct xen_spinlock to always fit in arch_spinlock_t If NR_CPUS < 256 then arch_spinlock_t is only 16 bits wide but struct xen_spinlock is 32 bits. When a spin lock is contended and xl->spinners is modified the two bytes immediately after the spin lock would be corrupted. This is a regression caused by 84eb950db13ca40a0572ce9957e14723500943d6 (x86, ticketlock: Clean up types and accessors) which reduced the size of arch_spinlock_t. Fix this by making xl->spinners a u8 if NR_CPUS < 256. A BUILD_BUG_ON() is also added to check the sizes of the two structures are compatible. In many cases this was not noticable as there would often be padding bytes after the lock (e.g., if any of CONFIG_GENERIC_LOCKBREAK, CONFIG_DEBUG_SPINLOCK, or CONFIG_DEBUG_LOCK_ALLOC were enabled). The bnx2 driver is affected. In struct bnx2, phy_lock and indirect_lock may have no padding after them. Contention on phy_lock would corrupt indirect_lock making it appear locked and the driver would deadlock. Signed-off-by: David Vrabel Signed-off-by: Jeremy Fitzhardinge Acked-by: Ian Campbell CC: stable@kernel.org #only 3.2 Signed-off-by: Konrad Rzeszutek Wilk diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index cc9b1e1..d69cc6c 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -116,9 +116,26 @@ static inline void spin_time_accum_blocked(u64 start) } #endif /* CONFIG_XEN_DEBUG_FS */ +/* + * Size struct xen_spinlock so it's the same as arch_spinlock_t. + */ +#if NR_CPUS < 256 +typedef u8 xen_spinners_t; +# define inc_spinners(xl) \ + asm(LOCK_PREFIX " incb %0" : "+m" ((xl)->spinners) : : "memory"); +# define dec_spinners(xl) \ + asm(LOCK_PREFIX " decb %0" : "+m" ((xl)->spinners) : : "memory"); +#else +typedef u16 xen_spinners_t; +# define inc_spinners(xl) \ + asm(LOCK_PREFIX " incw %0" : "+m" ((xl)->spinners) : : "memory"); +# define dec_spinners(xl) \ + asm(LOCK_PREFIX " decw %0" : "+m" ((xl)->spinners) : : "memory"); +#endif + struct xen_spinlock { unsigned char lock; /* 0 -> free; 1 -> locked */ - unsigned short spinners; /* count of waiting cpus */ + xen_spinners_t spinners; /* count of waiting cpus */ }; static int xen_spin_is_locked(struct arch_spinlock *lock) @@ -164,8 +181,7 @@ static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) wmb(); /* set lock of interest before count */ - asm(LOCK_PREFIX " incw %0" - : "+m" (xl->spinners) : : "memory"); + inc_spinners(xl); return prev; } @@ -176,8 +192,7 @@ static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) */ static inline void unspinning_lock(struct xen_spinlock *xl, struct xen_spinlock *prev) { - asm(LOCK_PREFIX " decw %0" - : "+m" (xl->spinners) : : "memory"); + dec_spinners(xl); wmb(); /* decrement count before restoring lock */ __this_cpu_write(lock_spinners, prev); } @@ -373,6 +388,8 @@ void xen_uninit_lock_cpu(int cpu) void __init xen_init_spinlocks(void) { + BUILD_BUG_ON(sizeof(struct xen_spinlock) > sizeof(arch_spinlock_t)); + pv_lock_ops.spin_is_locked = xen_spin_is_locked; pv_lock_ops.spin_is_contended = xen_spin_is_contended; pv_lock_ops.spin_lock = xen_spin_lock; -- cgit v0.10.2 From a3f83ab1a717c0e6c2f59a4cfdaa10707cc35c55 Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Sun, 22 Jan 2012 18:43:25 +0400 Subject: drm/radeon: fix invalid memory access in radeon_atrm_get_bios() At a boot time I observed following bug: BUG: unable to handle kernel paging request at ffff8800a4244000 IP: [] memcpy+0xb/0x120 PGD 1816063 PUD 1fe7d067 PMD 1ff9f067 PTE 80000000a4244160 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC CPU 0 Modules linked in: btusb bluetooth brcmsmac brcmutil crc8 cordic b43 radeon(+) mac80211 cfg80211 ttm ohci_hcd drm_kms_helper rfkill drm ssb agpgart mmc_core sp5100_tco video battery ac thermal processor rtc_cmos thermal_sys snd_hda_codec_hdmi joydev snd_hda_codec_conexant button bcma pcmcia snd_hda_intel snd_hda_codec snd_hwdep snd_pcm shpchp pcmcia_core k8temp snd_timer atl1c snd psmouse hwmon i2c_piix4 i2c_algo_bit soundcore evdev i2c_core ehci_hcd sg serio_raw snd_page_alloc loop btrfs Pid: 1008, comm: modprobe Not tainted 3.3.0-rc1 #21 LENOVO 20046 /AMD CRB RIP: 0010:[] [] memcpy+0xb/0x120 RSP: 0018:ffff8800aa72db00 EFLAGS: 00010246 RAX: ffff8800a4150000 RBX: 0000000000001000 RCX: 0000000000000087 RDX: 0000000000000000 RSI: ffff8800a4244000 RDI: ffff8800a4150bc8 RBP: ffff8800aa72db78 R08: 0000000000000010 R09: ffffffff8174bbec R10: ffffffff812ee010 R11: 0000000000000001 R12: 0000000000001000 R13: 0000000000010000 R14: ffff8800a4140000 R15: ffff8800aaba1800 FS: 00007ff9a3bd4720(0000) GS:ffff8800afa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: ffff8800a4244000 CR3: 00000000a9c18000 CR4: 00000000000006f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process modprobe (pid: 1008, threadinfo ffff8800aa72c000, task ffff8800aa0e4000) Stack: ffffffffa04e7c7b 0000000000000001 0000000000010000 ffff8800aa72db28 ffffffff00000001 0000000000001000 ffffffff8113cbef 0000000000000020 ffff8800a4243420 ffff880000000002 ffff8800aa72db08 ffff8800a9d42000 Call Trace: [] ? radeon_atrm_get_bios_chunk+0x8b/0xd0 [radeon] [] ? kmalloc_order_trace+0x3f/0xb0 [] radeon_get_bios+0x68/0x2f0 [radeon] [] rv770_init+0x40/0x280 [radeon] [] radeon_device_init+0x560/0x600 [radeon] [] radeon_driver_load_kms+0xaf/0x170 [radeon] [] drm_get_pci_dev+0x18e/0x2c0 [drm] [] radeon_pci_probe+0xad/0xb5 [radeon] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x88/0xb0 [] ? driver_sysfs_add+0x7a/0xb0 [] really_probe+0x68/0x180 [] driver_probe_device+0x45/0x70 [] __driver_attach+0xa3/0xb0 [] ? driver_probe_device+0x70/0x70 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x280 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] drm_pci_init+0x111/0x120 [drm] [] ? vga_switcheroo_register_handler+0x3a/0x60 [] ? 0xffffffffa0228fff [] radeon_init+0xec/0xee [radeon] [] do_one_initcall+0x42/0x180 [] sys_init_module+0x92/0x1e0 [] system_call_fastpath+0x16/0x1b Code: 58 2a 43 50 88 43 4e 48 83 c4 08 5b c9 c3 66 90 e8 cb fd ff ff eb e6 90 90 90 90 90 90 90 90 90 48 89 f8 89 d1 c1 e9 03 83 e2 07 48 a5 89 d1 f3 a4 c3 20 48 83 ea 20 4c 8b 06 4c 8b 4e 08 4c RIP [] memcpy+0xb/0x120 RSP CR2: ffff8800a4244000 ---[ end trace fcffa1599cf56382 ]--- Call to acpi_evaluate_object() not always returns 4096 bytes chunks, on my system it can return 2048 bytes chunk, so pass the length of retrieved chunk to memcpy(), not the length of the recieving buffer. Signed-off-by: Igor Murzov Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index 9d95792..c666a5b 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -58,7 +58,7 @@ static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, } obj = (union acpi_object *)buffer.pointer; - memcpy(bios+offset, obj->buffer.pointer, len); + memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); kfree(buffer.pointer); return len; } -- cgit v0.10.2 From 211fa4fc4e13492151e698d92b0dff56b29928ec Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Sun, 22 Jan 2012 18:47:28 +0400 Subject: drm/radeon: finish getting bios earlier Return a number of bytes read in radeon_atrm_get_bios_chunk() and properly check this value in radeon_atrm_get_bios(). If radeon_atrm_get_bios_chunk() read less bytes then were requested, it means that it finished reading bios data. Prior to this patch, condition in radeon_atrm_get_bios() was always equivalent to "if (ATRM_BIOS_PAGE <= 0)", so it was always false, thus radeon_atrm_get_bios() was trying to read past the bios data wasting boot time. On my lenovo ideapad u455 laptop this patch drops bios reading time from ~5.5s to ~1.5s. Signed-off-by: Igor Murzov Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index c666a5b..13ac63b 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -60,7 +60,7 @@ static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, obj = (union acpi_object *)buffer.pointer; memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); kfree(buffer.pointer); - return len; + return obj->buffer.length; } bool radeon_atrm_supported(struct pci_dev *pdev) diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 229a20f..501f488 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -120,7 +120,7 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev) ret = radeon_atrm_get_bios_chunk(rdev->bios, (i * ATRM_BIOS_PAGE), ATRM_BIOS_PAGE); - if (ret <= 0) + if (ret < ATRM_BIOS_PAGE) break; } -- cgit v0.10.2 From 3fa47d9efa6a0f5123e26e2c3ad54e3e1a1d108d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Jan 2012 14:56:39 -0500 Subject: drm/radeon/kms: move disp eng pll setup to init path We really only need to set it up once on init or resume rather than on every mode set. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 0fda830..807b89b 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -355,15 +355,12 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc, atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } -static void atombios_disable_ss(struct drm_crtc *crtc) +static void atombios_disable_ss(struct radeon_device *rdev, int pll_id) { - struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; u32 ss_cntl; if (ASIC_IS_DCE4(rdev)) { - switch (radeon_crtc->pll_id) { + switch (pll_id) { case ATOM_PPLL1: ss_cntl = RREG32(EVERGREEN_P1PLL_SS_CNTL); ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; @@ -379,7 +376,7 @@ static void atombios_disable_ss(struct drm_crtc *crtc) return; } } else if (ASIC_IS_AVIVO(rdev)) { - switch (radeon_crtc->pll_id) { + switch (pll_id) { case ATOM_PPLL1: ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); ss_cntl &= ~1; @@ -406,13 +403,11 @@ union atom_enable_ss { ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3; }; -static void atombios_crtc_program_ss(struct drm_crtc *crtc, +static void atombios_crtc_program_ss(struct radeon_device *rdev, int enable, int pll_id, struct radeon_atom_ss *ss) { - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); union atom_enable_ss args; @@ -479,7 +474,7 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc, } else if (ASIC_IS_AVIVO(rdev)) { if ((enable == ATOM_DISABLE) || (ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) { - atombios_disable_ss(crtc); + atombios_disable_ss(rdev, pll_id); return; } args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); @@ -491,7 +486,7 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc, } else { if ((enable == ATOM_DISABLE) || (ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) { - atombios_disable_ss(crtc); + atombios_disable_ss(rdev, pll_id); return; } args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); @@ -702,11 +697,9 @@ union set_pixel_clock { /* on DCE5, make sure the voltage is high enough to support the * required disp clk. */ -static void atombios_crtc_set_dcpll(struct drm_crtc *crtc, +static void atombios_crtc_set_dcpll(struct radeon_device *rdev, u32 dispclk) { - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; u8 frev, crev; int index; union set_pixel_clock args; @@ -996,7 +989,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, &ref_div, &post_div); - atombios_crtc_program_ss(crtc, ATOM_DISABLE, radeon_crtc->pll_id, &ss); + atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, &ss); atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, encoder_mode, radeon_encoder->encoder_id, mode->clock, @@ -1019,7 +1012,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode ss.step = step_size; } - atombios_crtc_program_ss(crtc, ATOM_ENABLE, radeon_crtc->pll_id, &ss); + atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, &ss); } } @@ -1494,6 +1487,24 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) } +void radeon_atom_dcpll_init(struct radeon_device *rdev) +{ + /* always set DCPLL */ + if (ASIC_IS_DCE4(rdev)) { + struct radeon_atom_ss ss; + bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_SS_ON_DCPLL, + rdev->clock.default_dispclk); + if (ss_enabled) + atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, &ss); + /* XXX: DCE5, make sure voltage, dispclk is high enough */ + atombios_crtc_set_dcpll(rdev, rdev->clock.default_dispclk); + if (ss_enabled) + atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, &ss); + } + +} + int atombios_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -1515,19 +1526,6 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, } } - /* always set DCPLL */ - if (ASIC_IS_DCE4(rdev)) { - struct radeon_atom_ss ss; - bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss, - ASIC_INTERNAL_SS_ON_DCPLL, - rdev->clock.default_dispclk); - if (ss_enabled) - atombios_crtc_program_ss(crtc, ATOM_DISABLE, ATOM_DCPLL, &ss); - /* XXX: DCE5, make sure voltage, dispclk is high enough */ - atombios_crtc_set_dcpll(crtc, rdev->clock.default_dispclk); - if (ss_enabled) - atombios_crtc_program_ss(crtc, ATOM_ENABLE, ATOM_DCPLL, &ss); - } atombios_crtc_set_pll(crtc, adjusted_mode); if (ASIC_IS_DCE4(rdev)) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 0afb13b..a811bc6 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -959,9 +959,11 @@ int radeon_resume_kms(struct drm_device *dev) radeon_fbdev_set_suspend(rdev, 0); console_unlock(); - /* init dig PHYs */ - if (rdev->is_atom_bios) + /* init dig PHYs, disp eng pll */ + if (rdev->is_atom_bios) { radeon_atom_encoder_init(rdev); + radeon_atom_dcpll_init(rdev); + } /* reset hpd state */ radeon_hpd_init(rdev); /* blat the mode back in */ diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index d3ffc18..8c49fef 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1305,9 +1305,11 @@ int radeon_modeset_init(struct radeon_device *rdev) return ret; } - /* init dig PHYs */ - if (rdev->is_atom_bios) + /* init dig PHYs, disp eng pll */ + if (rdev->is_atom_bios) { radeon_atom_encoder_init(rdev); + radeon_atom_dcpll_init(rdev); + } /* initialize hpd */ radeon_hpd_init(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 08ff857..8cb19f3 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -484,6 +484,7 @@ extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector); extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector); extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode); extern void radeon_atom_encoder_init(struct radeon_device *rdev); +extern void radeon_atom_dcpll_init(struct radeon_device *rdev); extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set); -- cgit v0.10.2 From 386d4d751e8e0b4b693bb724f09aae064ee5297d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Jan 2012 15:01:29 -0500 Subject: drm/radeon/kms: move panel mode setup into encoder mode set Needs to happen earlier in the mode set. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 6fb335a..a71557c 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -549,8 +549,8 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) return false; } -static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, - struct drm_connector *connector) +int radeon_dp_get_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; @@ -558,7 +558,7 @@ static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; if (!ASIC_IS_DCE4(rdev)) - return; + return panel_mode; if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == ENCODER_OBJECT_ID_NUTMEG) @@ -572,14 +572,7 @@ static void radeon_dp_set_panel_mode(struct drm_encoder *encoder, panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; } - atombios_dig_encoder_setup(encoder, - ATOM_ENCODER_CMD_SETUP_PANEL_MODE, - panel_mode); - - if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) && - (panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { - radeon_write_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_SET, 1); - } + return panel_mode; } void radeon_dp_set_link_config(struct drm_connector *connector, @@ -717,6 +710,8 @@ static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(dp_info->encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; u8 tmp; /* power up the sink */ @@ -732,7 +727,10 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_DOWNSPREAD_CTRL, 0); - radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector); + if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) && + (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) { + radeon_write_dpcd_reg(dp_info->radeon_connector, DP_EDP_CONFIGURATION_SET, 1); + } /* set the lane count on the sink */ tmp = dp_info->dp_lane_count; diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index f1f06ca..a3a9166 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1811,10 +1811,21 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: if (ASIC_IS_DCE4(rdev)) { + struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + + if (!connector) + dig->panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE; + else + dig->panel_mode = radeon_dp_get_panel_mode(encoder, connector); + /* disable the transmitter */ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); /* setup and enable the encoder */ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); + atombios_dig_encoder_setup(encoder, + ATOM_ENCODER_CMD_SETUP_PANEL_MODE, + dig->panel_mode); /* enable the transmitter */ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 8cb19f3..d34dcb6 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -362,6 +362,7 @@ struct radeon_encoder_atom_dig { struct backlight_device *bl_dev; int dpms_mode; uint8_t backlight_level; + int panel_mode; }; struct radeon_encoder_atom_dac { @@ -482,6 +483,8 @@ extern void radeon_dp_link_train(struct drm_encoder *encoder, extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector); extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector); extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector); +extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder, + struct drm_connector *connector); extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode); extern void radeon_atom_encoder_init(struct radeon_device *rdev); extern void radeon_atom_dcpll_init(struct radeon_device *rdev); -- cgit v0.10.2 From 3a47824d85eeca122895646f027dc63480994199 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Jan 2012 15:01:30 -0500 Subject: drm/radeon/kms: rework modeset sequence for DCE41 and DCE5 dig transmitter control table only has ENABLE/DISABLE actions on DCE4.1/DCE5. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=44955 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index a3a9166..79a4880 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1341,7 +1341,8 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) switch (mode) { case DRM_MODE_DPMS_ON: /* some early dce3.2 boards have a bug in their transmitter control table */ - if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730)) + if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730) || + ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); else atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); @@ -1351,8 +1352,6 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) ATOM_TRANSMITTER_ACTION_POWER_ON); radeon_dig_connector->edp_on = true; } - if (ASIC_IS_DCE4(rdev)) - atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); radeon_dp_link_train(encoder, connector); if (ASIC_IS_DCE4(rdev)) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); @@ -1363,7 +1362,10 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + else + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0); if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) { if (ASIC_IS_DCE4(rdev)) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0); @@ -1810,7 +1812,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - if (ASIC_IS_DCE4(rdev)) { + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { struct drm_connector *connector = radeon_get_connector_for_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; @@ -1819,13 +1821,16 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, else dig->panel_mode = radeon_dp_get_panel_mode(encoder, connector); - /* disable the transmitter */ - atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); /* setup and enable the encoder */ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP_PANEL_MODE, dig->panel_mode); + } else if (ASIC_IS_DCE4(rdev)) { + /* disable the transmitter */ + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0); + /* setup and enable the encoder */ + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0); /* enable the transmitter */ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); -- cgit v0.10.2 From 27d9cc8428367e0fec2fc0fc6385e9241b079c3a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Jan 2012 15:03:29 -0500 Subject: drm/radeon/kms: use drm_detect_hdmi_monitor for picking encoder mode We were previously just checking for audio. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 79a4880..f2f14a2 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -432,7 +432,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ - if (drm_detect_monitor_audio(radeon_connector->edid) && + if (drm_detect_hdmi_monitor(radeon_connector->edid) && radeon_audio) return ATOM_ENCODER_MODE_HDMI; else if (radeon_connector->use_digital) @@ -443,7 +443,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_HDMIA: default: - if (drm_detect_monitor_audio(radeon_connector->edid) && + if (drm_detect_hdmi_monitor(radeon_connector->edid) && radeon_audio) return ATOM_ENCODER_MODE_HDMI; else @@ -457,7 +457,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) return ATOM_ENCODER_MODE_DP; - else if (drm_detect_monitor_audio(radeon_connector->edid) && + else if (drm_detect_hdmi_monitor(radeon_connector->edid) && radeon_audio) return ATOM_ENCODER_MODE_HDMI; else -- cgit v0.10.2 From 9aa59993e226af94088adaee993eb8cfd33ae295 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 20 Jan 2012 15:03:30 -0500 Subject: drm/radeon/kms: refine TMDS dual link checks HDMI 1.3 defines single link clocks up to 340 Mhz. Refine the current dual link checks to only enable dual link for DVI > 165 Mhz or HDMI > 340 Mhz if the hw supports HDMI 1.3 (DCE3+). Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=44755 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 807b89b..8919352 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -518,6 +518,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, int encoder_mode = 0; u32 dp_clock = mode->clock; int bpc = 8; + bool is_duallink = false; /* reset the pll flags */ pll->flags = 0; @@ -552,6 +553,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (connector && connector->display_info.bpc) bpc = connector->display_info.bpc; encoder_mode = atombios_get_encoder_mode(encoder); + is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock); if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) { if (connector) { @@ -647,7 +649,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (dig->coherent_mode) args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_COHERENT_MODE; - if (mode->clock > 165000) + if (is_duallink) args.v3.sInput.ucDispPllConfig |= DISPPLL_CONFIG_DUAL_LINK; } diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index f2f14a2..b88c460 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -57,22 +57,6 @@ static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder) } } -static struct drm_connector * -radeon_get_connector_for_encoder_init(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct drm_connector *connector; - struct radeon_connector *radeon_connector; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - radeon_connector = to_radeon_connector(connector); - if (radeon_encoder->devices & radeon_connector->devices) - return connector; - } - return NULL; -} - static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -253,7 +237,7 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action) /* R4xx, R5xx */ args.ext_tmds.sXTmdsEncoder.ucEnable = action; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.ext_tmds.sXTmdsEncoder.ucMisc |= PANEL_ENCODER_MISC_DUAL; args.ext_tmds.sXTmdsEncoder.ucMisc |= ATOM_PANEL_MISC_888RGB; @@ -265,7 +249,7 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action) /* DFP1, CRT1, TV1 depending on the type of port */ args.dvo.sDVOEncoder.ucDeviceType = ATOM_DEVICE_DFP1_INDEX; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.dvo.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute |= PANEL_ENCODER_MISC_DUAL; break; case 3: @@ -349,7 +333,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) } else { if (dig->linkb) args.v1.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL; /*if (pScrn->rgbBits == 8) */ args.v1.ucMisc |= ATOM_PANEL_MISC_888RGB; @@ -388,7 +372,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) } else { if (dig->linkb) args.v2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL; } break; @@ -587,7 +571,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) args.v1.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v1.ucLaneNum = 8; else args.v1.ucLaneNum = 4; @@ -622,7 +606,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) args.v3.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v3.ucLaneNum = 8; else args.v3.ucLaneNum = 4; @@ -662,7 +646,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode)) args.v4.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v4.ucLaneNum = 8; else args.v4.ucLaneNum = 4; @@ -806,7 +790,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v1.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -821,7 +805,8 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if ((rdev->flags & RADEON_IS_IGP) && (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) { - if (is_dp || (radeon_encoder->pixel_clock <= 165000)) { + if (is_dp || + !radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) { if (igp_lane_info & 0x1) args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3; else if (igp_lane_info & 0x2) @@ -848,7 +833,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK; } break; @@ -863,7 +848,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v2.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -891,7 +876,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v2.acConfig.fCoherentMode = 1; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v2.acConfig.fDualLinkConnector = 1; } break; @@ -906,7 +891,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v3.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v3.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -914,7 +899,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v3.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v3.ucLaneNum = 8; else args.v3.ucLaneNum = 4; @@ -951,7 +936,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v3.acConfig.fCoherentMode = 1; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v3.acConfig.fDualLinkConnector = 1; } break; @@ -966,7 +951,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v4.usPixelClock = cpu_to_le16(dp_clock / 10); - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v4.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); else args.v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); @@ -974,7 +959,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t if (is_dp) args.v4.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v4.ucLaneNum = 8; else args.v4.ucLaneNum = 4; @@ -1014,7 +999,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { if (dig->coherent_mode) args.v4.acConfig.fCoherentMode = 1; - if (radeon_encoder->pixel_clock > 165000) + if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v4.acConfig.fDualLinkConnector = 1; } break; @@ -1137,7 +1122,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder, if (dp_clock == 270000) args.v1.sDigEncoder.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; args.v1.sDigEncoder.ucLaneNum = dp_lane_count; - } else if (radeon_encoder->pixel_clock > 165000) + } else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v1.sDigEncoder.ucLaneNum = 8; else args.v1.sDigEncoder.ucLaneNum = 4; @@ -1156,7 +1141,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder, else if (dp_clock == 540000) args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_DPLINKRATE_5_40GHZ; args.v3.sExtEncoder.ucLaneNum = dp_lane_count; - } else if (radeon_encoder->pixel_clock > 165000) + } else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v3.sExtEncoder.ucLaneNum = 8; else args.v3.sExtEncoder.ucLaneNum = 4; diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 4b27efa..9419c51 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -202,6 +202,22 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder) return NULL; } +struct drm_connector * +radeon_get_connector_for_encoder_init(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + radeon_connector = to_radeon_connector(connector); + if (radeon_encoder->devices & radeon_connector->devices) + return connector; + } + return NULL; +} + struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; @@ -288,3 +304,64 @@ void radeon_panel_mode_fixup(struct drm_encoder *encoder, } +bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, + u32 pixel_clock) +{ + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_connector *connector; + struct radeon_connector *radeon_connector; + struct radeon_connector_atom_dig *dig_connector; + + connector = radeon_get_connector_for_encoder(encoder); + /* if we don't have an active device yet, just use one of + * the connectors tied to the encoder. + */ + if (!connector) + connector = radeon_get_connector_for_encoder_init(encoder); + radeon_connector = to_radeon_connector(connector); + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_HDMIB: + if (radeon_connector->use_digital) { + /* HDMI 1.3 supports up to 340 Mhz over single link */ + if (ASIC_IS_DCE3(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (pixel_clock > 340000) + return true; + else + return false; + } else { + if (pixel_clock > 165000) + return true; + else + return false; + } + } else + return false; + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_DisplayPort: + dig_connector = radeon_connector->con_priv; + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) + return false; + else { + /* HDMI 1.3 supports up to 340 Mhz over single link */ + if (ASIC_IS_DCE3(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (pixel_clock > 340000) + return true; + else + return false; + } else { + if (pixel_clock > 165000) + return true; + else + return false; + } + } + default: + return false; + } +} + diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index d34dcb6..4330e32 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -467,6 +467,10 @@ radeon_atombios_get_tv_info(struct radeon_device *rdev); extern struct drm_connector * radeon_get_connector_for_encoder(struct drm_encoder *encoder); +extern struct drm_connector * +radeon_get_connector_for_encoder_init(struct drm_encoder *encoder); +extern bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, + u32 pixel_clock); extern u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder); extern u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector); -- cgit v0.10.2 From 15b63d35261ba3351d07e7937252f18bb6cbf814 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 24 Jan 2012 16:57:42 +0000 Subject: gma500: Fix shmem mapping GMA500 did it the old way and it's been on the TODO list to fix. Current kernels now blow up if we use the old way so we'd better do the work ! Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index e770bd1..5d5330f 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -20,6 +20,7 @@ */ #include +#include #include "psb_drv.h" @@ -203,9 +204,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt) gt->npage = pages; for (i = 0; i < pages; i++) { - /* FIXME: needs updating as per mail from Hugh Dickins */ - p = read_cache_page_gfp(mapping, i, - __GFP_COLD | GFP_KERNEL); + p = shmem_read_mapping_page(mapping, i); if (IS_ERR(p)) goto err; gt->pages[i] = p; -- cgit v0.10.2 From e912b6d27cea198980132f012d14f22247e19ad6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 24 Jan 2012 16:57:42 +0000 Subject: gma500: Fix shmem mapping GMA500 did it the old way and it's been on the TODO list to fix. Current kernels now blow up if we use the old way so we'd better do the work! Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index e770bd1..5d5330f 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -20,6 +20,7 @@ */ #include +#include #include "psb_drv.h" @@ -203,9 +204,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt) gt->npage = pages; for (i = 0; i < pages; i++) { - /* FIXME: needs updating as per mail from Hugh Dickins */ - p = read_cache_page_gfp(mapping, i, - __GFP_COLD | GFP_KERNEL); + p = shmem_read_mapping_page(mapping, i); if (IS_ERR(p)) goto err; gt->pages[i] = p; -- cgit v0.10.2 From 4a7cbb56fdbd92a47f57ca8b25bf5db35f0d6518 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 Jan 2012 11:17:26 +0000 Subject: regulator: Fix documentation for of_node parameter of regulator_register() Commit 5bc75a886353 ("kernel-doc: fix new warning in regulator core") added documentation for of_node to address a warning but the documentation didn't explain what the parameter is for so would be likely to be unhelpful for users. Clarify that. Signed-off-by: Mark Brown Signed-off-by: Linus Torvalds diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b1fa25d..e9a83f8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2731,7 +2731,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) * @dev: struct device for the regulator * @init_data: platform provided init data, passed through by driver * @driver_data: private regulator data - * @of_node: target open firmware device structure (may be NULL) + * @of_node: OpenFirmware node to parse for device tree bindings (may be + * NULL). * * Called by regulator drivers to register a regulator. * Returns 0 on success. -- cgit v0.10.2 From 1a5e29fc2b90daf71a60329c29a1886fd126169a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:51 -0800 Subject: kernel-doc: fix new warnings in device.h Fix new kernel-doc warnings: Warning(include/linux/device.h:299): No description found for parameter 'name' Warning(include/linux/device.h:299): No description found for parameter 'subsys' Warning(include/linux/device.h:299): No description found for parameter 'node' Warning(include/linux/device.h:299): No description found for parameter 'add_dev' Warning(include/linux/device.h:299): No description found for parameter 'remove_dev' Warning(include/linux/device.h:685): No description found for parameter 'id' Warning(include/linux/device.h:1009): No description found for parameter '__driver' Warning(include/linux/device.h:1009): No description found for parameter '__register' Warning(include/linux/device.h:1009): No description found for parameter '__unregister' Signed-off-by: Randy Dunlap Cc: Lars-Peter Clausen Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/device.h b/include/linux/device.h index 5b3adb8..b63fb39 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -279,11 +279,11 @@ struct device *driver_find_device(struct device_driver *drv, /** * struct subsys_interface - interfaces to device functions - * @name name of the device function - * @subsystem subsytem of the devices to attach to - * @node the list of functions registered at the subsystem - * @add device hookup to device function handler - * @remove device hookup to device function handler + * @name: name of the device function + * @subsys: subsytem of the devices to attach to + * @node: the list of functions registered at the subsystem + * @add_dev: device hookup to device function handler + * @remove_dev: device hookup to device function handler * * Simple interfaces attached to a subsystem. Multiple interfaces can * attach to a subsystem and its devices. Unlike drivers, they do not @@ -612,6 +612,7 @@ struct device_dma_parameters { * @archdata: For arch-specific additions. * @of_node: Associated device tree node. * @devt: For creating the sysfs "dev". + * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. * @devres_head: The resources list of the device. * @knode_class: The node used to add the device to the class list. @@ -1003,6 +1004,10 @@ extern long sysfs_deprecated; * Each module may only use this macro once, and calling it replaces * module_init() and module_exit(). * + * @__driver: driver name + * @__register: register function for this driver type + * @__unregister: unregister function for this driver type + * * Use this macro to construct bus specific macros for registering * drivers, and do not use it on its own. */ -- cgit v0.10.2 From 0863b04d1578879173aacbc5c7be749fccb70809 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:42 -0800 Subject: kernel-doc: fix new warnings in debugfs Fix new kernel-doc warnings: Warning(fs/debugfs/file.c:556): No description found for parameter 'nregs' Warning(fs/debugfs/file.c:556): Excess function parameter 'mregs' description in 'debugfs_print_regs32' Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index f65d445..ef023ee 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -540,7 +540,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_blob); * debugfs_print_regs32 - use seq_print to describe a set of registers * @s: the seq_file structure being used to generate output * @regs: an array if struct debugfs_reg32 structures - * @mregs: the length of the above array + * @nregs: the length of the above array * @base: the base address to be used in reading the registers * @prefix: a string to be prefixed to every output line * -- cgit v0.10.2 From 4f4ffe52e1e5ddb9708fe075aaef4424f1fb744a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:28 -0800 Subject: kernel-doc: fix new warnings in driver-core Fix new kernel-doc warnings: Warning(drivers/base/bus.c:925): No description found for parameter 'key' Warning(drivers/base/bus.c:1241): No description found for parameter 'subsys' Warning(drivers/base/bus.c:1241): No description found for parameter 'groups' Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 99dc592..40fb122 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -915,9 +915,10 @@ static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /** * __bus_register - register a driver-core subsystem - * @bus: bus. + * @bus: bus to register + * @key: lockdep class key * - * Once we have that, we registered the bus with the kobject + * Once we have that, we register the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the subsystem. */ @@ -1220,8 +1221,8 @@ static void system_root_device_release(struct device *dev) } /** * subsys_system_register - register a subsystem at /sys/devices/system/ - * @subsys - system subsystem - * @groups - default attributes for the root device + * @subsys: system subsystem + * @groups: default attributes for the root device * * All 'system' subsystems have a /sys/devices/system/ root device * with the name of the subsystem. The root device can carry subsystem- -- cgit v0.10.2 From b10d5efdf7892d18b3b7d899edce2c8d9b80aea9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 17 Jan 2012 11:39:00 -0500 Subject: Documentation update for the driver model core This patch (as1509) documents two important points regarding the use of device structures in the driver model: Structures must be initialized to all 0's before they are passed to device_initialize(). Structures must not be passed to device_add() or device_register() more than once. Although these restrictions have applied ever since the driver model was first created, they have not been mentioned anywhere. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/core.c b/drivers/base/core.c index 4a67cc0..ad29e92 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -632,6 +632,11 @@ static void klist_children_put(struct klist_node *n) * may be used for reference counting of @dev after calling this * function. * + * All fields in @dev must be initialized by the caller to 0, except + * for those explicitly set to some other value. The simplest + * approach is to use kzalloc() to allocate the structure containing + * @dev. + * * NOTE: Use put_device() to give up your reference instead of freeing * @dev directly once you have called this function. */ @@ -930,6 +935,13 @@ int device_private_init(struct device *dev) * to the global and sibling lists for the device, then * adds it to the other relevant subsystems of the driver model. * + * Do not call this routine or device_register() more than once for + * any device structure. The driver model core is not designed to work + * with devices that get unregistered and then spring back to life. + * (Among other things, it's very hard to guarantee that all references + * to the previous incarnation of @dev have been dropped.) Allocate + * and register a fresh new struct device instead. + * * NOTE: _Never_ directly free @dev after calling this function, even * if it returned an error! Always use put_device() to give up your * reference instead. @@ -1090,6 +1102,9 @@ name_error: * have a clearly defined need to use and refcount the device * before it is added to the hierarchy. * + * For more information, see the kerneldoc for device_initialize() + * and device_add(). + * * NOTE: _Never_ directly free @dev after calling this function, even * if it returned an error! Always use put_device() to give up the * reference initialized in this function instead. -- cgit v0.10.2 From 543f43ce87c45220a8ffbff5ff4b60122499ce5f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 15 Jan 2012 13:31:46 +0100 Subject: Documentation: devres: add allocation functions to list of supported calls Signed-off-by: Wolfram Sang Acked-by: Grant Likely Acked-by: Tejun Heo Cc: Randy Dunlap Cc: Greg KH Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 10c64c8..41c0c5d 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -233,6 +233,10 @@ certainly invest a bit more effort into libata core layer). 6. List of managed interfaces ----------------------------- +MEM + devm_kzalloc() + devm_kfree() + IO region devm_request_region() devm_request_mem_region() -- cgit v0.10.2 From 268863f43629ef88763400d0cae4a66c754a0d23 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Wed, 11 Jan 2012 15:12:06 +0000 Subject: base/core.c:fix typo in comment in function device_add Signed-off-by: majianpeng Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/core.c b/drivers/base/core.c index ad29e92..74dda4f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1034,7 +1034,7 @@ int device_add(struct device *dev) device_pm_add(dev); /* Notify clients of device addition. This call must come - * after dpm_sysf_add() and before kobject_uevent(). + * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, -- cgit v0.10.2 From 8381b5e88a19b780f19fc52b7dd67f2b05d07dca Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 23 Jan 2012 07:36:04 -0800 Subject: stable: update documentation to ask for kernel version It would save me an email round-trip asking which stable tree(s) that a patch should be applied to, so document that the tree number should be specified in the email request. Reported-by: Tomoya MORINAGA Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/stable_kernel_rules.txt b/Documentation/stable_kernel_rules.txt index 21fd05c..f0ab5cf 100644 --- a/Documentation/stable_kernel_rules.txt +++ b/Documentation/stable_kernel_rules.txt @@ -25,7 +25,8 @@ Procedure for submitting patches to the -stable tree: - Send the patch, after verifying that it follows the above rules, to stable@vger.kernel.org. You must note the upstream commit ID in the - changelog of your submission. + changelog of your submission, as well as the kernel version you wish + it to be applied to. - To have the patch automatically included in the stable tree, add the tag Cc: stable@vger.kernel.org in the sign-off area. Once the patch is merged it will be applied to -- cgit v0.10.2 From 55305afc30529143bdb5928b2154dccdf073acd5 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 10 Jan 2012 15:43:08 -0800 Subject: drivers/usb/misc/emi26.c & emi62.c: fix warnings drivers/usb/misc/emi26.c:40: warning: 'emi26_init' declared 'static' but never defined drivers/usb/misc/emi26.c:41: warning: 'emi26_exit' declared 'static' but never defined drivers/usb/misc/emi62.c:49: warning: 'emi62_init' declared 'static' but never defined drivers/usb/misc/emi62.c:50: warning: 'emi62_exit' declared 'static' but never defined Signed-off-by: Andrew Morton Signed-off-by: Paul Gortmaker Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c index d9b6a03..da97dce 100644 --- a/drivers/usb/misc/emi26.c +++ b/drivers/usb/misc/emi26.c @@ -37,9 +37,6 @@ static int emi26_set_reset(struct usb_device *dev, unsigned char reset_bit); static int emi26_load_firmware (struct usb_device *dev); static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *id); static void emi26_disconnect(struct usb_interface *intf); -static int __init emi26_init (void); -static void __exit emi26_exit (void); - /* thanks to drivers/usb/serial/keyspan_pda.c code */ static int emi26_writememory (struct usb_device *dev, int address, diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 9f39062..4e0f167 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -46,9 +46,6 @@ static int emi62_set_reset(struct usb_device *dev, unsigned char reset_bit); static int emi62_load_firmware (struct usb_device *dev); static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id); static void emi62_disconnect(struct usb_interface *intf); -static int __init emi62_init (void); -static void __exit emi62_exit (void); - /* thanks to drivers/usb/serial/keyspan_pda.c code */ static int emi62_writememory(struct usb_device *dev, int address, -- cgit v0.10.2 From eb833a9e0972f60beb4ab8104ad7ef6bf30f02fc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Jan 2012 23:33:37 +0100 Subject: USB: ftdi_sio: fix TIOCSSERIAL baud_base handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return EINVAL if new baud_base does not match the current one. The baud_base is device specific and can not be changed. This restores the old (pre-2005) behaviour which was changed due to a misunderstanding regarding this fact (see https://lkml.org/lkml/2005/1/20/84). Reported-by: Torbjörn Lofterud Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 01b6404..ff5a8e1 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1333,8 +1333,7 @@ static int set_serial_info(struct tty_struct *tty, goto check_and_exit; } - if ((new_serial.baud_base != priv->baud_base) && - (new_serial.baud_base < 9600)) { + if (new_serial.baud_base != priv->baud_base) { mutex_unlock(&priv->cfg_lock); return -EINVAL; } -- cgit v0.10.2 From 108e02b12921078a59dcacd048079ece48a4a983 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Jan 2012 01:46:00 +0100 Subject: USB: ftdi_sio: fix initial baud rate Fix regression introduced by commit b1ffb4c851f1 ("USB: Fix Corruption issue in USB ftdi driver ftdi_sio.c") which caused the termios settings to no longer be initialised at open. Consequently it was no longer possible to set the port to the default speed of 9600 baud without first changing to another baud rate and back again. Reported-by: Roland Ramthun Cc: stable Signed-off-by: Johan Hovold Tested-by: Roland Ramthun Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ff5a8e1..7dbdf1e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1823,6 +1823,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { + struct ktermios dummy; struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); int result; @@ -1841,8 +1842,10 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (tty) - ftdi_set_termios(tty, port, tty->termios); + if (tty) { + memset(&dummy, 0, sizeof(dummy)); + ftdi_set_termios(tty, port, &dummy); + } /* Start reading from the device */ result = usb_serial_generic_open(tty, port); -- cgit v0.10.2 From 55f13aeae0346f0c89bfface91ad9a97653dc433 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 18 Jan 2012 23:43:45 +0100 Subject: USB: ftdi_sio: add PID for TI XDS100v2 / BeagleBone A3 Port A for JTAG, port B for serial. Signed-off-by: Peter Korsgaard Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 7dbdf1e..5f42757 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -805,6 +805,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index df1d7da..f35ddef 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -39,6 +39,13 @@ /* www.candapter.com Ewert Energy Systems CANdapter device */ #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ +/* + * Texas Instruments XDS100v2 JTAG / BeagleBone A3 + * http://processors.wiki.ti.com/index.php/XDS100 + * http://beagleboard.org/bone + */ +#define TI_XDS100V2_PID 0xa6d0 + #define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ /* US Interface Navigator (http://www.usinterface.com/) */ -- cgit v0.10.2 From fc216ec363f4d174932df90bbf35c77d0540e561 Mon Sep 17 00:00:00 2001 From: Peter Naulls Date: Tue, 17 Jan 2012 18:27:09 -0800 Subject: USB: serial: ftdi additional IDs I tested this against 2.6.39 in the Ubuntu kernel, however I see the IDs are not in latest 3.2 git. This adds IDs for the FTDI controller in the Rainforest Automation Zigbee dongle. Signed-off-by: Peter Naulls Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5f42757..104aff5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -843,6 +843,7 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(ST_VID, ST_STMCLT1030_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index f35ddef..09237ff 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1175,3 +1175,9 @@ */ /* TagTracer MIFARE*/ #define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0 + +/* + * Rainforest Automation + */ +/* ZigBee controller */ +#define FTDI_RF_R106 0x8A28 -- cgit v0.10.2 From 9bef3d4197379a995fa80f81950bbbf8d32e9e8b Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 5 Jan 2012 18:21:43 -0500 Subject: serial: group all the 8250 related code together The drivers/tty/serial dir is already getting rather busy. Relocate the 8250 related drivers to their own subdir to reduce the clutter. Note that sunsu.c is not included in this move -- it is 8250-like hardware, but it does not use any of the existing infrastructure -- and does not depend on SERIAL_8250. Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c deleted file mode 100644 index 9f50c4e..0000000 --- a/drivers/tty/serial/8250.c +++ /dev/null @@ -1,3357 +0,0 @@ -/* - * Driver for 8250/16550-type serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King. - * - * 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. - * - * A note about mapbase / membase - * - * mapbase is the physical address of the IO port. - * membase is an 'ioremapped' cookie. - */ - -#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "8250.h" - -#ifdef CONFIG_SPARC -#include "suncore.h" -#endif - -/* - * Configuration: - * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option - * is unsafe when used on edge-triggered interrupts. - */ -static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; - -static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; - -static struct uart_driver serial8250_reg; - -static int serial_index(struct uart_port *port) -{ - return (serial8250_reg.minor - 64) + port->line; -} - -static unsigned int skip_txen_test; /* force skip of txen test at init time */ - -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - -#define PASS_LIMIT 512 - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - - -/* - * We default to IRQ0 for the "no irq" hack. Some - * machine types want others as well - they're free - * to redefine this in their header file. - */ -#define is_real_interrupt(irq) ((irq) != 0) - -#ifdef CONFIG_SERIAL_8250_DETECT_IRQ -#define CONFIG_SERIAL_DETECT_IRQ 1 -#endif -#ifdef CONFIG_SERIAL_8250_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS 1 -#endif - -/* - * HUB6 is always on. This will be removed once the header - * files have been cleaned. - */ -#define CONFIG_HUB6 1 - -#include -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ -#ifndef SERIAL_PORT_DFNS -#define SERIAL_PORT_DFNS -#endif - -static const struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS /* defined in asm/serial.h */ -}; - -#define UART_NR CONFIG_SERIAL_8250_NR_UARTS - -#ifdef CONFIG_SERIAL_8250_RSA - -#define PORT_RSA_MAX 4 -static unsigned long probe_rsa[PORT_RSA_MAX]; -static unsigned int probe_rsa_count; -#endif /* CONFIG_SERIAL_8250_RSA */ - -struct irq_info { - struct hlist_node node; - int irq; - spinlock_t lock; /* Protects list not the hash */ - struct list_head *head; -}; - -#define NR_IRQ_HASH 32 /* Can be adjusted later */ -static struct hlist_head irq_lists[NR_IRQ_HASH]; -static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */ - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial8250_config uart_config[] = { - [PORT_UNKNOWN] = { - .name = "unknown", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_8250] = { - .name = "8250", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16450] = { - .name = "16450", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16550] = { - .name = "16550", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16550A] = { - .name = "16550A", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, - [PORT_CIRRUS] = { - .name = "Cirrus", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16650] = { - .name = "ST16650", - .fifo_size = 1, - .tx_loadsz = 1, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16650V2] = { - .name = "ST16650V2", - .fifo_size = 32, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | - UART_FCR_T_TRIG_00, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16750] = { - .name = "TI16750", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | - UART_FCR7_64BYTE, - .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, - }, - [PORT_STARTECH] = { - .name = "Startech", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16C950] = { - .name = "16C950/954", - .fifo_size = 128, - .tx_loadsz = 128, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - /* UART_CAP_EFR breaks billionon CF bluetooth card. */ - .flags = UART_CAP_FIFO | UART_CAP_SLEEP, - }, - [PORT_16654] = { - .name = "ST16654", - .fifo_size = 64, - .tx_loadsz = 32, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | - UART_FCR_T_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16850] = { - .name = "XR16850", - .fifo_size = 128, - .tx_loadsz = 128, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_RSA] = { - .name = "RSA", - .fifo_size = 2048, - .tx_loadsz = 2048, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11, - .flags = UART_CAP_FIFO, - }, - [PORT_NS16550A] = { - .name = "NS16550A", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_NATSEMI, - }, - [PORT_XSCALE] = { - .name = "XScale", - .fifo_size = 32, - .tx_loadsz = 32, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE, - }, - [PORT_RM9000] = { - .name = "RM9000", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, - [PORT_OCTEON] = { - .name = "OCTEON", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, - [PORT_AR7] = { - .name = "AR7", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, - .flags = UART_CAP_FIFO | UART_CAP_AFE, - }, - [PORT_U6_16550A] = { - .name = "U6_16550A", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_AFE, - }, - [PORT_TEGRA] = { - .name = "Tegra", - .fifo_size = 32, - .tx_loadsz = 8, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | - UART_FCR_T_TRIG_01, - .flags = UART_CAP_FIFO | UART_CAP_RTOIE, - }, - [PORT_XR17D15X] = { - .name = "XR17D15X", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, - }, -}; - -#if defined(CONFIG_MIPS_ALCHEMY) - -/* Au1x00 UART hardware has a weird register layout */ -static const u8 au_io_in_map[] = { - [UART_RX] = 0, - [UART_IER] = 2, - [UART_IIR] = 3, - [UART_LCR] = 5, - [UART_MCR] = 6, - [UART_LSR] = 7, - [UART_MSR] = 8, -}; - -static const u8 au_io_out_map[] = { - [UART_TX] = 1, - [UART_IER] = 2, - [UART_FCR] = 4, - [UART_LCR] = 5, - [UART_MCR] = 6, -}; - -/* sane hardware needs no mapping */ -static inline int map_8250_in_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_AU) - return offset; - return au_io_in_map[offset]; -} - -static inline int map_8250_out_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_AU) - return offset; - return au_io_out_map[offset]; -} - -#elif defined(CONFIG_SERIAL_8250_RM9K) - -static const u8 - regmap_in[8] = { - [UART_RX] = 0x00, - [UART_IER] = 0x0c, - [UART_IIR] = 0x14, - [UART_LCR] = 0x1c, - [UART_MCR] = 0x20, - [UART_LSR] = 0x24, - [UART_MSR] = 0x28, - [UART_SCR] = 0x2c - }, - regmap_out[8] = { - [UART_TX] = 0x04, - [UART_IER] = 0x0c, - [UART_FCR] = 0x18, - [UART_LCR] = 0x1c, - [UART_MCR] = 0x20, - [UART_LSR] = 0x24, - [UART_MSR] = 0x28, - [UART_SCR] = 0x2c - }; - -static inline int map_8250_in_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_RM9000) - return offset; - return regmap_in[offset]; -} - -static inline int map_8250_out_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_RM9000) - return offset; - return regmap_out[offset]; -} - -#else - -/* sane hardware needs no mapping */ -#define map_8250_in_reg(up, offset) (offset) -#define map_8250_out_reg(up, offset) (offset) - -#endif - -static unsigned int hub6_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - outb(p->hub6 - 1 + offset, p->iobase); - return inb(p->iobase + 1); -} - -static void hub6_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - outb(p->hub6 - 1 + offset, p->iobase); - outb(value, p->iobase + 1); -} - -static unsigned int mem_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return readb(p->membase + offset); -} - -static void mem_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - writeb(value, p->membase + offset); -} - -static void mem32_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - writel(value, p->membase + offset); -} - -static unsigned int mem32_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return readl(p->membase + offset); -} - -static unsigned int au_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return __raw_readl(p->membase + offset); -} - -static void au_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - __raw_writel(value, p->membase + offset); -} - -static unsigned int io_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return inb(p->iobase + offset); -} - -static void io_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - outb(value, p->iobase + offset); -} - -static int serial8250_default_handle_irq(struct uart_port *port); - -static void set_io_from_upio(struct uart_port *p) -{ - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); - switch (p->iotype) { - case UPIO_HUB6: - p->serial_in = hub6_serial_in; - p->serial_out = hub6_serial_out; - break; - - case UPIO_MEM: - p->serial_in = mem_serial_in; - p->serial_out = mem_serial_out; - break; - - case UPIO_RM9000: - case UPIO_MEM32: - p->serial_in = mem32_serial_in; - p->serial_out = mem32_serial_out; - break; - - case UPIO_AU: - p->serial_in = au_serial_in; - p->serial_out = au_serial_out; - break; - - default: - p->serial_in = io_serial_in; - p->serial_out = io_serial_out; - break; - } - /* Remember loaded iotype */ - up->cur_iotype = p->iotype; - p->handle_irq = serial8250_default_handle_irq; -} - -static void -serial_out_sync(struct uart_8250_port *up, int offset, int value) -{ - struct uart_port *p = &up->port; - switch (p->iotype) { - case UPIO_MEM: - case UPIO_MEM32: - case UPIO_AU: - p->serial_out(p, offset, value); - p->serial_in(p, UART_LCR); /* safe, no side-effects */ - break; - default: - p->serial_out(p, offset, value); - } -} - -#define serial_in(up, offset) \ - (up->port.serial_in(&(up)->port, (offset))) -#define serial_out(up, offset, value) \ - (up->port.serial_out(&(up)->port, (offset), (value))) -/* - * We used to support using pause I/O for certain machines. We - * haven't supported this for a while, but just in case it's badly - * needed for certain old 386 machines, I've left these #define's - * in.... - */ -#define serial_inp(up, offset) serial_in(up, offset) -#define serial_outp(up, offset, value) serial_out(up, offset, value) - -/* Uart divisor latch read */ -static inline int _serial_dl_read(struct uart_8250_port *up) -{ - return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8; -} - -/* Uart divisor latch write */ -static inline void _serial_dl_write(struct uart_8250_port *up, int value) -{ - serial_outp(up, UART_DLL, value & 0xff); - serial_outp(up, UART_DLM, value >> 8 & 0xff); -} - -#if defined(CONFIG_MIPS_ALCHEMY) -/* Au1x00 haven't got a standard divisor latch */ -static int serial_dl_read(struct uart_8250_port *up) -{ - if (up->port.iotype == UPIO_AU) - return __raw_readl(up->port.membase + 0x28); - else - return _serial_dl_read(up); -} - -static void serial_dl_write(struct uart_8250_port *up, int value) -{ - if (up->port.iotype == UPIO_AU) - __raw_writel(value, up->port.membase + 0x28); - else - _serial_dl_write(up, value); -} -#elif defined(CONFIG_SERIAL_8250_RM9K) -static int serial_dl_read(struct uart_8250_port *up) -{ - return (up->port.iotype == UPIO_RM9000) ? - (((__raw_readl(up->port.membase + 0x10) << 8) | - (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) : - _serial_dl_read(up); -} - -static void serial_dl_write(struct uart_8250_port *up, int value) -{ - if (up->port.iotype == UPIO_RM9000) { - __raw_writel(value, up->port.membase + 0x08); - __raw_writel(value >> 8, up->port.membase + 0x10); - } else { - _serial_dl_write(up, value); - } -} -#else -#define serial_dl_read(up) _serial_dl_read(up) -#define serial_dl_write(up, value) _serial_dl_write(up, value) -#endif - -/* - * For the 16C950 - */ -static void serial_icr_write(struct uart_8250_port *up, int offset, int value) -{ - serial_out(up, UART_SCR, offset); - serial_out(up, UART_ICR, value); -} - -static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) -{ - unsigned int value; - - serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); - serial_out(up, UART_SCR, offset); - value = serial_in(up, UART_ICR); - serial_icr_write(up, UART_ACR, up->acr); - - return value; -} - -/* - * FIFO support. - */ -static void serial8250_clear_fifos(struct uart_8250_port *p) -{ - if (p->capabilities & UART_CAP_FIFO) { - serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(p, UART_FCR, 0); - } -} - -/* - * IER sleep support. UARTs which have EFRs need the "extended - * capability" bit enabled. Note that on XR16C850s, we need to - * reset LCR to write to IER. - */ -static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) -{ - if (p->capabilities & UART_CAP_SLEEP) { - if (p->capabilities & UART_CAP_EFR) { - serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(p, UART_EFR, UART_EFR_ECB); - serial_outp(p, UART_LCR, 0); - } - serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); - if (p->capabilities & UART_CAP_EFR) { - serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(p, UART_EFR, 0); - serial_outp(p, UART_LCR, 0); - } - } -} - -#ifdef CONFIG_SERIAL_8250_RSA -/* - * Attempts to turn on the RSA FIFO. Returns zero on failure. - * We set the port uart clock rate if we succeed. - */ -static int __enable_rsa(struct uart_8250_port *up) -{ - unsigned char mode; - int result; - - mode = serial_inp(up, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - - if (!result) { - serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - } - - if (result) - up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; - - return result; -} - -static void enable_rsa(struct uart_8250_port *up) -{ - if (up->port.type == PORT_RSA) { - if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); - __enable_rsa(up); - spin_unlock_irq(&up->port.lock); - } - if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) - serial_outp(up, UART_RSA_FRR, 0); - } -} - -/* - * Attempts to turn off the RSA FIFO. Returns zero on failure. - * It is unknown why interrupts were disabled in here. However, - * the caller is expected to preserve this behaviour by grabbing - * the spinlock before calling this function. - */ -static void disable_rsa(struct uart_8250_port *up) -{ - unsigned char mode; - int result; - - if (up->port.type == PORT_RSA && - up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); - - mode = serial_inp(up, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - - if (!result) { - serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - } - - if (result) - up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; - spin_unlock_irq(&up->port.lock); - } -} -#endif /* CONFIG_SERIAL_8250_RSA */ - -/* - * This is a quickie test to see how big the FIFO is. - * It doesn't work at all the time, more's the pity. - */ -static int size_fifo(struct uart_8250_port *up) -{ - unsigned char old_fcr, old_mcr, old_lcr; - unsigned short old_dl; - int count; - - old_lcr = serial_inp(up, UART_LCR); - serial_outp(up, UART_LCR, 0); - old_fcr = serial_inp(up, UART_FCR); - old_mcr = serial_inp(up, UART_MCR); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_MCR, UART_MCR_LOOP); - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - old_dl = serial_dl_read(up); - serial_dl_write(up, 0x0001); - serial_outp(up, UART_LCR, 0x03); - for (count = 0; count < 256; count++) - serial_outp(up, UART_TX, count); - mdelay(20);/* FIXME - schedule_timeout */ - for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && - (count < 256); count++) - serial_inp(up, UART_RX); - serial_outp(up, UART_FCR, old_fcr); - serial_outp(up, UART_MCR, old_mcr); - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_dl_write(up, old_dl); - serial_outp(up, UART_LCR, old_lcr); - - return count; -} - -/* - * Read UART ID using the divisor method - set DLL and DLM to zero - * and the revision will be in DLL and device type in DLM. We - * preserve the device state across this. - */ -static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) -{ - unsigned char old_dll, old_dlm, old_lcr; - unsigned int id; - - old_lcr = serial_inp(p, UART_LCR); - serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_A); - - old_dll = serial_inp(p, UART_DLL); - old_dlm = serial_inp(p, UART_DLM); - - serial_outp(p, UART_DLL, 0); - serial_outp(p, UART_DLM, 0); - - id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8; - - serial_outp(p, UART_DLL, old_dll); - serial_outp(p, UART_DLM, old_dlm); - serial_outp(p, UART_LCR, old_lcr); - - return id; -} - -/* - * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. - * When this function is called we know it is at least a StarTech - * 16650 V2, but it might be one of several StarTech UARTs, or one of - * its clones. (We treat the broken original StarTech 16650 V1 as a - * 16550, and why not? Startech doesn't seem to even acknowledge its - * existence.) - * - * What evil have men's minds wrought... - */ -static void autoconfig_has_efr(struct uart_8250_port *up) -{ - unsigned int id1, id2, id3, rev; - - /* - * Everything with an EFR has SLEEP - */ - up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; - - /* - * First we check to see if it's an Oxford Semiconductor UART. - * - * If we have to do this here because some non-National - * Semiconductor clone chips lock up if you try writing to the - * LSR register (which serial_icr_read does) - */ - - /* - * Check for Oxford Semiconductor 16C950. - * - * EFR [4] must be set else this test fails. - * - * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) - * claims that it's needed for 952 dual UART's (which are not - * recommended for new designs). - */ - up->acr = 0; - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, UART_EFR_ECB); - serial_out(up, UART_LCR, 0x00); - id1 = serial_icr_read(up, UART_ID1); - id2 = serial_icr_read(up, UART_ID2); - id3 = serial_icr_read(up, UART_ID3); - rev = serial_icr_read(up, UART_REV); - - DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); - - if (id1 == 0x16 && id2 == 0xC9 && - (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { - up->port.type = PORT_16C950; - - /* - * Enable work around for the Oxford Semiconductor 952 rev B - * chip which causes it to seriously miscalculate baud rates - * when DLL is 0. - */ - if (id3 == 0x52 && rev == 0x01) - up->bugs |= UART_BUG_QUOT; - return; - } - - /* - * We check for a XR16C850 by setting DLL and DLM to 0, and then - * reading back DLL and DLM. The chip type depends on the DLM - * value read back: - * 0x10 - XR16C850 and the DLL contains the chip revision. - * 0x12 - XR16C2850. - * 0x14 - XR16C854. - */ - id1 = autoconfig_read_divisor_id(up); - DEBUG_AUTOCONF("850id=%04x ", id1); - - id2 = id1 >> 8; - if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) { - up->port.type = PORT_16850; - return; - } - - /* - * It wasn't an XR16C850. - * - * We distinguish between the '654 and the '650 by counting - * how many bytes are in the FIFO. I'm using this for now, - * since that's the technique that was sent to me in the - * serial driver update, but I'm not convinced this works. - * I've had problems doing this in the past. -TYT - */ - if (size_fifo(up) == 64) - up->port.type = PORT_16654; - else - up->port.type = PORT_16650V2; -} - -/* - * We detected a chip without a FIFO. Only two fall into - * this category - the original 8250 and the 16450. The - * 16450 has a scratch register (accessible with LCR=0) - */ -static void autoconfig_8250(struct uart_8250_port *up) -{ - unsigned char scratch, status1, status2; - - up->port.type = PORT_8250; - - scratch = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0xa5); - status1 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0x5a); - status2 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, scratch); - - if (status1 == 0xa5 && status2 == 0x5a) - up->port.type = PORT_16450; -} - -static int broken_efr(struct uart_8250_port *up) -{ - /* - * Exar ST16C2550 "A2" devices incorrectly detect as - * having an EFR, and report an ID of 0x0201. See - * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html - */ - if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16) - return 1; - - return 0; -} - -static inline int ns16550a_goto_highspeed(struct uart_8250_port *up) -{ - unsigned char status; - - status = serial_in(up, 0x04); /* EXCR2 */ -#define PRESL(x) ((x) & 0x30) - if (PRESL(status) == 0x10) { - /* already in high speed mode */ - return 0; - } else { - status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - status |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, status); - } - return 1; -} - -/* - * We know that the chip has FIFOs. Does it have an EFR? The - * EFR is located in the same register position as the IIR and - * we know the top two bits of the IIR are currently set. The - * EFR should contain zero. Try to read the EFR. - */ -static void autoconfig_16550a(struct uart_8250_port *up) -{ - unsigned char status1, status2; - unsigned int iersave; - - up->port.type = PORT_16550A; - up->capabilities |= UART_CAP_FIFO; - - /* - * Check for presence of the EFR when DLAB is set. - * Only ST16C650V1 UARTs pass this test. - */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - if (serial_in(up, UART_EFR) == 0) { - serial_outp(up, UART_EFR, 0xA8); - if (serial_in(up, UART_EFR) != 0) { - DEBUG_AUTOCONF("EFRv1 "); - up->port.type = PORT_16650; - up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; - } else { - DEBUG_AUTOCONF("Motorola 8xxx DUART "); - } - serial_outp(up, UART_EFR, 0); - return; - } - - /* - * Maybe it requires 0xbf to be written to the LCR. - * (other ST16C650V2 UARTs, TI16C752A, etc) - */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) { - DEBUG_AUTOCONF("EFRv2 "); - autoconfig_has_efr(up); - return; - } - - /* - * Check for a National Semiconductor SuperIO chip. - * Attempt to switch to bank 2, read the value of the LOOP bit - * from EXCR1. Switch back to bank 0, change it in MCR. Then - * switch back to bank 2, read it from EXCR1 again and check - * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2 - */ - serial_outp(up, UART_LCR, 0); - status1 = serial_in(up, UART_MCR); - serial_outp(up, UART_LCR, 0xE0); - status2 = serial_in(up, 0x02); /* EXCR1 */ - - if (!((status2 ^ status1) & UART_MCR_LOOP)) { - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP); - serial_outp(up, UART_LCR, 0xE0); - status2 = serial_in(up, 0x02); /* EXCR1 */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_MCR, status1); - - if ((status2 ^ status1) & UART_MCR_LOOP) { - unsigned short quot; - - serial_outp(up, UART_LCR, 0xE0); - - quot = serial_dl_read(up); - quot <<= 3; - - if (ns16550a_goto_highspeed(up)) - serial_dl_write(up, quot); - - serial_outp(up, UART_LCR, 0); - - up->port.uartclk = 921600*16; - up->port.type = PORT_NS16550A; - up->capabilities |= UART_NATSEMI; - return; - } - } - - /* - * No EFR. Try to detect a TI16750, which only sets bit 5 of - * the IIR when 64 byte FIFO mode is enabled when DLAB is set. - * Try setting it with and without DLAB set. Cheap clones - * set bit 5 without DLAB set. - */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status1 = serial_in(up, UART_IIR) >> 5; - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status2 = serial_in(up, UART_IIR) >> 5; - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, 0); - - DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); - - if (status1 == 6 && status2 == 7) { - up->port.type = PORT_16750; - up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; - return; - } - - /* - * Try writing and reading the UART_IER_UUE bit (b6). - * If it works, this is probably one of the Xscale platform's - * internal UARTs. - * We're going to explicitly set the UUE bit to 0 before - * trying to write and read a 1 just to make sure it's not - * already a 1 and maybe locked there before we even start start. - */ - iersave = serial_in(up, UART_IER); - serial_outp(up, UART_IER, iersave & ~UART_IER_UUE); - if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { - /* - * OK it's in a known zero state, try writing and reading - * without disturbing the current state of the other bits. - */ - serial_outp(up, UART_IER, iersave | UART_IER_UUE); - if (serial_in(up, UART_IER) & UART_IER_UUE) { - /* - * It's an Xscale. - * We'll leave the UART_IER_UUE bit set to 1 (enabled). - */ - DEBUG_AUTOCONF("Xscale "); - up->port.type = PORT_XSCALE; - up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE; - return; - } - } else { - /* - * If we got here we couldn't force the IER_UUE bit to 0. - * Log it and continue. - */ - DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 "); - } - serial_outp(up, UART_IER, iersave); - - /* - * Exar uarts have EFR in a weird location - */ - if (up->port.flags & UPF_EXAR_EFR) { - up->port.type = PORT_XR17D15X; - up->capabilities |= UART_CAP_AFE | UART_CAP_EFR; - } - - /* - * We distinguish between 16550A and U6 16550A by counting - * how many bytes are in the FIFO. - */ - if (up->port.type == PORT_16550A && size_fifo(up) == 64) { - up->port.type = PORT_U6_16550A; - up->capabilities |= UART_CAP_AFE; - } -} - -/* - * This routine is called by rs_init() to initialize a specific serial - * port. It determines what type of UART chip this serial port is - * using: 8250, 16450, 16550, 16550A. The important question is - * whether or not this UART is a 16550A or not, since this will - * determine whether or not we can use its FIFO features or not. - */ -static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) -{ - unsigned char status1, scratch, scratch2, scratch3; - unsigned char save_lcr, save_mcr; - unsigned long flags; - - if (!up->port.iobase && !up->port.mapbase && !up->port.membase) - return; - - DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", - serial_index(&up->port), up->port.iobase, up->port.membase); - - /* - * We really do need global IRQs disabled here - we're going to - * be frobbing the chips IRQ enable register to see if it exists. - */ - spin_lock_irqsave(&up->port.lock, flags); - - up->capabilities = 0; - up->bugs = 0; - - if (!(up->port.flags & UPF_BUGGY_UART)) { - /* - * Do a simple existence test first; if we fail this, - * there's no point trying anything else. - * - * 0x80 is used as a nonsense port to prevent against - * false positives due to ISA bus float. The - * assumption is that 0x80 is a non-existent port; - * which should be safe since include/asm/io.h also - * makes this assumption. - * - * Note: this is safe as long as MCR bit 4 is clear - * and the device is in "PC" mode. - */ - scratch = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, 0); -#ifdef __i386__ - outb(0xff, 0x080); -#endif - /* - * Mask out IER[7:4] bits for test as some UARTs (e.g. TL - * 16C754B) allow only to modify them if an EFR bit is set. - */ - scratch2 = serial_inp(up, UART_IER) & 0x0f; - serial_outp(up, UART_IER, 0x0F); -#ifdef __i386__ - outb(0, 0x080); -#endif - scratch3 = serial_inp(up, UART_IER) & 0x0f; - serial_outp(up, UART_IER, scratch); - if (scratch2 != 0 || scratch3 != 0x0F) { - /* - * We failed; there's nothing here - */ - DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", - scratch2, scratch3); - goto out; - } - } - - save_mcr = serial_in(up, UART_MCR); - save_lcr = serial_in(up, UART_LCR); - - /* - * Check to see if a UART is really there. Certain broken - * internal modems based on the Rockwell chipset fail this - * test, because they apparently don't implement the loopback - * test mode. So this test is skipped on the COM 1 through - * COM 4 ports. This *should* be safe, since no board - * manufacturer would be stupid enough to design a board - * that conflicts with COM 1-4 --- we hope! - */ - if (!(up->port.flags & UPF_SKIP_TEST)) { - serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = serial_inp(up, UART_MSR) & 0xF0; - serial_outp(up, UART_MCR, save_mcr); - if (status1 != 0x90) { - DEBUG_AUTOCONF("LOOP test failed (%02x) ", - status1); - goto out; - } - } - - /* - * We're pretty sure there's a port here. Lets find out what - * type of port it is. The IIR top two bits allows us to find - * out if it's 8250 or 16450, 16550, 16550A or later. This - * determines what we test for next. - * - * We also initialise the EFR (if any) to zero for later. The - * EFR occupies the same register location as the FCR and IIR. - */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, 0); - serial_outp(up, UART_LCR, 0); - - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - scratch = serial_in(up, UART_IIR) >> 6; - - DEBUG_AUTOCONF("iir=%d ", scratch); - - switch (scratch) { - case 0: - autoconfig_8250(up); - break; - case 1: - up->port.type = PORT_UNKNOWN; - break; - case 2: - up->port.type = PORT_16550; - break; - case 3: - autoconfig_16550a(up); - break; - } - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * Only probe for RSA ports if we got the region. - */ - if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { - int i; - - for (i = 0 ; i < probe_rsa_count; ++i) { - if (probe_rsa[i] == up->port.iobase && - __enable_rsa(up)) { - up->port.type = PORT_RSA; - break; - } - } - } -#endif - - serial_outp(up, UART_LCR, save_lcr); - - if (up->capabilities != uart_config[up->port.type].flags) { - printk(KERN_WARNING - "ttyS%d: detected caps %08x should be %08x\n", - serial_index(&up->port), up->capabilities, - uart_config[up->port.type].flags); - } - - up->port.fifosize = uart_config[up->port.type].fifo_size; - up->capabilities = uart_config[up->port.type].flags; - up->tx_loadsz = uart_config[up->port.type].tx_loadsz; - - if (up->port.type == PORT_UNKNOWN) - goto out; - - /* - * Reset the UART. - */ -#ifdef CONFIG_SERIAL_8250_RSA - if (up->port.type == PORT_RSA) - serial_outp(up, UART_RSA_FRR, 0); -#endif - serial_outp(up, UART_MCR, save_mcr); - serial8250_clear_fifos(up); - serial_in(up, UART_RX); - if (up->capabilities & UART_CAP_UUE) - serial_outp(up, UART_IER, UART_IER_UUE); - else - serial_outp(up, UART_IER, 0); - - out: - spin_unlock_irqrestore(&up->port.lock, flags); - DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); -} - -static void autoconfig_irq(struct uart_8250_port *up) -{ - unsigned char save_mcr, save_ier; - unsigned char save_ICP = 0; - unsigned int ICP = 0; - unsigned long irqs; - int irq; - - if (up->port.flags & UPF_FOURPORT) { - ICP = (up->port.iobase & 0xfe0) | 0x1f; - save_ICP = inb_p(ICP); - outb_p(0x80, ICP); - (void) inb_p(ICP); - } - - /* forget possible initially masked and pending IRQ */ - probe_irq_off(probe_irq_on()); - save_mcr = serial_inp(up, UART_MCR); - save_ier = serial_inp(up, UART_IER); - serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); - - irqs = probe_irq_on(); - serial_outp(up, UART_MCR, 0); - udelay(10); - if (up->port.flags & UPF_FOURPORT) { - serial_outp(up, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS); - } else { - serial_outp(up, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); - } - serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ - (void)serial_inp(up, UART_LSR); - (void)serial_inp(up, UART_RX); - (void)serial_inp(up, UART_IIR); - (void)serial_inp(up, UART_MSR); - serial_outp(up, UART_TX, 0xFF); - udelay(20); - irq = probe_irq_off(irqs); - - serial_outp(up, UART_MCR, save_mcr); - serial_outp(up, UART_IER, save_ier); - - if (up->port.flags & UPF_FOURPORT) - outb_p(save_ICP, ICP); - - up->port.irq = (irq > 0) ? irq : 0; -} - -static inline void __stop_tx(struct uart_8250_port *p) -{ - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); - } -} - -static void serial8250_stop_tx(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - __stop_tx(up); - - /* - * We really want to stop the transmitter from sending. - */ - if (up->port.type == PORT_16C950) { - up->acr |= UART_ACR_TXDIS; - serial_icr_write(up, UART_ACR, up->acr); - } -} - -static void serial8250_start_tx(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - - if (up->bugs & UART_BUG_TXEN) { - unsigned char lsr; - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if ((up->port.type == PORT_RM9000) ? - (lsr & UART_LSR_THRE) : - (lsr & UART_LSR_TEMT)) - serial8250_tx_chars(up); - } - } - - /* - * Re-enable the transmitter if we disabled it. - */ - if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { - up->acr &= ~UART_ACR_TXDIS; - serial_icr_write(up, UART_ACR, up->acr); - } -} - -static void serial8250_stop_rx(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void serial8250_enable_ms(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - /* no MSR capabilities */ - if (up->bugs & UART_BUG_NOMSR) - return; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -/* - * Clear the Tegra rx fifo after a break - * - * FIXME: This needs to become a port specific callback once we have a - * framework for this - */ -static void clear_rx_fifo(struct uart_8250_port *up) -{ - unsigned int status, tmout = 10000; - do { - status = serial_in(up, UART_LSR); - if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) - status = serial_in(up, UART_RX); - else - break; - if (--tmout == 0) - break; - udelay(1); - } while (1); -} - -/* - * serial8250_rx_chars: processes according to the passed in LSR - * value, and returns the remaining LSR bits not handled - * by this Rx routine. - */ -unsigned char -serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch; - int max_count = 256; - char flag; - - do { - if (likely(lsr & UART_LSR_DR)) - ch = serial_inp(up, UART_RX); - else - /* - * Intel 82571 has a Serial Over Lan device that will - * set UART_LSR_BI without setting UART_LSR_DR when - * it receives a break. To avoid reading from the - * receive buffer without UART_LSR_DR bit set, we - * just force the read character to be 0 - */ - ch = 0; - - flag = TTY_NORMAL; - up->port.icount.rx++; - - lsr |= up->lsr_saved_flags; - up->lsr_saved_flags = 0; - - if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ - if (lsr & UART_LSR_BI) { - lsr &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * If tegra port then clear the rx fifo to - * accept another break/character. - */ - if (up->port.type == PORT_TEGRA) - clear_rx_fifo(up); - - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (lsr & UART_LSR_PE) - up->port.icount.parity++; - else if (lsr & UART_LSR_FE) - up->port.icount.frame++; - if (lsr & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ignored. - */ - lsr &= up->port.read_status_mask; - - if (lsr & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (lsr & UART_LSR_PE) - flag = TTY_PARITY; - else if (lsr & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - - uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); - -ignore_char: - lsr = serial_inp(up, UART_LSR); - } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); - spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&up->port.lock); - return lsr; -} -EXPORT_SYMBOL_GPL(serial8250_rx_chars); - -void serial8250_tx_chars(struct uart_8250_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_outp(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_tx_stopped(&up->port)) { - serial8250_stop_tx(&up->port); - return; - } - if (uart_circ_empty(xmit)) { - __stop_tx(up); - return; - } - - count = up->tx_loadsz; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - DEBUG_INTR("THRE..."); - - if (uart_circ_empty(xmit)) - __stop_tx(up); -} -EXPORT_SYMBOL_GPL(serial8250_tx_chars); - -unsigned int serial8250_modem_status(struct uart_8250_port *up) -{ - unsigned int status = serial_in(up, UART_MSR); - - status |= up->msr_saved_flags; - up->msr_saved_flags = 0; - if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && - up->port.state != NULL) { - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); - } - - return status; -} -EXPORT_SYMBOL_GPL(serial8250_modem_status); - -/* - * This handles the interrupt from one port. - */ -int serial8250_handle_irq(struct uart_port *port, unsigned int iir) -{ - unsigned char status; - unsigned long flags; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - if (iir & UART_IIR_NO_INT) - return 0; - - spin_lock_irqsave(&up->port.lock, flags); - - status = serial_inp(up, UART_LSR); - - DEBUG_INTR("status = %x...", status); - - if (status & (UART_LSR_DR | UART_LSR_BI)) - status = serial8250_rx_chars(up, status); - serial8250_modem_status(up); - if (status & UART_LSR_THRE) - serial8250_tx_chars(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - return 1; -} -EXPORT_SYMBOL_GPL(serial8250_handle_irq); - -static int serial8250_default_handle_irq(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned int iir = serial_in(up, UART_IIR); - - return serial8250_handle_irq(port, iir); -} - -/* - * This is the serial driver's interrupt routine. - * - * Arjan thinks the old way was overly complex, so it got simplified. - * Alan disagrees, saying that need the complexity to handle the weird - * nature of ISA shared interrupts. (This is a special exception.) - * - * In order to handle ISA shared interrupts properly, we need to check - * that all ports have been serviced, and therefore the ISA interrupt - * line has been de-asserted. - * - * This means we need to loop through all ports. checking that they - * don't have an interrupt pending. - */ -static irqreturn_t serial8250_interrupt(int irq, void *dev_id) -{ - struct irq_info *i = dev_id; - struct list_head *l, *end = NULL; - int pass_counter = 0, handled = 0; - - DEBUG_INTR("serial8250_interrupt(%d)...", irq); - - spin_lock(&i->lock); - - l = i->head; - do { - struct uart_8250_port *up; - struct uart_port *port; - bool skip; - - up = list_entry(l, struct uart_8250_port, list); - port = &up->port; - skip = pass_counter && up->port.flags & UPF_IIR_ONCE; - - if (!skip && port->handle_irq(port)) { - handled = 1; - end = NULL; - } else if (end == NULL) - end = l; - - l = l->next; - - if (l == i->head && pass_counter++ > PASS_LIMIT) { - /* If we hit this, we're dead. */ - printk_ratelimited(KERN_ERR - "serial8250: too much work for irq%d\n", irq); - break; - } - } while (l != end); - - spin_unlock(&i->lock); - - DEBUG_INTR("end.\n"); - - return IRQ_RETVAL(handled); -} - -/* - * To support ISA shared interrupts, we need to have one interrupt - * handler that ensures that the IRQ line has been deasserted - * before returning. Failing to do this will result in the IRQ - * line being stuck active, and, since ISA irqs are edge triggered, - * no more IRQs will be seen. - */ -static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) -{ - spin_lock_irq(&i->lock); - - if (!list_empty(i->head)) { - if (i->head == &up->list) - i->head = i->head->next; - list_del(&up->list); - } else { - BUG_ON(i->head != &up->list); - i->head = NULL; - } - spin_unlock_irq(&i->lock); - /* List empty so throw away the hash node */ - if (i->head == NULL) { - hlist_del(&i->node); - kfree(i); - } -} - -static int serial_link_irq_chain(struct uart_8250_port *up) -{ - struct hlist_head *h; - struct hlist_node *n; - struct irq_info *i; - int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; - - mutex_lock(&hash_mutex); - - h = &irq_lists[up->port.irq % NR_IRQ_HASH]; - - hlist_for_each(n, h) { - i = hlist_entry(n, struct irq_info, node); - if (i->irq == up->port.irq) - break; - } - - if (n == NULL) { - i = kzalloc(sizeof(struct irq_info), GFP_KERNEL); - if (i == NULL) { - mutex_unlock(&hash_mutex); - return -ENOMEM; - } - spin_lock_init(&i->lock); - i->irq = up->port.irq; - hlist_add_head(&i->node, h); - } - mutex_unlock(&hash_mutex); - - spin_lock_irq(&i->lock); - - if (i->head) { - list_add(&up->list, i->head); - spin_unlock_irq(&i->lock); - - ret = 0; - } else { - INIT_LIST_HEAD(&up->list); - i->head = &up->list; - spin_unlock_irq(&i->lock); - irq_flags |= up->port.irqflags; - ret = request_irq(up->port.irq, serial8250_interrupt, - irq_flags, "serial", i); - if (ret < 0) - serial_do_unlink(i, up); - } - - return ret; -} - -static void serial_unlink_irq_chain(struct uart_8250_port *up) -{ - struct irq_info *i; - struct hlist_node *n; - struct hlist_head *h; - - mutex_lock(&hash_mutex); - - h = &irq_lists[up->port.irq % NR_IRQ_HASH]; - - hlist_for_each(n, h) { - i = hlist_entry(n, struct irq_info, node); - if (i->irq == up->port.irq) - break; - } - - BUG_ON(n == NULL); - BUG_ON(i->head == NULL); - - if (list_empty(i->head)) - free_irq(up->port.irq, i); - - serial_do_unlink(i, up); - mutex_unlock(&hash_mutex); -} - -/* - * This function is used to handle ports that do not have an - * interrupt. This doesn't work very well for 16450's, but gives - * barely passable results for a 16550A. (Although at the expense - * of much CPU overhead). - */ -static void serial8250_timeout(unsigned long data) -{ - struct uart_8250_port *up = (struct uart_8250_port *)data; - - up->port.handle_irq(&up->port); - mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); -} - -static void serial8250_backup_timeout(unsigned long data) -{ - struct uart_8250_port *up = (struct uart_8250_port *)data; - unsigned int iir, ier = 0, lsr; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Must disable interrupts or else we risk racing with the interrupt - * based handler. - */ - if (is_real_interrupt(up->port.irq)) { - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - } - - iir = serial_in(up, UART_IIR); - - /* - * This should be a safe test for anyone who doesn't trust the - * IIR bits on their UART, but it's specifically designed for - * the "Diva" UART used on the management processor on many HP - * ia64 and parisc boxes. - */ - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && - (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && - (lsr & UART_LSR_THRE)) { - iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); - iir |= UART_IIR_THRI; - } - - if (!(iir & UART_IIR_NO_INT)) - serial8250_tx_chars(up); - - if (is_real_interrupt(up->port.irq)) - serial_out(up, UART_IER, ier); - - spin_unlock_irqrestore(&up->port.lock, flags); - - /* Standard timer interval plus 0.2s to keep the port running */ - mod_timer(&up->timer, - jiffies + uart_poll_timeout(&up->port) + HZ / 5); -} - -static unsigned int serial8250_tx_empty(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - unsigned int lsr; - - spin_lock_irqsave(&up->port.lock, flags); - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - spin_unlock_irqrestore(&up->port.lock, flags); - - return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; -} - -static unsigned int serial8250_get_mctrl(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned int status; - unsigned int ret; - - status = serial8250_modem_status(up); - - ret = 0; - if (status & UART_MSR_DCD) - ret |= TIOCM_CAR; - if (status & UART_MSR_RI) - ret |= TIOCM_RNG; - if (status & UART_MSR_DSR) - ret |= TIOCM_DSR; - if (status & UART_MSR_CTS) - ret |= TIOCM_CTS; - return ret; -} - -static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned char mcr = 0; - - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; - - serial_out(up, UART_MCR, mcr); -} - -static void serial8250_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* - * Wait for transmitter & holding register to empty - */ -static void wait_for_xmitr(struct uart_8250_port *up, int bits) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - for (;;) { - status = serial_in(up, UART_LSR); - - up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; - - if ((status & bits) == bits) - break; - if (--tmout == 0) - break; - udelay(1); - } - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - unsigned int tmout; - for (tmout = 1000000; tmout; tmout--) { - unsigned int msr = serial_in(up, UART_MSR); - up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; - if (msr & UART_MSR_CTS) - break; - udelay(1); - touch_nmi_watchdog(); - } - } -} - -#ifdef CONFIG_CONSOLE_POLL -/* - * Console polling routines for writing and reading from the uart while - * in an interrupt or debug context. - */ - -static int serial8250_get_poll_char(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned char lsr = serial_inp(up, UART_LSR); - - if (!(lsr & UART_LSR_DR)) - return NO_POLL_CHAR; - - return serial_inp(up, UART_RX); -} - - -static void serial8250_put_poll_char(struct uart_port *port, - unsigned char c) -{ - unsigned int ier; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - if (up->capabilities & UART_CAP_UUE) - serial_out(up, UART_IER, UART_IER_UUE); - else - serial_out(up, UART_IER, 0); - - wait_for_xmitr(up, BOTH_EMPTY); - /* - * Send the character out. - * If a LF, also do CR... - */ - serial_out(up, UART_TX, c); - if (c == 10) { - wait_for_xmitr(up, BOTH_EMPTY); - serial_out(up, UART_TX, 13); - } - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up, BOTH_EMPTY); - serial_out(up, UART_IER, ier); -} - -#endif /* CONFIG_CONSOLE_POLL */ - -static int serial8250_startup(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - unsigned char lsr, iir; - int retval; - - up->port.fifosize = uart_config[up->port.type].fifo_size; - up->tx_loadsz = uart_config[up->port.type].tx_loadsz; - up->capabilities = uart_config[up->port.type].flags; - up->mcr = 0; - - if (up->port.iotype != up->cur_iotype) - set_io_from_upio(port); - - if (up->port.type == PORT_16C950) { - /* Wake up and initialize UART */ - up->acr = 0; - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_IER, 0); - serial_outp(up, UART_LCR, 0); - serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_LCR, 0); - } - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * If this is an RSA port, see if we can kick it up to the - * higher speed clock. - */ - enable_rsa(up); -#endif - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - serial8250_clear_fifos(up); - - /* - * Clear the interrupt registers. - */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); - - /* - * At this point, there's no way the LSR could still be 0xff; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (!(up->port.flags & UPF_BUGGY_UART) && - (serial_inp(up, UART_LSR) == 0xff)) { - printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", - serial_index(&up->port)); - return -ENODEV; - } - - /* - * For a XR16C850, we need to set the trigger levels - */ - if (up->port.type == PORT_16850) { - unsigned char fctr; - - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - - fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); - serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); - serial_outp(up, UART_TRG, UART_TRG_96); - serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); - serial_outp(up, UART_TRG, UART_TRG_96); - - serial_outp(up, UART_LCR, 0); - } - - if (is_real_interrupt(up->port.irq)) { - unsigned char iir1; - /* - * Test for UARTs that do not reassert THRE when the - * transmitter is idle and the interrupt has already - * been cleared. Real 16550s should always reassert - * this interrupt whenever the transmitter is idle and - * the interrupt is enabled. Delays are necessary to - * allow register changes to become visible. - */ - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.irqflags & IRQF_SHARED) - disable_irq_nosync(up->port.irq); - - wait_for_xmitr(up, UART_LSR_THRE); - serial_out_sync(up, UART_IER, UART_IER_THRI); - udelay(1); /* allow THRE to set */ - iir1 = serial_in(up, UART_IIR); - serial_out(up, UART_IER, 0); - serial_out_sync(up, UART_IER, UART_IER_THRI); - udelay(1); /* allow a working UART time to re-assert THRE */ - iir = serial_in(up, UART_IIR); - serial_out(up, UART_IER, 0); - - if (up->port.irqflags & IRQF_SHARED) - enable_irq(up->port.irq); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * If the interrupt is not reasserted, setup a timer to - * kick the UART on a regular basis. - */ - if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { - up->bugs |= UART_BUG_THRE; - pr_debug("ttyS%d - using backup timer\n", - serial_index(port)); - } - } - - /* - * The above check will only give an accurate result the first time - * the port is opened so this value needs to be preserved. - */ - if (up->bugs & UART_BUG_THRE) { - up->timer.function = serial8250_backup_timeout; - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + - uart_poll_timeout(port) + HZ / 5); - } - - /* - * If the "interrupt" for this port doesn't correspond with any - * hardware interrupt, we use a timer-based system. The original - * driver used to do this with IRQ0. - */ - if (!is_real_interrupt(up->port.irq)) { - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); - } else { - retval = serial_link_irq_chain(up); - if (retval) - return retval; - } - - /* - * Now, initialize the UART - */ - serial_outp(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - if (!is_real_interrupt(up->port.irq)) - up->port.mctrl |= TIOCM_OUT1; - } else - /* - * Most PC uarts need OUT2 raised to enable interrupts. - */ - if (is_real_interrupt(up->port.irq)) - up->port.mctrl |= TIOCM_OUT2; - - serial8250_set_mctrl(&up->port, up->port.mctrl); - - /* Serial over Lan (SoL) hack: - Intel 8257x Gigabit ethernet chips have a - 16550 emulation, to be used for Serial Over Lan. - Those chips take a longer time than a normal - serial device to signalize that a transmission - data was queued. Due to that, the above test generally - fails. One solution would be to delay the reading of - iir. However, this is not reliable, since the timeout - is variable. So, let's just don't test if we receive - TX irq. This way, we'll never enable UART_BUG_TXEN. - */ - if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST) - goto dont_test_tx_en; - - /* - * Do a quick test to see if we receive an - * interrupt when we enable the TX irq. - */ - serial_outp(up, UART_IER, UART_IER_THRI); - lsr = serial_in(up, UART_LSR); - iir = serial_in(up, UART_IIR); - serial_outp(up, UART_IER, 0); - - if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { - if (!(up->bugs & UART_BUG_TXEN)) { - up->bugs |= UART_BUG_TXEN; - pr_debug("ttyS%d - enabling bad tx status workarounds\n", - serial_index(port)); - } - } else { - up->bugs &= ~UART_BUG_TXEN; - } - -dont_test_tx_en: - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Clear the interrupt registers again for luck, and clear the - * saved flags to avoid getting false values from polling - * routines or the previous session. - */ - serial_inp(up, UART_LSR); - serial_inp(up, UART_RX); - serial_inp(up, UART_IIR); - serial_inp(up, UART_MSR); - up->lsr_saved_flags = 0; - up->msr_saved_flags = 0; - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_outp(up, UART_IER, up->ier); - - if (up->port.flags & UPF_FOURPORT) { - unsigned int icp; - /* - * Enable interrupts on the AST Fourport board - */ - icp = (up->port.iobase & 0xfe0) | 0x01f; - outb_p(0x80, icp); - (void) inb_p(icp); - } - - return 0; -} - -static void serial8250_shutdown(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - serial_outp(up, UART_IER, 0); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - /* reset interrupts on the AST Fourport board */ - inb((up->port.iobase & 0xfe0) | 0x1f); - up->port.mctrl |= TIOCM_OUT1; - } else - up->port.mctrl &= ~TIOCM_OUT2; - - serial8250_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition and FIFOs - */ - serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); - serial8250_clear_fifos(up); - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * Reset the RSA board back to 115kbps compat mode. - */ - disable_rsa(up); -#endif - - /* - * Read data port to reset things, and then unlink from - * the IRQ chain. - */ - (void) serial_in(up, UART_RX); - - del_timer_sync(&up->timer); - up->timer.function = serial8250_timeout; - if (is_real_interrupt(up->port.irq)) - serial_unlink_irq_chain(up); -} - -static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) -{ - unsigned int quot; - - /* - * Handle magic divisors for baud rates above baud_base on - * SMSC SuperIO chips. - */ - if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/4)) - quot = 0x8001; - else if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/8)) - quot = 0x8002; - else - quot = uart_get_divisor(port, baud); - - return quot; -} - -void -serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned char cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (termios->c_cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, - port->uartclk / 16 / 0xffff, - port->uartclk / 16); - quot = serial8250_get_divisor(port, baud); - - /* - * Oxford Semi 952 rev B workaround - */ - if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) - quot++; - - if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { - if (baud < 2400) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; - else - fcr = uart_config[up->port.type].fcr; - } - - /* - * MCR-based auto flow control. When AFE is enabled, RTS will be - * deasserted when the receive FIFO contains more characters than - * the trigger, or the MCR RTS bit is cleared. In the case where - * the remote UART is not using CTS auto flow control, we must - * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. - */ - if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) { - up->mcr &= ~UART_MCR_AFE; - if (termios->c_cflag & CRTSCTS) - up->mcr |= UART_MCR_AFE; - } - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (!(up->bugs & UART_BUG_NOMSR) && - UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - if (up->capabilities & UART_CAP_UUE) - up->ier |= UART_IER_UUE; - if (up->capabilities & UART_CAP_RTOIE) - up->ier |= UART_IER_RTOIE; - - serial_out(up, UART_IER, up->ier); - - if (up->capabilities & UART_CAP_EFR) { - unsigned char efr = 0; - /* - * TI16C752/Startech hardware flow control. FIXME: - * - TI16C752 requires control thresholds to be set. - * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled. - */ - if (termios->c_cflag & CRTSCTS) - efr |= UART_EFR_CTS; - - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - if (up->port.flags & UPF_EXAR_EFR) - serial_outp(up, UART_XR_EFR, efr); - else - serial_outp(up, UART_EFR, efr); - } - -#ifdef CONFIG_ARCH_OMAP - /* Workaround to enable 115200 baud on OMAP1510 internal ports */ - if (cpu_is_omap1510() && is_omap_port(up)) { - if (baud == 115200) { - quot = 1; - serial_out(up, UART_OMAP_OSC_12M_SEL, 1); - } else - serial_out(up, UART_OMAP_OSC_12M_SEL, 0); - } -#endif - - if (up->capabilities & UART_NATSEMI) { - /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ - serial_outp(up, UART_LCR, 0xe0); - } else { - serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ - } - - serial_dl_write(up, quot); - - /* - * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR - * is written without DLAB set, this mode will be disabled. - */ - if (up->port.type == PORT_16750) - serial_outp(up, UART_FCR, fcr); - - serial_outp(up, UART_LCR, cval); /* reset DLAB */ - up->lcr = cval; /* Save LCR */ - if (up->port.type != PORT_16750) { - if (fcr & UART_FCR_ENABLE_FIFO) { - /* emulated UARTs (Lucent Venus 167x) need two steps */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - } - serial_outp(up, UART_FCR, fcr); /* set fcr */ - } - serial8250_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - /* Don't rewrite B0 */ - if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, baud, baud); -} -EXPORT_SYMBOL(serial8250_do_set_termios); - -static void -serial8250_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - if (port->set_termios) - port->set_termios(port, termios, old); - else - serial8250_do_set_termios(port, termios, old); -} - -static void -serial8250_set_ldisc(struct uart_port *port, int new) -{ - if (new == N_PPS) { - port->flags |= UPF_HARDPPS_CD; - serial8250_enable_ms(port); - } else - port->flags &= ~UPF_HARDPPS_CD; -} - - -void serial8250_do_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_8250_port *p = - container_of(port, struct uart_8250_port, port); - - serial8250_set_sleep(p, state != 0); -} -EXPORT_SYMBOL(serial8250_do_pm); - -static void -serial8250_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - if (port->pm) - port->pm(port, state, oldstate); - else - serial8250_do_pm(port, state, oldstate); -} - -static unsigned int serial8250_port_size(struct uart_8250_port *pt) -{ - if (pt->port.iotype == UPIO_AU) - return 0x1000; -#ifdef CONFIG_ARCH_OMAP - if (is_omap_port(pt)) - return 0x16 << pt->port.regshift; -#endif - return 8 << pt->port.regshift; -} - -/* - * Resource handling. - */ -static int serial8250_request_std_resource(struct uart_8250_port *up) -{ - unsigned int size = serial8250_port_size(up); - int ret = 0; - - switch (up->port.iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM: - if (!up->port.mapbase) - break; - - if (!request_mem_region(up->port.mapbase, size, "serial")) { - ret = -EBUSY; - break; - } - - if (up->port.flags & UPF_IOREMAP) { - up->port.membase = ioremap_nocache(up->port.mapbase, - size); - if (!up->port.membase) { - release_mem_region(up->port.mapbase, size); - ret = -ENOMEM; - } - } - break; - - case UPIO_HUB6: - case UPIO_PORT: - if (!request_region(up->port.iobase, size, "serial")) - ret = -EBUSY; - break; - } - return ret; -} - -static void serial8250_release_std_resource(struct uart_8250_port *up) -{ - unsigned int size = serial8250_port_size(up); - - switch (up->port.iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM: - if (!up->port.mapbase) - break; - - if (up->port.flags & UPF_IOREMAP) { - iounmap(up->port.membase); - up->port.membase = NULL; - } - - release_mem_region(up->port.mapbase, size); - break; - - case UPIO_HUB6: - case UPIO_PORT: - release_region(up->port.iobase, size); - break; - } -} - -static int serial8250_request_rsa_resource(struct uart_8250_port *up) -{ - unsigned long start = UART_RSA_BASE << up->port.regshift; - unsigned int size = 8 << up->port.regshift; - int ret = -EINVAL; - - switch (up->port.iotype) { - case UPIO_HUB6: - case UPIO_PORT: - start += up->port.iobase; - if (request_region(start, size, "serial-rsa")) - ret = 0; - else - ret = -EBUSY; - break; - } - - return ret; -} - -static void serial8250_release_rsa_resource(struct uart_8250_port *up) -{ - unsigned long offset = UART_RSA_BASE << up->port.regshift; - unsigned int size = 8 << up->port.regshift; - - switch (up->port.iotype) { - case UPIO_HUB6: - case UPIO_PORT: - release_region(up->port.iobase + offset, size); - break; - } -} - -static void serial8250_release_port(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - serial8250_release_std_resource(up); - if (up->port.type == PORT_RSA) - serial8250_release_rsa_resource(up); -} - -static int serial8250_request_port(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - int ret = 0; - - ret = serial8250_request_std_resource(up); - if (ret == 0 && up->port.type == PORT_RSA) { - ret = serial8250_request_rsa_resource(up); - if (ret < 0) - serial8250_release_std_resource(up); - } - - return ret; -} - -static void serial8250_config_port(struct uart_port *port, int flags) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - int probeflags = PROBE_ANY; - int ret; - - /* - * Find the region that we can probe for. This in turn - * tells us whether we can probe for the type of port. - */ - ret = serial8250_request_std_resource(up); - if (ret < 0) - return; - - ret = serial8250_request_rsa_resource(up); - if (ret < 0) - probeflags &= ~PROBE_RSA; - - if (up->port.iotype != up->cur_iotype) - set_io_from_upio(port); - - if (flags & UART_CONFIG_TYPE) - autoconfig(up, probeflags); - - /* if access method is AU, it is a 16550 with a quirk */ - if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) - up->bugs |= UART_BUG_NOMSR; - - if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) - autoconfig_irq(up); - - if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) - serial8250_release_rsa_resource(up); - if (up->port.type == PORT_UNKNOWN) - serial8250_release_std_resource(up); -} - -static int -serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if (ser->irq >= nr_irqs || ser->irq < 0 || - ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || - ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS || - ser->type == PORT_STARTECH) - return -EINVAL; - return 0; -} - -static const char * -serial8250_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops serial8250_pops = { - .tx_empty = serial8250_tx_empty, - .set_mctrl = serial8250_set_mctrl, - .get_mctrl = serial8250_get_mctrl, - .stop_tx = serial8250_stop_tx, - .start_tx = serial8250_start_tx, - .stop_rx = serial8250_stop_rx, - .enable_ms = serial8250_enable_ms, - .break_ctl = serial8250_break_ctl, - .startup = serial8250_startup, - .shutdown = serial8250_shutdown, - .set_termios = serial8250_set_termios, - .set_ldisc = serial8250_set_ldisc, - .pm = serial8250_pm, - .type = serial8250_type, - .release_port = serial8250_release_port, - .request_port = serial8250_request_port, - .config_port = serial8250_config_port, - .verify_port = serial8250_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = serial8250_get_poll_char, - .poll_put_char = serial8250_put_poll_char, -#endif -}; - -static struct uart_8250_port serial8250_ports[UART_NR]; - -static void (*serial8250_isa_config)(int port, struct uart_port *up, - unsigned short *capabilities); - -void serial8250_set_isa_configurator( - void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) -{ - serial8250_isa_config = v; -} -EXPORT_SYMBOL(serial8250_set_isa_configurator); - -static void __init serial8250_isa_init_ports(void) -{ - struct uart_8250_port *up; - static int first = 1; - int i, irqflag = 0; - - if (!first) - return; - first = 0; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - up->port.line = i; - spin_lock_init(&up->port.lock); - - init_timer(&up->timer); - up->timer.function = serial8250_timeout; - - /* - * ALPHA_KLUDGE_MCR needs to be killed. - */ - up->mcr_mask = ~ALPHA_KLUDGE_MCR; - up->mcr_force = ALPHA_KLUDGE_MCR; - - up->port.ops = &serial8250_pops; - } - - if (share_irqs) - irqflag = IRQF_SHARED; - - for (i = 0, up = serial8250_ports; - i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; - i++, up++) { - up->port.iobase = old_serial_port[i].port; - up->port.irq = irq_canonicalize(old_serial_port[i].irq); - up->port.irqflags = old_serial_port[i].irqflags; - up->port.uartclk = old_serial_port[i].baud_base * 16; - up->port.flags = old_serial_port[i].flags; - up->port.hub6 = old_serial_port[i].hub6; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; - set_io_from_upio(&up->port); - up->port.irqflags |= irqflag; - if (serial8250_isa_config != NULL) - serial8250_isa_config(i, &up->port, &up->capabilities); - - } -} - -static void -serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) -{ - up->port.type = type; - up->port.fifosize = uart_config[type].fifo_size; - up->capabilities = uart_config[type].flags; - up->tx_loadsz = uart_config[type].tx_loadsz; -} - -static void __init -serial8250_register_ports(struct uart_driver *drv, struct device *dev) -{ - int i; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - up->cur_iotype = 0xFF; - } - - serial8250_isa_init_ports(); - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - up->port.dev = dev; - - if (up->port.flags & UPF_FIXED_TYPE) - serial8250_init_fixed_type_port(up, up->port.type); - - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_8250_CONSOLE - -static void serial8250_console_putchar(struct uart_port *port, int ch) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - wait_for_xmitr(up, UART_LSR_THRE); - serial_out(up, UART_TX, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - */ -static void -serial8250_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_8250_port *up = &serial8250_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - touch_nmi_watchdog(); - - local_irq_save(flags); - if (up->port.sysrq) { - /* serial8250_handle_irq() already took the lock */ - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - - if (up->capabilities & UART_CAP_UUE) - serial_out(up, UART_IER, UART_IER_UUE); - else - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, serial8250_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up, BOTH_EMPTY); - serial_out(up, UART_IER, ier); - - /* - * The receive handling will happen properly because the - * receive ready bit will still be set; it is not cleared - * on read. However, modem control will not, we must - * call it if we have saved something in the saved flags - * while processing with interrupts off. - */ - if (up->msr_saved_flags) - serial8250_modem_status(up); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static int __init serial8250_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= nr_uarts) - co->index = 0; - port = &serial8250_ports[co->index].port; - if (!port->iobase && !port->membase) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static int serial8250_console_early_setup(void) -{ - return serial8250_find_port_for_earlycon(); -} - -static struct console serial8250_console = { - .name = "ttyS", - .write = serial8250_console_write, - .device = uart_console_device, - .setup = serial8250_console_setup, - .early_setup = serial8250_console_early_setup, - .flags = CON_PRINTBUFFER | CON_ANYTIME, - .index = -1, - .data = &serial8250_reg, -}; - -static int __init serial8250_console_init(void) -{ - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; - - serial8250_isa_init_ports(); - register_console(&serial8250_console); - return 0; -} -console_initcall(serial8250_console_init); - -int serial8250_find_port(struct uart_port *p) -{ - int line; - struct uart_port *port; - - for (line = 0; line < nr_uarts; line++) { - port = &serial8250_ports[line].port; - if (uart_match_port(p, port)) - return line; - } - return -ENODEV; -} - -#define SERIAL8250_CONSOLE &serial8250_console -#else -#define SERIAL8250_CONSOLE NULL -#endif - -static struct uart_driver serial8250_reg = { - .owner = THIS_MODULE, - .driver_name = "serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .cons = SERIAL8250_CONSOLE, -}; - -/* - * early_serial_setup - early registration for 8250 ports - * - * Setup an 8250 port structure prior to console initialisation. Use - * after console initialisation will cause undefined behaviour. - */ -int __init early_serial_setup(struct uart_port *port) -{ - struct uart_port *p; - - if (port->line >= ARRAY_SIZE(serial8250_ports)) - return -ENODEV; - - serial8250_isa_init_ports(); - p = &serial8250_ports[port->line].port; - p->iobase = port->iobase; - p->membase = port->membase; - p->irq = port->irq; - p->irqflags = port->irqflags; - p->uartclk = port->uartclk; - p->fifosize = port->fifosize; - p->regshift = port->regshift; - p->iotype = port->iotype; - p->flags = port->flags; - p->mapbase = port->mapbase; - p->private_data = port->private_data; - p->type = port->type; - p->line = port->line; - - set_io_from_upio(p); - if (port->serial_in) - p->serial_in = port->serial_in; - if (port->serial_out) - p->serial_out = port->serial_out; - if (port->handle_irq) - p->handle_irq = port->handle_irq; - else - p->handle_irq = serial8250_default_handle_irq; - - return 0; -} - -/** - * serial8250_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -void serial8250_suspend_port(int line) -{ - uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); -} - -/** - * serial8250_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -void serial8250_resume_port(int line) -{ - struct uart_8250_port *up = &serial8250_ports[line]; - - if (up->capabilities & UART_NATSEMI) { - /* Ensure it's still in high speed mode */ - serial_outp(up, UART_LCR, 0xE0); - - ns16550a_goto_highspeed(up); - - serial_outp(up, UART_LCR, 0); - up->port.uartclk = 921600*16; - } - uart_resume_port(&serial8250_reg, &up->port); -} - -/* - * Register a set of serial devices attached to a platform device. The - * list is terminated with a zero flags entry, which means we expect - * all entries to have at least UPF_BOOT_AUTOCONF set. - */ -static int __devinit serial8250_probe(struct platform_device *dev) -{ - struct plat_serial8250_port *p = dev->dev.platform_data; - struct uart_port port; - int ret, i, irqflag = 0; - - memset(&port, 0, sizeof(struct uart_port)); - - if (share_irqs) - irqflag = IRQF_SHARED; - - for (i = 0; p && p->flags != 0; p++, i++) { - port.iobase = p->iobase; - port.membase = p->membase; - port.irq = p->irq; - port.irqflags = p->irqflags; - port.uartclk = p->uartclk; - port.regshift = p->regshift; - port.iotype = p->iotype; - port.flags = p->flags; - port.mapbase = p->mapbase; - port.hub6 = p->hub6; - port.private_data = p->private_data; - port.type = p->type; - port.serial_in = p->serial_in; - port.serial_out = p->serial_out; - port.handle_irq = p->handle_irq; - port.set_termios = p->set_termios; - port.pm = p->pm; - port.dev = &dev->dev; - port.irqflags |= irqflag; - ret = serial8250_register_port(&port); - if (ret < 0) { - dev_err(&dev->dev, "unable to register port at index %d " - "(IO%lx MEM%llx IRQ%d): %d\n", i, - p->iobase, (unsigned long long)p->mapbase, - p->irq, ret); - } - } - return 0; -} - -/* - * Remove serial ports registered against a platform device. - */ -static int __devexit serial8250_remove(struct platform_device *dev) -{ - int i; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.dev == &dev->dev) - serial8250_unregister_port(i); - } - return 0; -} - -static int serial8250_suspend(struct platform_device *dev, pm_message_t state) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - uart_suspend_port(&serial8250_reg, &up->port); - } - - return 0; -} - -static int serial8250_resume(struct platform_device *dev) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - serial8250_resume_port(i); - } - - return 0; -} - -static struct platform_driver serial8250_isa_driver = { - .probe = serial8250_probe, - .remove = __devexit_p(serial8250_remove), - .suspend = serial8250_suspend, - .resume = serial8250_resume, - .driver = { - .name = "serial8250", - .owner = THIS_MODULE, - }, -}; - -/* - * This "device" covers _all_ ISA 8250-compatible serial devices listed - * in the table in include/asm/serial.h - */ -static struct platform_device *serial8250_isa_devs; - -/* - * serial8250_register_port and serial8250_unregister_port allows for - * 16x50 serial ports to be configured at run-time, to support PCMCIA - * modems and PCI multiport cards. - */ -static DEFINE_MUTEX(serial_mutex); - -static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *port) -{ - int i; - - /* - * First, find a port entry which matches. - */ - for (i = 0; i < nr_uarts; i++) - if (uart_match_port(&serial8250_ports[i].port, port)) - return &serial8250_ports[i]; - - /* - * We didn't find a matching entry, so look for the first - * free entry. We look for one which hasn't been previously - * used (indicated by zero iobase). - */ - for (i = 0; i < nr_uarts; i++) - if (serial8250_ports[i].port.type == PORT_UNKNOWN && - serial8250_ports[i].port.iobase == 0) - return &serial8250_ports[i]; - - /* - * That also failed. Last resort is to find any entry which - * doesn't have a real port associated with it. - */ - for (i = 0; i < nr_uarts; i++) - if (serial8250_ports[i].port.type == PORT_UNKNOWN) - return &serial8250_ports[i]; - - return NULL; -} - -/** - * serial8250_register_port - register a serial port - * @port: serial port template - * - * Configure the serial port specified by the request. If the - * port exists and is in use, it is hung up and unregistered - * first. - * - * The port is then probed and if necessary the IRQ is autodetected - * If this fails an error is returned. - * - * On success the port is ready to use and the line number is returned. - */ -int serial8250_register_port(struct uart_port *port) -{ - struct uart_8250_port *uart; - int ret = -ENOSPC; - - if (port->uartclk == 0) - return -EINVAL; - - mutex_lock(&serial_mutex); - - uart = serial8250_find_match_or_unused(port); - if (uart) { - uart_remove_one_port(&serial8250_reg, &uart->port); - - uart->port.iobase = port->iobase; - uart->port.membase = port->membase; - uart->port.irq = port->irq; - uart->port.irqflags = port->irqflags; - uart->port.uartclk = port->uartclk; - uart->port.fifosize = port->fifosize; - uart->port.regshift = port->regshift; - uart->port.iotype = port->iotype; - uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; - uart->port.mapbase = port->mapbase; - uart->port.private_data = port->private_data; - if (port->dev) - uart->port.dev = port->dev; - - if (port->flags & UPF_FIXED_TYPE) - serial8250_init_fixed_type_port(uart, port->type); - - set_io_from_upio(&uart->port); - /* Possibly override default I/O functions. */ - if (port->serial_in) - uart->port.serial_in = port->serial_in; - if (port->serial_out) - uart->port.serial_out = port->serial_out; - if (port->handle_irq) - uart->port.handle_irq = port->handle_irq; - /* Possibly override set_termios call */ - if (port->set_termios) - uart->port.set_termios = port->set_termios; - if (port->pm) - uart->port.pm = port->pm; - - if (serial8250_isa_config != NULL) - serial8250_isa_config(0, &uart->port, - &uart->capabilities); - - ret = uart_add_one_port(&serial8250_reg, &uart->port); - if (ret == 0) - ret = uart->port.line; - } - mutex_unlock(&serial_mutex); - - return ret; -} -EXPORT_SYMBOL(serial8250_register_port); - -/** - * serial8250_unregister_port - remove a 16x50 serial port at runtime - * @line: serial line number - * - * Remove one serial port. This may not be called from interrupt - * context. We hand the port back to the our control. - */ -void serial8250_unregister_port(int line) -{ - struct uart_8250_port *uart = &serial8250_ports[line]; - - mutex_lock(&serial_mutex); - uart_remove_one_port(&serial8250_reg, &uart->port); - if (serial8250_isa_devs) { - uart->port.flags &= ~UPF_BOOT_AUTOCONF; - uart->port.type = PORT_UNKNOWN; - uart->port.dev = &serial8250_isa_devs->dev; - uart->capabilities = uart_config[uart->port.type].flags; - uart_add_one_port(&serial8250_reg, &uart->port); - } else { - uart->port.dev = NULL; - } - mutex_unlock(&serial_mutex); -} -EXPORT_SYMBOL(serial8250_unregister_port); - -static int __init serial8250_init(void) -{ - int ret; - - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; - - printk(KERN_INFO "Serial: 8250/16550 driver, " - "%d ports, IRQ sharing %sabled\n", nr_uarts, - share_irqs ? "en" : "dis"); - -#ifdef CONFIG_SPARC - ret = sunserial_register_minors(&serial8250_reg, UART_NR); -#else - serial8250_reg.nr = UART_NR; - ret = uart_register_driver(&serial8250_reg); -#endif - if (ret) - goto out; - - serial8250_isa_devs = platform_device_alloc("serial8250", - PLAT8250_DEV_LEGACY); - if (!serial8250_isa_devs) { - ret = -ENOMEM; - goto unreg_uart_drv; - } - - ret = platform_device_add(serial8250_isa_devs); - if (ret) - goto put_dev; - - serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); - - ret = platform_driver_register(&serial8250_isa_driver); - if (ret == 0) - goto out; - - platform_device_del(serial8250_isa_devs); -put_dev: - platform_device_put(serial8250_isa_devs); -unreg_uart_drv: -#ifdef CONFIG_SPARC - sunserial_unregister_minors(&serial8250_reg, UART_NR); -#else - uart_unregister_driver(&serial8250_reg); -#endif -out: - return ret; -} - -static void __exit serial8250_exit(void) -{ - struct platform_device *isa_dev = serial8250_isa_devs; - - /* - * This tells serial8250_unregister_port() not to re-register - * the ports (thereby making serial8250_isa_driver permanently - * in use.) - */ - serial8250_isa_devs = NULL; - - platform_driver_unregister(&serial8250_isa_driver); - platform_device_unregister(isa_dev); - -#ifdef CONFIG_SPARC - sunserial_unregister_minors(&serial8250_reg, UART_NR); -#else - uart_unregister_driver(&serial8250_reg); -#endif -} - -module_init(serial8250_init); -module_exit(serial8250_exit); - -EXPORT_SYMBOL(serial8250_suspend_port); -EXPORT_SYMBOL(serial8250_resume_port); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); - -module_param(share_irqs, uint, 0644); -MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" - " (unsafe)"); - -module_param(nr_uarts, uint, 0644); -MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); - -module_param(skip_txen_test, uint, 0644); -MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); - -#ifdef CONFIG_SERIAL_8250_RSA -module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); -MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); -#endif -MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); diff --git a/drivers/tty/serial/8250.h b/drivers/tty/serial/8250.h deleted file mode 100644 index ae027be..0000000 --- a/drivers/tty/serial/8250.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Driver for 8250/16550-type serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King. - * - * 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. - */ - -#include - -struct uart_8250_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short capabilities; /* port capabilities */ - unsigned short bugs; /* port bugs */ - unsigned int tx_loadsz; /* transmit fifo load size */ - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char cur_iotype; /* Running I/O type */ - - /* - * Some bits in registers are cleared on a read, so they must - * be saved whenever the register is read but the bits will not - * be immediately processed. - */ -#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS - unsigned char lsr_saved_flags; -#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA - unsigned char msr_saved_flags; -}; - -struct old_serial_port { - unsigned int uart; - unsigned int baud_base; - unsigned int port; - unsigned int irq; - unsigned int flags; - unsigned char hub6; - unsigned char io_type; - unsigned char *iomem_base; - unsigned short iomem_reg_shift; - unsigned long irqflags; -}; - -/* - * This replaces serial_uart_config in include/linux/serial.h - */ -struct serial8250_config { - const char *name; - unsigned short fifo_size; - unsigned short tx_loadsz; - unsigned char fcr; - unsigned int flags; -}; - -#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ -#define UART_CAP_EFR (1 << 9) /* UART has EFR */ -#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ -#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ -#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ -#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ - -#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ -#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ -#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */ -#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ - -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) - -#ifdef CONFIG_SERIAL_8250_SHARE_IRQ -#define SERIAL8250_SHARE_IRQS 1 -#else -#define SERIAL8250_SHARE_IRQS 0 -#endif - -#if defined(__alpha__) && !defined(CONFIG_PCI) -/* - * Digital did something really horribly wrong with the OUT1 and OUT2 - * lines on at least some ALPHA's. The failure mode is that if either - * is cleared, the machine locks up with endless interrupts. - */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) -#elif defined(CONFIG_SBC8560) -/* - * WindRiver did something similarly broken on their SBC8560 board. The - * UART tristates its IRQ output while OUT2 is clear, but they pulled - * the interrupt line _up_ instead of down, so if we register the IRQ - * while the UART is in that state, we die in an IRQ storm. */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) -#else -#define ALPHA_KLUDGE_MCR 0 -#endif diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c new file mode 100644 index 0000000..9f50c4e --- /dev/null +++ b/drivers/tty/serial/8250/8250.c @@ -0,0 +1,3357 @@ +/* + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * 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. + * + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. + * membase is an 'ioremapped' cookie. + */ + +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8250.h" + +#ifdef CONFIG_SPARC +#include "suncore.h" +#endif + +/* + * Configuration: + * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option + * is unsafe when used on edge-triggered interrupts. + */ +static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; + +static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; + +static struct uart_driver serial8250_reg; + +static int serial_index(struct uart_port *port) +{ + return (serial8250_reg.minor - 64) + port->line; +} + +static unsigned int skip_txen_test; /* force skip of txen test at init time */ + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 512 + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define CONFIG_SERIAL_DETECT_IRQ 1 +#endif +#ifdef CONFIG_SERIAL_8250_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif + +/* + * HUB6 is always on. This will be removed once the header + * files have been cleaned. + */ +#define CONFIG_HUB6 1 + +#include +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static const struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +#define UART_NR CONFIG_SERIAL_8250_NR_UARTS + +#ifdef CONFIG_SERIAL_8250_RSA + +#define PORT_RSA_MAX 4 +static unsigned long probe_rsa[PORT_RSA_MAX]; +static unsigned int probe_rsa_count; +#endif /* CONFIG_SERIAL_8250_RSA */ + +struct irq_info { + struct hlist_node node; + int irq; + spinlock_t lock; /* Protects list not the hash */ + struct list_head *head; +}; + +#define NR_IRQ_HASH 32 /* Can be adjusted later */ +static struct hlist_head irq_lists[NR_IRQ_HASH]; +static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */ + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial8250_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_8250] = { + .name = "8250", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16450] = { + .name = "16450", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16550] = { + .name = "16550", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16550A] = { + .name = "16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_CIRRUS] = { + .name = "Cirrus", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16650] = { + .name = "ST16650", + .fifo_size = 1, + .tx_loadsz = 1, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16650V2] = { + .name = "ST16650V2", + .fifo_size = 32, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_00, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16750] = { + .name = "TI16750", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR7_64BYTE, + .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, + }, + [PORT_STARTECH] = { + .name = "Startech", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16C950] = { + .name = "16C950/954", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + /* UART_CAP_EFR breaks billionon CF bluetooth card. */ + .flags = UART_CAP_FIFO | UART_CAP_SLEEP, + }, + [PORT_16654] = { + .name = "ST16654", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16850] = { + .name = "XR16850", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_RSA] = { + .name = "RSA", + .fifo_size = 2048, + .tx_loadsz = 2048, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11, + .flags = UART_CAP_FIFO, + }, + [PORT_NS16550A] = { + .name = "NS16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_NATSEMI, + }, + [PORT_XSCALE] = { + .name = "XScale", + .fifo_size = 32, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE, + }, + [PORT_RM9000] = { + .name = "RM9000", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_OCTEON] = { + .name = "OCTEON", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_AR7] = { + .name = "AR7", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_U6_16550A] = { + .name = "U6_16550A", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_TEGRA] = { + .name = "Tegra", + .fifo_size = 32, + .tx_loadsz = 8, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_01, + .flags = UART_CAP_FIFO | UART_CAP_RTOIE, + }, + [PORT_XR17D15X] = { + .name = "XR17D15X", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, + }, +}; + +#if defined(CONFIG_MIPS_ALCHEMY) + +/* Au1x00 UART hardware has a weird register layout */ +static const u8 au_io_in_map[] = { + [UART_RX] = 0, + [UART_IER] = 2, + [UART_IIR] = 3, + [UART_LCR] = 5, + [UART_MCR] = 6, + [UART_LSR] = 7, + [UART_MSR] = 8, +}; + +static const u8 au_io_out_map[] = { + [UART_TX] = 1, + [UART_IER] = 2, + [UART_FCR] = 4, + [UART_LCR] = 5, + [UART_MCR] = 6, +}; + +/* sane hardware needs no mapping */ +static inline int map_8250_in_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_AU) + return offset; + return au_io_in_map[offset]; +} + +static inline int map_8250_out_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_AU) + return offset; + return au_io_out_map[offset]; +} + +#elif defined(CONFIG_SERIAL_8250_RM9K) + +static const u8 + regmap_in[8] = { + [UART_RX] = 0x00, + [UART_IER] = 0x0c, + [UART_IIR] = 0x14, + [UART_LCR] = 0x1c, + [UART_MCR] = 0x20, + [UART_LSR] = 0x24, + [UART_MSR] = 0x28, + [UART_SCR] = 0x2c + }, + regmap_out[8] = { + [UART_TX] = 0x04, + [UART_IER] = 0x0c, + [UART_FCR] = 0x18, + [UART_LCR] = 0x1c, + [UART_MCR] = 0x20, + [UART_LSR] = 0x24, + [UART_MSR] = 0x28, + [UART_SCR] = 0x2c + }; + +static inline int map_8250_in_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_RM9000) + return offset; + return regmap_in[offset]; +} + +static inline int map_8250_out_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_RM9000) + return offset; + return regmap_out[offset]; +} + +#else + +/* sane hardware needs no mapping */ +#define map_8250_in_reg(up, offset) (offset) +#define map_8250_out_reg(up, offset) (offset) + +#endif + +static unsigned int hub6_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + return inb(p->iobase + 1); +} + +static void hub6_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + outb(value, p->iobase + 1); +} + +static unsigned int mem_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return readb(p->membase + offset); +} + +static void mem_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + writeb(value, p->membase + offset); +} + +static void mem32_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + writel(value, p->membase + offset); +} + +static unsigned int mem32_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return readl(p->membase + offset); +} + +static unsigned int au_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return __raw_readl(p->membase + offset); +} + +static void au_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + __raw_writel(value, p->membase + offset); +} + +static unsigned int io_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return inb(p->iobase + offset); +} + +static void io_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + outb(value, p->iobase + offset); +} + +static int serial8250_default_handle_irq(struct uart_port *port); + +static void set_io_from_upio(struct uart_port *p) +{ + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); + switch (p->iotype) { + case UPIO_HUB6: + p->serial_in = hub6_serial_in; + p->serial_out = hub6_serial_out; + break; + + case UPIO_MEM: + p->serial_in = mem_serial_in; + p->serial_out = mem_serial_out; + break; + + case UPIO_RM9000: + case UPIO_MEM32: + p->serial_in = mem32_serial_in; + p->serial_out = mem32_serial_out; + break; + + case UPIO_AU: + p->serial_in = au_serial_in; + p->serial_out = au_serial_out; + break; + + default: + p->serial_in = io_serial_in; + p->serial_out = io_serial_out; + break; + } + /* Remember loaded iotype */ + up->cur_iotype = p->iotype; + p->handle_irq = serial8250_default_handle_irq; +} + +static void +serial_out_sync(struct uart_8250_port *up, int offset, int value) +{ + struct uart_port *p = &up->port; + switch (p->iotype) { + case UPIO_MEM: + case UPIO_MEM32: + case UPIO_AU: + p->serial_out(p, offset, value); + p->serial_in(p, UART_LCR); /* safe, no side-effects */ + break; + default: + p->serial_out(p, offset, value); + } +} + +#define serial_in(up, offset) \ + (up->port.serial_in(&(up)->port, (offset))) +#define serial_out(up, offset, value) \ + (up->port.serial_out(&(up)->port, (offset), (value))) +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + +/* Uart divisor latch read */ +static inline int _serial_dl_read(struct uart_8250_port *up) +{ + return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8; +} + +/* Uart divisor latch write */ +static inline void _serial_dl_write(struct uart_8250_port *up, int value) +{ + serial_outp(up, UART_DLL, value & 0xff); + serial_outp(up, UART_DLM, value >> 8 & 0xff); +} + +#if defined(CONFIG_MIPS_ALCHEMY) +/* Au1x00 haven't got a standard divisor latch */ +static int serial_dl_read(struct uart_8250_port *up) +{ + if (up->port.iotype == UPIO_AU) + return __raw_readl(up->port.membase + 0x28); + else + return _serial_dl_read(up); +} + +static void serial_dl_write(struct uart_8250_port *up, int value) +{ + if (up->port.iotype == UPIO_AU) + __raw_writel(value, up->port.membase + 0x28); + else + _serial_dl_write(up, value); +} +#elif defined(CONFIG_SERIAL_8250_RM9K) +static int serial_dl_read(struct uart_8250_port *up) +{ + return (up->port.iotype == UPIO_RM9000) ? + (((__raw_readl(up->port.membase + 0x10) << 8) | + (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) : + _serial_dl_read(up); +} + +static void serial_dl_write(struct uart_8250_port *up, int value) +{ + if (up->port.iotype == UPIO_RM9000) { + __raw_writel(value, up->port.membase + 0x08); + __raw_writel(value >> 8, up->port.membase + 0x10); + } else { + _serial_dl_write(up, value); + } +} +#else +#define serial_dl_read(up) _serial_dl_read(up) +#define serial_dl_write(up, value) _serial_dl_write(up, value) +#endif + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_8250_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} + +/* + * FIFO support. + */ +static void serial8250_clear_fifos(struct uart_8250_port *p) +{ + if (p->capabilities & UART_CAP_FIFO) { + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(p, UART_FCR, 0); + } +} + +/* + * IER sleep support. UARTs which have EFRs need the "extended + * capability" bit enabled. Note that on XR16C850s, we need to + * reset LCR to write to IER. + */ +static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) +{ + if (p->capabilities & UART_CAP_SLEEP) { + if (p->capabilities & UART_CAP_EFR) { + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(p, UART_EFR, UART_EFR_ECB); + serial_outp(p, UART_LCR, 0); + } + serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); + if (p->capabilities & UART_CAP_EFR) { + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(p, UART_EFR, 0); + serial_outp(p, UART_LCR, 0); + } + } +} + +#ifdef CONFIG_SERIAL_8250_RSA +/* + * Attempts to turn on the RSA FIFO. Returns zero on failure. + * We set the port uart clock rate if we succeed. + */ +static int __enable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; + + return result; +} + +static void enable_rsa(struct uart_8250_port *up) +{ + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + __enable_rsa(up); + spin_unlock_irq(&up->port.lock); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(up, UART_RSA_FRR, 0); + } +} + +/* + * Attempts to turn off the RSA FIFO. Returns zero on failure. + * It is unknown why interrupts were disabled in here. However, + * the caller is expected to preserve this behaviour by grabbing + * the spinlock before calling this function. + */ +static void disable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; + spin_unlock_irq(&up->port.lock); + } +} +#endif /* CONFIG_SERIAL_8250_RSA */ + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct uart_8250_port *up) +{ + unsigned char old_fcr, old_mcr, old_lcr; + unsigned short old_dl; + int count; + + old_lcr = serial_inp(up, UART_LCR); + serial_outp(up, UART_LCR, 0); + old_fcr = serial_inp(up, UART_FCR); + old_mcr = serial_inp(up, UART_MCR); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_MCR, UART_MCR_LOOP); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + old_dl = serial_dl_read(up); + serial_dl_write(up, 0x0001); + serial_outp(up, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(up, UART_TX, count); + mdelay(20);/* FIXME - schedule_timeout */ + for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(up, UART_RX); + serial_outp(up, UART_FCR, old_fcr); + serial_outp(up, UART_MCR, old_mcr); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_dl_write(up, old_dl); + serial_outp(up, UART_LCR, old_lcr); + + return count; +} + +/* + * Read UART ID using the divisor method - set DLL and DLM to zero + * and the revision will be in DLL and device type in DLM. We + * preserve the device state across this. + */ +static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) +{ + unsigned char old_dll, old_dlm, old_lcr; + unsigned int id; + + old_lcr = serial_inp(p, UART_LCR); + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_A); + + old_dll = serial_inp(p, UART_DLL); + old_dlm = serial_inp(p, UART_DLM); + + serial_outp(p, UART_DLL, 0); + serial_outp(p, UART_DLM, 0); + + id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8; + + serial_outp(p, UART_DLL, old_dll); + serial_outp(p, UART_DLM, old_dlm); + serial_outp(p, UART_LCR, old_lcr); + + return id; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void autoconfig_has_efr(struct uart_8250_port *up) +{ + unsigned int id1, id2, id3, rev; + + /* + * Everything with an EFR has SLEEP + */ + up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + + /* + * Check for Oxford Semiconductor 16C950. + * + * EFR [4] must be set else this test fails. + * + * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) + * claims that it's needed for 952 dual UART's (which are not + * recommended for new designs). + */ + up->acr = 0; + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, 0x00); + id1 = serial_icr_read(up, UART_ID1); + id2 = serial_icr_read(up, UART_ID2); + id3 = serial_icr_read(up, UART_ID3); + rev = serial_icr_read(up, UART_REV); + + DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); + + if (id1 == 0x16 && id2 == 0xC9 && + (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { + up->port.type = PORT_16C950; + + /* + * Enable work around for the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if (id3 == 0x52 && rev == 0x01) + up->bugs |= UART_BUG_QUOT; + return; + } + + /* + * We check for a XR16C850 by setting DLL and DLM to 0, and then + * reading back DLL and DLM. The chip type depends on the DLM + * value read back: + * 0x10 - XR16C850 and the DLL contains the chip revision. + * 0x12 - XR16C2850. + * 0x14 - XR16C854. + */ + id1 = autoconfig_read_divisor_id(up); + DEBUG_AUTOCONF("850id=%04x ", id1); + + id2 = id1 >> 8; + if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) { + up->port.type = PORT_16850; + return; + } + + /* + * It wasn't an XR16C850. + * + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(up) == 64) + up->port.type = PORT_16654; + else + up->port.type = PORT_16650V2; +} + +/* + * We detected a chip without a FIFO. Only two fall into + * this category - the original 8250 and the 16450. The + * 16450 has a scratch register (accessible with LCR=0) + */ +static void autoconfig_8250(struct uart_8250_port *up) +{ + unsigned char scratch, status1, status2; + + up->port.type = PORT_8250; + + scratch = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0xa5); + status1 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0x5a); + status2 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, scratch); + + if (status1 == 0xa5 && status2 == 0x5a) + up->port.type = PORT_16450; +} + +static int broken_efr(struct uart_8250_port *up) +{ + /* + * Exar ST16C2550 "A2" devices incorrectly detect as + * having an EFR, and report an ID of 0x0201. See + * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html + */ + if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16) + return 1; + + return 0; +} + +static inline int ns16550a_goto_highspeed(struct uart_8250_port *up) +{ + unsigned char status; + + status = serial_in(up, 0x04); /* EXCR2 */ +#define PRESL(x) ((x) & 0x30) + if (PRESL(status) == 0x10) { + /* already in high speed mode */ + return 0; + } else { + status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + status |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_outp(up, 0x04, status); + } + return 1; +} + +/* + * We know that the chip has FIFOs. Does it have an EFR? The + * EFR is located in the same register position as the IIR and + * we know the top two bits of the IIR are currently set. The + * EFR should contain zero. Try to read the EFR. + */ +static void autoconfig_16550a(struct uart_8250_port *up) +{ + unsigned char status1, status2; + unsigned int iersave; + + up->port.type = PORT_16550A; + up->capabilities |= UART_CAP_FIFO; + + /* + * Check for presence of the EFR when DLAB is set. + * Only ST16C650V1 UARTs pass this test. + */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + if (serial_in(up, UART_EFR) == 0) { + serial_outp(up, UART_EFR, 0xA8); + if (serial_in(up, UART_EFR) != 0) { + DEBUG_AUTOCONF("EFRv1 "); + up->port.type = PORT_16650; + up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; + } else { + DEBUG_AUTOCONF("Motorola 8xxx DUART "); + } + serial_outp(up, UART_EFR, 0); + return; + } + + /* + * Maybe it requires 0xbf to be written to the LCR. + * (other ST16C650V2 UARTs, TI16C752A, etc) + */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) { + DEBUG_AUTOCONF("EFRv2 "); + autoconfig_has_efr(up); + return; + } + + /* + * Check for a National Semiconductor SuperIO chip. + * Attempt to switch to bank 2, read the value of the LOOP bit + * from EXCR1. Switch back to bank 0, change it in MCR. Then + * switch back to bank 2, read it from EXCR1 again and check + * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2 + */ + serial_outp(up, UART_LCR, 0); + status1 = serial_in(up, UART_MCR); + serial_outp(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + + if (!((status2 ^ status1) & UART_MCR_LOOP)) { + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP); + serial_outp(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_MCR, status1); + + if ((status2 ^ status1) & UART_MCR_LOOP) { + unsigned short quot; + + serial_outp(up, UART_LCR, 0xE0); + + quot = serial_dl_read(up); + quot <<= 3; + + if (ns16550a_goto_highspeed(up)) + serial_dl_write(up, quot); + + serial_outp(up, UART_LCR, 0); + + up->port.uartclk = 921600*16; + up->port.type = PORT_NS16550A; + up->capabilities |= UART_NATSEMI; + return; + } + } + + /* + * No EFR. Try to detect a TI16750, which only sets bit 5 of + * the IIR when 64 byte FIFO mode is enabled when DLAB is set. + * Try setting it with and without DLAB set. Cheap clones + * set bit 5 without DLAB set. + */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status1 = serial_in(up, UART_IIR) >> 5; + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status2 = serial_in(up, UART_IIR) >> 5; + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, 0); + + DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); + + if (status1 == 6 && status2 == 7) { + up->port.type = PORT_16750; + up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; + return; + } + + /* + * Try writing and reading the UART_IER_UUE bit (b6). + * If it works, this is probably one of the Xscale platform's + * internal UARTs. + * We're going to explicitly set the UUE bit to 0 before + * trying to write and read a 1 just to make sure it's not + * already a 1 and maybe locked there before we even start start. + */ + iersave = serial_in(up, UART_IER); + serial_outp(up, UART_IER, iersave & ~UART_IER_UUE); + if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { + /* + * OK it's in a known zero state, try writing and reading + * without disturbing the current state of the other bits. + */ + serial_outp(up, UART_IER, iersave | UART_IER_UUE); + if (serial_in(up, UART_IER) & UART_IER_UUE) { + /* + * It's an Xscale. + * We'll leave the UART_IER_UUE bit set to 1 (enabled). + */ + DEBUG_AUTOCONF("Xscale "); + up->port.type = PORT_XSCALE; + up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE; + return; + } + } else { + /* + * If we got here we couldn't force the IER_UUE bit to 0. + * Log it and continue. + */ + DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 "); + } + serial_outp(up, UART_IER, iersave); + + /* + * Exar uarts have EFR in a weird location + */ + if (up->port.flags & UPF_EXAR_EFR) { + up->port.type = PORT_XR17D15X; + up->capabilities |= UART_CAP_AFE | UART_CAP_EFR; + } + + /* + * We distinguish between 16550A and U6 16550A by counting + * how many bytes are in the FIFO. + */ + if (up->port.type == PORT_16550A && size_fifo(up) == 64) { + up->port.type = PORT_U6_16550A; + up->capabilities |= UART_CAP_AFE; + } +} + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + if (!up->port.iobase && !up->port.mapbase && !up->port.membase) + return; + + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", + serial_index(&up->port), up->port.iobase, up->port.membase); + + /* + * We really do need global IRQs disabled here - we're going to + * be frobbing the chips IRQ enable register to see if it exists. + */ + spin_lock_irqsave(&up->port.lock, flags); + + up->capabilities = 0; + up->bugs = 0; + + if (!(up->port.flags & UPF_BUGGY_UART)) { + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. + * + * 0x80 is used as a nonsense port to prevent against + * false positives due to ISA bus float. The + * assumption is that 0x80 is a non-existent port; + * which should be safe since include/asm/io.h also + * makes this assumption. + * + * Note: this is safe as long as MCR bit 4 is clear + * and the device is in "PC" mode. + */ + scratch = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + /* + * Mask out IER[7:4] bits for test as some UARTs (e.g. TL + * 16C754B) allow only to modify them if an EFR bit is set. + */ + scratch2 = serial_inp(up, UART_IER) & 0x0f; + serial_outp(up, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(up, UART_IER) & 0x0f; + serial_outp(up, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + /* + * We failed; there's nothing here + */ + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(up, UART_MCR); + save_lcr = serial_in(up, UART_LCR); + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufacturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(up->port.flags & UPF_SKIP_TEST)) { + serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(up, UART_MSR) & 0xF0; + serial_outp(up, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; + } + } + + /* + * We're pretty sure there's a port here. Lets find out what + * type of port it is. The IIR top two bits allows us to find + * out if it's 8250 or 16450, 16550, 16550A or later. This + * determines what we test for next. + * + * We also initialise the EFR (if any) to zero for later. The + * EFR occupies the same register location as the FCR and IIR. + */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(up, UART_EFR, 0); + serial_outp(up, UART_LCR, 0); + + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(up, UART_IIR) >> 6; + + DEBUG_AUTOCONF("iir=%d ", scratch); + + switch (scratch) { + case 0: + autoconfig_8250(up); + break; + case 1: + up->port.type = PORT_UNKNOWN; + break; + case 2: + up->port.type = PORT_16550; + break; + case 3: + autoconfig_16550a(up); + break; + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Only probe for RSA ports if we got the region. + */ + if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { + int i; + + for (i = 0 ; i < probe_rsa_count; ++i) { + if (probe_rsa[i] == up->port.iobase && + __enable_rsa(up)) { + up->port.type = PORT_RSA; + break; + } + } + } +#endif + + serial_outp(up, UART_LCR, save_lcr); + + if (up->capabilities != uart_config[up->port.type].flags) { + printk(KERN_WARNING + "ttyS%d: detected caps %08x should be %08x\n", + serial_index(&up->port), up->capabilities, + uart_config[up->port.type].flags); + } + + up->port.fifosize = uart_config[up->port.type].fifo_size; + up->capabilities = uart_config[up->port.type].flags; + up->tx_loadsz = uart_config[up->port.type].tx_loadsz; + + if (up->port.type == PORT_UNKNOWN) + goto out; + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.type == PORT_RSA) + serial_outp(up, UART_RSA_FRR, 0); +#endif + serial_outp(up, UART_MCR, save_mcr); + serial8250_clear_fifos(up); + serial_in(up, UART_RX); + if (up->capabilities & UART_CAP_UUE) + serial_outp(up, UART_IER, UART_IER_UUE); + else + serial_outp(up, UART_IER, 0); + + out: + spin_unlock_irqrestore(&up->port.lock, flags); + DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); +} + +static void autoconfig_irq(struct uart_8250_port *up) +{ + unsigned char save_mcr, save_ier; + unsigned char save_ICP = 0; + unsigned int ICP = 0; + unsigned long irqs; + int irq; + + if (up->port.flags & UPF_FOURPORT) { + ICP = (up->port.iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(up, UART_MCR); + save_ier = serial_inp(up, UART_IER); + serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(up, UART_MCR, 0); + udelay(10); + if (up->port.flags & UPF_FOURPORT) { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(up, UART_LSR); + (void)serial_inp(up, UART_RX); + (void)serial_inp(up, UART_IIR); + (void)serial_inp(up, UART_MSR); + serial_outp(up, UART_TX, 0xFF); + udelay(20); + irq = probe_irq_off(irqs); + + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_IER, save_ier); + + if (up->port.flags & UPF_FOURPORT) + outb_p(save_ICP, ICP); + + up->port.irq = (irq > 0) ? irq : 0; +} + +static inline void __stop_tx(struct uart_8250_port *p) +{ + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } +} + +static void serial8250_stop_tx(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + __stop_tx(up); + + /* + * We really want to stop the transmitter from sending. + */ + if (up->port.type == PORT_16C950) { + up->acr |= UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_start_tx(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + + if (up->bugs & UART_BUG_TXEN) { + unsigned char lsr; + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + if ((up->port.type == PORT_RM9000) ? + (lsr & UART_LSR_THRE) : + (lsr & UART_LSR_TEMT)) + serial8250_tx_chars(up); + } + } + + /* + * Re-enable the transmitter if we disabled it. + */ + if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { + up->acr &= ~UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + /* no MSR capabilities */ + if (up->bugs & UART_BUG_NOMSR) + return; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +/* + * Clear the Tegra rx fifo after a break + * + * FIXME: This needs to become a port specific callback once we have a + * framework for this + */ +static void clear_rx_fifo(struct uart_8250_port *up) +{ + unsigned int status, tmout = 10000; + do { + status = serial_in(up, UART_LSR); + if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + status = serial_in(up, UART_RX); + else + break; + if (--tmout == 0) + break; + udelay(1); + } while (1); +} + +/* + * serial8250_rx_chars: processes according to the passed in LSR + * value, and returns the remaining LSR bits not handled + * by this Rx routine. + */ +unsigned char +serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch; + int max_count = 256; + char flag; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_inp(up, UART_RX); + else + /* + * Intel 82571 has a Serial Over Lan device that will + * set UART_LSR_BI without setting UART_LSR_DR when + * it receives a break. To avoid reading from the + * receive buffer without UART_LSR_DR bit set, we + * just force the read character to be 0 + */ + ch = 0; + + flag = TTY_NORMAL; + up->port.icount.rx++; + + lsr |= up->lsr_saved_flags; + up->lsr_saved_flags = 0; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * If tegra port then clear the rx fifo to + * accept another break/character. + */ + if (up->port.type == PORT_TEGRA) + clear_rx_fifo(up); + + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) + up->port.icount.parity++; + else if (lsr & UART_LSR_FE) + up->port.icount.frame++; + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + + if (lsr & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); + +ignore_char: + lsr = serial_inp(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); + return lsr; +} +EXPORT_SYMBOL_GPL(serial8250_rx_chars); + +void serial8250_tx_chars(struct uart_8250_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_outp(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_tx_stopped(&up->port)) { + serial8250_stop_tx(&up->port); + return; + } + if (uart_circ_empty(xmit)) { + __stop_tx(up); + return; + } + + count = up->tx_loadsz; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + __stop_tx(up); +} +EXPORT_SYMBOL_GPL(serial8250_tx_chars); + +unsigned int serial8250_modem_status(struct uart_8250_port *up) +{ + unsigned int status = serial_in(up, UART_MSR); + + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + return status; +} +EXPORT_SYMBOL_GPL(serial8250_modem_status); + +/* + * This handles the interrupt from one port. + */ +int serial8250_handle_irq(struct uart_port *port, unsigned int iir) +{ + unsigned char status; + unsigned long flags; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + if (iir & UART_IIR_NO_INT) + return 0; + + spin_lock_irqsave(&up->port.lock, flags); + + status = serial_inp(up, UART_LSR); + + DEBUG_INTR("status = %x...", status); + + if (status & (UART_LSR_DR | UART_LSR_BI)) + status = serial8250_rx_chars(up, status); + serial8250_modem_status(up); + if (status & UART_LSR_THRE) + serial8250_tx_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; +} +EXPORT_SYMBOL_GPL(serial8250_handle_irq); + +static int serial8250_default_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned int iir = serial_in(up, UART_IIR); + + return serial8250_handle_irq(port, iir); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t serial8250_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0, handled = 0; + + DEBUG_INTR("serial8250_interrupt(%d)...", irq); + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_8250_port *up; + struct uart_port *port; + bool skip; + + up = list_entry(l, struct uart_8250_port, list); + port = &up->port; + skip = pass_counter && up->port.flags & UPF_IIR_ONCE; + + if (!skip && port->handle_irq(port)) { + handled = 1; + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + /* If we hit this, we're dead. */ + printk_ratelimited(KERN_ERR + "serial8250: too much work for irq%d\n", irq); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_RETVAL(handled); +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + spin_unlock_irq(&i->lock); + /* List empty so throw away the hash node */ + if (i->head == NULL) { + hlist_del(&i->node); + kfree(i); + } +} + +static int serial_link_irq_chain(struct uart_8250_port *up) +{ + struct hlist_head *h; + struct hlist_node *n; + struct irq_info *i; + int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; + + mutex_lock(&hash_mutex); + + h = &irq_lists[up->port.irq % NR_IRQ_HASH]; + + hlist_for_each(n, h) { + i = hlist_entry(n, struct irq_info, node); + if (i->irq == up->port.irq) + break; + } + + if (n == NULL) { + i = kzalloc(sizeof(struct irq_info), GFP_KERNEL); + if (i == NULL) { + mutex_unlock(&hash_mutex); + return -ENOMEM; + } + spin_lock_init(&i->lock); + i->irq = up->port.irq; + hlist_add_head(&i->node, h); + } + mutex_unlock(&hash_mutex); + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + irq_flags |= up->port.irqflags; + ret = request_irq(up->port.irq, serial8250_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i; + struct hlist_node *n; + struct hlist_head *h; + + mutex_lock(&hash_mutex); + + h = &irq_lists[up->port.irq % NR_IRQ_HASH]; + + hlist_for_each(n, h) { + i = hlist_entry(n, struct irq_info, node); + if (i->irq == up->port.irq) + break; + } + + BUG_ON(n == NULL); + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) + free_irq(up->port.irq, i); + + serial_do_unlink(i, up); + mutex_unlock(&hash_mutex); +} + +/* + * This function is used to handle ports that do not have an + * interrupt. This doesn't work very well for 16450's, but gives + * barely passable results for a 16550A. (Although at the expense + * of much CPU overhead). + */ +static void serial8250_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + + up->port.handle_irq(&up->port); + mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); +} + +static void serial8250_backup_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + unsigned int iir, ier = 0, lsr; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Must disable interrupts or else we risk racing with the interrupt + * based handler. + */ + if (is_real_interrupt(up->port.irq)) { + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + } + + iir = serial_in(up, UART_IIR); + + /* + * This should be a safe test for anyone who doesn't trust the + * IIR bits on their UART, but it's specifically designed for + * the "Diva" UART used on the management processor on many HP + * ia64 and parisc boxes. + */ + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && + (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && + (lsr & UART_LSR_THRE)) { + iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir |= UART_IIR_THRI; + } + + if (!(iir & UART_IIR_NO_INT)) + serial8250_tx_chars(up); + + if (is_real_interrupt(up->port.irq)) + serial_out(up, UART_IER, ier); + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* Standard timer interval plus 0.2s to keep the port running */ + mod_timer(&up->timer, + jiffies + uart_poll_timeout(&up->port) + HZ / 5); +} + +static unsigned int serial8250_tx_empty(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + unsigned int lsr; + + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + spin_unlock_irqrestore(&up->port.lock, flags); + + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; +} + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned int status; + unsigned int ret; + + status = serial8250_modem_status(up); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned char mcr = 0; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_8250_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + for (;;) { + status = serial_in(up, UART_LSR); + + up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; + + if ((status & bits) == bits) + break; + if (--tmout == 0) + break; + udelay(1); + } + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + unsigned int tmout; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) + break; + udelay(1); + touch_nmi_watchdog(); + } + } +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial8250_get_poll_char(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned char lsr = serial_inp(up, UART_LSR); + + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; + + return serial_inp(up, UART_RX); +} + + +static void serial8250_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + if (up->capabilities & UART_CAP_UUE) + serial_out(up, UART_IER, UART_IER_UUE); + else + serial_out(up, UART_IER, 0); + + wait_for_xmitr(up, BOTH_EMPTY); + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, c); + if (c == 10) { + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_TX, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_IER, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static int serial8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + unsigned char lsr, iir; + int retval; + + up->port.fifosize = uart_config[up->port.type].fifo_size; + up->tx_loadsz = uart_config[up->port.type].tx_loadsz; + up->capabilities = uart_config[up->port.type].flags; + up->mcr = 0; + + if (up->port.iotype != up->cur_iotype) + set_io_from_upio(port); + + if (up->port.type == PORT_16C950) { + /* Wake up and initialize UART */ + up->acr = 0; + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_IER, 0); + serial_outp(up, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + enable_rsa(up); +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial8250_clear_fifos(up); + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + /* + * At this point, there's no way the LSR could still be 0xff; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (!(up->port.flags & UPF_BUGGY_UART) && + (serial_inp(up, UART_LSR) == 0xff)) { + printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", + serial_index(&up->port)); + return -ENODEV; + } + + /* + * For a XR16C850, we need to set the trigger levels + */ + if (up->port.type == PORT_16850) { + unsigned char fctr; + + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + + fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); + serial_outp(up, UART_TRG, UART_TRG_96); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); + serial_outp(up, UART_TRG, UART_TRG_96); + + serial_outp(up, UART_LCR, 0); + } + + if (is_real_interrupt(up->port.irq)) { + unsigned char iir1; + /* + * Test for UARTs that do not reassert THRE when the + * transmitter is idle and the interrupt has already + * been cleared. Real 16550s should always reassert + * this interrupt whenever the transmitter is idle and + * the interrupt is enabled. Delays are necessary to + * allow register changes to become visible. + */ + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.irqflags & IRQF_SHARED) + disable_irq_nosync(up->port.irq); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_out_sync(up, UART_IER, UART_IER_THRI); + udelay(1); /* allow THRE to set */ + iir1 = serial_in(up, UART_IIR); + serial_out(up, UART_IER, 0); + serial_out_sync(up, UART_IER, UART_IER_THRI); + udelay(1); /* allow a working UART time to re-assert THRE */ + iir = serial_in(up, UART_IIR); + serial_out(up, UART_IER, 0); + + if (up->port.irqflags & IRQF_SHARED) + enable_irq(up->port.irq); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * If the interrupt is not reasserted, setup a timer to + * kick the UART on a regular basis. + */ + if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { + up->bugs |= UART_BUG_THRE; + pr_debug("ttyS%d - using backup timer\n", + serial_index(port)); + } + } + + /* + * The above check will only give an accurate result the first time + * the port is opened so this value needs to be preserved. + */ + if (up->bugs & UART_BUG_THRE) { + up->timer.function = serial8250_backup_timeout; + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + + uart_poll_timeout(port) + HZ / 5); + } + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Now, initialize the UART + */ + serial_outp(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + if (!is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT1; + } else + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + if (is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + + /* Serial over Lan (SoL) hack: + Intel 8257x Gigabit ethernet chips have a + 16550 emulation, to be used for Serial Over Lan. + Those chips take a longer time than a normal + serial device to signalize that a transmission + data was queued. Due to that, the above test generally + fails. One solution would be to delay the reading of + iir. However, this is not reliable, since the timeout + is variable. So, let's just don't test if we receive + TX irq. This way, we'll never enable UART_BUG_TXEN. + */ + if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST) + goto dont_test_tx_en; + + /* + * Do a quick test to see if we receive an + * interrupt when we enable the TX irq. + */ + serial_outp(up, UART_IER, UART_IER_THRI); + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + serial_outp(up, UART_IER, 0); + + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (!(up->bugs & UART_BUG_TXEN)) { + up->bugs |= UART_BUG_TXEN; + pr_debug("ttyS%d - enabling bad tx status workarounds\n", + serial_index(port)); + } + } else { + up->bugs &= ~UART_BUG_TXEN; + } + +dont_test_tx_en: + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Clear the interrupt registers again for luck, and clear the + * saved flags to avoid getting false values from polling + * routines or the previous session. + */ + serial_inp(up, UART_LSR); + serial_inp(up, UART_RX); + serial_inp(up, UART_IIR); + serial_inp(up, UART_MSR); + up->lsr_saved_flags = 0; + up->msr_saved_flags = 0; + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(up, UART_IER, up->ier); + + if (up->port.flags & UPF_FOURPORT) { + unsigned int icp; + /* + * Enable interrupts on the AST Fourport board + */ + icp = (up->port.iobase & 0xfe0) | 0x01f; + outb_p(0x80, icp); + (void) inb_p(icp); + } + + return 0; +} + +static void serial8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_outp(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((up->port.iobase & 0xfe0) | 0x1f); + up->port.mctrl |= TIOCM_OUT1; + } else + up->port.mctrl &= ~TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); + serial8250_clear_fifos(up); + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + disable_rsa(up); +#endif + + /* + * Read data port to reset things, and then unlink from + * the IRQ chain. + */ + (void) serial_in(up, UART_RX); + + del_timer_sync(&up->timer); + up->timer.function = serial8250_timeout; + if (is_real_interrupt(up->port.irq)) + serial_unlink_irq_chain(up); +} + +static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int quot; + + /* + * Handle magic divisors for baud rates above baud_base on + * SMSC SuperIO chips. + */ + if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/4)) + quot = 0x8001; + else if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/8)) + quot = 0x8002; + else + quot = uart_get_divisor(port, baud); + + return quot; +} + +void +serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + quot = serial8250_get_divisor(port, baud); + + /* + * Oxford Semi 952 rev B workaround + */ + if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) + quot++; + + if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { + if (baud < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + else + fcr = uart_config[up->port.type].fcr; + } + + /* + * MCR-based auto flow control. When AFE is enabled, RTS will be + * deasserted when the receive FIFO contains more characters than + * the trigger, or the MCR RTS bit is cleared. In the case where + * the remote UART is not using CTS auto flow control, we must + * have sufficient FIFO entries for the latency of the remote + * UART to respond. IOW, at least 32 bytes of FIFO. + */ + if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) { + up->mcr &= ~UART_MCR_AFE; + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + } + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (!(up->bugs & UART_BUG_NOMSR) && + UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + if (up->capabilities & UART_CAP_UUE) + up->ier |= UART_IER_UUE; + if (up->capabilities & UART_CAP_RTOIE) + up->ier |= UART_IER_RTOIE; + + serial_out(up, UART_IER, up->ier); + + if (up->capabilities & UART_CAP_EFR) { + unsigned char efr = 0; + /* + * TI16C752/Startech hardware flow control. FIXME: + * - TI16C752 requires control thresholds to be set. + * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled. + */ + if (termios->c_cflag & CRTSCTS) + efr |= UART_EFR_CTS; + + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + if (up->port.flags & UPF_EXAR_EFR) + serial_outp(up, UART_XR_EFR, efr); + else + serial_outp(up, UART_EFR, efr); + } + +#ifdef CONFIG_ARCH_OMAP + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (cpu_is_omap1510() && is_omap_port(up)) { + if (baud == 115200) { + quot = 1; + serial_out(up, UART_OMAP_OSC_12M_SEL, 1); + } else + serial_out(up, UART_OMAP_OSC_12M_SEL, 0); + } +#endif + + if (up->capabilities & UART_NATSEMI) { + /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ + serial_outp(up, UART_LCR, 0xe0); + } else { + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + } + + serial_dl_write(up, quot); + + /* + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR + * is written without DLAB set, this mode will be disabled. + */ + if (up->port.type == PORT_16750) + serial_outp(up, UART_FCR, fcr); + + serial_outp(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + if (up->port.type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(up, UART_FCR, fcr); /* set fcr */ + } + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} +EXPORT_SYMBOL(serial8250_do_set_termios); + +static void +serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + if (port->set_termios) + port->set_termios(port, termios, old); + else + serial8250_do_set_termios(port, termios, old); +} + +static void +serial8250_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + serial8250_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + + +void serial8250_do_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *p = + container_of(port, struct uart_8250_port, port); + + serial8250_set_sleep(p, state != 0); +} +EXPORT_SYMBOL(serial8250_do_pm); + +static void +serial8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + if (port->pm) + port->pm(port, state, oldstate); + else + serial8250_do_pm(port, state, oldstate); +} + +static unsigned int serial8250_port_size(struct uart_8250_port *pt) +{ + if (pt->port.iotype == UPIO_AU) + return 0x1000; +#ifdef CONFIG_ARCH_OMAP + if (is_omap_port(pt)) + return 0x16 << pt->port.regshift; +#endif + return 8 << pt->port.regshift; +} + +/* + * Resource handling. + */ +static int serial8250_request_std_resource(struct uart_8250_port *up) +{ + unsigned int size = serial8250_port_size(up); + int ret = 0; + + switch (up->port.iotype) { + case UPIO_AU: + case UPIO_TSI: + case UPIO_MEM32: + case UPIO_MEM: + if (!up->port.mapbase) + break; + + if (!request_mem_region(up->port.mapbase, size, "serial")) { + ret = -EBUSY; + break; + } + + if (up->port.flags & UPF_IOREMAP) { + up->port.membase = ioremap_nocache(up->port.mapbase, + size); + if (!up->port.membase) { + release_mem_region(up->port.mapbase, size); + ret = -ENOMEM; + } + } + break; + + case UPIO_HUB6: + case UPIO_PORT: + if (!request_region(up->port.iobase, size, "serial")) + ret = -EBUSY; + break; + } + return ret; +} + +static void serial8250_release_std_resource(struct uart_8250_port *up) +{ + unsigned int size = serial8250_port_size(up); + + switch (up->port.iotype) { + case UPIO_AU: + case UPIO_TSI: + case UPIO_MEM32: + case UPIO_MEM: + if (!up->port.mapbase) + break; + + if (up->port.flags & UPF_IOREMAP) { + iounmap(up->port.membase); + up->port.membase = NULL; + } + + release_mem_region(up->port.mapbase, size); + break; + + case UPIO_HUB6: + case UPIO_PORT: + release_region(up->port.iobase, size); + break; + } +} + +static int serial8250_request_rsa_resource(struct uart_8250_port *up) +{ + unsigned long start = UART_RSA_BASE << up->port.regshift; + unsigned int size = 8 << up->port.regshift; + int ret = -EINVAL; + + switch (up->port.iotype) { + case UPIO_HUB6: + case UPIO_PORT: + start += up->port.iobase; + if (request_region(start, size, "serial-rsa")) + ret = 0; + else + ret = -EBUSY; + break; + } + + return ret; +} + +static void serial8250_release_rsa_resource(struct uart_8250_port *up) +{ + unsigned long offset = UART_RSA_BASE << up->port.regshift; + unsigned int size = 8 << up->port.regshift; + + switch (up->port.iotype) { + case UPIO_HUB6: + case UPIO_PORT: + release_region(up->port.iobase + offset, size); + break; + } +} + +static void serial8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + serial8250_release_std_resource(up); + if (up->port.type == PORT_RSA) + serial8250_release_rsa_resource(up); +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + int ret = 0; + + ret = serial8250_request_std_resource(up); + if (ret == 0 && up->port.type == PORT_RSA) { + ret = serial8250_request_rsa_resource(up); + if (ret < 0) + serial8250_release_std_resource(up); + } + + return ret; +} + +static void serial8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + int probeflags = PROBE_ANY; + int ret; + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + ret = serial8250_request_std_resource(up); + if (ret < 0) + return; + + ret = serial8250_request_rsa_resource(up); + if (ret < 0) + probeflags &= ~PROBE_RSA; + + if (up->port.iotype != up->cur_iotype) + set_io_from_upio(port); + + if (flags & UART_CONFIG_TYPE) + autoconfig(up, probeflags); + + /* if access method is AU, it is a 16550 with a quirk */ + if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) + up->bugs |= UART_BUG_NOMSR; + + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(up); + + if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) + serial8250_release_rsa_resource(up); + if (up->port.type == PORT_UNKNOWN) + serial8250_release_std_resource(up); +} + +static int +serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= nr_irqs || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char * +serial8250_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops serial8250_pops = { + .tx_empty = serial8250_tx_empty, + .set_mctrl = serial8250_set_mctrl, + .get_mctrl = serial8250_get_mctrl, + .stop_tx = serial8250_stop_tx, + .start_tx = serial8250_start_tx, + .stop_rx = serial8250_stop_rx, + .enable_ms = serial8250_enable_ms, + .break_ctl = serial8250_break_ctl, + .startup = serial8250_startup, + .shutdown = serial8250_shutdown, + .set_termios = serial8250_set_termios, + .set_ldisc = serial8250_set_ldisc, + .pm = serial8250_pm, + .type = serial8250_type, + .release_port = serial8250_release_port, + .request_port = serial8250_request_port, + .config_port = serial8250_config_port, + .verify_port = serial8250_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial8250_get_poll_char, + .poll_put_char = serial8250_put_poll_char, +#endif +}; + +static struct uart_8250_port serial8250_ports[UART_NR]; + +static void (*serial8250_isa_config)(int port, struct uart_port *up, + unsigned short *capabilities); + +void serial8250_set_isa_configurator( + void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) +{ + serial8250_isa_config = v; +} +EXPORT_SYMBOL(serial8250_set_isa_configurator); + +static void __init serial8250_isa_init_ports(void) +{ + struct uart_8250_port *up; + static int first = 1; + int i, irqflag = 0; + + if (!first) + return; + first = 0; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + up->port.line = i; + spin_lock_init(&up->port.lock); + + init_timer(&up->timer); + up->timer.function = serial8250_timeout; + + /* + * ALPHA_KLUDGE_MCR needs to be killed. + */ + up->mcr_mask = ~ALPHA_KLUDGE_MCR; + up->mcr_force = ALPHA_KLUDGE_MCR; + + up->port.ops = &serial8250_pops; + } + + if (share_irqs) + irqflag = IRQF_SHARED; + + for (i = 0, up = serial8250_ports; + i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.irqflags = old_serial_port[i].irqflags; + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags; + up->port.hub6 = old_serial_port[i].hub6; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + set_io_from_upio(&up->port); + up->port.irqflags |= irqflag; + if (serial8250_isa_config != NULL) + serial8250_isa_config(i, &up->port, &up->capabilities); + + } +} + +static void +serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) +{ + up->port.type = type; + up->port.fifosize = uart_config[type].fifo_size; + up->capabilities = uart_config[type].flags; + up->tx_loadsz = uart_config[type].tx_loadsz; +} + +static void __init +serial8250_register_ports(struct uart_driver *drv, struct device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + up->cur_iotype = 0xFF; + } + + serial8250_isa_init_ports(); + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + up->port.dev = dev; + + if (up->port.flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(up, up->port.type); + + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +static void serial8250_console_putchar(struct uart_port *port, int ch) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_out(up, UART_TX, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +serial8250_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + touch_nmi_watchdog(); + + local_irq_save(flags); + if (up->port.sysrq) { + /* serial8250_handle_irq() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + + if (up->capabilities & UART_CAP_UUE) + serial_out(up, UART_IER, UART_IER_UUE); + else + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial8250_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_IER, ier); + + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + serial8250_modem_status(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init serial8250_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= nr_uarts) + co->index = 0; + port = &serial8250_ports[co->index].port; + if (!port->iobase && !port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static int serial8250_console_early_setup(void) +{ + return serial8250_find_port_for_earlycon(); +} + +static struct console serial8250_console = { + .name = "ttyS", + .write = serial8250_console_write, + .device = uart_console_device, + .setup = serial8250_console_setup, + .early_setup = serial8250_console_early_setup, + .flags = CON_PRINTBUFFER | CON_ANYTIME, + .index = -1, + .data = &serial8250_reg, +}; + +static int __init serial8250_console_init(void) +{ + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + + serial8250_isa_init_ports(); + register_console(&serial8250_console); + return 0; +} +console_initcall(serial8250_console_init); + +int serial8250_find_port(struct uart_port *p) +{ + int line; + struct uart_port *port; + + for (line = 0; line < nr_uarts; line++) { + port = &serial8250_ports[line].port; + if (uart_match_port(p, port)) + return line; + } + return -ENODEV; +} + +#define SERIAL8250_CONSOLE &serial8250_console +#else +#define SERIAL8250_CONSOLE NULL +#endif + +static struct uart_driver serial8250_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .cons = SERIAL8250_CONSOLE, +}; + +/* + * early_serial_setup - early registration for 8250 ports + * + * Setup an 8250 port structure prior to console initialisation. Use + * after console initialisation will cause undefined behaviour. + */ +int __init early_serial_setup(struct uart_port *port) +{ + struct uart_port *p; + + if (port->line >= ARRAY_SIZE(serial8250_ports)) + return -ENODEV; + + serial8250_isa_init_ports(); + p = &serial8250_ports[port->line].port; + p->iobase = port->iobase; + p->membase = port->membase; + p->irq = port->irq; + p->irqflags = port->irqflags; + p->uartclk = port->uartclk; + p->fifosize = port->fifosize; + p->regshift = port->regshift; + p->iotype = port->iotype; + p->flags = port->flags; + p->mapbase = port->mapbase; + p->private_data = port->private_data; + p->type = port->type; + p->line = port->line; + + set_io_from_upio(p); + if (port->serial_in) + p->serial_in = port->serial_in; + if (port->serial_out) + p->serial_out = port->serial_out; + if (port->handle_irq) + p->handle_irq = port->handle_irq; + else + p->handle_irq = serial8250_default_handle_irq; + + return 0; +} + +/** + * serial8250_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void serial8250_suspend_port(int line) +{ + uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); +} + +/** + * serial8250_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void serial8250_resume_port(int line) +{ + struct uart_8250_port *up = &serial8250_ports[line]; + + if (up->capabilities & UART_NATSEMI) { + /* Ensure it's still in high speed mode */ + serial_outp(up, UART_LCR, 0xE0); + + ns16550a_goto_highspeed(up); + + serial_outp(up, UART_LCR, 0); + up->port.uartclk = 921600*16; + } + uart_resume_port(&serial8250_reg, &up->port); +} + +/* + * Register a set of serial devices attached to a platform device. The + * list is terminated with a zero flags entry, which means we expect + * all entries to have at least UPF_BOOT_AUTOCONF set. + */ +static int __devinit serial8250_probe(struct platform_device *dev) +{ + struct plat_serial8250_port *p = dev->dev.platform_data; + struct uart_port port; + int ret, i, irqflag = 0; + + memset(&port, 0, sizeof(struct uart_port)); + + if (share_irqs) + irqflag = IRQF_SHARED; + + for (i = 0; p && p->flags != 0; p++, i++) { + port.iobase = p->iobase; + port.membase = p->membase; + port.irq = p->irq; + port.irqflags = p->irqflags; + port.uartclk = p->uartclk; + port.regshift = p->regshift; + port.iotype = p->iotype; + port.flags = p->flags; + port.mapbase = p->mapbase; + port.hub6 = p->hub6; + port.private_data = p->private_data; + port.type = p->type; + port.serial_in = p->serial_in; + port.serial_out = p->serial_out; + port.handle_irq = p->handle_irq; + port.set_termios = p->set_termios; + port.pm = p->pm; + port.dev = &dev->dev; + port.irqflags |= irqflag; + ret = serial8250_register_port(&port); + if (ret < 0) { + dev_err(&dev->dev, "unable to register port at index %d " + "(IO%lx MEM%llx IRQ%d): %d\n", i, + p->iobase, (unsigned long long)p->mapbase, + p->irq, ret); + } + } + return 0; +} + +/* + * Remove serial ports registered against a platform device. + */ +static int __devexit serial8250_remove(struct platform_device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.dev == &dev->dev) + serial8250_unregister_port(i); + } + return 0; +} + +static int serial8250_suspend(struct platform_device *dev, pm_message_t state) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_suspend_port(&serial8250_reg, &up->port); + } + + return 0; +} + +static int serial8250_resume(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + serial8250_resume_port(i); + } + + return 0; +} + +static struct platform_driver serial8250_isa_driver = { + .probe = serial8250_probe, + .remove = __devexit_p(serial8250_remove), + .suspend = serial8250_suspend, + .resume = serial8250_resume, + .driver = { + .name = "serial8250", + .owner = THIS_MODULE, + }, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices listed + * in the table in include/asm/serial.h + */ +static struct platform_device *serial8250_isa_devs; + +/* + * serial8250_register_port and serial8250_unregister_port allows for + * 16x50 serial ports to be configured at run-time, to support PCMCIA + * modems and PCI multiport cards. + */ +static DEFINE_MUTEX(serial_mutex); + +static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *port) +{ + int i; + + /* + * First, find a port entry which matches. + */ + for (i = 0; i < nr_uarts; i++) + if (uart_match_port(&serial8250_ports[i].port, port)) + return &serial8250_ports[i]; + + /* + * We didn't find a matching entry, so look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + for (i = 0; i < nr_uarts; i++) + if (serial8250_ports[i].port.type == PORT_UNKNOWN && + serial8250_ports[i].port.iobase == 0) + return &serial8250_ports[i]; + + /* + * That also failed. Last resort is to find any entry which + * doesn't have a real port associated with it. + */ + for (i = 0; i < nr_uarts; i++) + if (serial8250_ports[i].port.type == PORT_UNKNOWN) + return &serial8250_ports[i]; + + return NULL; +} + +/** + * serial8250_register_port - register a serial port + * @port: serial port template + * + * Configure the serial port specified by the request. If the + * port exists and is in use, it is hung up and unregistered + * first. + * + * The port is then probed and if necessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int serial8250_register_port(struct uart_port *port) +{ + struct uart_8250_port *uart; + int ret = -ENOSPC; + + if (port->uartclk == 0) + return -EINVAL; + + mutex_lock(&serial_mutex); + + uart = serial8250_find_match_or_unused(port); + if (uart) { + uart_remove_one_port(&serial8250_reg, &uart->port); + + uart->port.iobase = port->iobase; + uart->port.membase = port->membase; + uart->port.irq = port->irq; + uart->port.irqflags = port->irqflags; + uart->port.uartclk = port->uartclk; + uart->port.fifosize = port->fifosize; + uart->port.regshift = port->regshift; + uart->port.iotype = port->iotype; + uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; + uart->port.mapbase = port->mapbase; + uart->port.private_data = port->private_data; + if (port->dev) + uart->port.dev = port->dev; + + if (port->flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(uart, port->type); + + set_io_from_upio(&uart->port); + /* Possibly override default I/O functions. */ + if (port->serial_in) + uart->port.serial_in = port->serial_in; + if (port->serial_out) + uart->port.serial_out = port->serial_out; + if (port->handle_irq) + uart->port.handle_irq = port->handle_irq; + /* Possibly override set_termios call */ + if (port->set_termios) + uart->port.set_termios = port->set_termios; + if (port->pm) + uart->port.pm = port->pm; + + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); + + ret = uart_add_one_port(&serial8250_reg, &uart->port); + if (ret == 0) + ret = uart->port.line; + } + mutex_unlock(&serial_mutex); + + return ret; +} +EXPORT_SYMBOL(serial8250_register_port); + +/** + * serial8250_unregister_port - remove a 16x50 serial port at runtime + * @line: serial line number + * + * Remove one serial port. This may not be called from interrupt + * context. We hand the port back to the our control. + */ +void serial8250_unregister_port(int line) +{ + struct uart_8250_port *uart = &serial8250_ports[line]; + + mutex_lock(&serial_mutex); + uart_remove_one_port(&serial8250_reg, &uart->port); + if (serial8250_isa_devs) { + uart->port.flags &= ~UPF_BOOT_AUTOCONF; + uart->port.type = PORT_UNKNOWN; + uart->port.dev = &serial8250_isa_devs->dev; + uart->capabilities = uart_config[uart->port.type].flags; + uart_add_one_port(&serial8250_reg, &uart->port); + } else { + uart->port.dev = NULL; + } + mutex_unlock(&serial_mutex); +} +EXPORT_SYMBOL(serial8250_unregister_port); + +static int __init serial8250_init(void) +{ + int ret; + + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + + printk(KERN_INFO "Serial: 8250/16550 driver, " + "%d ports, IRQ sharing %sabled\n", nr_uarts, + share_irqs ? "en" : "dis"); + +#ifdef CONFIG_SPARC + ret = sunserial_register_minors(&serial8250_reg, UART_NR); +#else + serial8250_reg.nr = UART_NR; + ret = uart_register_driver(&serial8250_reg); +#endif + if (ret) + goto out; + + serial8250_isa_devs = platform_device_alloc("serial8250", + PLAT8250_DEV_LEGACY); + if (!serial8250_isa_devs) { + ret = -ENOMEM; + goto unreg_uart_drv; + } + + ret = platform_device_add(serial8250_isa_devs); + if (ret) + goto put_dev; + + serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); + + ret = platform_driver_register(&serial8250_isa_driver); + if (ret == 0) + goto out; + + platform_device_del(serial8250_isa_devs); +put_dev: + platform_device_put(serial8250_isa_devs); +unreg_uart_drv: +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +out: + return ret; +} + +static void __exit serial8250_exit(void) +{ + struct platform_device *isa_dev = serial8250_isa_devs; + + /* + * This tells serial8250_unregister_port() not to re-register + * the ports (thereby making serial8250_isa_driver permanently + * in use.) + */ + serial8250_isa_devs = NULL; + + platform_driver_unregister(&serial8250_isa_driver); + platform_device_unregister(isa_dev); + +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +} + +module_init(serial8250_init); +module_exit(serial8250_exit); + +EXPORT_SYMBOL(serial8250_suspend_port); +EXPORT_SYMBOL(serial8250_resume_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); + +module_param(share_irqs, uint, 0644); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" + " (unsafe)"); + +module_param(nr_uarts, uint, 0644); +MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); + +module_param(skip_txen_test, uint, 0644); +MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); + +#ifdef CONFIG_SERIAL_8250_RSA +module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +#endif +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h new file mode 100644 index 0000000..ae027be --- /dev/null +++ b/drivers/tty/serial/8250/8250.h @@ -0,0 +1,105 @@ +/* + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * 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. + */ + +#include + +struct uart_8250_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short capabilities; /* port capabilities */ + unsigned short bugs; /* port bugs */ + unsigned int tx_loadsz; /* transmit fifo load size */ + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char cur_iotype; /* Running I/O type */ + + /* + * Some bits in registers are cleared on a read, so they must + * be saved whenever the register is read but the bits will not + * be immediately processed. + */ +#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS + unsigned char lsr_saved_flags; +#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA + unsigned char msr_saved_flags; +}; + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char hub6; + unsigned char io_type; + unsigned char *iomem_base; + unsigned short iomem_reg_shift; + unsigned long irqflags; +}; + +/* + * This replaces serial_uart_config in include/linux/serial.h + */ +struct serial8250_config { + const char *name; + unsigned short fifo_size; + unsigned short tx_loadsz; + unsigned char fcr; + unsigned int flags; +}; + +#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ +#define UART_CAP_EFR (1 << 9) /* UART has EFR */ +#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ +#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ +#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ +#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ + +#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ +#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ +#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */ +#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ +#define SERIAL8250_SHARE_IRQS 1 +#else +#define SERIAL8250_SHARE_IRQS 0 +#endif + +#if defined(__alpha__) && !defined(CONFIG_PCI) +/* + * Digital did something really horribly wrong with the OUT1 and OUT2 + * lines on at least some ALPHA's. The failure mode is that if either + * is cleared, the machine locks up with endless interrupts. + */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) +#elif defined(CONFIG_SBC8560) +/* + * WindRiver did something similarly broken on their SBC8560 board. The + * UART tristates its IRQ output while OUT2 is clear, but they pulled + * the interrupt line _up_ instead of down, so if we register the IRQ + * while the UART is in that state, we die in an IRQ storm. */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) +#else +#define ALPHA_KLUDGE_MCR 0 +#endif diff --git a/drivers/tty/serial/8250/8250_accent.c b/drivers/tty/serial/8250/8250_accent.c new file mode 100644 index 0000000..34b51c6 --- /dev/null +++ b/drivers/tty/serial/8250/8250_accent.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port accent_data[] = { + PORT(0x330, 4), + PORT(0x338, 4), + { }, +}; + +static struct platform_device accent_device = { + .name = "serial8250", + .id = PLAT8250_DEV_ACCENT, + .dev = { + .platform_data = accent_data, + }, +}; + +static int __init accent_init(void) +{ + return platform_device_register(&accent_device); +} + +module_init(accent_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c new file mode 100644 index 0000000..b0ce8c5 --- /dev/null +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -0,0 +1,141 @@ +/* + * linux/drivers/serial/acorn.c + * + * Copyright (C) 1996-2003 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "8250.h" + +#define MAX_PORTS 3 + +struct serial_card_type { + unsigned int num_ports; + unsigned int uartclk; + unsigned int type; + unsigned int offset[MAX_PORTS]; +}; + +struct serial_card_info { + unsigned int num_ports; + int ports[MAX_PORTS]; + void __iomem *vaddr; +}; + +static int __devinit +serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct serial_card_info *info; + struct serial_card_type *type = id->data; + struct uart_port port; + unsigned long bus_addr; + unsigned int i; + + info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->num_ports = type->num_ports; + + bus_addr = ecard_resource_start(ec, type->type); + info->vaddr = ecardm_iomap(ec, type->type, 0, 0); + if (!info->vaddr) { + kfree(info); + return -ENOMEM; + } + + ecard_set_drvdata(ec, info); + + memset(&port, 0, sizeof(struct uart_port)); + port.irq = ec->irq; + port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + port.uartclk = type->uartclk; + port.iotype = UPIO_MEM; + port.regshift = 2; + port.dev = &ec->dev; + + for (i = 0; i < info->num_ports; i ++) { + port.membase = info->vaddr + type->offset[i]; + port.mapbase = bus_addr + type->offset[i]; + + info->ports[i] = serial8250_register_port(&port); + } + + return 0; +} + +static void __devexit serial_card_remove(struct expansion_card *ec) +{ + struct serial_card_info *info = ecard_get_drvdata(ec); + int i; + + ecard_set_drvdata(ec, NULL); + + for (i = 0; i < info->num_ports; i++) + if (info->ports[i] > 0) + serial8250_unregister_port(info->ports[i]); + + kfree(info); +} + +static struct serial_card_type atomwide_type = { + .num_ports = 3, + .uartclk = 7372800, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2800, 0x2400, 0x2000 }, +}; + +static struct serial_card_type serport_type = { + .num_ports = 2, + .uartclk = 3686400, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2000, 0x2020 }, +}; + +static const struct ecard_id serial_cids[] = { + { MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, &atomwide_type }, + { MANU_SERPORT, PROD_SERPORT_DSPORT, &serport_type }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver serial_card_driver = { + .probe = serial_card_probe, + .remove = __devexit_p(serial_card_remove), + .id_table = serial_cids, + .drv = { + .name = "8250_acorn", + }, +}; + +static int __init serial_card_init(void) +{ + return ecard_register_driver(&serial_card_driver); +} + +static void __exit serial_card_exit(void) +{ + ecard_remove_driver(&serial_card_driver); +} + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); +MODULE_LICENSE("GPL"); + +module_init(serial_card_init); +module_exit(serial_card_exit); diff --git a/drivers/tty/serial/8250/8250_boca.c b/drivers/tty/serial/8250/8250_boca.c new file mode 100644 index 0000000..d125dc1 --- /dev/null +++ b/drivers/tty/serial/8250/8250_boca.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port boca_data[] = { + PORT(0x100, 12), + PORT(0x108, 12), + PORT(0x110, 12), + PORT(0x118, 12), + PORT(0x120, 12), + PORT(0x128, 12), + PORT(0x130, 12), + PORT(0x138, 12), + PORT(0x140, 12), + PORT(0x148, 12), + PORT(0x150, 12), + PORT(0x158, 12), + PORT(0x160, 12), + PORT(0x168, 12), + PORT(0x170, 12), + PORT(0x178, 12), + { }, +}; + +static struct platform_device boca_device = { + .name = "serial8250", + .id = PLAT8250_DEV_BOCA, + .dev = { + .platform_data = boca_data, + }, +}; + +static int __init boca_init(void) +{ + return platform_device_register(&boca_device); +} + +module_init(boca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Boca cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c new file mode 100644 index 0000000..f574eef --- /dev/null +++ b/drivers/tty/serial/8250/8250_dw.c @@ -0,0 +1,184 @@ +/* + * Synopsys DesignWare 8250 driver. + * + * Copyright 2011 Picochip, Jamie Iles. + * + * 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. + * + * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the + * LCR is written whilst busy. If it is, then a busy detect interrupt is + * raised, the LCR needs to be rewritten and the uart status register read. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dw8250_data { + int last_lcr; + int line; +}; + +static void dw8250_serial_out(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + if (offset == UART_LCR) + d->last_lcr = value; + + offset <<= p->regshift; + writeb(value, p->membase + offset); +} + +static unsigned int dw8250_serial_in(struct uart_port *p, int offset) +{ + offset <<= p->regshift; + + return readb(p->membase + offset); +} + +static void dw8250_serial_out32(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + if (offset == UART_LCR) + d->last_lcr = value; + + offset <<= p->regshift; + writel(value, p->membase + offset); +} + +static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) +{ + offset <<= p->regshift; + + return readl(p->membase + offset); +} + +/* Offset for the DesignWare's UART Status Register. */ +#define UART_USR 0x1f + +static int dw8250_handle_irq(struct uart_port *p) +{ + struct dw8250_data *d = p->private_data; + unsigned int iir = p->serial_in(p, UART_IIR); + + if (serial8250_handle_irq(p, iir)) { + return 1; + } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + /* Clear the USR and write the LCR again. */ + (void)p->serial_in(p, UART_USR); + p->serial_out(p, d->last_lcr, UART_LCR); + + return 1; + } + + return 0; +} + +static int __devinit dw8250_probe(struct platform_device *pdev) +{ + struct uart_port port = {}; + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct device_node *np = pdev->dev.of_node; + u32 val; + struct dw8250_data *data; + + if (!regs || !irq) { + dev_err(&pdev->dev, "no registers/irq defined\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + port.private_data = data; + + spin_lock_init(&port.lock); + port.mapbase = regs->start; + port.irq = irq->start; + port.handle_irq = dw8250_handle_irq; + port.type = PORT_8250; + port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | + UPF_FIXED_PORT | UPF_FIXED_TYPE; + port.dev = &pdev->dev; + + port.iotype = UPIO_MEM; + port.serial_in = dw8250_serial_in; + port.serial_out = dw8250_serial_out; + if (!of_property_read_u32(np, "reg-io-width", &val)) { + switch (val) { + case 1: + break; + case 4: + port.iotype = UPIO_MEM32; + port.serial_in = dw8250_serial_in32; + port.serial_out = dw8250_serial_out32; + break; + default: + dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n", + val); + return -EINVAL; + } + } + + if (!of_property_read_u32(np, "reg-shift", &val)) + port.regshift = val; + + if (of_property_read_u32(np, "clock-frequency", &val)) { + dev_err(&pdev->dev, "no clock-frequency property set\n"); + return -EINVAL; + } + port.uartclk = val; + + data->line = serial8250_register_port(&port); + if (data->line < 0) + return data->line; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int __devexit dw8250_remove(struct platform_device *pdev) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + + return 0; +} + +static const struct of_device_id dw8250_match[] = { + { .compatible = "snps,dw-apb-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw8250_match); + +static struct platform_driver dw8250_platform_driver = { + .driver = { + .name = "dw-apb-uart", + .owner = THIS_MODULE, + .of_match_table = dw8250_match, + }, + .probe = dw8250_probe, + .remove = __devexit_p(dw8250_remove), +}; + +module_platform_driver(dw8250_platform_driver); + +MODULE_AUTHOR("Jamie Iles"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver"); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c new file mode 100644 index 0000000..eaafb98 --- /dev/null +++ b/drivers/tty/serial/8250/8250_early.c @@ -0,0 +1,287 @@ +/* + * Early serial console for 8250/16550 devices + * + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, + * and on early_printk.c by Andi Kleen. + * + * This is for use before the serial driver has initialized, in + * particular, before the UARTs have been discovered and named. + * Instead of specifying the console device as, e.g., "ttyS0", + * we locate the device directly by its MMIO or I/O port address. + * + * The user can specify the device directly, e.g., + * earlycon=uart8250,io,0x3f8,9600n8 + * earlycon=uart8250,mmio,0xff5e0000,115200n8 + * earlycon=uart8250,mmio32,0xff5e0000,115200n8 + * or + * console=uart8250,io,0x3f8,9600n8 + * console=uart8250,mmio,0xff5e0000,115200n8 + * console=uart8250,mmio32,0xff5e0000,115200n8 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FIX_EARLYCON_MEM +#include +#include +#endif + +struct early_serial8250_device { + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; +}; + +static struct early_serial8250_device early_device; + +static unsigned int __init serial_in(struct uart_port *port, int offset) +{ + switch (port->iotype) { + case UPIO_MEM: + return readb(port->membase + offset); + case UPIO_MEM32: + return readl(port->membase + (offset << 2)); + case UPIO_PORT: + return inb(port->iobase + offset); + default: + return 0; + } +} + +static void __init serial_out(struct uart_port *port, int offset, int value) +{ + switch (port->iotype) { + case UPIO_MEM: + writeb(value, port->membase + offset); + break; + case UPIO_MEM32: + writel(value, port->membase + (offset << 2)); + break; + case UPIO_PORT: + outb(value, port->iobase + offset); + break; + } +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static void __init wait_for_xmitr(struct uart_port *port) +{ + unsigned int status; + + for (;;) { + status = serial_in(port, UART_LSR); + if ((status & BOTH_EMPTY) == BOTH_EMPTY) + return; + cpu_relax(); + } +} + +static void __init serial_putc(struct uart_port *port, int c) +{ + wait_for_xmitr(port); + serial_out(port, UART_TX, c); +} + +static void __init early_serial8250_write(struct console *console, + const char *s, unsigned int count) +{ + struct uart_port *port = &early_device.port; + unsigned int ier; + + /* Save the IER and disable interrupts */ + ier = serial_in(port, UART_IER); + serial_out(port, UART_IER, 0); + + uart_console_write(port, s, count, serial_putc); + + /* Wait for transmitter to become empty and restore the IER */ + wait_for_xmitr(port); + serial_out(port, UART_IER, ier); +} + +static unsigned int __init probe_baud(struct uart_port *port) +{ + unsigned char lcr, dll, dlm; + unsigned int quot; + + lcr = serial_in(port, UART_LCR); + serial_out(port, UART_LCR, lcr | UART_LCR_DLAB); + dll = serial_in(port, UART_DLL); + dlm = serial_in(port, UART_DLM); + serial_out(port, UART_LCR, lcr); + + quot = (dlm << 8) | dll; + return (port->uartclk / 16) / quot; +} + +static void __init init_port(struct early_serial8250_device *device) +{ + struct uart_port *port = &device->port; + unsigned int divisor; + unsigned char c; + + serial_out(port, UART_LCR, 0x3); /* 8n1 */ + serial_out(port, UART_IER, 0); /* no interrupt */ + serial_out(port, UART_FCR, 0); /* no fifo */ + serial_out(port, UART_MCR, 0x3); /* DTR + RTS */ + + divisor = port->uartclk / (16 * device->baud); + c = serial_in(port, UART_LCR); + serial_out(port, UART_LCR, c | UART_LCR_DLAB); + serial_out(port, UART_DLL, divisor & 0xff); + serial_out(port, UART_DLM, (divisor >> 8) & 0xff); + serial_out(port, UART_LCR, c & ~UART_LCR_DLAB); +} + +static int __init parse_options(struct early_serial8250_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length; + + if (!options) + return -ENODEV; + + port->uartclk = BASE_BAUD * 16; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), + &options, 0); + if (mmio32) + port->regshift = 2; +#ifdef CONFIG_FIX_EARLYCON_MEM + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, + port->mapbase & PAGE_MASK); + port->membase = + (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + port->membase += port->mapbase & ~PAGE_MASK; +#else + port->membase = ioremap_nocache(port->mapbase, 64); + if (!port->membase) { + printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", + __func__, + (unsigned long long) port->mapbase); + return -ENOMEM; + } +#endif + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + port->iobase = simple_strtoul(options + 3, &options, 0); + mmio = 0; + } else + return -EINVAL; + + options = strchr(options, ','); + if (options) { + options++; + device->baud = simple_strtoul(options, NULL, 0); + length = min(strcspn(options, " "), sizeof(device->options)); + strncpy(device->options, options, length); + } else { + device->baud = probe_baud(port); + snprintf(device->options, sizeof(device->options), "%u", + device->baud); + } + + if (mmio || mmio32) + printk(KERN_INFO + "Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (unsigned long long)port->mapbase, + device->options); + else + printk(KERN_INFO + "Early serial console at I/O port 0x%lx (options '%s')\n", + port->iobase, + device->options); + + return 0; +} + +static struct console early_serial8250_console __initdata = { + .name = "uart", + .write = early_serial8250_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static int __init early_serial8250_setup(char *options) +{ + struct early_serial8250_device *device = &early_device; + int err; + + if (device->port.membase || device->port.iobase) + return 0; + + err = parse_options(device, options); + if (err < 0) + return err; + + init_port(device); + return 0; +} + +int __init setup_early_serial8250_console(char *cmdline) +{ + char *options; + int err; + + options = strstr(cmdline, "uart8250,"); + if (!options) { + options = strstr(cmdline, "uart,"); + if (!options) + return 0; + } + + options = strchr(cmdline, ',') + 1; + err = early_serial8250_setup(options); + if (err < 0) + return err; + + register_console(&early_serial8250_console); + + return 0; +} + +int serial8250_find_port_for_earlycon(void) +{ + struct early_serial8250_device *device = &early_device; + struct uart_port *port = &device->port; + int line; + int ret; + + if (!device->port.membase && !device->port.iobase) + return -ENODEV; + + line = serial8250_find_port(port); + if (line < 0) + return -ENODEV; + + ret = update_console_cmdline("uart", 8250, + "ttyS", line, device->options); + if (ret < 0) + ret = update_console_cmdline("uart", 0, + "ttyS", line, device->options); + + return ret; +} + +early_param("earlycon", setup_early_serial8250_console); diff --git a/drivers/tty/serial/8250/8250_exar_st16c554.c b/drivers/tty/serial/8250/8250_exar_st16c554.c new file mode 100644 index 0000000..bf53aab --- /dev/null +++ b/drivers/tty/serial/8250/8250_exar_st16c554.c @@ -0,0 +1,50 @@ +/* + * Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com > + * Based on 8250_boca. + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port exar_data[] = { + PORT(0x100, 5), + PORT(0x108, 5), + PORT(0x110, 5), + PORT(0x118, 5), + { }, +}; + +static struct platform_device exar_device = { + .name = "serial8250", + .id = PLAT8250_DEV_EXAR_ST16C554, + .dev = { + .platform_data = exar_data, + }, +}; + +static int __init exar_init(void) +{ + return platform_device_register(&exar_device); +} + +module_init(exar_init); + +MODULE_AUTHOR("Paul B Schroeder"); +MODULE_DESCRIPTION("8250 serial probe module for Exar cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_fourport.c b/drivers/tty/serial/8250/8250_fourport.c new file mode 100644 index 0000000..be15826 --- /dev/null +++ b/drivers/tty/serial/8250/8250_fourport.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \ + } + +static struct plat_serial8250_port fourport_data[] = { + PORT(0x1a0, 9), + PORT(0x1a8, 9), + PORT(0x1b0, 9), + PORT(0x1b8, 9), + PORT(0x2a0, 5), + PORT(0x2a8, 5), + PORT(0x2b0, 5), + PORT(0x2b8, 5), + { }, +}; + +static struct platform_device fourport_device = { + .name = "serial8250", + .id = PLAT8250_DEV_FOURPORT, + .dev = { + .platform_data = fourport_data, + }, +}; + +static int __init fourport_init(void) +{ + return platform_device_register(&fourport_device); +} + +module_init(fourport_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c new file mode 100644 index 0000000..f4d3c47 --- /dev/null +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -0,0 +1,63 @@ +#include +#include + +#include "8250.h" + +/* + * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This isn't a full driver; it just provides an alternate IRQ + * handler to deal with an errata. Everything else is just + * using the bog standard 8250 support. + * + * We follow code flow of serial8250_default_handle_irq() but add + * a check for a break and insert a dummy read on the Rx for the + * immediately following IRQ event. + * + * We re-use the already existing "bug handling" lsr_saved_flags + * field to carry the "what we just did" information from the one + * IRQ event to the next one. + */ + +int fsl8250_handle_irq(struct uart_port *port) +{ + unsigned char lsr, orig_lsr; + unsigned long flags; + unsigned int iir; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + + iir = port->serial_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + spin_unlock_irqrestore(&up->port.lock, flags); + return 0; + } + + /* This is the WAR; if last event was BRK, then read and return */ + if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { + up->lsr_saved_flags &= ~UART_LSR_BI; + port->serial_in(port, UART_RX); + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; + } + + lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); + + if (lsr & (UART_LSR_DR | UART_LSR_BI)) + lsr = serial8250_rx_chars(up, lsr); + + serial8250_modem_status(up); + + if (lsr & UART_LSR_THRE) + serial8250_tx_chars(up); + + up->lsr_saved_flags = orig_lsr; + spin_unlock_irqrestore(&up->port.lock, flags); + return 1; +} diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c new file mode 100644 index 0000000..d8c0ffb --- /dev/null +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -0,0 +1,122 @@ +/* + * Serial Device Initialisation for Lasi/Asp/Wax/Dino + * + * (c) Copyright Matthew Wilcox 2001-2002 + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "8250.h" + +static int __init serial_init_chip(struct parisc_device *dev) +{ + struct uart_port port; + unsigned long address; + int err; + + if (!dev->irq) { + /* We find some unattached serial ports by walking native + * busses. These should be silently ignored. Otherwise, + * what we have here is a missing parent device, so tell + * the user what they're missing. + */ + if (parisc_parent(dev)->id.hw_type != HPHW_IOA) + printk(KERN_INFO + "Serial: device 0x%llx not configured.\n" + "Enable support for Wax, Lasi, Asp or Dino.\n", + (unsigned long long)dev->hpa.start); + return -ENODEV; + } + + address = dev->hpa.start; + if (dev->id.sversion != 0x8d) + address += 0x800; + + memset(&port, 0, sizeof(port)); + port.iotype = UPIO_MEM; + /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ + port.uartclk = 7272727; + port.mapbase = address; + port.membase = ioremap_nocache(address, 16); + port.irq = dev->irq; + port.flags = UPF_BOOT_AUTOCONF; + port.dev = &dev->dev; + + err = serial8250_register_port(&port); + if (err < 0) { + printk(KERN_WARNING + "serial8250_register_port returned error %d\n", err); + iounmap(port.membase); + return err; + } + + return 0; +} + +static struct parisc_device_id serial_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, + { 0 } +}; + +/* Hack. Some machines have SERIAL_0 attached to Lasi and SERIAL_1 + * attached to Dino. Unfortunately, Dino appears before Lasi in the device + * tree. To ensure that ttyS0 == SERIAL_0, we register two drivers; one + * which only knows about Lasi and then a second which will find all the + * other serial ports. HPUX ignores this problem. + */ +static struct parisc_device_id lasi_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */ + { 0 } +}; + + +MODULE_DEVICE_TABLE(parisc, serial_tbl); + +static struct parisc_driver lasi_driver = { + .name = "serial_1", + .id_table = lasi_tbl, + .probe = serial_init_chip, +}; + +static struct parisc_driver serial_driver = { + .name = "serial", + .id_table = serial_tbl, + .probe = serial_init_chip, +}; + +static int __init probe_serial_gsc(void) +{ + register_parisc_driver(&lasi_driver); + register_parisc_driver(&serial_driver); + return 0; +} + +module_init(probe_serial_gsc); + +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c new file mode 100644 index 0000000..c13438c --- /dev/null +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -0,0 +1,327 @@ +/* + * Driver for the 98626/98644/internal serial interface on hp300/hp400 + * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) + * + * Ported from 2.2 and modified to use the normal 8250 driver + * by Kars de Jong , May 2004. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "8250.h" + +#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) +#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? +#endif + +#ifdef CONFIG_HPAPCI +struct hp300_port +{ + struct hp300_port *next; /* next port */ + int line; /* line (tty) number */ +}; + +static struct hp300_port *hp300_ports; +#endif + +#ifdef CONFIG_HPDCA + +static int __devinit hpdca_init_one(struct dio_dev *d, + const struct dio_device_id *ent); +static void __devexit hpdca_remove_one(struct dio_dev *d); + +static struct dio_device_id hpdca_dio_tbl[] = { + { DIO_ID_DCA0 }, + { DIO_ID_DCA0REM }, + { DIO_ID_DCA1 }, + { DIO_ID_DCA1REM }, + { 0 } +}; + +static struct dio_driver hpdca_driver = { + .name = "hpdca", + .id_table = hpdca_dio_tbl, + .probe = hpdca_init_one, + .remove = __devexit_p(hpdca_remove_one), +}; + +#endif + +static unsigned int num_ports; + +extern int hp300_uart_scode; + +/* Offset to UART registers from base of DCA */ +#define UART_OFFSET 17 + +#define DCA_ID 0x01 /* ID (read), reset (write) */ +#define DCA_IC 0x03 /* Interrupt control */ + +/* Interrupt control */ +#define DCA_IC_IE 0x80 /* Master interrupt enable */ + +#define HPDCA_BAUD_BASE 153600 + +/* Base address of the Frodo part */ +#define FRODO_BASE (0x41c000) + +/* + * Where we find the 8250-like APCI ports, and how far apart they are. + */ +#define FRODO_APCIBASE 0x0 +#define FRODO_APCISPACE 0x20 +#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE)) + +#define HPAPCI_BAUD_BASE 500400 + +#ifdef CONFIG_SERIAL_8250_CONSOLE +/* + * Parse the bootinfo to find descriptions for headless console and + * debug serial ports and register them with the 8250 driver. + * This function should be called before serial_console_init() is called + * to make sure the serial console will be available for use. IA-64 kernel + * calls this function from setup_arch() after the EFI and ACPI tables have + * been parsed. + */ +int __init hp300_setup_serial_console(void) +{ + int scode; + struct uart_port port; + + memset(&port, 0, sizeof(port)); + + if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX) + return 0; + + if (DIO_SCINHOLE(hp300_uart_scode)) + return 0; + + scode = hp300_uart_scode; + + /* Memory mapped I/O */ + port.iotype = UPIO_MEM; + port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; + port.type = PORT_UNKNOWN; + + /* Check for APCI console */ + if (scode == 256) { +#ifdef CONFIG_HPAPCI + printk(KERN_INFO "Serial console is HP APCI 1\n"); + + port.uartclk = HPAPCI_BAUD_BASE * 16; + port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 2; + add_preferred_console("ttyS", port.line, "9600n8"); +#else + printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); + return 0; +#endif + } else { +#ifdef CONFIG_HPDCA + unsigned long pa = dio_scodetophysaddr(scode); + if (!pa) + return 0; + + printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode); + + port.uartclk = HPDCA_BAUD_BASE * 16; + port.mapbase = (pa + UART_OFFSET); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 1; + port.irq = DIO_IPL(pa + DIO_VIRADDRBASE); + + /* Enable board-interrupts */ + out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); + + if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) + add_preferred_console("ttyS", port.line, "9600n8"); +#else + printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); + return 0; +#endif + } + + if (early_serial_setup(&port) < 0) + printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n"); + return 0; +} +#endif /* CONFIG_SERIAL_8250_CONSOLE */ + +#ifdef CONFIG_HPDCA +static int __devinit hpdca_init_one(struct dio_dev *d, + const struct dio_device_id *ent) +{ + struct uart_port port; + int line; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (hp300_uart_scode == d->scode) { + /* Already got it. */ + return 0; + } +#endif + memset(&port, 0, sizeof(struct uart_port)); + + /* Memory mapped I/O */ + port.iotype = UPIO_MEM; + port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; + port.irq = d->ipl; + port.uartclk = HPDCA_BAUD_BASE * 16; + port.mapbase = (d->resource.start + UART_OFFSET); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 1; + port.dev = &d->dev; + line = serial8250_register_port(&port); + + if (line < 0) { + printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" + " irq %d failed\n", d->scode, port.irq); + return -ENOMEM; + } + + /* Enable board-interrupts */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); + dio_set_drvdata(d, (void *)line); + + /* Reset the DCA */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); + udelay(100); + + num_ports++; + + return 0; +} +#endif + +static int __init hp300_8250_init(void) +{ + static int called; +#ifdef CONFIG_HPAPCI + int line; + unsigned long base; + struct uart_port uport; + struct hp300_port *port; + int i; +#endif + if (called) + return -ENODEV; + called = 1; + + if (!MACH_IS_HP300) + return -ENODEV; + +#ifdef CONFIG_HPDCA + dio_register_driver(&hpdca_driver); +#endif +#ifdef CONFIG_HPAPCI + if (hp300_model < HP_400) { + if (!num_ports) + return -ENODEV; + return 0; + } + /* These models have the Frodo chip. + * Port 0 is reserved for the Apollo Domain keyboard. + * Port 1 is either the console or the DCA. + */ + for (i = 1; i < 4; i++) { + /* Port 1 is the console on a 425e, on other machines it's + * mapped to DCA. + */ +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (i == 1) + continue; +#endif + + /* Create new serial device */ + port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + memset(&uport, 0, sizeof(struct uart_port)); + + base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); + + /* Memory mapped I/O */ + uport.iotype = UPIO_MEM; + uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ + | UPF_BOOT_AUTOCONF; + /* XXX - no interrupt support yet */ + uport.irq = 0; + uport.uartclk = HPAPCI_BAUD_BASE * 16; + uport.mapbase = base; + uport.membase = (char *)(base + DIO_VIRADDRBASE); + uport.regshift = 2; + + line = serial8250_register_port(&uport); + + if (line < 0) { + printk(KERN_NOTICE "8250_hp300: register_serial() APCI" + " %d irq %d failed\n", i, uport.irq); + kfree(port); + continue; + } + + port->line = line; + port->next = hp300_ports; + hp300_ports = port; + + num_ports++; + } +#endif + + /* Any boards found? */ + if (!num_ports) + return -ENODEV; + + return 0; +} + +#ifdef CONFIG_HPDCA +static void __devexit hpdca_remove_one(struct dio_dev *d) +{ + int line; + + line = (int) dio_get_drvdata(d); + if (d->resource.start) { + /* Disable board-interrupts */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0); + } + serial8250_unregister_port(line); +} +#endif + +static void __exit hp300_8250_exit(void) +{ +#ifdef CONFIG_HPAPCI + struct hp300_port *port, *to_free; + + for (port = hp300_ports; port; ) { + serial8250_unregister_port(port->line); + to_free = port; + port = port->next; + kfree(to_free); + } + + hp300_ports = NULL; +#endif +#ifdef CONFIG_HPDCA + dio_unregister_driver(&hpdca_driver); +#endif +} + +module_init(hp300_8250_init); +module_exit(hp300_8250_exit); +MODULE_DESCRIPTION("HP DCA/APCI serial driver"); +MODULE_AUTHOR("Kars de Jong "); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_hub6.c b/drivers/tty/serial/8250/8250_hub6.c new file mode 100644 index 0000000..a5c778e --- /dev/null +++ b/drivers/tty/serial/8250/8250_hub6.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define HUB6(card,port) \ + { \ + .iobase = 0x302, \ + .irq = 3, \ + .uartclk = 1843200, \ + .iotype = UPIO_HUB6, \ + .flags = UPF_BOOT_AUTOCONF, \ + .hub6 = (card) << 6 | (port) << 3 | 1, \ + } + +static struct plat_serial8250_port hub6_data[] = { + HUB6(0, 0), + HUB6(0, 1), + HUB6(0, 2), + HUB6(0, 3), + HUB6(0, 4), + HUB6(0, 5), + HUB6(1, 0), + HUB6(1, 1), + HUB6(1, 2), + HUB6(1, 3), + HUB6(1, 4), + HUB6(1, 5), + { }, +}; + +static struct platform_device hub6_device = { + .name = "serial8250", + .id = PLAT8250_DEV_HUB6, + .dev = { + .platform_data = hub6_data, + }, +}; + +static int __init hub6_init(void) +{ + return platform_device_register(&hub6_device); +} + +module_init(hub6_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_mca.c b/drivers/tty/serial/8250/8250_mca.c new file mode 100644 index 0000000..d20abf0 --- /dev/null +++ b/drivers/tty/serial/8250/8250_mca.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +/* + * FIXME: Should we be doing AUTO_IRQ here? + */ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ +#else +#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST +#endif + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = MCA_FLAGS, \ + } + +static struct plat_serial8250_port mca_data[] = { + PORT(0x3220, 3), + PORT(0x3228, 3), + PORT(0x4220, 3), + PORT(0x4228, 3), + PORT(0x5220, 3), + PORT(0x5228, 3), + { }, +}; + +static struct platform_device mca_device = { + .name = "serial8250", + .id = PLAT8250_DEV_MCA, + .dev = { + .platform_data = mca_data, + }, +}; + +static int __init mca_init(void) +{ + if (!MCA_bus) + return -ENODEV; + return platform_device_register(&mca_device); +} + +module_init(mca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for MCA ports"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c new file mode 100644 index 0000000..da2b0b0 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pci.c @@ -0,0 +1,4223 @@ +/* + * Probe module for 8250/16550-type PCI serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8250.h" + +#undef SERIAL_DEBUG_PCI + +/* + * init function returns: + * > 0 - number of ports + * = 0 - use board->num_ports + * < 0 - error + */ +struct pci_serial_quirk { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; + int (*probe)(struct pci_dev *dev); + int (*init)(struct pci_dev *dev); + int (*setup)(struct serial_private *, + const struct pciserial_board *, + struct uart_port *, int); + void (*exit)(struct pci_dev *dev); +}; + +#define PCI_NUM_BAR_RESOURCES 6 + +struct serial_private { + struct pci_dev *dev; + unsigned int nr; + void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; + struct pci_serial_quirk *quirk; + int line[0]; +}; + +static int pci_default_setup(struct serial_private*, + const struct pciserial_board*, struct uart_port*, int); + +static void moan_device(const char *str, struct pci_dev *dev) +{ + printk(KERN_WARNING + "%s: %s\n" + "Please send the output of lspci -vv, this\n" + "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + "manufacturer and name of serial board or\n" + "modem board to rmk+serial@arm.linux.org.uk.\n", + pci_name(dev), str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + +static int +setup_port(struct serial_private *priv, struct uart_port *port, + int bar, int offset, int regshift) +{ + struct pci_dev *dev = priv->dev; + unsigned long base, len; + + if (bar >= PCI_NUM_BAR_RESOURCES) + return -EINVAL; + + base = pci_resource_start(dev, bar); + + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + len = pci_resource_len(dev, bar); + + if (!priv->remapped_bar[bar]) + priv->remapped_bar[bar] = ioremap_nocache(base, len); + if (!priv->remapped_bar[bar]) + return -ENOMEM; + + port->iotype = UPIO_MEM; + port->iobase = 0; + port->mapbase = base + offset; + port->membase = priv->remapped_bar[bar] + offset; + port->regshift = regshift; + } else { + port->iotype = UPIO_PORT; + port->iobase = base + offset; + port->mapbase = 0; + port->membase = NULL; + port->regshift = 0; + } + return 0; +} + +/* + * ADDI-DATA GmbH communication cards + */ +static int addidata_apci7800_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar = 0, offset = board->first_offset; + bar = FL_GET_BASE(board->flags); + + if (idx < 2) { + offset += idx * board->uart_offset; + } else if ((idx >= 2) && (idx < 4)) { + bar += 1; + offset += ((idx - 2) * board->uart_offset); + } else if ((idx >= 4) && (idx < 6)) { + bar += 2; + offset += ((idx - 4) * board->uart_offset); + } else if (idx >= 6) { + bar += 3; + offset += ((idx - 6) * board->uart_offset); + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * AFAVLAB uses a different mixture of BARs and offsets + * Not that ugly ;) -- HW + */ +static int +afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = FL_GET_BASE(board->flags); + if (idx < 4) + bar += idx; + else { + bar = 4; + offset += (idx - 4) * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * HP's Remote Management Console. The Diva chip came in several + * different versions. N-class, L2000 and A500 have two Diva chips, each + * with 3 UARTs (the third UART on the second chip is unused). Superdome + * and Keystone have one Diva chip with 3 UARTs. Some later machines have + * one Diva chip, but it has been expanded to 5 UARTs. + */ +static int pci_hp_diva_init(struct pci_dev *dev) +{ + int rc = 0; + + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + rc = 3; + break; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + rc = 2; + break; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + rc = 4; + break; + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + case PCI_DEVICE_ID_HP_DIVA_HURRICANE: + rc = 1; + break; + } + + return rc; +} + +/* + * HP's Diva chip puts the 4th/5th serial port further out, and + * some serial ports are supposed to be hidden on certain models. + */ +static int +pci_hp_diva_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int offset = board->first_offset; + unsigned int bar = FL_GET_BASE(board->flags); + + switch (priv->dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + if (idx == 3) + idx++; + break; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + if (idx > 0) + idx++; + if (idx > 2) + idx++; + break; + } + if (idx > 2) + offset = 0x18; + + offset += idx * board->uart_offset; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * Added for EKF Intel i960 serial boards + */ +static int pci_inteli960ni_init(struct pci_dev *dev) +{ + unsigned long oldval; + + if (!(dev->subsystem_device & 0x1000)) + return -ENODEV; + + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void *)&oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return -ENODEV; + } + return 0; +} + +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + */ +static int pci_plx9050_init(struct pci_dev *dev) +{ + u8 irq_config; + void __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM || + dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS) + irq_config = 0x43; + + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; + /* + * enable/disable interrupts + */ + p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + writel(irq_config, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + + return 0; +} + +static void __devexit pci_plx9050_exit(struct pci_dev *dev) +{ + u8 __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) + return; + + /* + * disable interrupts + */ + p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); + if (p != NULL) { + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + } +} + +#define NI8420_INT_ENABLE_REG 0x38 +#define NI8420_INT_ENABLE_BIT 0x2000 + +static void __devexit pci_ni8420_exit(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return; + + /* Disable the CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT), + p + NI8420_INT_ENABLE_REG); + iounmap(p); +} + + +/* MITE registers */ +#define MITE_IOWBSR1 0xc4 +#define MITE_IOWCR1 0xf4 +#define MITE_LCIMR1 0x08 +#define MITE_LCIMR2 0x10 + +#define MITE_LCIMR2_CLR_CPU_IE (1 << 30) + +static void __devexit pci_ni8430_exit(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return; + + /* Disable the CPU Interrupt */ + writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2); + iounmap(p); +} + +/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ +static int +sbs_setup(struct serial_private *priv, const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = 0; + + if (idx < 4) { + /* first four channels map to 0, 0x100, 0x200, 0x300 */ + offset += idx * board->uart_offset; + } else if (idx < 8) { + /* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */ + offset += idx * board->uart_offset + 0xC00; + } else /* we have only 8 ports on PMC-OCTALPRO */ + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* +* This does initialization for PMC OCTALPRO cards: +* maps the device memory, resets the UARTs (needed, bc +* if the module is removed and inserted again, the card +* is in the sleep mode) and enables global interrupt. +*/ + +/* global control register offset for SBS PMC-OctalPro */ +#define OCT_REG_CR_OFF 0x500 + +static int sbs_init(struct pci_dev *dev) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(dev, 0); + + if (p == NULL) + return -ENOMEM; + /* Set bit-4 Control Register (UART RESET) in to reset the uarts */ + writeb(0x10, p + OCT_REG_CR_OFF); + udelay(50); + writeb(0x0, p + OCT_REG_CR_OFF); + + /* Set bit-2 (INTENABLE) of Control Register */ + writeb(0x4, p + OCT_REG_CR_OFF); + iounmap(p); + + return 0; +} + +/* + * Disables the global interrupt of PMC-OctalPro + */ + +static void __devexit sbs_exit(struct pci_dev *dev) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(dev, 0); + /* FIXME: What if resource_len < OCT_REG_CR_OFF */ + if (p != NULL) + writeb(0, p + OCT_REG_CR_OFF); + iounmap(p); +} + +/* + * SIIG serial cards have an PCI interface chip which also controls + * the UART clocking frequency. Each UART can be clocked independently + * (except cards equipped with 4 UARTs) and initial clocking settings + * are stored in the EEPROM chip. It can cause problems because this + * version of serial driver doesn't support differently clocked UART's + * on single PCI card. To prevent this, initialization functions set + * high frequency clocking for all UART's on given card. It is safe (I + * hope) because it doesn't touch EEPROM settings to prevent conflicts + * with other OSes (like M$ DOS). + * + * SIIG support added by Andrey Panin , 10/1999 + * + * There is two family of SIIG serial cards with different PCI + * interface chip and different configuration methods: + * - 10x cards have control registers in IO and/or memory space; + * - 20x cards have control registers in standard PCI configuration space. + * + * Note: all 10x cards have PCI device ids 0x10.. + * all 20x cards have PCI device ids 0x20.. + * + * There are also Quartet Serial cards which use Oxford Semiconductor + * 16954 quad UART PCI chip clocked by 18.432 MHz quartz. + * + * Note: some SIIG cards are probed by the parport_serial object. + */ + +#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) + +static int pci_siig10x_init(struct pci_dev *dev) +{ + u16 data; + void __iomem *p; + + switch (dev->device & 0xfff8) { + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; + } + + p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + + writew(readw(p + 0x28) & data, p + 0x28); + readw(p + 0x28); + iounmap(p); + return 0; +} + +#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) + +static int pci_siig20x_init(struct pci_dev *dev) +{ + u8 data; + + /* Change clock frequency for the first UART. */ + pci_read_config_byte(dev, 0x6f, &data); + pci_write_config_byte(dev, 0x6f, data & 0xef); + + /* If this card has 2 UART, we have to do the same with second UART. */ + if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || + ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { + pci_read_config_byte(dev, 0x73, &data); + pci_write_config_byte(dev, 0x73, data & 0xef); + } + return 0; +} + +static int pci_siig_init(struct pci_dev *dev) +{ + unsigned int type = dev->device & 0xff00; + + if (type == 0x1000) + return pci_siig10x_init(dev); + else if (type == 0x2000) + return pci_siig20x_init(dev); + + moan_device("Unknown SIIG card", dev); + return -ENODEV; +} + +static int pci_siig_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; + + if (idx > 3) { + bar = 4; + offset = (idx - 4) * 8; + } + + return setup_port(priv, port, bar, offset, 0); +} + +/* + * Timedia has an explosion of boards, and to avoid the PCI table from + * growing *huge*, we use this function to collapse some 70 entries + * in the PCI table into one, for sanity's and compactness's sake. + */ +static const unsigned short timedia_single_port[] = { + 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 +}; + +static const unsigned short timedia_dual_port[] = { + 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, + 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, + 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, + 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, + 0xD079, 0 +}; + +static const unsigned short timedia_quad_port[] = { + 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, + 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, + 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, + 0xB157, 0 +}; + +static const unsigned short timedia_eight_port[] = { + 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, + 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 +}; + +static const struct timedia_struct { + int num; + const unsigned short *ids; +} timedia_data[] = { + { 1, timedia_single_port }, + { 2, timedia_dual_port }, + { 4, timedia_quad_port }, + { 8, timedia_eight_port } +}; + +/* + * There are nearly 70 different Timedia/SUNIX PCI serial devices. Instead of + * listing them individually, this driver merely grabs them all with + * PCI_ANY_ID. Some of these devices, however, also feature a parallel port, + * and should be left free to be claimed by parport_serial instead. + */ +static int pci_timedia_probe(struct pci_dev *dev) +{ + /* + * Check the third digit of the subdevice ID + * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel) + */ + if ((dev->subsystem_device & 0x00f0) >= 0x70) { + dev_info(&dev->dev, + "ignoring Timedia subdevice %04x for parport_serial\n", + dev->subsystem_device); + return -ENODEV; + } + + return 0; +} + +static int pci_timedia_init(struct pci_dev *dev) +{ + const unsigned short *ids; + int i, j; + + for (i = 0; i < ARRAY_SIZE(timedia_data); i++) { + ids = timedia_data[i].ids; + for (j = 0; ids[j]; j++) + if (dev->subsystem_device == ids[j]) + return timedia_data[i].num; + } + return 0; +} + +/* + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT + */ +static int +pci_timedia_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar = 0, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 0; + break; + case 1: + offset = board->uart_offset; + bar = 0; + break; + case 2: + bar = 1; + break; + case 3: + offset = board->uart_offset; + /* FALLTHROUGH */ + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + bar = idx - 2; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * Some Titan cards are also a little weird + */ +static int +titan_400l_800l_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 1; + break; + case 1: + bar = 2; + break; + default: + bar = 4; + offset = (idx - 2) * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int pci_xircom_init(struct pci_dev *dev) +{ + msleep(100); + return 0; +} + +static int pci_ni8420_init(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return 0; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return -ENOMEM; + + /* Enable CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT, + p + NI8420_INT_ENABLE_REG); + + iounmap(p); + return 0; +} + +#define MITE_IOWBSR1_WSIZE 0xa +#define MITE_IOWBSR1_WIN_OFFSET 0x800 +#define MITE_IOWBSR1_WENAB (1 << 7) +#define MITE_LCIMR1_IO_IE_0 (1 << 24) +#define MITE_LCIMR2_SET_CPU_IE (1 << 31) +#define MITE_IOWCR1_RAMSEL_MASK 0xfffffffe + +static int pci_ni8430_init(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + u32 device_window; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return 0; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return -ENOMEM; + + /* Set device window address and size in BAR0 */ + device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) + | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; + writel(device_window, p + MITE_IOWBSR1); + + /* Set window access to go to RAMSEL IO address space */ + writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK), + p + MITE_IOWCR1); + + /* Enable IO Bus Interrupt 0 */ + writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1); + + /* Enable CPU Interrupt */ + writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2); + + iounmap(p); + return 0; +} + +/* UART Port Control Register */ +#define NI8430_PORTCON 0x0f +#define NI8430_PORTCON_TXVR_ENABLE (1 << 3) + +static int +pci_ni8430_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar, offset = board->first_offset; + + if (idx >= board->num_ports) + return 1; + + bar = FL_GET_BASE(board->flags); + offset += idx * board->uart_offset; + + base = pci_resource_start(priv->dev, bar); + len = pci_resource_len(priv->dev, bar); + p = ioremap_nocache(base, len); + + /* enable the transceiver */ + writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, + p + offset + NI8430_PORTCON); + + iounmap(p); + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int pci_netmos_9900_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar; + + if ((priv->dev->subsystem_device & 0xff00) == 0x3000) { + /* netmos apparently orders BARs by datasheet layout, so serial + * ports get BARs 0 and 3 (or 1 and 4 for memmapped) + */ + bar = 3 * idx; + + return setup_port(priv, port, bar, 0, board->reg_shift); + } else { + return pci_default_setup(priv, board, port, idx); + } +} + +/* the 99xx series comes with a range of device IDs and a variety + * of capabilities: + * + * 9900 has varying capabilities and can cascade to sub-controllers + * (cascading should be purely internal) + * 9904 is hardwired with 4 serial ports + * 9912 and 9922 are hardwired with 2 serial ports + */ +static int pci_netmos_9900_numports(struct pci_dev *dev) +{ + unsigned int c = dev->class; + unsigned int pi; + unsigned short sub_serports; + + pi = (c & 0xff); + + if (pi == 2) { + return 1; + } else if ((pi == 0) && + (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { + /* two possibilities: 0x30ps encodes number of parallel and + * serial ports, or 0x1000 indicates *something*. This is not + * immediately obvious, since the 2s1p+4s configuration seems + * to offer all functionality on functions 0..2, while still + * advertising the same function 3 as the 4s+2s1p config. + */ + sub_serports = dev->subsystem_device & 0xf; + if (sub_serports > 0) { + return sub_serports; + } else { + printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + return 0; + } + } + + moan_device("unknown NetMos/Mostech program interface", dev); + return 0; +} + +static int pci_netmos_init(struct pci_dev *dev) +{ + /* subdevice 0x00PS means

parallel, serial */ + unsigned int num_serial = dev->subsystem_device & 0xf; + + if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || + (dev->device == PCI_DEVICE_ID_NETMOS_9865)) + return 0; + + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && + dev->subsystem_device == 0x0299) + return 0; + + switch (dev->device) { /* FALLTHROUGH on all */ + case PCI_DEVICE_ID_NETMOS_9904: + case PCI_DEVICE_ID_NETMOS_9912: + case PCI_DEVICE_ID_NETMOS_9922: + case PCI_DEVICE_ID_NETMOS_9900: + num_serial = pci_netmos_9900_numports(dev); + break; + + default: + if (num_serial == 0 ) { + moan_device("unknown NetMos/Mostech device", dev); + } + } + + if (num_serial == 0) + return -ENODEV; + + return num_serial; +} + +/* + * These chips are available with optionally one parallel port and up to + * two serial ports. Unfortunately they all have the same product id. + * + * Basic configuration is done over a region of 32 I/O ports. The base + * ioport is called INTA or INTC, depending on docs/other drivers. + * + * The region of the 32 I/O ports is configured in POSIO0R... + */ + +/* registers */ +#define ITE_887x_MISCR 0x9c +#define ITE_887x_INTCBAR 0x78 +#define ITE_887x_UARTBAR 0x7c +#define ITE_887x_PS0BAR 0x10 +#define ITE_887x_POSIO0 0x60 + +/* I/O space size */ +#define ITE_887x_IOSIZE 32 +/* I/O space size (bits 26-24; 8 bytes = 011b) */ +#define ITE_887x_POSIO_IOSIZE_8 (3 << 24) +/* I/O space size (bits 26-24; 32 bytes = 101b) */ +#define ITE_887x_POSIO_IOSIZE_32 (5 << 24) +/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */ +#define ITE_887x_POSIO_SPEED (3 << 29) +/* enable IO_Space bit */ +#define ITE_887x_POSIO_ENABLE (1 << 31) + +static int pci_ite887x_init(struct pci_dev *dev) +{ + /* inta_addr are the configuration addresses of the ITE */ + static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, + 0x200, 0x280, 0 }; + int ret, i, type; + struct resource *iobase = NULL; + u32 miscr, uartbar, ioport; + + /* search for the base-ioport */ + i = 0; + while (inta_addr[i] && iobase == NULL) { + iobase = request_region(inta_addr[i], ITE_887x_IOSIZE, + "ite887x"); + if (iobase != NULL) { + /* write POSIO0R - speed | size | ioport */ + pci_write_config_dword(dev, ITE_887x_POSIO0, + ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | + ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]); + /* write INTCBAR - ioport */ + pci_write_config_dword(dev, ITE_887x_INTCBAR, + inta_addr[i]); + ret = inb(inta_addr[i]); + if (ret != 0xff) { + /* ioport connected */ + break; + } + release_region(iobase->start, ITE_887x_IOSIZE); + iobase = NULL; + } + i++; + } + + if (!inta_addr[i]) { + printk(KERN_ERR "ite887x: could not find iobase\n"); + return -ENODEV; + } + + /* start of undocumented type checking (see parport_pc.c) */ + type = inb(iobase->start + 0x18) & 0x0f; + + switch (type) { + case 0x2: /* ITE8871 (1P) */ + case 0xa: /* ITE8875 (1P) */ + ret = 0; + break; + case 0xe: /* ITE8872 (2S1P) */ + ret = 2; + break; + case 0x6: /* ITE8873 (1S) */ + ret = 1; + break; + case 0x8: /* ITE8874 (2S) */ + ret = 2; + break; + default: + moan_device("Unknown ITE887x", dev); + ret = -ENODEV; + } + + /* configure all serial ports */ + for (i = 0; i < ret; i++) { + /* read the I/O port from the device */ + pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)), + &ioport); + ioport &= 0x0000FF00; /* the actual base address */ + pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)), + ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | + ITE_887x_POSIO_IOSIZE_8 | ioport); + + /* write the ioport to the UARTBAR */ + pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar); + uartbar &= ~(0xffff << (16 * i)); /* clear half the reg */ + uartbar |= (ioport << (16 * i)); /* set the ioport */ + pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar); + + /* get current config */ + pci_read_config_dword(dev, ITE_887x_MISCR, &miscr); + /* disable interrupts (UARTx_Routing[3:0]) */ + miscr &= ~(0xf << (12 - 4 * i)); + /* activate the UART (UARTx_En) */ + miscr |= 1 << (23 - i); + /* write new config with activated UART */ + pci_write_config_dword(dev, ITE_887x_MISCR, miscr); + } + + if (ret <= 0) { + /* the device has no UARTs if we get here */ + release_region(iobase->start, ITE_887x_IOSIZE); + } + + return ret; +} + +static void __devexit pci_ite887x_exit(struct pci_dev *dev) +{ + u32 ioport; + /* the ioport is bit 0-15 in POSIO0R */ + pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport); + ioport &= 0xffff; + release_region(ioport, ITE_887x_IOSIZE); +} + +/* + * Oxford Semiconductor Inc. + * Check that device is part of the Tornado range of devices, then determine + * the number of ports available on the device. + */ +static int pci_oxsemi_tornado_init(struct pci_dev *dev) +{ + u8 __iomem *p; + unsigned long deviceID; + unsigned int number_uarts = 0; + + /* OxSemi Tornado devices are all 0xCxxx */ + if (dev->vendor == PCI_VENDOR_ID_OXSEMI && + (dev->device & 0xF000) != 0xC000) + return 0; + + p = pci_iomap(dev, 0, 5); + if (p == NULL) + return -ENOMEM; + + deviceID = ioread32(p); + /* Tornado device */ + if (deviceID == 0x07000200) { + number_uarts = ioread8(p + 4); + printk(KERN_DEBUG + "%d ports detected on Oxford PCI Express device\n", + number_uarts); + } + pci_iounmap(dev, p); + return number_uarts; +} + +static int +pci_default_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> + (board->reg_shift + 3); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int +ce4100_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + int ret; + + ret = setup_port(priv, port, 0, 0, board->reg_shift); + port->iotype = UPIO_MEM32; + port->type = PORT_XSCALE; + port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + port->regshift = 2; + + return ret; +} + +static int +pci_omegapci_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + return setup_port(priv, port, 2, idx * 8, 0); +} + +static int skip_tx_en_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_NO_TXEN_TEST; + printk(KERN_DEBUG "serial8250: skipping TxEn test for device " + "[%04x:%04x] subsystem [%04x:%04x]\n", + priv->dev->vendor, + priv->dev->device, + priv->dev->subsystem_vendor, + priv->dev->subsystem_device); + + return pci_default_setup(priv, board, port, idx); +} + +static int kt_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_IIR_ONCE; + return skip_tx_en_setup(priv, board, port, idx); +} + +static int pci_eg20t_init(struct pci_dev *dev) +{ +#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE) + return -ENODEV; +#else + return 0; +#endif +} + +static int +pci_xr17c154_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_EXAR_EFR; + return pci_default_setup(priv, board, port, idx); +} + +static int try_enable_msi(struct pci_dev *dev) +{ + /* use msi if available, but fallback to legacy otherwise */ + pci_enable_msi(dev); + return 0; +} + +static void disable_msi(struct pci_dev *dev) +{ + pci_disable_msi(dev); +} + +#define PCI_VENDOR_ID_SBSMODULARIO 0x124B +#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B +#define PCI_DEVICE_ID_OCTPRO 0x0001 +#define PCI_SUBDEVICE_ID_OCTPRO232 0x0108 +#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 +#define PCI_SUBDEVICE_ID_POCTAL232 0x0308 +#define PCI_SUBDEVICE_ID_POCTAL422 0x0408 +#define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 +#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 +#define PCI_DEVICE_ID_TITAN_200I 0x8028 +#define PCI_DEVICE_ID_TITAN_400I 0x8048 +#define PCI_DEVICE_ID_TITAN_800I 0x8088 +#define PCI_DEVICE_ID_TITAN_800EH 0xA007 +#define PCI_DEVICE_ID_TITAN_800EHB 0xA008 +#define PCI_DEVICE_ID_TITAN_400EH 0xA009 +#define PCI_DEVICE_ID_TITAN_100E 0xA010 +#define PCI_DEVICE_ID_TITAN_200E 0xA012 +#define PCI_DEVICE_ID_TITAN_400E 0xA013 +#define PCI_DEVICE_ID_TITAN_800E 0xA014 +#define PCI_DEVICE_ID_TITAN_200EI 0xA016 +#define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_TITAN_400V3 0xA310 +#define PCI_DEVICE_ID_TITAN_410V3 0xA312 +#define PCI_DEVICE_ID_TITAN_800V3 0xA314 +#define PCI_DEVICE_ID_TITAN_800V3B 0xA315 +#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 +#define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 +#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 +#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d + +/* Unknown vendors/cards - this should not be in linux/pci_ids.h */ +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 + +/* + * Master list of serial port init/setup/exit quirks. + * This does not describe the general nature of the port. + * (ie, baud base, number and location of ports, etc) + * + * This list is ordered alphabetically by vendor then device. + * Specific entries must come before more generic entries. + */ +static struct pci_serial_quirk pci_serial_quirks[] __refdata = { + /* + * ADDI-DATA GmbH communication cards + */ + { + .vendor = PCI_VENDOR_ID_ADDIDATA_OLD, + .device = PCI_DEVICE_ID_ADDIDATA_APCI7800, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = addidata_apci7800_setup, + }, + /* + * AFAVLAB cards - these may be called via parport_serial + * It is not clear whether this applies to all products. + */ + { + .vendor = PCI_VENDOR_ID_AFAVLAB, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = afavlab_setup, + }, + /* + * HP Diva + */ + { + .vendor = PCI_VENDOR_ID_HP, + .device = PCI_DEVICE_ID_HP_DIVA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_hp_diva_init, + .setup = pci_hp_diva_setup, + }, + /* + * Intel + */ + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_80960_RP, + .subvendor = 0xe4bf, + .subdevice = PCI_ANY_ID, + .init = pci_inteli960ni_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_8257X_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573L_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573E_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CE4100_UART, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = ce4100_serial_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = try_enable_msi, + .setup = kt_serial_setup, + .exit = disable_msi, + }, + /* + * ITE + */ + { + .vendor = PCI_VENDOR_ID_ITE, + .device = PCI_DEVICE_ID_ITE_8872, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ite887x_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ite887x_exit), + }, + /* + * National Instruments + */ + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2324I, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2322I, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8422_2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8422_2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8430_setup, + .exit = __devexit_p(pci_ni8430_exit), + }, + /* + * Panacom + */ + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * PLX + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9030, + .subvendor = PCI_SUBVENDOR_ID_PERLE, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_EXSYS, + .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, + .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_ROMULUS, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * SBS Technologies, Inc., PMC-OCTALPRO 232 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_OCTPRO232, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SBS Technologies, Inc., PMC-OCTALPRO 422 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_OCTPRO422, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SBS Technologies, Inc., P-Octal 232 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_POCTAL232, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SBS Technologies, Inc., P-Octal 422 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_POCTAL422, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SIIG cards - these may be called via parport_serial + */ + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig_init, + .setup = pci_siig_setup, + }, + /* + * Titan cards + */ + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_400L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_800L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + /* + * Timedia cards + */ + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_DEVICE_ID_TIMEDIA_1889, + .subvendor = PCI_VENDOR_ID_TIMEDIA, + .subdevice = PCI_ANY_ID, + .probe = pci_timedia_probe, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_timedia_setup, + }, + /* + * Exar cards + */ + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17C152, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17c154_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17C154, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17c154_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17C158, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17c154_setup, + }, + /* + * Xircom cards + */ + { + .vendor = PCI_VENDOR_ID_XIRCOM, + .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_xircom_init, + .setup = pci_default_setup, + }, + /* + * Netmos cards - these may be called via parport_serial + */ + { + .vendor = PCI_VENDOR_ID_NETMOS, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_netmos_init, + .setup = pci_netmos_9900_setup, + }, + /* + * For Oxford Semiconductor Tornado based devices + */ + { + .vendor = PCI_VENDOR_ID_OXSEMI, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_MAINPINE, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_DIGI, + .device = PCIE_DEVICE_ID_NEO_2_OX_IBM, + .subvendor = PCI_SUBVENDOR_ID_IBM, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8811, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8812, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8813, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8814, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8027, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8028, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8029, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x800C, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x800D, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + /* + * Cronyx Omega PCI (PLX-chip based) + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_CRONYX_OMEGA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_omegapci_setup, + }, + /* + * Default "match everything" terminator entry + */ + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + } +}; + +static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) +{ + return quirk_id == PCI_ANY_ID || quirk_id == dev_id; +} + +static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) +{ + struct pci_serial_quirk *quirk; + + for (quirk = pci_serial_quirks; ; quirk++) + if (quirk_id_matches(quirk->vendor, dev->vendor) && + quirk_id_matches(quirk->device, dev->device) && + quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && + quirk_id_matches(quirk->subdevice, dev->subsystem_device)) + break; + return quirk; +} + +static inline int get_pci_irq(struct pci_dev *dev, + const struct pciserial_board *board) +{ + if (board->flags & FL_NOIRQ) + return 0; + else + return dev->irq; +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + * + * The makeup of these names are: + * pbn_bn{_bt}_n_baud{_offsetinhex} + * + * bn = PCI BAR number + * bt = Index using PCI BARs + * n = number of serial ports + * baud = baud rate + * offsetinhex = offset for each sequential port (in hex) + * + * This table is sorted by (in order): bn, bt, baud, offsetindex, n. + * + * Please note: in theory if n = 1, _bt infix should make no difference. + * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 + */ +enum pci_board_num_t { + pbn_default = 0, + + pbn_b0_1_115200, + pbn_b0_2_115200, + pbn_b0_4_115200, + pbn_b0_5_115200, + pbn_b0_8_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_2_1130000, + + pbn_b0_4_1152000, + + pbn_b0_2_1843200, + pbn_b0_4_1843200, + + pbn_b0_2_1843200_200, + pbn_b0_4_1843200_200, + pbn_b0_8_1843200_200, + + pbn_b0_1_4000000, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_4_115200, + pbn_b0_bt_8_115200, + + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + pbn_b0_bt_4_460800, + + pbn_b0_bt_1_921600, + pbn_b0_bt_2_921600, + pbn_b0_bt_4_921600, + pbn_b0_bt_8_921600, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + pbn_b1_16_115200, + + pbn_b1_1_921600, + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1250000, + + pbn_b1_bt_1_115200, + pbn_b1_bt_2_115200, + pbn_b1_bt_4_115200, + + pbn_b1_bt_2_921600, + + pbn_b1_1_1382400, + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_1_115200, + pbn_b2_2_115200, + pbn_b2_4_115200, + pbn_b2_8_115200, + + pbn_b2_1_460800, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + + pbn_b2_1_921600, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_8_1152000, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + + pbn_b2_bt_2_921600, + pbn_b2_bt_4_921600, + + pbn_b3_2_115200, + pbn_b3_4_115200, + pbn_b3_8_115200, + + pbn_b4_bt_2_921600, + pbn_b4_bt_4_921600, + pbn_b4_bt_8_921600, + + /* + * Board-specific versions. + */ + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_exsys_4055, + pbn_plx_romulus, + pbn_oxsemi, + pbn_oxsemi_1_4000000, + pbn_oxsemi_2_4000000, + pbn_oxsemi_4_4000000, + pbn_oxsemi_8_4000000, + pbn_intel_i960, + pbn_sgi_ioc3, + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, + pbn_sbsxrsio, + pbn_exar_XR17C152, + pbn_exar_XR17C154, + pbn_exar_XR17C158, + pbn_exar_ibm_saturn, + pbn_pasemi_1682M, + pbn_ni8430_2, + pbn_ni8430_4, + pbn_ni8430_8, + pbn_ni8430_16, + pbn_ADDIDATA_PCIe_1_3906250, + pbn_ADDIDATA_PCIe_2_3906250, + pbn_ADDIDATA_PCIe_4_3906250, + pbn_ADDIDATA_PCIe_8_3906250, + pbn_ce4100_1_115200, + pbn_omegapci, + pbn_NETMOS9900_2s_115200, +}; + +/* + * uart_offset - the space between channels + * reg_shift - describes how the UART registers are mapped + * to PCI memory by the card. + * For example IER register on SBS, Inc. PMC-OctPro is located at + * offset 0x10 from the UART base, while UART_IER is defined as 1 + * in include/linux/serial_reg.h, + * see first lines of serial_in() and serial_out() in 8250.c +*/ + +static struct pciserial_board pci_boards[] __devinitdata = { + [pbn_default] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_2_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_4_115200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_5_115200] = { + .flags = FL_BASE0, + .num_ports = 5, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_8_115200] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_921600] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_2_921600] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_4_921600] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b0_2_1130000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1130000, + .uart_offset = 8, + }, + + [pbn_b0_4_1152000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1152000, + .uart_offset = 8, + }, + + [pbn_b0_2_1843200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1843200, + .uart_offset = 8, + }, + [pbn_b0_4_1843200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1843200, + .uart_offset = 8, + }, + + [pbn_b0_2_1843200_200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1843200, + .uart_offset = 0x200, + }, + [pbn_b0_4_1843200_200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1843200, + .uart_offset = 0x200, + }, + [pbn_b0_8_1843200_200] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 1843200, + .uart_offset = 0x200, + }, + [pbn_b0_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_2_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_4_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_8_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_2_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_4_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_2_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_4_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_8_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_115200] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_2_115200] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_4_115200] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_8_115200] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_16_115200] = { + .flags = FL_BASE1, + .num_ports = 16, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_1_921600] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_921600] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_4_921600] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_8_921600] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_1250000] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1250000, + .uart_offset = 8, + }, + + [pbn_b1_bt_1_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_bt_2_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_bt_4_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_bt_2_921600] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_1382400] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_2_1382400] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_4_1382400] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_8_1382400] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 1382400, + .uart_offset = 8, + }, + + [pbn_b2_1_115200] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_2_115200] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_4_115200] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_8_115200] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_1_460800] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_4_460800] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_8_460800] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_16_460800] = { + .flags = FL_BASE2, + .num_ports = 16, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b2_1_921600] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_4_921600] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_8_921600] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b2_8_1152000] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 1152000, + .uart_offset = 8, + }, + + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_2_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_4_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_bt_2_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_bt_4_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b3_2_115200] = { + .flags = FL_BASE3, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_4_115200] = { + .flags = FL_BASE3, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_8_115200] = { + .flags = FL_BASE3, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b4_bt_2_921600] = { + .flags = FL_BASE4, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_4_921600] = { + .flags = FL_BASE4, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_8_921600] = { + .flags = FL_BASE4, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + /* + * Entries following this are board-specific. + */ + + /* + * Panacom - IOMEM + */ + [pbn_panacom] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom2] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom4] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + + [pbn_exsys_4055] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + /* I think this entry is broken - the first_offset looks wrong --rmk */ + [pbn_plx_romulus] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x03, + }, + + /* + * This board uses the size of PCI Base region 0 to + * signal now many ports are available + */ + [pbn_oxsemi] = { + .flags = FL_BASE0|FL_REGION_SZ_CAP, + .num_ports = 32, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_oxsemi_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_2_4000000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_4_4000000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_8_4000000] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + + + /* + * EKF addition for i960 Boards form EKF with serial port. + * Max 256 ports. + */ + [pbn_intel_i960] = { + .flags = FL_BASE0, + .num_ports = 32, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x10000, + }, + [pbn_sgi_ioc3] = { + .flags = FL_BASE0|FL_NOIRQ, + .num_ports = 1, + .base_baud = 458333, + .uart_offset = 8, + .reg_shift = 0, + .first_offset = 0x20178, + }, + + /* + * Computone - uses IOMEM. + */ + [pbn_computone_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_6] = { + .flags = FL_BASE0, + .num_ports = 6, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_sbsxrsio] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 256, + .reg_shift = 4, + }, + /* + * Exar Corp. XR17C15[248] Dual/Quad/Octal UART + * Only basic 16550A support. + * XR17C15[24] are not tested, but they should work. + */ + [pbn_exar_XR17C152] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_exar_XR17C154] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_exar_XR17C158] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_exar_ibm_saturn] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 0x200, + }, + + /* + * PA Semi PWRficient PA6T-1682M on-chip UART + */ + [pbn_pasemi_1682M] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 8333333, + }, + /* + * National Instruments 843x + */ + [pbn_ni8430_16] = { + .flags = FL_BASE0, + .num_ports = 16, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + /* + * ADDI-DATA GmbH PCI-Express communication cards + */ + [pbn_ADDIDATA_PCIe_1_3906250] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_2_3906250] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_4_3906250] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_8_3906250] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ce4100_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .reg_shift = 2, + }, + [pbn_omegapci] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 0x200, + }, + [pbn_NETMOS9900_2s_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + }, +}; + +static const struct pci_device_id softmodem_blacklist[] = { + { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ + { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ + { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ +}; + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, 1 on failure. + */ +static int __devinit +serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) +{ + const struct pci_device_id *blacklist; + int num_iomem, num_port, first_port = -1, i; + + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + * + * (Should we try to make guesses for multiport serial devices + * later?) + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return -ENODEV; + + /* + * Do not access blacklisted devices that are known not to + * feature serial ports. + */ + for (blacklist = softmodem_blacklist; + blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist); + blacklist++) { + if (dev->vendor == blacklist->vendor && + dev->device == blacklist->device) + return -ENODEV; + } + + num_iomem = num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, + * use it. We guess the number of ports based on the IO + * region size. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + board->num_ports = pci_resource_len(dev, first_port) / 8; + return 0; + } + + /* + * Now guess if we've got a board which indexes by BARs. + * Each IO BAR should be 8 bytes, and they should follow + * consecutively. + */ + first_port = -1; + num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO && + pci_resource_len(dev, i) == 8 && + (first_port == -1 || (first_port + num_port) == i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + } + + if (num_port > 1) { + board->flags = first_port | FL_BASE_BARS; + board->num_ports = num_port; + return 0; + } + + return -ENODEV; +} + +static inline int +serial_pci_matches(const struct pciserial_board *board, + const struct pciserial_board *guessed) +{ + return + board->num_ports == guessed->num_ports && + board->base_baud == guessed->base_baud && + board->uart_offset == guessed->uart_offset && + board->reg_shift == guessed->reg_shift && + board->first_offset == guessed->first_offset; +} + +struct serial_private * +pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) +{ + struct uart_port serial_port; + struct serial_private *priv; + struct pci_serial_quirk *quirk; + int rc, nr_ports, i; + + nr_ports = board->num_ports; + + /* + * Find an init and setup quirks. + */ + quirk = find_quirk(dev); + + /* + * Run the new-style initialization function. + * The initialization function returns: + * <0 - error + * 0 - use board->num_ports + * >0 - number of ports + */ + if (quirk->init) { + rc = quirk->init(dev); + if (rc < 0) { + priv = ERR_PTR(rc); + goto err_out; + } + if (rc) + nr_ports = rc; + } + + priv = kzalloc(sizeof(struct serial_private) + + sizeof(unsigned int) * nr_ports, + GFP_KERNEL); + if (!priv) { + priv = ERR_PTR(-ENOMEM); + goto err_deinit; + } + + priv->dev = dev; + priv->quirk = quirk; + + memset(&serial_port, 0, sizeof(struct uart_port)); + serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + serial_port.uartclk = board->base_baud * 16; + serial_port.irq = get_pci_irq(dev, board); + serial_port.dev = &dev->dev; + + for (i = 0; i < nr_ports; i++) { + if (quirk->setup(priv, board, &serial_port, i)) + break; + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", + serial_port.iobase, serial_port.irq, serial_port.iotype); +#endif + + priv->line[i] = serial8250_register_port(&serial_port); + if (priv->line[i] < 0) { + printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); + break; + } + } + priv->nr = i; + return priv; + +err_deinit: + if (quirk->exit) + quirk->exit(dev); +err_out: + return priv; +} +EXPORT_SYMBOL_GPL(pciserial_init_ports); + +void pciserial_remove_ports(struct serial_private *priv) +{ + struct pci_serial_quirk *quirk; + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_unregister_port(priv->line[i]); + + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (priv->remapped_bar[i]) + iounmap(priv->remapped_bar[i]); + priv->remapped_bar[i] = NULL; + } + + /* + * Find the exit quirks. + */ + quirk = find_quirk(priv->dev); + if (quirk->exit) + quirk->exit(priv->dev); + + kfree(priv); +} +EXPORT_SYMBOL_GPL(pciserial_remove_ports); + +void pciserial_suspend_ports(struct serial_private *priv) +{ + int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_suspend_port(priv->line[i]); +} +EXPORT_SYMBOL_GPL(pciserial_suspend_ports); + +void pciserial_resume_ports(struct serial_private *priv) +{ + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(priv->dev); + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_resume_port(priv->line[i]); +} +EXPORT_SYMBOL_GPL(pciserial_resume_ports); + +/* + * Probe one serial board. Unfortunately, there is no rhyme nor reason + * to the arrangement of serial ports on a PCI card. + */ +static int __devinit +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct pci_serial_quirk *quirk; + struct serial_private *priv; + const struct pciserial_board *board; + struct pciserial_board tmp; + int rc; + + quirk = find_quirk(dev); + if (quirk->probe) { + rc = quirk->probe(dev); + if (rc) + return rc; + } + + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { + printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", + ent->driver_data); + return -EINVAL; + } + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + pci_save_state(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default) { + /* + * Use a copy of the pci_board entry for this; + * avoid changing entries in the table. + */ + memcpy(&tmp, board, sizeof(struct pciserial_board)); + board = &tmp; + + /* + * We matched one of our class entries. Try to + * determine the parameters of this board. + */ + rc = serial_pci_guess_board(dev, &tmp); + if (rc) + goto disable; + } else { + /* + * We matched an explicit entry. If we are able to + * detect this boards settings with our heuristic, + * then we no longer need this entry. + */ + memcpy(&tmp, &pci_boards[pbn_default], + sizeof(struct pciserial_board)); + rc = serial_pci_guess_board(dev, &tmp); + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); + } + + priv = pciserial_init_ports(dev, board); + if (!IS_ERR(priv)) { + pci_set_drvdata(dev, priv); + return 0; + } + + rc = PTR_ERR(priv); + + disable: + pci_disable_device(dev); + return rc; +} + +static void __devexit pciserial_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + pci_set_drvdata(dev, NULL); + + pciserial_remove_ports(priv); + + pci_disable_device(dev); +} + +#ifdef CONFIG_PM +static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) + pciserial_suspend_ports(priv); + + pci_save_state(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + return 0; +} + +static int pciserial_resume_one(struct pci_dev *dev) +{ + int err; + struct serial_private *priv = pci_get_drvdata(dev); + + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + + if (priv) { + /* + * The device may have been disabled. Re-enable it. + */ + err = pci_enable_device(dev); + /* FIXME: We cannot simply error out here */ + if (err) + printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); + pciserial_resume_ports(priv); + } + return 0; +} +#endif + +static struct pci_device_id serial_pci_tbl[] = { + /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, + PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0, + pbn_b1_2_1250000 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0, + pbn_b0_2_1843200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0, + pbn_b0_4_1843200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_AFAVLAB, + PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, + pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, + 0, 0, pbn_exar_ibm_saturn }, + + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + /* + * VScom SPCOM800, from sl@s.pl + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, + /* Unknown card - subdevice 0x1584 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, + PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, + PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, + pbn_exsys_4055 }, + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, + 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, + 0, 0, + pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_OXSEMI, 0x9505, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + + /* + * The below card is a little controversial since it is the + * subject of a PCI vendor/device ID clash. (See + * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). + * For now just used the hex ID 0x950a. + */ + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0, + pbn_b0_2_115200 }, + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_1130000 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, + PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, + PCI_ANY_ID , PCI_ANY_ID, 0, 0, + pbn_b2_8_1152000 }, + + /* + * Oxford Semiconductor Inc. Tornado PCI express device range. + */ + { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + /* + * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado + */ + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, + pbn_oxsemi_8_4000000 }, + + /* + * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado + */ + { PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, + PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + + /* + * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, + * from skokodyn@yahoo.com + */ + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0, + pbn_sbsxrsio }, + + /* + * Digitan DS560-558, from jimd@esoft.com + */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, + 0, 0, pbn_computone_4 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, + 0, 0, pbn_computone_8 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_921600 }, + + /* + * AFAVLAB serial card, from Harald Welte + */ + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + + /* + * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408). + * Cards are identified by their subsystem vendor IDs, which + * (in hex) match the model number. + * + * Note that JC140x are RS422/485 cards which require ox950 + * ACR = 0x10, and as such are not currently fully supported. + */ + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, +/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1402, 0x0002, 0, 0, + pbn_b0_2_921600 }, */ +/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1404, 0x0004, 0, 0, + pbn_b0_4_921600 }, */ + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + /* + * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com + */ + { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_1382400 }, + + /* + * Dell Remote Access Card III - Tim_T_Murphy@Dell.com + */ + { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_1382400 }, + + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* + * Xircom Cardbus/Ethernet combos + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + /* + * Elsa Model 56K PCI Modem, from Andreas Rath + */ + { PCI_VENDOR_ID_ROCKWELL, 0x1004, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + + /* + * HP Diva card + */ + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0, + pbn_b1_1_115200 }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_5_115200 }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_2_115200 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_4_115200 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_8_115200 }, + + /* + * Exar Corp. XR17C15[248] Dual/Quad/Octal UART + */ + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17C152 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17C154 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17C158 }, + + /* + * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) + */ + { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + /* + * ITE + */ + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b1_bt_1_115200 }, + + /* + * IntaShield IS-200 + */ + { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0811 */ + pbn_b2_2_115200 }, + /* + * IntaShield IS-400 + */ + { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */ + pbn_b2_4_115200 }, + /* + * Perle PCI-RAS cards + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4, + 0, 0, pbn_b2_4_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8, + 0, 0, pbn_b2_8_921600 }, + + /* + * Mainpine series cards: Fairly standard layout but fools + * parts of the autodetect in some cases and uses otherwise + * unmatched communications subclasses in the PCI Express case + */ + + { /* RockForceDUO */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0200, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUATRO */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0300, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceDUO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0400, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUATRO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0500, + 0, 0, pbn_b0_4_115200 }, + { /* RockForce+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0600, + 0, 0, pbn_b0_2_115200 }, + { /* RockForce+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0700, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceOCTO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0800, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceDUO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0C00, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUARTRO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0D00, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceOCTO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x1D00, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceD1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2000, + 0, 0, pbn_b0_1_115200 }, + { /* RockForceF1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2100, + 0, 0, pbn_b0_1_115200 }, + { /* RockForceD2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2200, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceF2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2300, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceD4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2400, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceF4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2500, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceD8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2600, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceF8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2700, + 0, 0, pbn_b0_8_115200 }, + { /* IQ Express D1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3000, + 0, 0, pbn_b0_1_115200 }, + { /* IQ Express F1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3100, + 0, 0, pbn_b0_1_115200 }, + { /* IQ Express D2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3200, + 0, 0, pbn_b0_2_115200 }, + { /* IQ Express F2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3300, + 0, 0, pbn_b0_2_115200 }, + { /* IQ Express D4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3400, + 0, 0, pbn_b0_4_115200 }, + { /* IQ Express F4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3500, + 0, 0, pbn_b0_4_115200 }, + { /* IQ Express D8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3C00, + 0, 0, pbn_b0_8_115200 }, + { /* IQ Express F8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3D00, + 0, 0, pbn_b0_8_115200 }, + + + /* + * PA Semi PA6T-1682M on-chip UART + */ + { PCI_VENDOR_ID_PASEMI, 0xa004, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pasemi_1682M }, + + /* + * National Instruments + */ + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_16_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_16_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_8 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_8 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + + /* + * ADDI-DATA GmbH communication cards + */ + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA_OLD, + PCI_DEVICE_ID_ADDIDATA_APCI7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b1_8_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7800_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_8_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_4_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_2_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_1_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_8_3906250 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_VENDOR_ID_IBM, 0x0299, + 0, 0, pbn_b0_bt_2_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + /* the 9901 is a rebranded 9912 */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002, + 0, 0, pbn_NETMOS9900_2s_115200 }, + + /* + * Best Connectivity and Rosewill PCI Multi I/O cards + */ + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3002, + 0, 0, pbn_b0_bt_2_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004, + 0, 0, pbn_b0_bt_4_115200 }, + /* Intel CE4100 */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ce4100_1_115200 }, + + /* + * Cronyx Omega PCI + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_omegapci }, + + /* + * These entries match devices with class COMMUNICATION_SERIAL, + * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL + */ + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, + 0xffff00, pbn_default }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MODEM << 8, + 0xffff00, pbn_default }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + 0xffff00, pbn_default }, + { 0, } +}; + +static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, + pci_channel_state_t state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + if (priv) + pciserial_suspend_ports(priv); + + pci_disable_device(dev); + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) +{ + int rc; + + rc = pci_enable_device(dev); + + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + pci_restore_state(dev); + pci_save_state(dev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void serial8250_io_resume(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) + pciserial_resume_ports(priv); +} + +static struct pci_error_handlers serial8250_err_handler = { + .error_detected = serial8250_io_error_detected, + .slot_reset = serial8250_io_slot_reset, + .resume = serial8250_io_resume, +}; + +static struct pci_driver serial_pci_driver = { + .name = "serial", + .probe = pciserial_init_one, + .remove = __devexit_p(pciserial_remove_one), +#ifdef CONFIG_PM + .suspend = pciserial_suspend_one, + .resume = pciserial_resume_one, +#endif + .id_table = serial_pci_tbl, + .err_handler = &serial8250_err_handler, +}; + +static int __init serial8250_pci_init(void) +{ + return pci_register_driver(&serial_pci_driver); +} + +static void __exit serial8250_pci_exit(void) +{ + pci_unregister_driver(&serial_pci_driver); +} + +module_init(serial8250_pci_init); +module_exit(serial8250_pci_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); +MODULE_DEVICE_TABLE(pci, serial_pci_tbl); diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c new file mode 100644 index 0000000..a2f2365 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -0,0 +1,524 @@ +/* + * Probe module for 8250/16550-type ISAPNP serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * Ported to the Linux PnP Layer - (C) Adam Belay. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "8250.h" + +#define UNKNOWN_DEV 0x3000 + + +static const struct pnp_device_id pnp_dev_table[] = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "AAC000F", 0 }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { "ADC0001", 0 }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { "ADC0002", 0 }, + /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ + { "AEI0250", 0 }, + /* Actiontec ISA PNP 56K X2 Fax Modem */ + { "AEI1240", 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, + /* AZT3005 PnP SOUND DEVICE */ + { "AZT4001", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { "BRI0A49", 0 }, + /* Boca Research 33,600 ACF Modem */ + { "BRI1400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI3400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI0A49", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { "CPI4050", 0 }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { "CTL3001", 0 }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { "CTL3011", 0 }, + /* Davicom ISA 33.6K Modem */ + { "DAV0336", 0 }, + /* Creative */ + /* Creative Modem Blaster Flash56 DI5601-1 */ + { "DMB1032", 0 }, + /* Creative Modem Blaster V.90 DI5660 */ + { "DMB2001", 0 }, + /* E-Tech */ + /* E-Tech CyberBULLET PC56RVP */ + { "ETT0002", 0 }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { "FUJ0202", 0 }, + /* Fujitsu FMV-FX431 Plug & Play */ + { "FUJ0205", 0 }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { "FUJ0206", 0 }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { "FUJ0209", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "GVC000F", 0 }, + /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ + { "GVC0303", 0 }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { "HAY0001", 0 }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { "HAY000C", 0 }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { "HAY000D", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5670", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5674", 0 }, + /* Hayes Accura 56K Fax Modem PnP */ + { "HAY5675", 0 }, + /* Hayes 288, V.34 + FAX */ + { "HAYF000", 0 }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { "HAYF001", 0 }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { "IBM0033", 0 }, + /* Intermec */ + /* Intermec CV60 touchscreen port */ + { "PNP4972", 0 }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { "IXDC801", 0 }, + /* Intertex 33k6 56k Voice EXT PnP */ + { "IXDC901", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDD801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDD901", 0 }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { "IXDF401", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDF801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDF901", 0 }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { "KOR4522", 0 }, + /* KXPro 33.6 Vocal ASVD PnP */ + { "KORF661", 0 }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { "LAS4040", 0 }, + /* Lasat Safire 560 PnP */ + { "LAS4540", 0 }, + /* Lasat Safire 336 PnP */ + { "LAS5440", 0 }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { "MNP0281", 0 }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { "MNP0336", 0 }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { "MNP0339", 0 }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { "MNP0342", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0500", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0501", 0 }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { "MNP0502", 0 }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { "MOT1105", 0 }, + /* Motorola TA210 Plug & Play */ + { "MOT1111", 0 }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { "MOT1114", 0 }, + /* Motorola BitSURFR Plug & Play */ + { "MOT1115", 0 }, + /* Motorola Lifestyle 28.8 Internal */ + { "MOT1190", 0 }, + /* Motorola V.3400 Plug & Play */ + { "MOT1501", 0 }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { "MOT1502", 0 }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { "MOT1505", 0 }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { "MOT1509", 0 }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { "MOT150A", 0 }, + /* Motorola VoiceSURFR 56K External PnP */ + { "MOT150F", 0 }, + /* Motorola ModemSURFR 56K External PnP */ + { "MOT1510", 0 }, + /* Motorola ModemSURFR 56K Internal PnP */ + { "MOT1550", 0 }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { "MOT1560", 0 }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { "MOT1580", 0 }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { "MOT15B0", 0 }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { "MOT15F0", 0 }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { "MVX00A1", 0 }, + /* PC Rider K56 Phone System PnP */ + { "MVX00F2", 0 }, + /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ + { "nEC8241", 0 }, + /* Pace 56 Voice Internal Plug & Play Modem */ + { "PMC2430", 0 }, + /* Generic */ + /* Generic standard PC COM port */ + { "PNP0500", 0 }, + /* Generic 16550A-compatible COM port */ + { "PNP0501", 0 }, + /* Compaq 14400 Modem */ + { "PNPC000", 0 }, + /* Compaq 2400/9600 Modem */ + { "PNPC001", 0 }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { "PNPC031", 0 }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { "PNPC032", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC100", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC101", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC102", 0 }, + /* Standard Modem*/ + { "PNPC103", 0 }, + /* Standard 9600 bps Modem*/ + { "PNPC104", 0 }, + /* Standard 14400 bps Modem*/ + { "PNPC105", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC106", 0 }, + /* Standard Modem */ + { "PNPC107", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC108", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC109", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10A", 0 }, + /* Standard Modem */ + { "PNPC10B", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC10C", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC10D", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10E", 0 }, + /* Standard Modem */ + { "PNPC10F", 0 }, + /* Standard PCMCIA Card Modem */ + { "PNP2000", 0 }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { "ROK0030", 0 }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { "ROK0100", 0 }, + /* Rockwell 28.8 */ + { "ROK4120", 0 }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { "ROK4920", 0 }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { "RSS00A0", 0 }, + /* Viking 56K FAX INT */ + { "RSS0262", 0 }, + /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ + { "RSS0250", 0 }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { "SUP1310", 0 }, + /* SupraExpress 336i PnP Voice Modem */ + { "SUP1381", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1421", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1590", 0 }, + /* SupraExpress 336i Sp ASVD */ + { "SUP1620", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1760", 0 }, + /* SupraExpress 56i Sp Intl */ + { "SUP2171", 0 }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { "TEX0011", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "UAC000F", 0 }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { "USR0000", 0 }, + /* U.S. Robotics Sporster 33.6K Fax INT PnP */ + { "USR0002", 0 }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { "USR0004", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR0006", 0 }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { "USR0007", 0 }, + /* U.S. Robotics Courier V.Everything INT PnP */ + { "USR0009", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR2002", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR2070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR2080", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3031", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3050", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR3080", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3090", 0 }, + /* U.S. Robotics 56K Message */ + { "USR9100", 0 }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { "USR9160", 0 }, + /* U.S. Robotics 56K FAX INT PnP*/ + { "USR9170", 0 }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { "USR9180", 0 }, + /* U.S. Robotics 56K Voice INT PnP*/ + { "USR9190", 0 }, + /* Wacom tablets */ + { "WACFXXX", 0 }, + /* Compaq touchscreen */ + { "FPI2002", 0 }, + /* Fujitsu Stylistic touchscreens */ + { "FUJ02B2", 0 }, + { "FUJ02B3", 0 }, + /* Fujitsu Stylistic LT touchscreens */ + { "FUJ02B4", 0 }, + /* Passive Fujitsu Stylistic touchscreens */ + { "FUJ02B6", 0 }, + { "FUJ02B7", 0 }, + { "FUJ02B8", 0 }, + { "FUJ02B9", 0 }, + { "FUJ02BC", 0 }, + /* Fujitsu Wacom Tablet PC device */ + { "FUJ02E5", 0 }, + /* Fujitsu P-series tablet PC device */ + { "FUJ02E6", 0 }, + /* Fujitsu Wacom 2FGT Tablet PC device */ + { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, + /* + * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in + * disguise) + */ + { "LTS0001", 0 }, + /* Rockwell's (PORALiNK) 33600 INT PNP */ + { "WCI0003", 0 }, + /* Unknown PnP modems */ + { "PNPCXXX", UNKNOWN_DEV }, + /* More unknown PnP modems */ + { "PNPDXXX", UNKNOWN_DEV }, + { "", 0 } +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +static char *modem_names[] __devinitdata = { + "MODEM", "Modem", "modem", "FAX", "Fax", "fax", + "56K", "56k", "K56", "33.6", "28.8", "14.4", + "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", + "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL +}; + +static int __devinit check_name(char *name) +{ + char **tmp; + + for (tmp = modem_names; *tmp; tmp++) + if (strstr(name, *tmp)) + return 1; + + return 0; +} + +static int __devinit check_resources(struct pnp_dev *dev) +{ + resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; + int i; + + for (i = 0; i < ARRAY_SIZE(base); i++) { + if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) + return 1; + } + + return 0; +} + +/* + * Given a complete unknown PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base address + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) +{ + if (!(check_name(pnp_dev_name(dev)) || + (dev->card && check_name(dev->card->name)))) + return -ENODEV; + + if (check_resources(dev)) + return 0; + + return -ENODEV; +} + +static int __devinit +serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct uart_port port; + int ret, line, flags = dev_id->driver_data; + + if (flags & UNKNOWN_DEV) { + ret = serial_pnp_guess_board(dev, &flags); + if (ret < 0) + return ret; + } + + memset(&port, 0, sizeof(struct uart_port)); + if (pnp_irq_valid(dev, 0)) + port.irq = pnp_irq(dev, 0); + if (pnp_port_valid(dev, 0)) { + port.iobase = pnp_port_start(dev, 0); + port.iotype = UPIO_PORT; + } else if (pnp_mem_valid(dev, 0)) { + port.mapbase = pnp_mem_start(dev, 0); + port.iotype = UPIO_MEM; + port.flags = UPF_IOREMAP; + } else + return -ENODEV; + +#ifdef SERIAL_DEBUG_PNP + printk(KERN_DEBUG + "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", + port.iobase, port.mapbase, port.irq, port.iotype); +#endif + + port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) + port.flags |= UPF_SHARE_IRQ; + port.uartclk = 1843200; + port.dev = &dev->dev; + + line = serial8250_register_port(&port); + if (line < 0) + return -ENODEV; + + pnp_set_drvdata(dev, (void *)((long)line + 1)); + return 0; +} + +static void __devexit serial_pnp_remove(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + if (line) + serial8250_unregister_port(line - 1); +} + +#ifdef CONFIG_PM +static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_suspend_port(line - 1); + return 0; +} + +static int serial_pnp_resume(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_resume_port(line - 1); + return 0; +} +#else +#define serial_pnp_suspend NULL +#define serial_pnp_resume NULL +#endif /* CONFIG_PM */ + +static struct pnp_driver serial_pnp_driver = { + .name = "serial", + .probe = serial_pnp_probe, + .remove = __devexit_p(serial_pnp_remove), + .suspend = serial_pnp_suspend, + .resume = serial_pnp_resume, + .id_table = pnp_dev_table, +}; + +static int __init serial8250_pnp_init(void) +{ + return pnp_register_driver(&serial_pnp_driver); +} + +static void __exit serial8250_pnp_exit(void) +{ + pnp_unregister_driver(&serial_pnp_driver); +} + +module_init(serial8250_pnp_init); +module_exit(serial8250_pnp_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig new file mode 100644 index 0000000..591f801 --- /dev/null +++ b/drivers/tty/serial/8250/Kconfig @@ -0,0 +1,280 @@ +# +# The 8250/16550 serial drivers. You shouldn't be in this list unless +# you somehow have an implicit or explicit dependency on SERIAL_8250. +# + +config SERIAL_8250 + tristate "8250/16550 and compatible serial support" + select SERIAL_CORE + ---help--- + This selects whether you want to include the driver for the standard + serial ports. The standard answer is Y. People who might say N + here are those that are setting up dedicated Ethernet WWW/FTP + servers, or users that have one of the various bus mice instead of a + serial mouse and don't intend to use their machine's standard serial + port for anything. (Note that the Cyclades and Stallion multi + serial port drivers do not need this driver built in for them to + work.) + + To compile this driver as a module, choose M here: the + module will be called 8250. + [WARNING: Do not compile this driver as a module if you are using + non-standard serial ports, since the configuration information will + be lost when the driver is unloaded. This limitation may be lifted + in the future.] + + BTW1: If you have a mouseman serial mouse which is not recognized by + the X window system, try running gpm first. + + BTW2: If you intend to use a software modem (also called Winmodem) + under Linux, forget it. These modems are crippled and require + proprietary drivers which are only available under Windows. + + Most people will say Y or M here, so that they can use serial mice, + modems and similar devices connecting to the standard serial ports. + +config SERIAL_8250_CONSOLE + bool "Console on 8250/16550 and compatible serial port" + depends on SERIAL_8250=y + select SERIAL_CORE_CONSOLE + ---help--- + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). This could be useful if some terminal or printer is connected + to that serial port. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS1". (Try "man bootparam" or see the documentation of + your boot loader (grub or lilo or loadlin) about how to pass options + to the kernel at boot time.) + + If you don't have a VGA card installed and you say Y here, the + kernel will automatically use the first serial line, /dev/ttyS0, as + system console. + + You can set that using a kernel command line option such as + "console=uart8250,io,0x3f8,9600n8" + "console=uart8250,mmio,0xff5e0000,115200n8". + and it will switch to normal serial console when the corresponding + port is ready. + "earlycon=uart8250,io,0x3f8,9600n8" + "earlycon=uart8250,mmio,0xff5e0000,115200n8". + it will not only setup early console. + + If unsure, say N. + +config FIX_EARLYCON_MEM + bool + depends on X86 + default y + +config SERIAL_8250_GSC + tristate + depends on SERIAL_8250 && GSC + default SERIAL_8250 + +config SERIAL_8250_PCI + tristate "8250/16550 PCI device support" if EXPERT + depends on SERIAL_8250 && PCI + default SERIAL_8250 + help + This builds standard PCI serial support. You may be able to + disable this feature if you only need legacy serial support. + Saves about 9K. + +config SERIAL_8250_PNP + tristate "8250/16550 PNP device support" if EXPERT + depends on SERIAL_8250 && PNP + default SERIAL_8250 + help + This builds standard PNP serial support. You may be able to + disable this feature if you only need legacy serial support. + +config SERIAL_8250_HP300 + tristate + depends on SERIAL_8250 && HP300 + default SERIAL_8250 + +config SERIAL_8250_CS + tristate "8250/16550 PCMCIA device support" + depends on PCMCIA && SERIAL_8250 + ---help--- + Say Y here to enable support for 16-bit PCMCIA serial devices, + including serial port cards, modems, and the modem functions of + multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are + credit-card size devices often used with laptops.) + + To compile this driver as a module, choose M here: the + module will be called serial_cs. + + If unsure, say N. + +config SERIAL_8250_NR_UARTS + int "Maximum number of 8250/16550 serial ports" + depends on SERIAL_8250 + default "4" + help + Set this to the number of serial ports you want the driver + to support. This includes any ports discovered via ACPI or + PCI enumeration and any ports that may be added at run-time + via hot-plug, or any ISA multi-port serial cards. + +config SERIAL_8250_RUNTIME_UARTS + int "Number of 8250/16550 serial ports to register at runtime" + depends on SERIAL_8250 + range 0 SERIAL_8250_NR_UARTS + default "4" + help + Set this to the maximum number of serial ports you want + the kernel to register at boot time. This can be overridden + with the module parameter "nr_uarts", or boot-time parameter + 8250.nr_uarts + +config SERIAL_8250_EXTENDED + bool "Extended 8250/16550 serial driver options" + depends on SERIAL_8250 + help + If you wish to use any non-standard features of the standard "dumb" + driver, say Y here. This includes HUB6 support, shared serial + interrupts, special multiport support, support for more than the + four COM 1/2/3/4 boards, etc. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial driver options. If unsure, say N. + +config SERIAL_8250_MANY_PORTS + bool "Support more than 4 legacy serial ports" + depends on SERIAL_8250_EXTENDED && !IA64 + help + Say Y here if you have dumb serial boards other than the four + standard COM 1/2/3/4 ports. This may happen if you have an AST + FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available + from ), or other custom + serial port hardware which acts similar to standard serial port + hardware. If you only use the standard COM 1/2/3/4 ports, you can + say N here to save some memory. You can also say Y if you have an + "intelligent" multiport card such as Cyclades, Digiboards, etc. + +# +# Multi-port serial cards +# + +config SERIAL_8250_FOURPORT + tristate "Support Fourport cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an AST FourPort serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_fourport. + +config SERIAL_8250_ACCENT + tristate "Support Accent cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an Accent Async serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_accent. + +config SERIAL_8250_BOCA + tristate "Support Boca cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a Boca serial board. Please read the Boca + mini-HOWTO, available from + + To compile this driver as a module, choose M here: the module + will be called 8250_boca. + +config SERIAL_8250_EXAR_ST16C554 + tristate "Support Exar ST16C554/554D Quad UART" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + The Uplogix Envoy TU301 uses this Exar Quad UART. If you are + tinkering with your Envoy TU301, or have a machine with this UART, + say Y here. + + To compile this driver as a module, choose M here: the module + will be called 8250_exar_st16c554. + +config SERIAL_8250_HUB6 + tristate "Support Hub6 cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a HUB6 serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_hub6. + +# +# Misc. options/drivers. +# + +config SERIAL_8250_SHARE_IRQ + bool "Support for sharing serial interrupts" + depends on SERIAL_8250_EXTENDED + help + Some serial boards have hardware support which allows multiple dumb + serial ports on the same board to share a single IRQ. To enable + support for this in the serial driver, say Y here. + +config SERIAL_8250_DETECT_IRQ + bool "Autodetect IRQ on standard ports (unsafe)" + depends on SERIAL_8250_EXTENDED + help + Say Y here if you want the kernel to try to guess which IRQ + to use for your serial port. + + This is considered unsafe; it is far better to configure the IRQ in + a boot script using the setserial command. + + If unsure, say N. + +config SERIAL_8250_RSA + bool "Support RSA serial ports" + depends on SERIAL_8250_EXTENDED + help + ::: To be written ::: + +config SERIAL_8250_MCA + tristate "Support 8250-type ports on MCA buses" + depends on SERIAL_8250 != n && MCA + help + Say Y here if you have a MCA serial ports. + + To compile this driver as a module, choose M here: the module + will be called 8250_mca. + +config SERIAL_8250_ACORN + tristate "Acorn expansion card serial port support" + depends on ARCH_ACORN && SERIAL_8250 + help + If you have an Atomwide Serial card or Serial Port card for an Acorn + system, say Y to this option. The driver can handle 1, 2, or 3 port + cards. If unsure, say N. + +config SERIAL_8250_RM9K + bool "Support for MIPS RM9xxx integrated serial port" + depends on SERIAL_8250 != n && SERIAL_RM9000 + select SERIAL_8250_SHARE_IRQ + help + Selecting this option will add support for the integrated serial + port hardware found on MIPS RM9122 and similar processors. + If unsure, say N. + +config SERIAL_8250_FSL + bool + depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 + default PPC + +config SERIAL_8250_DW + tristate "Support for Synopsys DesignWare 8250 quirks" + depends on SERIAL_8250 && OF + help + Selecting this option will enable handling of the extra features + present in the Synopsys DesignWare APB UART. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile new file mode 100644 index 0000000..867bba7 --- /dev/null +++ b/drivers/tty/serial/8250/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the 8250 serial device drivers. +# + +obj-$(CONFIG_SERIAL_8250) += 8250.o +obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o +obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o +obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o +obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o +obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o +obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o +obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o +obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o +obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o +obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o +obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o +obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o diff --git a/drivers/tty/serial/8250/m32r_sio.c b/drivers/tty/serial/8250/m32r_sio.c new file mode 100644 index 0000000..94a6792 --- /dev/null +++ b/drivers/tty/serial/8250/m32r_sio.c @@ -0,0 +1,1191 @@ +/* + * m32r_sio.c + * + * Driver for M32R serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.c. + * + * Copyright (C) 2001 Russell King. + * Copyright (C) 2004 Hirokazu Takata + * + * 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. + */ + +/* + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. Currently, we don't + * support this very well, and it may well be dropped from this driver + * in future. As such, mapbase should be NULL. + * + * membase is an 'ioremapped' cookie. This is compatible with the old + * serial.c driver, and is currently the preferred form. + */ + +#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PORT_M32R_BASE PORT_M32R_SIO +#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) +#define BAUD_RATE 115200 + +#include +#include "m32r_sio.h" +#include "m32r_sio_reg.h" + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 256 + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +#define BASE_BAUD 115200 + +/* Standard COM flags */ +#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) + +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#if defined(CONFIG_PLAT_USRV) + +#define SERIAL_PORT_DFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ + +#else /* !CONFIG_PLAT_USRV */ + +#if defined(CONFIG_SERIAL_M32R_PLDSIO) +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ + STD_COM_FLAGS }, /* ttyS0 */ +#else +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ + STD_COM_FLAGS }, /* ttyS0 */ +#endif + +#endif /* !CONFIG_PLAT_USRV */ + +static struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS +}; + +#define UART_NR ARRAY_SIZE(old_serial_port) + +struct uart_sio_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short rev; + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char lsr_break_flag; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +static struct irq_info irq_lists[NR_IRQS]; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uart_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .dfl_xmit_fifo_size = 1, + .flags = 0, + }, + [PORT_INDEX(PORT_M32R_SIO)] = { + .name = "M32RSIO", + .dfl_xmit_fifo_size = 1, + .flags = 0, + }, +}; + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + +#define __sio_in(x) inw((unsigned long)(x)) +#define __sio_out(v,x) outw((v),(unsigned long)(x)) + +static inline void sio_set_baud_rate(unsigned long baud) +{ + unsigned short sbaud; + sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1; + __sio_out(sbaud, PLD_ESIO0BAUR); +} + +static void sio_reset(void) +{ + unsigned short tmp; + + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0CR); + sio_set_baud_rate(BAUD_RATE); + __sio_out(0x0300, PLD_ESIO0CR); + __sio_out(0x0003, PLD_ESIO0CR); +} + +static void sio_init(void) +{ + unsigned short tmp; + + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0CR); + __sio_out(0x0300, PLD_ESIO0CR); + __sio_out(0x0003, PLD_ESIO0CR); +} + +static void sio_error(int *status) +{ + printk("SIO0 error[%04x]\n", *status); + do { + sio_init(); + } while ((*status = __sio_in(PLD_ESIO0CR)) != 3); +} + +#else /* not CONFIG_SERIAL_M32R_PLDSIO */ + +#define __sio_in(x) inl(x) +#define __sio_out(v,x) outl((v),(x)) + +static inline void sio_set_baud_rate(unsigned long baud) +{ + unsigned long i, j; + + i = boot_cpu_data.bus_clock / (baud * 16); + j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud; + i -= 1; + j = (j + 1) >> 1; + + __sio_out(i, M32R_SIO0_BAUR_PORTL); + __sio_out(j, M32R_SIO0_RBAUR_PORTL); +} + +static void sio_reset(void) +{ + __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */ + __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */ + __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */ + sio_set_baud_rate(BAUD_RATE); + __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL); + __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */ +} + +static void sio_init(void) +{ + unsigned int tmp; + + tmp = __sio_in(M32R_SIO0_RXB_PORTL); + tmp = __sio_in(M32R_SIO0_RXB_PORTL); + tmp = __sio_in(M32R_SIO0_STS_PORTL); + __sio_out(0x00000003, M32R_SIO0_CR_PORTL); +} + +static void sio_error(int *status) +{ + printk("SIO0 error[%04x]\n", *status); + do { + sio_init(); + } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3); +} + +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +static unsigned int sio_in(struct uart_sio_port *up, int offset) +{ + return __sio_in(up->port.iobase + offset); +} + +static void sio_out(struct uart_sio_port *up, int offset, int value) +{ + __sio_out(value, up->port.iobase + offset); +} + +static unsigned int serial_in(struct uart_sio_port *up, int offset) +{ + if (!offset) + return 0; + + return __sio_in(offset); +} + +static void serial_out(struct uart_sio_port *up, int offset, int value) +{ + if (!offset) + return; + + __sio_out(value, offset); +} + +static void m32r_sio_stop_tx(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void m32r_sio_start_tx(struct uart_port *port) +{ +#ifdef CONFIG_SERIAL_M32R_PLDSIO + struct uart_sio_port *up = (struct uart_sio_port *)port; + struct circ_buf *xmit = &up->port.state->xmit; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + } + while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); +#else + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +#endif +} + +static void m32r_sio_stop_rx(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void m32r_sio_enable_ms(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void receive_chars(struct uart_sio_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch; + unsigned char flag; + int max_count = 256; + + do { + ch = sio_in(up, SIORXB); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= up->port.read_status_mask; + + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } + + if (*status & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) + tty_insert_flip_char(tty, ch, flag); + + if (*status & UART_LSR_OE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_sio_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { +#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */ + serial_out(up, UART_TX, up->port.x_char); +#endif + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + m32r_sio_stop_tx(&up->port); + return; + } + + count = up->port.fifosize; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + while (!(serial_in(up, UART_LSR) & UART_LSR_THRE)); + + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + m32r_sio_stop_tx(&up->port); +} + +/* + * This handles the interrupt from one port. + */ +static inline void m32r_sio_handle_port(struct uart_sio_port *up, + unsigned int status) +{ + DEBUG_INTR("status = %x...", status); + + if (status & 0x04) + receive_chars(up, &status); + if (status & 0x01) + transmit_chars(up); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0; + + DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO +// if (irq == PLD_IRQ_SIO0_SND) +// irq = PLD_IRQ_SIO0_RCV; +#else + if (irq == M32R_IRQ_SIO0_S) + irq = M32R_IRQ_SIO0_R; +#endif + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_sio_port *up; + unsigned int sts; + + up = list_entry(l, struct uart_sio_port, list); + + sts = sio_in(up, SIOSTS); + if (sts & 0x5) { + spin_lock(&up->port.lock); + m32r_sio_handle_port(up, sts); + spin_unlock(&up->port.lock); + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + if (sts & 0xe0) + sio_error(&sts); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_HANDLED; +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct uart_sio_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + int ret, irq_flags = 0; + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(up->port.irq, m32r_sio_interrupt, + irq_flags, "SIO0-RX", i); + ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt, + irq_flags, "SIO0-TX", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_sio_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) { + free_irq(up->port.irq, i); + free_irq(up->port.irq + 1, i); + } + + serial_do_unlink(i, up); +} + +/* + * This function is used to handle ports that do not have an interrupt. + */ +static void m32r_sio_timeout(unsigned long data) +{ + struct uart_sio_port *up = (struct uart_sio_port *)data; + unsigned int timeout; + unsigned int sts; + + sts = sio_in(up, SIOSTS); + if (sts & 0x5) { + spin_lock(&up->port.lock); + m32r_sio_handle_port(up, sts); + spin_unlock(&up->port.lock); + } + + timeout = up->port.timeout; + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + mod_timer(&up->timer, jiffies + timeout); +} + +static unsigned int m32r_sio_tx_empty(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int m32r_sio_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + +} + +static void m32r_sio_break_ctl(struct uart_port *port, int break_state) +{ + +} + +static int m32r_sio_startup(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + int retval; + + sio_init(); + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + * - M32R_SIO: 0x0c + * - M32R_PLDSIO: 0x04 + */ + up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + sio_out(up, SIOTRCR, up->ier); + + /* + * And clear the interrupt registers again for luck. + */ + sio_reset(); + + return 0; +} + +static void m32r_sio_shutdown(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + sio_out(up, SIOTRCR, 0); + + /* + * Disable break condition and FIFOs + */ + + sio_init(); + + if (!is_real_interrupt(up->port.irq)) + del_timer_sync(&up->timer); + else + serial_unlink_irq_chain(up); +} + +static unsigned int m32r_sio_get_divisor(struct uart_port *port, + unsigned int baud) +{ + return uart_get_divisor(port, baud); +} + +static void m32r_sio_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned char cval = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Ask the core to calculate the divisor for us. + */ +#ifdef CONFIG_SERIAL_M32R_PLDSIO + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); +#else + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); +#endif + quot = m32r_sio_get_divisor(port, baud); + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + sio_set_baud_rate(baud); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + up->lcr = cval; /* Save LCR */ + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void m32r_sio_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (up->pm) + up->pm(port, state, oldstate); +} + +/* + * Resource handling. This is complicated by the fact that resources + * depend on the port type. Maybe we should be claiming the standard + * 8250 ports, and then trying to get other resources as necessary? + */ +static int +m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; +#ifndef CONFIG_SERIAL_M32R_PLDSIO + unsigned long start; +#endif + int ret = 0; + + switch (up->port.iotype) { + case UPIO_MEM: + if (up->port.mapbase) { +#ifdef CONFIG_SERIAL_M32R_PLDSIO + *res = request_mem_region(up->port.mapbase, size, "serial"); +#else + start = up->port.mapbase; + *res = request_mem_region(start, size, "serial"); +#endif + if (!*res) + ret = -EBUSY; + } + break; + + case UPIO_PORT: + *res = request_region(up->port.iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static void m32r_sio_release_port(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long start, offset = 0, size = 0; + + size <<= up->port.regshift; + + switch (up->port.iotype) { + case UPIO_MEM: + if (up->port.mapbase) { + /* + * Unmap the area. + */ + iounmap(up->port.membase); + up->port.membase = NULL; + + start = up->port.mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << up->port.regshift); + } + break; + + case UPIO_PORT: + start = up->port.iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << up->port.regshift); + break; + + default: + break; + } +} + +static int m32r_sio_request_port(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + struct resource *res = NULL; + int ret = 0; + + ret = m32r_sio_request_std_resource(up, &res); + + /* + * If we have a mapbase, then request that as well. + */ + if (ret == 0 && up->port.flags & UPF_IOREMAP) { + int size = resource_size(res); + + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) + ret = -ENOMEM; + } + + if (ret < 0) { + if (res) + release_resource(res); + } + + return ret; +} + +static void m32r_sio_config_port(struct uart_port *port, int unused) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); + up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int +m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= nr_irqs || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type >= ARRAY_SIZE(uart_config)) + return -EINVAL; + return 0; +} + +static const char * +m32r_sio_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops m32r_sio_pops = { + .tx_empty = m32r_sio_tx_empty, + .set_mctrl = m32r_sio_set_mctrl, + .get_mctrl = m32r_sio_get_mctrl, + .stop_tx = m32r_sio_stop_tx, + .start_tx = m32r_sio_start_tx, + .stop_rx = m32r_sio_stop_rx, + .enable_ms = m32r_sio_enable_ms, + .break_ctl = m32r_sio_break_ctl, + .startup = m32r_sio_startup, + .shutdown = m32r_sio_shutdown, + .set_termios = m32r_sio_set_termios, + .pm = m32r_sio_pm, + .type = m32r_sio_type, + .release_port = m32r_sio_release_port, + .request_port = m32r_sio_request_port, + .config_port = m32r_sio_config_port, + .verify_port = m32r_sio_verify_port, +}; + +static struct uart_sio_port m32r_sio_ports[UART_NR]; + +static void __init m32r_sio_init_ports(void) +{ + struct uart_sio_port *up; + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.ops = &m32r_sio_pops; + } +} + +static void __init m32r_sio_register_ports(struct uart_driver *drv) +{ + int i; + + m32r_sio_init_ports(); + + for (i = 0; i < UART_NR; i++) { + struct uart_sio_port *up = &m32r_sio_ports[i]; + + up->port.line = i; + up->port.ops = &m32r_sio_pops; + init_timer(&up->timer); + up->timer.function = m32r_sio_timeout; + + up->mcr_mask = ~0; + up->mcr_force = 0; + + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_sio_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = sio_in(up, SIOSTS); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & UART_EMPTY) != UART_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) + udelay(1); + } +} + +static void m32r_sio_console_putchar(struct uart_port *port, int ch) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + wait_for_xmitr(up); + sio_out(up, SIOTXB, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void m32r_sio_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_sio_port *up = &m32r_sio_ports[co->index]; + unsigned int ier; + + /* + * First save the UER then disable the interrupts + */ + ier = sio_in(up, SIOTRCR); + sio_out(up, SIOTRCR, 0); + + uart_console_write(&up->port, s, count, m32r_sio_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, SIOTRCR, ier); +} + +static int __init m32r_sio_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &m32r_sio_ports[co->index].port; + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver m32r_sio_reg; +static struct console m32r_sio_console = { + .name = "ttyS", + .write = m32r_sio_console_write, + .device = uart_console_device, + .setup = m32r_sio_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &m32r_sio_reg, +}; + +static int __init m32r_sio_console_init(void) +{ + sio_reset(); + sio_init(); + m32r_sio_init_ports(); + register_console(&m32r_sio_console); + return 0; +} +console_initcall(m32r_sio_console_init); + +#define M32R_SIO_CONSOLE &m32r_sio_console +#else +#define M32R_SIO_CONSOLE NULL +#endif + +static struct uart_driver m32r_sio_reg = { + .owner = THIS_MODULE, + .driver_name = "sio", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = UART_NR, + .cons = M32R_SIO_CONSOLE, +}; + +/** + * m32r_sio_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void m32r_sio_suspend_port(int line) +{ + uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); +} + +/** + * m32r_sio_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void m32r_sio_resume_port(int line) +{ + uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); +} + +static int __init m32r_sio_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: M32R SIO driver\n"); + + for (i = 0; i < nr_irqs; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = uart_register_driver(&m32r_sio_reg); + if (ret >= 0) + m32r_sio_register_ports(&m32r_sio_reg); + + return ret; +} + +static void __exit m32r_sio_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port); + + uart_unregister_driver(&m32r_sio_reg); +} + +module_init(m32r_sio_init); +module_exit(m32r_sio_exit); + +EXPORT_SYMBOL(m32r_sio_suspend_port); +EXPORT_SYMBOL(m32r_sio_resume_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/tty/serial/8250/m32r_sio.h b/drivers/tty/serial/8250/m32r_sio.h new file mode 100644 index 0000000..e9b7e11 --- /dev/null +++ b/drivers/tty/serial/8250/m32r_sio.h @@ -0,0 +1,48 @@ +/* + * m32r_sio.h + * + * Driver for M32R serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.h. + * + * Copyright (C) 2001 Russell King. + * Copyright (C) 2004 Hirokazu Takata + * + * 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. + */ + + +struct m32r_sio_probe { + struct module *owner; + int (*pci_init_one)(struct pci_dev *dev); + void (*pci_remove_one)(struct pci_dev *dev); + void (*pnp_init)(void); +}; + +int m32r_sio_register_probe(struct m32r_sio_probe *probe); +void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); +void m32r_sio_get_irq_map(unsigned int *map); +void m32r_sio_suspend_port(int line); +void m32r_sio_resume_port(int line); + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char io_type; + unsigned char __iomem *iomem_base; + unsigned short iomem_reg_shift; +}; + +#define _INLINE_ inline + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/tty/serial/8250/m32r_sio_reg.h b/drivers/tty/serial/8250/m32r_sio_reg.h new file mode 100644 index 0000000..4671473 --- /dev/null +++ b/drivers/tty/serial/8250/m32r_sio_reg.h @@ -0,0 +1,152 @@ +/* + * m32r_sio_reg.h + * + * Copyright (C) 1992, 1994 by Theodore Ts'o. + * Copyright (C) 2004 Hirokazu Takata + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + * + * These are the UART port assignments, expressed as offsets from the base + * register. These assignments should hold for any serial port based on + * a 8250, 16450, or 16550(A). + */ + +#ifndef _M32R_SIO_REG_H +#define _M32R_SIO_REG_H + + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + +#define SIOCR 0x000 +#define SIOMOD0 0x002 +#define SIOMOD1 0x004 +#define SIOSTS 0x006 +#define SIOTRCR 0x008 +#define SIOBAUR 0x00a +// #define SIORBAUR 0x018 +#define SIOTXB 0x00c +#define SIORXB 0x00e + +#define UART_RX ((unsigned long) PLD_ESIO0RXB) + /* In: Receive buffer (DLAB=0) */ +#define UART_TX ((unsigned long) PLD_ESIO0TXB) + /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER ((unsigned long) PLD_ESIO0INTCR) + /* Out: Interrupt Enable Register */ +#define UART_FCTR 0 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 0 /* In: Interrupt ID Register */ +#define UART_FCR 0 /* Out: FIFO Control Register */ +#define UART_EFR 0 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 0 /* Out: Line Control Register */ +#define UART_MCR 0 /* Out: Modem Control Register */ +#define UART_LSR ((unsigned long) PLD_ESIO0STS) + /* In: Line Status Register */ +#define UART_MSR 0 /* In: Modem Status Register */ +#define UART_SCR 0 /* I/O: Scratch Register */ +#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +#else /* not CONFIG_SERIAL_M32R_PLDSIO */ + +#define SIOCR 0x000 +#define SIOMOD0 0x004 +#define SIOMOD1 0x008 +#define SIOSTS 0x00c +#define SIOTRCR 0x010 +#define SIOBAUR 0x014 +#define SIORBAUR 0x018 +#define SIOTXB 0x01c +#define SIORXB 0x020 + +#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */ +#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */ +#define UART_FCTR 0 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 0 /* In: Interrupt ID Register */ +#define UART_FCR 0 /* Out: FIFO Control Register */ +#define UART_EFR 0 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 0 /* Out: Line Control Register */ +#define UART_MCR 0 /* Out: Modem Control Register */ +#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */ +#define UART_MSR 0 /* In: Modem Status Register */ +#define UART_SCR 0 /* I/O: Scratch Register */ +#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * These are the definitions for the Line Control Register + * + * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting + * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +/* + * These are the definitions for the Line Status Register + */ +#define UART_LSR_TEMT 0x02 /* Transmitter empty */ +#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x00 /* Break interrupt indicator */ +#define UART_LSR_FE 0x80 /* Frame error indicator */ +#define UART_LSR_PE 0x40 /* Parity error indicator */ +#define UART_LSR_OE 0x20 /* Overrun error indicator */ +#define UART_LSR_DR 0x04 /* Receiver data ready */ + +/* + * These are the definitions for the Interrupt Identification Register + */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ + +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +/* + * These are the definitions for the Interrupt Enable Register + */ +#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */ + +#endif /* _M32R_SIO_REG_H */ diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c new file mode 100644 index 0000000..8609060 --- /dev/null +++ b/drivers/tty/serial/8250/serial_cs.c @@ -0,0 +1,870 @@ +/*====================================================================== + + A driver for PCMCIA serial devices + + serial_cs.c 1.134 2002/05/04 05:48:53 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "8250.h" + + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Enable the speaker? */ +static int do_sound = 1; +/* Skip strict UART tests? */ +static int buggy_uart; + +module_param(do_sound, int, 0444); +module_param(buggy_uart, int, 0444); + +/*====================================================================*/ + +/* Table of multi-port card ID's */ + +struct serial_quirk { + unsigned int manfid; + unsigned int prodid; + int multi; /* 1 = multifunction, > 1 = # ports */ + void (*config)(struct pcmcia_device *); + void (*setup)(struct pcmcia_device *, struct uart_port *); + void (*wakeup)(struct pcmcia_device *); + int (*post)(struct pcmcia_device *); +}; + +struct serial_info { + struct pcmcia_device *p_dev; + int ndev; + int multi; + int slave; + int manfid; + int prodid; + int c950ctrl; + int line[4]; + const struct serial_quirk *quirk; +}; + +struct serial_cfg_mem { + tuple_t tuple; + cisparse_t parse; + u_char buf[256]; +}; + +/* + * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" + * manfid 0x0160, 0x0104 + * This card appears to have a 14.7456MHz clock. + */ +/* Generic Modem: MD55x (GPRS/EDGE) have + * Elan VPU16551 UART with 14.7456MHz oscillator + * manfid 0x015D, 0x4C45 + */ +static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) +{ + port->uartclk = 14745600; +} + +static int quirk_post_ibm(struct pcmcia_device *link) +{ + u8 val; + int ret; + + ret = pcmcia_read_config_byte(link, 0x800, &val); + if (ret) + goto failed; + + ret = pcmcia_write_config_byte(link, 0x800, val | 1); + if (ret) + goto failed; + return 0; + + failed: + return -ENODEV; +} + +/* + * Nokia cards are not really multiport cards. Shouldn't this + * be handled by setting the quirk entry .multi = 0 | 1 ? + */ +static void quirk_config_nokia(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->multi > 1) + info->multi = 1; +} + +static void quirk_wakeup_oxsemi(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->c950ctrl) + outb(12, info->c950ctrl + 1); +} + +/* request_region? oxsemi branch does no request_region too... */ +/* + * This sequence is needed to properly initialize MC45 attached to OXCF950. + * I tried decreasing these msleep()s, but it worked properly (survived + * 1000 stop/start operations) with these timeouts (or bigger). + */ +static void quirk_wakeup_possio_gcc(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + unsigned int ctrl = info->c950ctrl; + + outb(0xA, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(300); + outb(0xC, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(200); + outb(0xF, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(100); + outb(0xC, ctrl + 1); +} + +/* + * Socket Dual IO: this enables irq's for second port + */ +static void quirk_config_socket(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->multi) + link->config_flags |= CONF_ENABLE_ESR; +} + +static const struct serial_quirk quirks[] = { + { + .manfid = 0x0160, + .prodid = 0x0104, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { + .manfid = 0x015D, + .prodid = 0x4C45, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { + .manfid = MANFID_IBM, + .prodid = ~0, + .multi = -1, + .post = quirk_post_ibm, + }, { + .manfid = MANFID_INTEL, + .prodid = PRODID_INTEL_DUAL_RS232, + .multi = 2, + }, { + .manfid = MANFID_NATINST, + .prodid = PRODID_NATINST_QUAD_RS232, + .multi = 4, + }, { + .manfid = MANFID_NOKIA, + .prodid = ~0, + .multi = -1, + .config = quirk_config_nokia, + }, { + .manfid = MANFID_OMEGA, + .prodid = PRODID_OMEGA_QSP_100, + .multi = 4, + }, { + .manfid = MANFID_OXSEMI, + .prodid = ~0, + .multi = -1, + .wakeup = quirk_wakeup_oxsemi, + }, { + .manfid = MANFID_POSSIO, + .prodid = PRODID_POSSIO_GCC, + .multi = -1, + .wakeup = quirk_wakeup_possio_gcc, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_D1, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_G, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_QUAD_RS232, + .multi = 4, + }, { + .manfid = MANFID_SOCKET, + .prodid = PRODID_SOCKET_DUAL_RS232, + .multi = 2, + .config = quirk_config_socket, + }, { + .manfid = MANFID_SOCKET, + .prodid = ~0, + .multi = -1, + .config = quirk_config_socket, + } +}; + + +static int serial_config(struct pcmcia_device * link); + + +static void serial_remove(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + dev_dbg(&link->dev, "serial_release\n"); + + /* + * Recheck to see if the device is still configured. + */ + for (i = 0; i < info->ndev; i++) + serial8250_unregister_port(info->line[i]); + + if (!info->slave) + pcmcia_disable_device(link); +} + +static int serial_suspend(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + for (i = 0; i < info->ndev; i++) + serial8250_suspend_port(info->line[i]); + + return 0; +} + +static int serial_resume(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + for (i = 0; i < info->ndev; i++) + serial8250_resume_port(info->line[i]); + + if (info->quirk && info->quirk->wakeup) + info->quirk->wakeup(link); + + return 0; +} + +static int serial_probe(struct pcmcia_device *link) +{ + struct serial_info *info; + + dev_dbg(&link->dev, "serial_attach()\n"); + + /* Create new serial device */ + info = kzalloc(sizeof (*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->p_dev = link; + link->priv = info; + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + if (do_sound) + link->config_flags |= CONF_ENABLE_SPKR; + + return serial_config(link); +} + +static void serial_detach(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + dev_dbg(&link->dev, "serial_detach\n"); + + /* + * Ensure that the ports have been released. + */ + serial_remove(link); + + /* free bits */ + kfree(info); +} + +/*====================================================================*/ + +static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, + unsigned int iobase, int irq) +{ + struct uart_port port; + int line; + + memset(&port, 0, sizeof (struct uart_port)); + port.iobase = iobase; + port.irq = irq; + port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; + port.uartclk = 1843200; + port.dev = &handle->dev; + if (buggy_uart) + port.flags |= UPF_BUGGY_UART; + + if (info->quirk && info->quirk->setup) + info->quirk->setup(handle, &port); + + line = serial8250_register_port(&port); + if (line < 0) { + printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " + "0x%04lx, irq %d failed\n", (u_long)iobase, irq); + return -EINVAL; + } + + info->line[info->ndev] = line; + info->ndev++; + + return 0; +} + +/*====================================================================*/ + +static int pfc_config(struct pcmcia_device *p_dev) +{ + unsigned int port = 0; + struct serial_info *info = p_dev->priv; + + if ((p_dev->resource[1]->end != 0) && + (resource_size(p_dev->resource[1]) == 8)) { + port = p_dev->resource[1]->start; + info->slave = 1; + } else if ((info->manfid == MANFID_OSITECH) && + (resource_size(p_dev->resource[0]) == 0x40)) { + port = p_dev->resource[0]->start + 0x28; + info->slave = 1; + } + if (info->slave) + return setup_serial(p_dev, info, port, p_dev->irq); + + dev_warn(&p_dev->dev, "no usable port range found, giving up\n"); + return -ENODEV; +} + +static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + static const int size_table[2] = { 8, 16 }; + int *try = priv_data; + + if (p_dev->resource[0]->start == 0) + return -ENODEV; + + if ((*try & 0x1) == 0) + p_dev->io_lines = 16; + + if (p_dev->resource[0]->end != size_table[(*try >> 1)]) + return -ENODEV; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); +} + +static int simple_config_check_notpicky(struct pcmcia_device *p_dev, + void *priv_data) +{ + static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + int j; + + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; + } + return -ENODEV; +} + +static int simple_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i = -ENODEV, try; + + /* First pass: look for a config entry that looks normal. + * Two tries: without IO aliases, then with aliases */ + link->config_flags |= CONF_AUTO_SET_VPP; + for (try = 0; try < 4; try++) + if (!pcmcia_loop_config(link, simple_config_check, &try)) + goto found_port; + + /* Second pass: try to find an entry that isn't picky about + its base address, then try to grab any standard serial port + address, and finally try to get any free port. */ + if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) + goto found_port; + + dev_warn(&link->dev, "no usable port range found, giving up\n"); + return -1; + +found_port: + if (info->multi && (info->manfid == MANFID_3COM)) + link->config_index &= ~(0x08); + + /* + * Apply any configuration quirks. + */ + if (info->quirk && info->quirk->config) + info->quirk->config(link); + + i = pcmcia_enable_device(link); + if (i != 0) + return -1; + return setup_serial(link, info, link->resource[0]->start, link->irq); +} + +static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + int *multi = priv_data; + + if (p_dev->resource[1]->end) + return -EINVAL; + + /* The quad port cards have bad CIS's, so just look for a + window larger than 8 ports and assume it will be right */ + if (p_dev->resource[0]->end <= 8) + return -EINVAL; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = *multi * 8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + return 0; +} + +static int multi_config_check_notpicky(struct pcmcia_device *p_dev, + void *priv_data) +{ + int *base2 = priv_data; + + if (!p_dev->resource[0]->end || !p_dev->resource[1]->end || + p_dev->resource[0]->start + 8 != p_dev->resource[1]->start) + return -ENODEV; + + p_dev->resource[0]->end = p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + + *base2 = p_dev->resource[0]->start + 8; + return 0; +} + +static int multi_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i, base2 = 0; + + /* First, look for a generic full-sized window */ + if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) + base2 = link->resource[0]->start + 8; + else { + /* If that didn't work, look for two windows */ + info->multi = 2; + if (pcmcia_loop_config(link, multi_config_check_notpicky, + &base2)) { + dev_warn(&link->dev, "no usable port range " + "found, giving up\n"); + return -ENODEV; + } + } + + if (!link->irq) + dev_warn(&link->dev, "no usable IRQ found, continuing...\n"); + + /* + * Apply any configuration quirks. + */ + if (info->quirk && info->quirk->config) + info->quirk->config(link); + + i = pcmcia_enable_device(link); + if (i != 0) + return -ENODEV; + + /* The Oxford Semiconductor OXCF950 cards are in fact single-port: + * 8 registers are for the UART, the others are extra registers. + * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. + */ + if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO && + info->prodid == PRODID_POSSIO_GCC)) { + int err; + + if (link->config_index == 1 || + link->config_index == 3) { + err = setup_serial(link, info, base2, + link->irq); + base2 = link->resource[0]->start; + } else { + err = setup_serial(link, info, link->resource[0]->start, + link->irq); + } + info->c950ctrl = base2; + + /* + * FIXME: We really should wake up the port prior to + * handing it over to the serial layer. + */ + if (info->quirk && info->quirk->wakeup) + info->quirk->wakeup(link); + + return 0; + } + + setup_serial(link, info, link->resource[0]->start, link->irq); + for (i = 0; i < info->multi - 1; i++) + setup_serial(link, info, base2 + (8 * i), + link->irq); + return 0; +} + +static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) +{ + struct serial_info *info = p_dev->priv; + + if (!p_dev->resource[0]->end) + return -EINVAL; + + if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) + info->multi = p_dev->resource[0]->end >> 3; + + if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) + && (p_dev->resource[1]->end == 8)) + info->multi = 2; + + return 0; /* break */ +} + + +static int serial_config(struct pcmcia_device * link) +{ + struct serial_info *info = link->priv; + int i; + + dev_dbg(&link->dev, "serial_config\n"); + + /* Is this a compliant multifunction card? */ + info->multi = (link->socket->functions > 1); + + /* Is this a multiport card? */ + info->manfid = link->manf_id; + info->prodid = link->card_id; + + for (i = 0; i < ARRAY_SIZE(quirks); i++) + if ((quirks[i].manfid == ~0 || + quirks[i].manfid == info->manfid) && + (quirks[i].prodid == ~0 || + quirks[i].prodid == info->prodid)) { + info->quirk = &quirks[i]; + break; + } + + /* Another check for dual-serial cards: look for either serial or + multifunction cards that ask for appropriate IO port ranges */ + if ((info->multi == 0) && + (link->has_func_id) && + (link->socket->pcmcia_pfc == 0) && + ((link->func_id == CISTPL_FUNCID_MULTI) || + (link->func_id == CISTPL_FUNCID_SERIAL))) + pcmcia_loop_config(link, serial_check_for_multi, info); + + /* + * Apply any multi-port quirk. + */ + if (info->quirk && info->quirk->multi != -1) + info->multi = info->quirk->multi; + + dev_info(&link->dev, + "trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n", + link->manf_id, link->card_id, + link->socket->pcmcia_pfc, info->multi, info->quirk); + if (link->socket->pcmcia_pfc) + i = pfc_config(link); + else if (info->multi > 1) + i = multi_config(link); + else + i = simple_config(link); + + if (i || info->ndev == 0) + goto failed; + + /* + * Apply any post-init quirk. FIXME: This should really happen + * before we register the port, since it might already be in use. + */ + if (info->quirk && info->quirk->post) + if (info->quirk->post(link)) + goto failed; + + return 0; + +failed: + dev_warn(&link->dev, "failed to initialize\n"); + serial_remove(link); + return -ENODEV; +} + +static const struct pcmcia_device_id serial_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0b05), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020), + PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), + PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), + PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), + PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), + PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */ + PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), + PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), + PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e), + PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), + PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), + PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), + PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), + PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), + PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b), + PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), + PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb), + PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447), + PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), + PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), + PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41), + PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), + PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38), + PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ + PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), + PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), + PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b), + /* too generic */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */ + PCMCIA_DEVICE_FUNC_ID(2), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, serial_ids); + +MODULE_FIRMWARE("cis/PCMLM28.cis"); +MODULE_FIRMWARE("cis/DP83903.cis"); +MODULE_FIRMWARE("cis/3CCFEM556.cis"); +MODULE_FIRMWARE("cis/3CXEM556.cis"); +MODULE_FIRMWARE("cis/SW_8xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_7xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_555_SER.cis"); +MODULE_FIRMWARE("cis/MT5634ZLX.cis"); +MODULE_FIRMWARE("cis/COMpad2.cis"); +MODULE_FIRMWARE("cis/COMpad4.cis"); +MODULE_FIRMWARE("cis/RS-COM-2P.cis"); + +static struct pcmcia_driver serial_cs_driver = { + .owner = THIS_MODULE, + .name = "serial_cs", + .probe = serial_probe, + .remove = serial_detach, + .id_table = serial_ids, + .suspend = serial_suspend, + .resume = serial_resume, +}; + +static int __init init_serial_cs(void) +{ + return pcmcia_register_driver(&serial_cs_driver); +} + +static void __exit exit_serial_cs(void) +{ + pcmcia_unregister_driver(&serial_cs_driver); +} + +module_init(init_serial_cs); +module_exit(exit_serial_cs); + +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_accent.c b/drivers/tty/serial/8250_accent.c deleted file mode 100644 index 34b51c6..0000000 --- a/drivers/tty/serial/8250_accent.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } - -static struct plat_serial8250_port accent_data[] = { - PORT(0x330, 4), - PORT(0x338, 4), - { }, -}; - -static struct platform_device accent_device = { - .name = "serial8250", - .id = PLAT8250_DEV_ACCENT, - .dev = { - .platform_data = accent_data, - }, -}; - -static int __init accent_init(void) -{ - return platform_device_register(&accent_device); -} - -module_init(accent_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_acorn.c b/drivers/tty/serial/8250_acorn.c deleted file mode 100644 index b0ce8c5..0000000 --- a/drivers/tty/serial/8250_acorn.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * linux/drivers/serial/acorn.c - * - * Copyright (C) 1996-2003 Russell King. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "8250.h" - -#define MAX_PORTS 3 - -struct serial_card_type { - unsigned int num_ports; - unsigned int uartclk; - unsigned int type; - unsigned int offset[MAX_PORTS]; -}; - -struct serial_card_info { - unsigned int num_ports; - int ports[MAX_PORTS]; - void __iomem *vaddr; -}; - -static int __devinit -serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct serial_card_info *info; - struct serial_card_type *type = id->data; - struct uart_port port; - unsigned long bus_addr; - unsigned int i; - - info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->num_ports = type->num_ports; - - bus_addr = ecard_resource_start(ec, type->type); - info->vaddr = ecardm_iomap(ec, type->type, 0, 0); - if (!info->vaddr) { - kfree(info); - return -ENOMEM; - } - - ecard_set_drvdata(ec, info); - - memset(&port, 0, sizeof(struct uart_port)); - port.irq = ec->irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - port.uartclk = type->uartclk; - port.iotype = UPIO_MEM; - port.regshift = 2; - port.dev = &ec->dev; - - for (i = 0; i < info->num_ports; i ++) { - port.membase = info->vaddr + type->offset[i]; - port.mapbase = bus_addr + type->offset[i]; - - info->ports[i] = serial8250_register_port(&port); - } - - return 0; -} - -static void __devexit serial_card_remove(struct expansion_card *ec) -{ - struct serial_card_info *info = ecard_get_drvdata(ec); - int i; - - ecard_set_drvdata(ec, NULL); - - for (i = 0; i < info->num_ports; i++) - if (info->ports[i] > 0) - serial8250_unregister_port(info->ports[i]); - - kfree(info); -} - -static struct serial_card_type atomwide_type = { - .num_ports = 3, - .uartclk = 7372800, - .type = ECARD_RES_IOCSLOW, - .offset = { 0x2800, 0x2400, 0x2000 }, -}; - -static struct serial_card_type serport_type = { - .num_ports = 2, - .uartclk = 3686400, - .type = ECARD_RES_IOCSLOW, - .offset = { 0x2000, 0x2020 }, -}; - -static const struct ecard_id serial_cids[] = { - { MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, &atomwide_type }, - { MANU_SERPORT, PROD_SERPORT_DSPORT, &serport_type }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver serial_card_driver = { - .probe = serial_card_probe, - .remove = __devexit_p(serial_card_remove), - .id_table = serial_cids, - .drv = { - .name = "8250_acorn", - }, -}; - -static int __init serial_card_init(void) -{ - return ecard_register_driver(&serial_card_driver); -} - -static void __exit serial_card_exit(void) -{ - ecard_remove_driver(&serial_card_driver); -} - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); -MODULE_LICENSE("GPL"); - -module_init(serial_card_init); -module_exit(serial_card_exit); diff --git a/drivers/tty/serial/8250_boca.c b/drivers/tty/serial/8250_boca.c deleted file mode 100644 index d125dc1..0000000 --- a/drivers/tty/serial/8250_boca.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } - -static struct plat_serial8250_port boca_data[] = { - PORT(0x100, 12), - PORT(0x108, 12), - PORT(0x110, 12), - PORT(0x118, 12), - PORT(0x120, 12), - PORT(0x128, 12), - PORT(0x130, 12), - PORT(0x138, 12), - PORT(0x140, 12), - PORT(0x148, 12), - PORT(0x150, 12), - PORT(0x158, 12), - PORT(0x160, 12), - PORT(0x168, 12), - PORT(0x170, 12), - PORT(0x178, 12), - { }, -}; - -static struct platform_device boca_device = { - .name = "serial8250", - .id = PLAT8250_DEV_BOCA, - .dev = { - .platform_data = boca_data, - }, -}; - -static int __init boca_init(void) -{ - return platform_device_register(&boca_device); -} - -module_init(boca_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for Boca cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_dw.c b/drivers/tty/serial/8250_dw.c deleted file mode 100644 index f574eef..0000000 --- a/drivers/tty/serial/8250_dw.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Synopsys DesignWare 8250 driver. - * - * Copyright 2011 Picochip, Jamie Iles. - * - * 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. - * - * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the - * LCR is written whilst busy. If it is, then a busy detect interrupt is - * raised, the LCR needs to be rewritten and the uart status register read. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct dw8250_data { - int last_lcr; - int line; -}; - -static void dw8250_serial_out(struct uart_port *p, int offset, int value) -{ - struct dw8250_data *d = p->private_data; - - if (offset == UART_LCR) - d->last_lcr = value; - - offset <<= p->regshift; - writeb(value, p->membase + offset); -} - -static unsigned int dw8250_serial_in(struct uart_port *p, int offset) -{ - offset <<= p->regshift; - - return readb(p->membase + offset); -} - -static void dw8250_serial_out32(struct uart_port *p, int offset, int value) -{ - struct dw8250_data *d = p->private_data; - - if (offset == UART_LCR) - d->last_lcr = value; - - offset <<= p->regshift; - writel(value, p->membase + offset); -} - -static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) -{ - offset <<= p->regshift; - - return readl(p->membase + offset); -} - -/* Offset for the DesignWare's UART Status Register. */ -#define UART_USR 0x1f - -static int dw8250_handle_irq(struct uart_port *p) -{ - struct dw8250_data *d = p->private_data; - unsigned int iir = p->serial_in(p, UART_IIR); - - if (serial8250_handle_irq(p, iir)) { - return 1; - } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { - /* Clear the USR and write the LCR again. */ - (void)p->serial_in(p, UART_USR); - p->serial_out(p, d->last_lcr, UART_LCR); - - return 1; - } - - return 0; -} - -static int __devinit dw8250_probe(struct platform_device *pdev) -{ - struct uart_port port = {}; - struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - struct device_node *np = pdev->dev.of_node; - u32 val; - struct dw8250_data *data; - - if (!regs || !irq) { - dev_err(&pdev->dev, "no registers/irq defined\n"); - return -EINVAL; - } - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - port.private_data = data; - - spin_lock_init(&port.lock); - port.mapbase = regs->start; - port.irq = irq->start; - port.handle_irq = dw8250_handle_irq; - port.type = PORT_8250; - port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | - UPF_FIXED_PORT | UPF_FIXED_TYPE; - port.dev = &pdev->dev; - - port.iotype = UPIO_MEM; - port.serial_in = dw8250_serial_in; - port.serial_out = dw8250_serial_out; - if (!of_property_read_u32(np, "reg-io-width", &val)) { - switch (val) { - case 1: - break; - case 4: - port.iotype = UPIO_MEM32; - port.serial_in = dw8250_serial_in32; - port.serial_out = dw8250_serial_out32; - break; - default: - dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n", - val); - return -EINVAL; - } - } - - if (!of_property_read_u32(np, "reg-shift", &val)) - port.regshift = val; - - if (of_property_read_u32(np, "clock-frequency", &val)) { - dev_err(&pdev->dev, "no clock-frequency property set\n"); - return -EINVAL; - } - port.uartclk = val; - - data->line = serial8250_register_port(&port); - if (data->line < 0) - return data->line; - - platform_set_drvdata(pdev, data); - - return 0; -} - -static int __devexit dw8250_remove(struct platform_device *pdev) -{ - struct dw8250_data *data = platform_get_drvdata(pdev); - - serial8250_unregister_port(data->line); - - return 0; -} - -static const struct of_device_id dw8250_match[] = { - { .compatible = "snps,dw-apb-uart" }, - { /* Sentinel */ } -}; -MODULE_DEVICE_TABLE(of, dw8250_match); - -static struct platform_driver dw8250_platform_driver = { - .driver = { - .name = "dw-apb-uart", - .owner = THIS_MODULE, - .of_match_table = dw8250_match, - }, - .probe = dw8250_probe, - .remove = __devexit_p(dw8250_remove), -}; - -module_platform_driver(dw8250_platform_driver); - -MODULE_AUTHOR("Jamie Iles"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver"); diff --git a/drivers/tty/serial/8250_early.c b/drivers/tty/serial/8250_early.c deleted file mode 100644 index eaafb98..0000000 --- a/drivers/tty/serial/8250_early.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Early serial console for 8250/16550 devices - * - * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. - * Bjorn Helgaas - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, - * and on early_printk.c by Andi Kleen. - * - * This is for use before the serial driver has initialized, in - * particular, before the UARTs have been discovered and named. - * Instead of specifying the console device as, e.g., "ttyS0", - * we locate the device directly by its MMIO or I/O port address. - * - * The user can specify the device directly, e.g., - * earlycon=uart8250,io,0x3f8,9600n8 - * earlycon=uart8250,mmio,0xff5e0000,115200n8 - * earlycon=uart8250,mmio32,0xff5e0000,115200n8 - * or - * console=uart8250,io,0x3f8,9600n8 - * console=uart8250,mmio,0xff5e0000,115200n8 - * console=uart8250,mmio32,0xff5e0000,115200n8 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_FIX_EARLYCON_MEM -#include -#include -#endif - -struct early_serial8250_device { - struct uart_port port; - char options[16]; /* e.g., 115200n8 */ - unsigned int baud; -}; - -static struct early_serial8250_device early_device; - -static unsigned int __init serial_in(struct uart_port *port, int offset) -{ - switch (port->iotype) { - case UPIO_MEM: - return readb(port->membase + offset); - case UPIO_MEM32: - return readl(port->membase + (offset << 2)); - case UPIO_PORT: - return inb(port->iobase + offset); - default: - return 0; - } -} - -static void __init serial_out(struct uart_port *port, int offset, int value) -{ - switch (port->iotype) { - case UPIO_MEM: - writeb(value, port->membase + offset); - break; - case UPIO_MEM32: - writel(value, port->membase + (offset << 2)); - break; - case UPIO_PORT: - outb(value, port->iobase + offset); - break; - } -} - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static void __init wait_for_xmitr(struct uart_port *port) -{ - unsigned int status; - - for (;;) { - status = serial_in(port, UART_LSR); - if ((status & BOTH_EMPTY) == BOTH_EMPTY) - return; - cpu_relax(); - } -} - -static void __init serial_putc(struct uart_port *port, int c) -{ - wait_for_xmitr(port); - serial_out(port, UART_TX, c); -} - -static void __init early_serial8250_write(struct console *console, - const char *s, unsigned int count) -{ - struct uart_port *port = &early_device.port; - unsigned int ier; - - /* Save the IER and disable interrupts */ - ier = serial_in(port, UART_IER); - serial_out(port, UART_IER, 0); - - uart_console_write(port, s, count, serial_putc); - - /* Wait for transmitter to become empty and restore the IER */ - wait_for_xmitr(port); - serial_out(port, UART_IER, ier); -} - -static unsigned int __init probe_baud(struct uart_port *port) -{ - unsigned char lcr, dll, dlm; - unsigned int quot; - - lcr = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, lcr | UART_LCR_DLAB); - dll = serial_in(port, UART_DLL); - dlm = serial_in(port, UART_DLM); - serial_out(port, UART_LCR, lcr); - - quot = (dlm << 8) | dll; - return (port->uartclk / 16) / quot; -} - -static void __init init_port(struct early_serial8250_device *device) -{ - struct uart_port *port = &device->port; - unsigned int divisor; - unsigned char c; - - serial_out(port, UART_LCR, 0x3); /* 8n1 */ - serial_out(port, UART_IER, 0); /* no interrupt */ - serial_out(port, UART_FCR, 0); /* no fifo */ - serial_out(port, UART_MCR, 0x3); /* DTR + RTS */ - - divisor = port->uartclk / (16 * device->baud); - c = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, c | UART_LCR_DLAB); - serial_out(port, UART_DLL, divisor & 0xff); - serial_out(port, UART_DLM, (divisor >> 8) & 0xff); - serial_out(port, UART_LCR, c & ~UART_LCR_DLAB); -} - -static int __init parse_options(struct early_serial8250_device *device, - char *options) -{ - struct uart_port *port = &device->port; - int mmio, mmio32, length; - - if (!options) - return -ENODEV; - - port->uartclk = BASE_BAUD * 16; - - mmio = !strncmp(options, "mmio,", 5); - mmio32 = !strncmp(options, "mmio32,", 7); - if (mmio || mmio32) { - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); - port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), - &options, 0); - if (mmio32) - port->regshift = 2; -#ifdef CONFIG_FIX_EARLYCON_MEM - set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, - port->mapbase & PAGE_MASK); - port->membase = - (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); - port->membase += port->mapbase & ~PAGE_MASK; -#else - port->membase = ioremap_nocache(port->mapbase, 64); - if (!port->membase) { - printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", - __func__, - (unsigned long long) port->mapbase); - return -ENOMEM; - } -#endif - } else if (!strncmp(options, "io,", 3)) { - port->iotype = UPIO_PORT; - port->iobase = simple_strtoul(options + 3, &options, 0); - mmio = 0; - } else - return -EINVAL; - - options = strchr(options, ','); - if (options) { - options++; - device->baud = simple_strtoul(options, NULL, 0); - length = min(strcspn(options, " "), sizeof(device->options)); - strncpy(device->options, options, length); - } else { - device->baud = probe_baud(port); - snprintf(device->options, sizeof(device->options), "%u", - device->baud); - } - - if (mmio || mmio32) - printk(KERN_INFO - "Early serial console at MMIO%s 0x%llx (options '%s')\n", - mmio32 ? "32" : "", - (unsigned long long)port->mapbase, - device->options); - else - printk(KERN_INFO - "Early serial console at I/O port 0x%lx (options '%s')\n", - port->iobase, - device->options); - - return 0; -} - -static struct console early_serial8250_console __initdata = { - .name = "uart", - .write = early_serial8250_write, - .flags = CON_PRINTBUFFER | CON_BOOT, - .index = -1, -}; - -static int __init early_serial8250_setup(char *options) -{ - struct early_serial8250_device *device = &early_device; - int err; - - if (device->port.membase || device->port.iobase) - return 0; - - err = parse_options(device, options); - if (err < 0) - return err; - - init_port(device); - return 0; -} - -int __init setup_early_serial8250_console(char *cmdline) -{ - char *options; - int err; - - options = strstr(cmdline, "uart8250,"); - if (!options) { - options = strstr(cmdline, "uart,"); - if (!options) - return 0; - } - - options = strchr(cmdline, ',') + 1; - err = early_serial8250_setup(options); - if (err < 0) - return err; - - register_console(&early_serial8250_console); - - return 0; -} - -int serial8250_find_port_for_earlycon(void) -{ - struct early_serial8250_device *device = &early_device; - struct uart_port *port = &device->port; - int line; - int ret; - - if (!device->port.membase && !device->port.iobase) - return -ENODEV; - - line = serial8250_find_port(port); - if (line < 0) - return -ENODEV; - - ret = update_console_cmdline("uart", 8250, - "ttyS", line, device->options); - if (ret < 0) - ret = update_console_cmdline("uart", 0, - "ttyS", line, device->options); - - return ret; -} - -early_param("earlycon", setup_early_serial8250_console); diff --git a/drivers/tty/serial/8250_exar_st16c554.c b/drivers/tty/serial/8250_exar_st16c554.c deleted file mode 100644 index bf53aab..0000000 --- a/drivers/tty/serial/8250_exar_st16c554.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com > - * Based on 8250_boca. - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } - -static struct plat_serial8250_port exar_data[] = { - PORT(0x100, 5), - PORT(0x108, 5), - PORT(0x110, 5), - PORT(0x118, 5), - { }, -}; - -static struct platform_device exar_device = { - .name = "serial8250", - .id = PLAT8250_DEV_EXAR_ST16C554, - .dev = { - .platform_data = exar_data, - }, -}; - -static int __init exar_init(void) -{ - return platform_device_register(&exar_device); -} - -module_init(exar_init); - -MODULE_AUTHOR("Paul B Schroeder"); -MODULE_DESCRIPTION("8250 serial probe module for Exar cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_fourport.c b/drivers/tty/serial/8250_fourport.c deleted file mode 100644 index be15826..0000000 --- a/drivers/tty/serial/8250_fourport.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \ - } - -static struct plat_serial8250_port fourport_data[] = { - PORT(0x1a0, 9), - PORT(0x1a8, 9), - PORT(0x1b0, 9), - PORT(0x1b8, 9), - PORT(0x2a0, 5), - PORT(0x2a8, 5), - PORT(0x2b0, 5), - PORT(0x2b8, 5), - { }, -}; - -static struct platform_device fourport_device = { - .name = "serial8250", - .id = PLAT8250_DEV_FOURPORT, - .dev = { - .platform_data = fourport_data, - }, -}; - -static int __init fourport_init(void) -{ - return platform_device_register(&fourport_device); -} - -module_init(fourport_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_fsl.c b/drivers/tty/serial/8250_fsl.c deleted file mode 100644 index f4d3c47..0000000 --- a/drivers/tty/serial/8250_fsl.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - -#include "8250.h" - -/* - * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This isn't a full driver; it just provides an alternate IRQ - * handler to deal with an errata. Everything else is just - * using the bog standard 8250 support. - * - * We follow code flow of serial8250_default_handle_irq() but add - * a check for a break and insert a dummy read on the Rx for the - * immediately following IRQ event. - * - * We re-use the already existing "bug handling" lsr_saved_flags - * field to carry the "what we just did" information from the one - * IRQ event to the next one. - */ - -int fsl8250_handle_irq(struct uart_port *port) -{ - unsigned char lsr, orig_lsr; - unsigned long flags; - unsigned int iir; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - spin_lock_irqsave(&up->port.lock, flags); - - iir = port->serial_in(port, UART_IIR); - if (iir & UART_IIR_NO_INT) { - spin_unlock_irqrestore(&up->port.lock, flags); - return 0; - } - - /* This is the WAR; if last event was BRK, then read and return */ - if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { - up->lsr_saved_flags &= ~UART_LSR_BI; - port->serial_in(port, UART_RX); - spin_unlock_irqrestore(&up->port.lock, flags); - return 1; - } - - lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); - - if (lsr & (UART_LSR_DR | UART_LSR_BI)) - lsr = serial8250_rx_chars(up, lsr); - - serial8250_modem_status(up); - - if (lsr & UART_LSR_THRE) - serial8250_tx_chars(up); - - up->lsr_saved_flags = orig_lsr; - spin_unlock_irqrestore(&up->port.lock, flags); - return 1; -} diff --git a/drivers/tty/serial/8250_gsc.c b/drivers/tty/serial/8250_gsc.c deleted file mode 100644 index d8c0ffb..0000000 --- a/drivers/tty/serial/8250_gsc.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Serial Device Initialisation for Lasi/Asp/Wax/Dino - * - * (c) Copyright Matthew Wilcox 2001-2002 - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "8250.h" - -static int __init serial_init_chip(struct parisc_device *dev) -{ - struct uart_port port; - unsigned long address; - int err; - - if (!dev->irq) { - /* We find some unattached serial ports by walking native - * busses. These should be silently ignored. Otherwise, - * what we have here is a missing parent device, so tell - * the user what they're missing. - */ - if (parisc_parent(dev)->id.hw_type != HPHW_IOA) - printk(KERN_INFO - "Serial: device 0x%llx not configured.\n" - "Enable support for Wax, Lasi, Asp or Dino.\n", - (unsigned long long)dev->hpa.start); - return -ENODEV; - } - - address = dev->hpa.start; - if (dev->id.sversion != 0x8d) - address += 0x800; - - memset(&port, 0, sizeof(port)); - port.iotype = UPIO_MEM; - /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - port.uartclk = 7272727; - port.mapbase = address; - port.membase = ioremap_nocache(address, 16); - port.irq = dev->irq; - port.flags = UPF_BOOT_AUTOCONF; - port.dev = &dev->dev; - - err = serial8250_register_port(&port); - if (err < 0) { - printk(KERN_WARNING - "serial8250_register_port returned error %d\n", err); - iounmap(port.membase); - return err; - } - - return 0; -} - -static struct parisc_device_id serial_tbl[] = { - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, - { 0 } -}; - -/* Hack. Some machines have SERIAL_0 attached to Lasi and SERIAL_1 - * attached to Dino. Unfortunately, Dino appears before Lasi in the device - * tree. To ensure that ttyS0 == SERIAL_0, we register two drivers; one - * which only knows about Lasi and then a second which will find all the - * other serial ports. HPUX ignores this problem. - */ -static struct parisc_device_id lasi_tbl[] = { - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */ - { 0 } -}; - - -MODULE_DEVICE_TABLE(parisc, serial_tbl); - -static struct parisc_driver lasi_driver = { - .name = "serial_1", - .id_table = lasi_tbl, - .probe = serial_init_chip, -}; - -static struct parisc_driver serial_driver = { - .name = "serial", - .id_table = serial_tbl, - .probe = serial_init_chip, -}; - -static int __init probe_serial_gsc(void) -{ - register_parisc_driver(&lasi_driver); - register_parisc_driver(&serial_driver); - return 0; -} - -module_init(probe_serial_gsc); - -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_hp300.c b/drivers/tty/serial/8250_hp300.c deleted file mode 100644 index c13438c..0000000 --- a/drivers/tty/serial/8250_hp300.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Driver for the 98626/98644/internal serial interface on hp300/hp400 - * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) - * - * Ported from 2.2 and modified to use the normal 8250 driver - * by Kars de Jong , May 2004. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "8250.h" - -#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) -#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? -#endif - -#ifdef CONFIG_HPAPCI -struct hp300_port -{ - struct hp300_port *next; /* next port */ - int line; /* line (tty) number */ -}; - -static struct hp300_port *hp300_ports; -#endif - -#ifdef CONFIG_HPDCA - -static int __devinit hpdca_init_one(struct dio_dev *d, - const struct dio_device_id *ent); -static void __devexit hpdca_remove_one(struct dio_dev *d); - -static struct dio_device_id hpdca_dio_tbl[] = { - { DIO_ID_DCA0 }, - { DIO_ID_DCA0REM }, - { DIO_ID_DCA1 }, - { DIO_ID_DCA1REM }, - { 0 } -}; - -static struct dio_driver hpdca_driver = { - .name = "hpdca", - .id_table = hpdca_dio_tbl, - .probe = hpdca_init_one, - .remove = __devexit_p(hpdca_remove_one), -}; - -#endif - -static unsigned int num_ports; - -extern int hp300_uart_scode; - -/* Offset to UART registers from base of DCA */ -#define UART_OFFSET 17 - -#define DCA_ID 0x01 /* ID (read), reset (write) */ -#define DCA_IC 0x03 /* Interrupt control */ - -/* Interrupt control */ -#define DCA_IC_IE 0x80 /* Master interrupt enable */ - -#define HPDCA_BAUD_BASE 153600 - -/* Base address of the Frodo part */ -#define FRODO_BASE (0x41c000) - -/* - * Where we find the 8250-like APCI ports, and how far apart they are. - */ -#define FRODO_APCIBASE 0x0 -#define FRODO_APCISPACE 0x20 -#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE)) - -#define HPAPCI_BAUD_BASE 500400 - -#ifdef CONFIG_SERIAL_8250_CONSOLE -/* - * Parse the bootinfo to find descriptions for headless console and - * debug serial ports and register them with the 8250 driver. - * This function should be called before serial_console_init() is called - * to make sure the serial console will be available for use. IA-64 kernel - * calls this function from setup_arch() after the EFI and ACPI tables have - * been parsed. - */ -int __init hp300_setup_serial_console(void) -{ - int scode; - struct uart_port port; - - memset(&port, 0, sizeof(port)); - - if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX) - return 0; - - if (DIO_SCINHOLE(hp300_uart_scode)) - return 0; - - scode = hp300_uart_scode; - - /* Memory mapped I/O */ - port.iotype = UPIO_MEM; - port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; - port.type = PORT_UNKNOWN; - - /* Check for APCI console */ - if (scode == 256) { -#ifdef CONFIG_HPAPCI - printk(KERN_INFO "Serial console is HP APCI 1\n"); - - port.uartclk = HPAPCI_BAUD_BASE * 16; - port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); - port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); - port.regshift = 2; - add_preferred_console("ttyS", port.line, "9600n8"); -#else - printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); - return 0; -#endif - } else { -#ifdef CONFIG_HPDCA - unsigned long pa = dio_scodetophysaddr(scode); - if (!pa) - return 0; - - printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode); - - port.uartclk = HPDCA_BAUD_BASE * 16; - port.mapbase = (pa + UART_OFFSET); - port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); - port.regshift = 1; - port.irq = DIO_IPL(pa + DIO_VIRADDRBASE); - - /* Enable board-interrupts */ - out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); - - if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) - add_preferred_console("ttyS", port.line, "9600n8"); -#else - printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); - return 0; -#endif - } - - if (early_serial_setup(&port) < 0) - printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n"); - return 0; -} -#endif /* CONFIG_SERIAL_8250_CONSOLE */ - -#ifdef CONFIG_HPDCA -static int __devinit hpdca_init_one(struct dio_dev *d, - const struct dio_device_id *ent) -{ - struct uart_port port; - int line; - -#ifdef CONFIG_SERIAL_8250_CONSOLE - if (hp300_uart_scode == d->scode) { - /* Already got it. */ - return 0; - } -#endif - memset(&port, 0, sizeof(struct uart_port)); - - /* Memory mapped I/O */ - port.iotype = UPIO_MEM; - port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; - port.irq = d->ipl; - port.uartclk = HPDCA_BAUD_BASE * 16; - port.mapbase = (d->resource.start + UART_OFFSET); - port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); - port.regshift = 1; - port.dev = &d->dev; - line = serial8250_register_port(&port); - - if (line < 0) { - printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" - " irq %d failed\n", d->scode, port.irq); - return -ENOMEM; - } - - /* Enable board-interrupts */ - out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); - dio_set_drvdata(d, (void *)line); - - /* Reset the DCA */ - out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); - udelay(100); - - num_ports++; - - return 0; -} -#endif - -static int __init hp300_8250_init(void) -{ - static int called; -#ifdef CONFIG_HPAPCI - int line; - unsigned long base; - struct uart_port uport; - struct hp300_port *port; - int i; -#endif - if (called) - return -ENODEV; - called = 1; - - if (!MACH_IS_HP300) - return -ENODEV; - -#ifdef CONFIG_HPDCA - dio_register_driver(&hpdca_driver); -#endif -#ifdef CONFIG_HPAPCI - if (hp300_model < HP_400) { - if (!num_ports) - return -ENODEV; - return 0; - } - /* These models have the Frodo chip. - * Port 0 is reserved for the Apollo Domain keyboard. - * Port 1 is either the console or the DCA. - */ - for (i = 1; i < 4; i++) { - /* Port 1 is the console on a 425e, on other machines it's - * mapped to DCA. - */ -#ifdef CONFIG_SERIAL_8250_CONSOLE - if (i == 1) - continue; -#endif - - /* Create new serial device */ - port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - memset(&uport, 0, sizeof(struct uart_port)); - - base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); - - /* Memory mapped I/O */ - uport.iotype = UPIO_MEM; - uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ - | UPF_BOOT_AUTOCONF; - /* XXX - no interrupt support yet */ - uport.irq = 0; - uport.uartclk = HPAPCI_BAUD_BASE * 16; - uport.mapbase = base; - uport.membase = (char *)(base + DIO_VIRADDRBASE); - uport.regshift = 2; - - line = serial8250_register_port(&uport); - - if (line < 0) { - printk(KERN_NOTICE "8250_hp300: register_serial() APCI" - " %d irq %d failed\n", i, uport.irq); - kfree(port); - continue; - } - - port->line = line; - port->next = hp300_ports; - hp300_ports = port; - - num_ports++; - } -#endif - - /* Any boards found? */ - if (!num_ports) - return -ENODEV; - - return 0; -} - -#ifdef CONFIG_HPDCA -static void __devexit hpdca_remove_one(struct dio_dev *d) -{ - int line; - - line = (int) dio_get_drvdata(d); - if (d->resource.start) { - /* Disable board-interrupts */ - out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0); - } - serial8250_unregister_port(line); -} -#endif - -static void __exit hp300_8250_exit(void) -{ -#ifdef CONFIG_HPAPCI - struct hp300_port *port, *to_free; - - for (port = hp300_ports; port; ) { - serial8250_unregister_port(port->line); - to_free = port; - port = port->next; - kfree(to_free); - } - - hp300_ports = NULL; -#endif -#ifdef CONFIG_HPDCA - dio_unregister_driver(&hpdca_driver); -#endif -} - -module_init(hp300_8250_init); -module_exit(hp300_8250_exit); -MODULE_DESCRIPTION("HP DCA/APCI serial driver"); -MODULE_AUTHOR("Kars de Jong "); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_hub6.c b/drivers/tty/serial/8250_hub6.c deleted file mode 100644 index a5c778e..0000000 --- a/drivers/tty/serial/8250_hub6.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define HUB6(card,port) \ - { \ - .iobase = 0x302, \ - .irq = 3, \ - .uartclk = 1843200, \ - .iotype = UPIO_HUB6, \ - .flags = UPF_BOOT_AUTOCONF, \ - .hub6 = (card) << 6 | (port) << 3 | 1, \ - } - -static struct plat_serial8250_port hub6_data[] = { - HUB6(0, 0), - HUB6(0, 1), - HUB6(0, 2), - HUB6(0, 3), - HUB6(0, 4), - HUB6(0, 5), - HUB6(1, 0), - HUB6(1, 1), - HUB6(1, 2), - HUB6(1, 3), - HUB6(1, 4), - HUB6(1, 5), - { }, -}; - -static struct platform_device hub6_device = { - .name = "serial8250", - .id = PLAT8250_DEV_HUB6, - .dev = { - .platform_data = hub6_data, - }, -}; - -static int __init hub6_init(void) -{ - return platform_device_register(&hub6_device); -} - -module_init(hub6_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_mca.c b/drivers/tty/serial/8250_mca.c deleted file mode 100644 index d20abf0..0000000 --- a/drivers/tty/serial/8250_mca.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include - -/* - * FIXME: Should we be doing AUTO_IRQ here? - */ -#ifdef CONFIG_SERIAL_8250_DETECT_IRQ -#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ -#else -#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST -#endif - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = MCA_FLAGS, \ - } - -static struct plat_serial8250_port mca_data[] = { - PORT(0x3220, 3), - PORT(0x3228, 3), - PORT(0x4220, 3), - PORT(0x4228, 3), - PORT(0x5220, 3), - PORT(0x5228, 3), - { }, -}; - -static struct platform_device mca_device = { - .name = "serial8250", - .id = PLAT8250_DEV_MCA, - .dev = { - .platform_data = mca_data, - }, -}; - -static int __init mca_init(void) -{ - if (!MCA_bus) - return -ENODEV; - return platform_device_register(&mca_device); -} - -module_init(mca_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for MCA ports"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c deleted file mode 100644 index da2b0b0..0000000 --- a/drivers/tty/serial/8250_pci.c +++ /dev/null @@ -1,4223 +0,0 @@ -/* - * Probe module for 8250/16550-type PCI serial ports. - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "8250.h" - -#undef SERIAL_DEBUG_PCI - -/* - * init function returns: - * > 0 - number of ports - * = 0 - use board->num_ports - * < 0 - error - */ -struct pci_serial_quirk { - u32 vendor; - u32 device; - u32 subvendor; - u32 subdevice; - int (*probe)(struct pci_dev *dev); - int (*init)(struct pci_dev *dev); - int (*setup)(struct serial_private *, - const struct pciserial_board *, - struct uart_port *, int); - void (*exit)(struct pci_dev *dev); -}; - -#define PCI_NUM_BAR_RESOURCES 6 - -struct serial_private { - struct pci_dev *dev; - unsigned int nr; - void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; - struct pci_serial_quirk *quirk; - int line[0]; -}; - -static int pci_default_setup(struct serial_private*, - const struct pciserial_board*, struct uart_port*, int); - -static void moan_device(const char *str, struct pci_dev *dev) -{ - printk(KERN_WARNING - "%s: %s\n" - "Please send the output of lspci -vv, this\n" - "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" - "manufacturer and name of serial board or\n" - "modem board to rmk+serial@arm.linux.org.uk.\n", - pci_name(dev), str, dev->vendor, dev->device, - dev->subsystem_vendor, dev->subsystem_device); -} - -static int -setup_port(struct serial_private *priv, struct uart_port *port, - int bar, int offset, int regshift) -{ - struct pci_dev *dev = priv->dev; - unsigned long base, len; - - if (bar >= PCI_NUM_BAR_RESOURCES) - return -EINVAL; - - base = pci_resource_start(dev, bar); - - if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - len = pci_resource_len(dev, bar); - - if (!priv->remapped_bar[bar]) - priv->remapped_bar[bar] = ioremap_nocache(base, len); - if (!priv->remapped_bar[bar]) - return -ENOMEM; - - port->iotype = UPIO_MEM; - port->iobase = 0; - port->mapbase = base + offset; - port->membase = priv->remapped_bar[bar] + offset; - port->regshift = regshift; - } else { - port->iotype = UPIO_PORT; - port->iobase = base + offset; - port->mapbase = 0; - port->membase = NULL; - port->regshift = 0; - } - return 0; -} - -/* - * ADDI-DATA GmbH communication cards - */ -static int addidata_apci7800_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar = 0, offset = board->first_offset; - bar = FL_GET_BASE(board->flags); - - if (idx < 2) { - offset += idx * board->uart_offset; - } else if ((idx >= 2) && (idx < 4)) { - bar += 1; - offset += ((idx - 2) * board->uart_offset); - } else if ((idx >= 4) && (idx < 6)) { - bar += 2; - offset += ((idx - 4) * board->uart_offset); - } else if (idx >= 6) { - bar += 3; - offset += ((idx - 6) * board->uart_offset); - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * AFAVLAB uses a different mixture of BARs and offsets - * Not that ugly ;) -- HW - */ -static int -afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset; - - bar = FL_GET_BASE(board->flags); - if (idx < 4) - bar += idx; - else { - bar = 4; - offset += (idx - 4) * board->uart_offset; - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * HP's Remote Management Console. The Diva chip came in several - * different versions. N-class, L2000 and A500 have two Diva chips, each - * with 3 UARTs (the third UART on the second chip is unused). Superdome - * and Keystone have one Diva chip with 3 UARTs. Some later machines have - * one Diva chip, but it has been expanded to 5 UARTs. - */ -static int pci_hp_diva_init(struct pci_dev *dev) -{ - int rc = 0; - - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_TOSCA1: - case PCI_DEVICE_ID_HP_DIVA_HALFDOME: - case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - rc = 3; - break; - case PCI_DEVICE_ID_HP_DIVA_TOSCA2: - rc = 2; - break; - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - rc = 4; - break; - case PCI_DEVICE_ID_HP_DIVA_POWERBAR: - case PCI_DEVICE_ID_HP_DIVA_HURRICANE: - rc = 1; - break; - } - - return rc; -} - -/* - * HP's Diva chip puts the 4th/5th serial port further out, and - * some serial ports are supposed to be hidden on certain models. - */ -static int -pci_hp_diva_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int offset = board->first_offset; - unsigned int bar = FL_GET_BASE(board->flags); - - switch (priv->dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - if (idx == 3) - idx++; - break; - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - if (idx > 0) - idx++; - if (idx > 2) - idx++; - break; - } - if (idx > 2) - offset = 0x18; - - offset += idx * board->uart_offset; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * Added for EKF Intel i960 serial boards - */ -static int pci_inteli960ni_init(struct pci_dev *dev) -{ - unsigned long oldval; - - if (!(dev->subsystem_device & 0x1000)) - return -ENODEV; - - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void *)&oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return -ENODEV; - } - return 0; -} - -/* - * Some PCI serial cards using the PLX 9050 PCI interface chip require - * that the card interrupt be explicitly enabled or disabled. This - * seems to be mainly needed on card using the PLX which also use I/O - * mapped memory. - */ -static int pci_plx9050_init(struct pci_dev *dev) -{ - u8 irq_config; - void __iomem *p; - - if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar 0", dev); - return 0; - } - - irq_config = 0x41; - if (dev->vendor == PCI_VENDOR_ID_PANACOM || - dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS) - irq_config = 0x43; - - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the - * deep FIFOs - */ - irq_config = 0x5b; - /* - * enable/disable interrupts - */ - p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); - if (p == NULL) - return -ENOMEM; - writel(irq_config, p + 0x4c); - - /* - * Read the register back to ensure that it took effect. - */ - readl(p + 0x4c); - iounmap(p); - - return 0; -} - -static void __devexit pci_plx9050_exit(struct pci_dev *dev) -{ - u8 __iomem *p; - - if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) - return; - - /* - * disable interrupts - */ - p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); - if (p != NULL) { - writel(0, p + 0x4c); - - /* - * Read the register back to ensure that it took effect. - */ - readl(p + 0x4c); - iounmap(p); - } -} - -#define NI8420_INT_ENABLE_REG 0x38 -#define NI8420_INT_ENABLE_BIT 0x2000 - -static void __devexit pci_ni8420_exit(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return; - - /* Disable the CPU Interrupt */ - writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT), - p + NI8420_INT_ENABLE_REG); - iounmap(p); -} - - -/* MITE registers */ -#define MITE_IOWBSR1 0xc4 -#define MITE_IOWCR1 0xf4 -#define MITE_LCIMR1 0x08 -#define MITE_LCIMR2 0x10 - -#define MITE_LCIMR2_CLR_CPU_IE (1 << 30) - -static void __devexit pci_ni8430_exit(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return; - - /* Disable the CPU Interrupt */ - writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2); - iounmap(p); -} - -/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ -static int -sbs_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset; - - bar = 0; - - if (idx < 4) { - /* first four channels map to 0, 0x100, 0x200, 0x300 */ - offset += idx * board->uart_offset; - } else if (idx < 8) { - /* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */ - offset += idx * board->uart_offset + 0xC00; - } else /* we have only 8 ports on PMC-OCTALPRO */ - return 1; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* -* This does initialization for PMC OCTALPRO cards: -* maps the device memory, resets the UARTs (needed, bc -* if the module is removed and inserted again, the card -* is in the sleep mode) and enables global interrupt. -*/ - -/* global control register offset for SBS PMC-OctalPro */ -#define OCT_REG_CR_OFF 0x500 - -static int sbs_init(struct pci_dev *dev) -{ - u8 __iomem *p; - - p = pci_ioremap_bar(dev, 0); - - if (p == NULL) - return -ENOMEM; - /* Set bit-4 Control Register (UART RESET) in to reset the uarts */ - writeb(0x10, p + OCT_REG_CR_OFF); - udelay(50); - writeb(0x0, p + OCT_REG_CR_OFF); - - /* Set bit-2 (INTENABLE) of Control Register */ - writeb(0x4, p + OCT_REG_CR_OFF); - iounmap(p); - - return 0; -} - -/* - * Disables the global interrupt of PMC-OctalPro - */ - -static void __devexit sbs_exit(struct pci_dev *dev) -{ - u8 __iomem *p; - - p = pci_ioremap_bar(dev, 0); - /* FIXME: What if resource_len < OCT_REG_CR_OFF */ - if (p != NULL) - writeb(0, p + OCT_REG_CR_OFF); - iounmap(p); -} - -/* - * SIIG serial cards have an PCI interface chip which also controls - * the UART clocking frequency. Each UART can be clocked independently - * (except cards equipped with 4 UARTs) and initial clocking settings - * are stored in the EEPROM chip. It can cause problems because this - * version of serial driver doesn't support differently clocked UART's - * on single PCI card. To prevent this, initialization functions set - * high frequency clocking for all UART's on given card. It is safe (I - * hope) because it doesn't touch EEPROM settings to prevent conflicts - * with other OSes (like M$ DOS). - * - * SIIG support added by Andrey Panin , 10/1999 - * - * There is two family of SIIG serial cards with different PCI - * interface chip and different configuration methods: - * - 10x cards have control registers in IO and/or memory space; - * - 20x cards have control registers in standard PCI configuration space. - * - * Note: all 10x cards have PCI device ids 0x10.. - * all 20x cards have PCI device ids 0x20.. - * - * There are also Quartet Serial cards which use Oxford Semiconductor - * 16954 quad UART PCI chip clocked by 18.432 MHz quartz. - * - * Note: some SIIG cards are probed by the parport_serial object. - */ - -#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) - -static int pci_siig10x_init(struct pci_dev *dev) -{ - u16 data; - void __iomem *p; - - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; - } - - p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); - if (p == NULL) - return -ENOMEM; - - writew(readw(p + 0x28) & data, p + 0x28); - readw(p + 0x28); - iounmap(p); - return 0; -} - -#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) - -static int pci_siig20x_init(struct pci_dev *dev) -{ - u8 data; - - /* Change clock frequency for the first UART. */ - pci_read_config_byte(dev, 0x6f, &data); - pci_write_config_byte(dev, 0x6f, data & 0xef); - - /* If this card has 2 UART, we have to do the same with second UART. */ - if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || - ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { - pci_read_config_byte(dev, 0x73, &data); - pci_write_config_byte(dev, 0x73, data & 0xef); - } - return 0; -} - -static int pci_siig_init(struct pci_dev *dev) -{ - unsigned int type = dev->device & 0xff00; - - if (type == 0x1000) - return pci_siig10x_init(dev); - else if (type == 0x2000) - return pci_siig20x_init(dev); - - moan_device("Unknown SIIG card", dev); - return -ENODEV; -} - -static int pci_siig_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; - - if (idx > 3) { - bar = 4; - offset = (idx - 4) * 8; - } - - return setup_port(priv, port, bar, offset, 0); -} - -/* - * Timedia has an explosion of boards, and to avoid the PCI table from - * growing *huge*, we use this function to collapse some 70 entries - * in the PCI table into one, for sanity's and compactness's sake. - */ -static const unsigned short timedia_single_port[] = { - 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 -}; - -static const unsigned short timedia_dual_port[] = { - 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, - 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, - 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, - 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, - 0xD079, 0 -}; - -static const unsigned short timedia_quad_port[] = { - 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, - 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, - 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, - 0xB157, 0 -}; - -static const unsigned short timedia_eight_port[] = { - 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, - 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 -}; - -static const struct timedia_struct { - int num; - const unsigned short *ids; -} timedia_data[] = { - { 1, timedia_single_port }, - { 2, timedia_dual_port }, - { 4, timedia_quad_port }, - { 8, timedia_eight_port } -}; - -/* - * There are nearly 70 different Timedia/SUNIX PCI serial devices. Instead of - * listing them individually, this driver merely grabs them all with - * PCI_ANY_ID. Some of these devices, however, also feature a parallel port, - * and should be left free to be claimed by parport_serial instead. - */ -static int pci_timedia_probe(struct pci_dev *dev) -{ - /* - * Check the third digit of the subdevice ID - * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel) - */ - if ((dev->subsystem_device & 0x00f0) >= 0x70) { - dev_info(&dev->dev, - "ignoring Timedia subdevice %04x for parport_serial\n", - dev->subsystem_device); - return -ENODEV; - } - - return 0; -} - -static int pci_timedia_init(struct pci_dev *dev) -{ - const unsigned short *ids; - int i, j; - - for (i = 0; i < ARRAY_SIZE(timedia_data); i++) { - ids = timedia_data[i].ids; - for (j = 0; ids[j]; j++) - if (dev->subsystem_device == ids[j]) - return timedia_data[i].num; - } - return 0; -} - -/* - * Timedia/SUNIX uses a mixture of BARs and offsets - * Ugh, this is ugly as all hell --- TYT - */ -static int -pci_timedia_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar = 0, offset = board->first_offset; - - switch (idx) { - case 0: - bar = 0; - break; - case 1: - offset = board->uart_offset; - bar = 0; - break; - case 2: - bar = 1; - break; - case 3: - offset = board->uart_offset; - /* FALLTHROUGH */ - case 4: /* BAR 2 */ - case 5: /* BAR 3 */ - case 6: /* BAR 4 */ - case 7: /* BAR 5 */ - bar = idx - 2; - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * Some Titan cards are also a little weird - */ -static int -titan_400l_800l_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset; - - switch (idx) { - case 0: - bar = 1; - break; - case 1: - bar = 2; - break; - default: - bar = 4; - offset = (idx - 2) * board->uart_offset; - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -static int pci_xircom_init(struct pci_dev *dev) -{ - msleep(100); - return 0; -} - -static int pci_ni8420_init(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return 0; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return -ENOMEM; - - /* Enable CPU Interrupt */ - writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT, - p + NI8420_INT_ENABLE_REG); - - iounmap(p); - return 0; -} - -#define MITE_IOWBSR1_WSIZE 0xa -#define MITE_IOWBSR1_WIN_OFFSET 0x800 -#define MITE_IOWBSR1_WENAB (1 << 7) -#define MITE_LCIMR1_IO_IE_0 (1 << 24) -#define MITE_LCIMR2_SET_CPU_IE (1 << 31) -#define MITE_IOWCR1_RAMSEL_MASK 0xfffffffe - -static int pci_ni8430_init(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - u32 device_window; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return 0; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return -ENOMEM; - - /* Set device window address and size in BAR0 */ - device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) - | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; - writel(device_window, p + MITE_IOWBSR1); - - /* Set window access to go to RAMSEL IO address space */ - writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK), - p + MITE_IOWCR1); - - /* Enable IO Bus Interrupt 0 */ - writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1); - - /* Enable CPU Interrupt */ - writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2); - - iounmap(p); - return 0; -} - -/* UART Port Control Register */ -#define NI8430_PORTCON 0x0f -#define NI8430_PORTCON_TXVR_ENABLE (1 << 3) - -static int -pci_ni8430_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar, offset = board->first_offset; - - if (idx >= board->num_ports) - return 1; - - bar = FL_GET_BASE(board->flags); - offset += idx * board->uart_offset; - - base = pci_resource_start(priv->dev, bar); - len = pci_resource_len(priv->dev, bar); - p = ioremap_nocache(base, len); - - /* enable the transceiver */ - writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, - p + offset + NI8430_PORTCON); - - iounmap(p); - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -static int pci_netmos_9900_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar; - - if ((priv->dev->subsystem_device & 0xff00) == 0x3000) { - /* netmos apparently orders BARs by datasheet layout, so serial - * ports get BARs 0 and 3 (or 1 and 4 for memmapped) - */ - bar = 3 * idx; - - return setup_port(priv, port, bar, 0, board->reg_shift); - } else { - return pci_default_setup(priv, board, port, idx); - } -} - -/* the 99xx series comes with a range of device IDs and a variety - * of capabilities: - * - * 9900 has varying capabilities and can cascade to sub-controllers - * (cascading should be purely internal) - * 9904 is hardwired with 4 serial ports - * 9912 and 9922 are hardwired with 2 serial ports - */ -static int pci_netmos_9900_numports(struct pci_dev *dev) -{ - unsigned int c = dev->class; - unsigned int pi; - unsigned short sub_serports; - - pi = (c & 0xff); - - if (pi == 2) { - return 1; - } else if ((pi == 0) && - (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { - /* two possibilities: 0x30ps encodes number of parallel and - * serial ports, or 0x1000 indicates *something*. This is not - * immediately obvious, since the 2s1p+4s configuration seems - * to offer all functionality on functions 0..2, while still - * advertising the same function 3 as the 4s+2s1p config. - */ - sub_serports = dev->subsystem_device & 0xf; - if (sub_serports > 0) { - return sub_serports; - } else { - printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); - return 0; - } - } - - moan_device("unknown NetMos/Mostech program interface", dev); - return 0; -} - -static int pci_netmos_init(struct pci_dev *dev) -{ - /* subdevice 0x00PS means

parallel, serial */ - unsigned int num_serial = dev->subsystem_device & 0xf; - - if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || - (dev->device == PCI_DEVICE_ID_NETMOS_9865)) - return 0; - - if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && - dev->subsystem_device == 0x0299) - return 0; - - switch (dev->device) { /* FALLTHROUGH on all */ - case PCI_DEVICE_ID_NETMOS_9904: - case PCI_DEVICE_ID_NETMOS_9912: - case PCI_DEVICE_ID_NETMOS_9922: - case PCI_DEVICE_ID_NETMOS_9900: - num_serial = pci_netmos_9900_numports(dev); - break; - - default: - if (num_serial == 0 ) { - moan_device("unknown NetMos/Mostech device", dev); - } - } - - if (num_serial == 0) - return -ENODEV; - - return num_serial; -} - -/* - * These chips are available with optionally one parallel port and up to - * two serial ports. Unfortunately they all have the same product id. - * - * Basic configuration is done over a region of 32 I/O ports. The base - * ioport is called INTA or INTC, depending on docs/other drivers. - * - * The region of the 32 I/O ports is configured in POSIO0R... - */ - -/* registers */ -#define ITE_887x_MISCR 0x9c -#define ITE_887x_INTCBAR 0x78 -#define ITE_887x_UARTBAR 0x7c -#define ITE_887x_PS0BAR 0x10 -#define ITE_887x_POSIO0 0x60 - -/* I/O space size */ -#define ITE_887x_IOSIZE 32 -/* I/O space size (bits 26-24; 8 bytes = 011b) */ -#define ITE_887x_POSIO_IOSIZE_8 (3 << 24) -/* I/O space size (bits 26-24; 32 bytes = 101b) */ -#define ITE_887x_POSIO_IOSIZE_32 (5 << 24) -/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */ -#define ITE_887x_POSIO_SPEED (3 << 29) -/* enable IO_Space bit */ -#define ITE_887x_POSIO_ENABLE (1 << 31) - -static int pci_ite887x_init(struct pci_dev *dev) -{ - /* inta_addr are the configuration addresses of the ITE */ - static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, - 0x200, 0x280, 0 }; - int ret, i, type; - struct resource *iobase = NULL; - u32 miscr, uartbar, ioport; - - /* search for the base-ioport */ - i = 0; - while (inta_addr[i] && iobase == NULL) { - iobase = request_region(inta_addr[i], ITE_887x_IOSIZE, - "ite887x"); - if (iobase != NULL) { - /* write POSIO0R - speed | size | ioport */ - pci_write_config_dword(dev, ITE_887x_POSIO0, - ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | - ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]); - /* write INTCBAR - ioport */ - pci_write_config_dword(dev, ITE_887x_INTCBAR, - inta_addr[i]); - ret = inb(inta_addr[i]); - if (ret != 0xff) { - /* ioport connected */ - break; - } - release_region(iobase->start, ITE_887x_IOSIZE); - iobase = NULL; - } - i++; - } - - if (!inta_addr[i]) { - printk(KERN_ERR "ite887x: could not find iobase\n"); - return -ENODEV; - } - - /* start of undocumented type checking (see parport_pc.c) */ - type = inb(iobase->start + 0x18) & 0x0f; - - switch (type) { - case 0x2: /* ITE8871 (1P) */ - case 0xa: /* ITE8875 (1P) */ - ret = 0; - break; - case 0xe: /* ITE8872 (2S1P) */ - ret = 2; - break; - case 0x6: /* ITE8873 (1S) */ - ret = 1; - break; - case 0x8: /* ITE8874 (2S) */ - ret = 2; - break; - default: - moan_device("Unknown ITE887x", dev); - ret = -ENODEV; - } - - /* configure all serial ports */ - for (i = 0; i < ret; i++) { - /* read the I/O port from the device */ - pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)), - &ioport); - ioport &= 0x0000FF00; /* the actual base address */ - pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)), - ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | - ITE_887x_POSIO_IOSIZE_8 | ioport); - - /* write the ioport to the UARTBAR */ - pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar); - uartbar &= ~(0xffff << (16 * i)); /* clear half the reg */ - uartbar |= (ioport << (16 * i)); /* set the ioport */ - pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar); - - /* get current config */ - pci_read_config_dword(dev, ITE_887x_MISCR, &miscr); - /* disable interrupts (UARTx_Routing[3:0]) */ - miscr &= ~(0xf << (12 - 4 * i)); - /* activate the UART (UARTx_En) */ - miscr |= 1 << (23 - i); - /* write new config with activated UART */ - pci_write_config_dword(dev, ITE_887x_MISCR, miscr); - } - - if (ret <= 0) { - /* the device has no UARTs if we get here */ - release_region(iobase->start, ITE_887x_IOSIZE); - } - - return ret; -} - -static void __devexit pci_ite887x_exit(struct pci_dev *dev) -{ - u32 ioport; - /* the ioport is bit 0-15 in POSIO0R */ - pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport); - ioport &= 0xffff; - release_region(ioport, ITE_887x_IOSIZE); -} - -/* - * Oxford Semiconductor Inc. - * Check that device is part of the Tornado range of devices, then determine - * the number of ports available on the device. - */ -static int pci_oxsemi_tornado_init(struct pci_dev *dev) -{ - u8 __iomem *p; - unsigned long deviceID; - unsigned int number_uarts = 0; - - /* OxSemi Tornado devices are all 0xCxxx */ - if (dev->vendor == PCI_VENDOR_ID_OXSEMI && - (dev->device & 0xF000) != 0xC000) - return 0; - - p = pci_iomap(dev, 0, 5); - if (p == NULL) - return -ENOMEM; - - deviceID = ioread32(p); - /* Tornado device */ - if (deviceID == 0x07000200) { - number_uarts = ioread8(p + 4); - printk(KERN_DEBUG - "%d ports detected on Oxford PCI Express device\n", - number_uarts); - } - pci_iounmap(dev, p); - return number_uarts; -} - -static int -pci_default_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset, maxnr; - - bar = FL_GET_BASE(board->flags); - if (board->flags & FL_BASE_BARS) - bar += idx; - else - offset += idx * board->uart_offset; - - maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> - (board->reg_shift + 3); - - if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) - return 1; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -static int -ce4100_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - int ret; - - ret = setup_port(priv, port, 0, 0, board->reg_shift); - port->iotype = UPIO_MEM32; - port->type = PORT_XSCALE; - port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); - port->regshift = 2; - - return ret; -} - -static int -pci_omegapci_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - return setup_port(priv, port, 2, idx * 8, 0); -} - -static int skip_tx_en_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - port->flags |= UPF_NO_TXEN_TEST; - printk(KERN_DEBUG "serial8250: skipping TxEn test for device " - "[%04x:%04x] subsystem [%04x:%04x]\n", - priv->dev->vendor, - priv->dev->device, - priv->dev->subsystem_vendor, - priv->dev->subsystem_device); - - return pci_default_setup(priv, board, port, idx); -} - -static int kt_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - port->flags |= UPF_IIR_ONCE; - return skip_tx_en_setup(priv, board, port, idx); -} - -static int pci_eg20t_init(struct pci_dev *dev) -{ -#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE) - return -ENODEV; -#else - return 0; -#endif -} - -static int -pci_xr17c154_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - port->flags |= UPF_EXAR_EFR; - return pci_default_setup(priv, board, port, idx); -} - -static int try_enable_msi(struct pci_dev *dev) -{ - /* use msi if available, but fallback to legacy otherwise */ - pci_enable_msi(dev); - return 0; -} - -static void disable_msi(struct pci_dev *dev) -{ - pci_disable_msi(dev); -} - -#define PCI_VENDOR_ID_SBSMODULARIO 0x124B -#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B -#define PCI_DEVICE_ID_OCTPRO 0x0001 -#define PCI_SUBDEVICE_ID_OCTPRO232 0x0108 -#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 -#define PCI_SUBDEVICE_ID_POCTAL232 0x0308 -#define PCI_SUBDEVICE_ID_POCTAL422 0x0408 -#define PCI_VENDOR_ID_ADVANTECH 0x13fe -#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 -#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 -#define PCI_DEVICE_ID_TITAN_200I 0x8028 -#define PCI_DEVICE_ID_TITAN_400I 0x8048 -#define PCI_DEVICE_ID_TITAN_800I 0x8088 -#define PCI_DEVICE_ID_TITAN_800EH 0xA007 -#define PCI_DEVICE_ID_TITAN_800EHB 0xA008 -#define PCI_DEVICE_ID_TITAN_400EH 0xA009 -#define PCI_DEVICE_ID_TITAN_100E 0xA010 -#define PCI_DEVICE_ID_TITAN_200E 0xA012 -#define PCI_DEVICE_ID_TITAN_400E 0xA013 -#define PCI_DEVICE_ID_TITAN_800E 0xA014 -#define PCI_DEVICE_ID_TITAN_200EI 0xA016 -#define PCI_DEVICE_ID_TITAN_200EISI 0xA017 -#define PCI_DEVICE_ID_TITAN_400V3 0xA310 -#define PCI_DEVICE_ID_TITAN_410V3 0xA312 -#define PCI_DEVICE_ID_TITAN_800V3 0xA314 -#define PCI_DEVICE_ID_TITAN_800V3B 0xA315 -#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 -#define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 -#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 -#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d - -/* Unknown vendors/cards - this should not be in linux/pci_ids.h */ -#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 - -/* - * Master list of serial port init/setup/exit quirks. - * This does not describe the general nature of the port. - * (ie, baud base, number and location of ports, etc) - * - * This list is ordered alphabetically by vendor then device. - * Specific entries must come before more generic entries. - */ -static struct pci_serial_quirk pci_serial_quirks[] __refdata = { - /* - * ADDI-DATA GmbH communication cards - */ - { - .vendor = PCI_VENDOR_ID_ADDIDATA_OLD, - .device = PCI_DEVICE_ID_ADDIDATA_APCI7800, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = addidata_apci7800_setup, - }, - /* - * AFAVLAB cards - these may be called via parport_serial - * It is not clear whether this applies to all products. - */ - { - .vendor = PCI_VENDOR_ID_AFAVLAB, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = afavlab_setup, - }, - /* - * HP Diva - */ - { - .vendor = PCI_VENDOR_ID_HP, - .device = PCI_DEVICE_ID_HP_DIVA, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_hp_diva_init, - .setup = pci_hp_diva_setup, - }, - /* - * Intel - */ - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_80960_RP, - .subvendor = 0xe4bf, - .subdevice = PCI_ANY_ID, - .init = pci_inteli960ni_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_8257X_SOL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = skip_tx_en_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_82573L_SOL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = skip_tx_en_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_82573E_SOL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = skip_tx_en_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CE4100_UART, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = ce4100_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = try_enable_msi, - .setup = kt_serial_setup, - .exit = disable_msi, - }, - /* - * ITE - */ - { - .vendor = PCI_VENDOR_ID_ITE, - .device = PCI_DEVICE_ID_ITE_8872, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ite887x_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ite887x_exit), - }, - /* - * National Instruments - */ - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI23216, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2328, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2324, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2322, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2324I, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2322I, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_23216, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_2328, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_2324, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_2322, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8422_2324, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8422_2322, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8430_init, - .setup = pci_ni8430_setup, - .exit = __devexit_p(pci_ni8430_exit), - }, - /* - * Panacom - */ - { - .vendor = PCI_VENDOR_ID_PANACOM, - .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PANACOM, - .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - /* - * PLX - */ - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9030, - .subvendor = PCI_SUBVENDOR_ID_PERLE, - .subdevice = PCI_ANY_ID, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_SUBVENDOR_ID_EXSYS, - .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, - .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_VENDOR_ID_PLX, - .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_ROMULUS, - .subvendor = PCI_VENDOR_ID_PLX, - .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - /* - * SBS Technologies, Inc., PMC-OCTALPRO 232 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_OCTPRO232, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SBS Technologies, Inc., PMC-OCTALPRO 422 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_OCTPRO422, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SBS Technologies, Inc., P-Octal 232 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_POCTAL232, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SBS Technologies, Inc., P-Octal 422 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_POCTAL422, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SIIG cards - these may be called via parport_serial - */ - { - .vendor = PCI_VENDOR_ID_SIIG, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_siig_init, - .setup = pci_siig_setup, - }, - /* - * Titan cards - */ - { - .vendor = PCI_VENDOR_ID_TITAN, - .device = PCI_DEVICE_ID_TITAN_400L, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = titan_400l_800l_setup, - }, - { - .vendor = PCI_VENDOR_ID_TITAN, - .device = PCI_DEVICE_ID_TITAN_800L, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = titan_400l_800l_setup, - }, - /* - * Timedia cards - */ - { - .vendor = PCI_VENDOR_ID_TIMEDIA, - .device = PCI_DEVICE_ID_TIMEDIA_1889, - .subvendor = PCI_VENDOR_ID_TIMEDIA, - .subdevice = PCI_ANY_ID, - .probe = pci_timedia_probe, - .init = pci_timedia_init, - .setup = pci_timedia_setup, - }, - { - .vendor = PCI_VENDOR_ID_TIMEDIA, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_timedia_setup, - }, - /* - * Exar cards - */ - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17C152, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17c154_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17C154, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17c154_setup, - }, - { - .vendor = PCI_VENDOR_ID_EXAR, - .device = PCI_DEVICE_ID_EXAR_XR17C158, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_xr17c154_setup, - }, - /* - * Xircom cards - */ - { - .vendor = PCI_VENDOR_ID_XIRCOM, - .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_xircom_init, - .setup = pci_default_setup, - }, - /* - * Netmos cards - these may be called via parport_serial - */ - { - .vendor = PCI_VENDOR_ID_NETMOS, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_netmos_init, - .setup = pci_netmos_9900_setup, - }, - /* - * For Oxford Semiconductor Tornado based devices - */ - { - .vendor = PCI_VENDOR_ID_OXSEMI, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_MAINPINE, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_DIGI, - .device = PCIE_DEVICE_ID_NEO_2_OX_IBM, - .subvendor = PCI_SUBVENDOR_ID_IBM, - .subdevice = PCI_ANY_ID, - .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x8811, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x8812, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x8813, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = 0x8814, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = 0x10DB, - .device = 0x8027, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = 0x10DB, - .device = 0x8028, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = 0x10DB, - .device = 0x8029, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = 0x10DB, - .device = 0x800C, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - { - .vendor = 0x10DB, - .device = 0x800D, - .init = pci_eg20t_init, - .setup = pci_default_setup, - }, - /* - * Cronyx Omega PCI (PLX-chip based) - */ - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_CRONYX_OMEGA, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_omegapci_setup, - }, - /* - * Default "match everything" terminator entry - */ - { - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_default_setup, - } -}; - -static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) -{ - return quirk_id == PCI_ANY_ID || quirk_id == dev_id; -} - -static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) -{ - struct pci_serial_quirk *quirk; - - for (quirk = pci_serial_quirks; ; quirk++) - if (quirk_id_matches(quirk->vendor, dev->vendor) && - quirk_id_matches(quirk->device, dev->device) && - quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && - quirk_id_matches(quirk->subdevice, dev->subsystem_device)) - break; - return quirk; -} - -static inline int get_pci_irq(struct pci_dev *dev, - const struct pciserial_board *board) -{ - if (board->flags & FL_NOIRQ) - return 0; - else - return dev->irq; -} - -/* - * This is the configuration table for all of the PCI serial boards - * which we support. It is directly indexed by the pci_board_num_t enum - * value, which is encoded in the pci_device_id PCI probe table's - * driver_data member. - * - * The makeup of these names are: - * pbn_bn{_bt}_n_baud{_offsetinhex} - * - * bn = PCI BAR number - * bt = Index using PCI BARs - * n = number of serial ports - * baud = baud rate - * offsetinhex = offset for each sequential port (in hex) - * - * This table is sorted by (in order): bn, bt, baud, offsetindex, n. - * - * Please note: in theory if n = 1, _bt infix should make no difference. - * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 - */ -enum pci_board_num_t { - pbn_default = 0, - - pbn_b0_1_115200, - pbn_b0_2_115200, - pbn_b0_4_115200, - pbn_b0_5_115200, - pbn_b0_8_115200, - - pbn_b0_1_921600, - pbn_b0_2_921600, - pbn_b0_4_921600, - - pbn_b0_2_1130000, - - pbn_b0_4_1152000, - - pbn_b0_2_1843200, - pbn_b0_4_1843200, - - pbn_b0_2_1843200_200, - pbn_b0_4_1843200_200, - pbn_b0_8_1843200_200, - - pbn_b0_1_4000000, - - pbn_b0_bt_1_115200, - pbn_b0_bt_2_115200, - pbn_b0_bt_4_115200, - pbn_b0_bt_8_115200, - - pbn_b0_bt_1_460800, - pbn_b0_bt_2_460800, - pbn_b0_bt_4_460800, - - pbn_b0_bt_1_921600, - pbn_b0_bt_2_921600, - pbn_b0_bt_4_921600, - pbn_b0_bt_8_921600, - - pbn_b1_1_115200, - pbn_b1_2_115200, - pbn_b1_4_115200, - pbn_b1_8_115200, - pbn_b1_16_115200, - - pbn_b1_1_921600, - pbn_b1_2_921600, - pbn_b1_4_921600, - pbn_b1_8_921600, - - pbn_b1_2_1250000, - - pbn_b1_bt_1_115200, - pbn_b1_bt_2_115200, - pbn_b1_bt_4_115200, - - pbn_b1_bt_2_921600, - - pbn_b1_1_1382400, - pbn_b1_2_1382400, - pbn_b1_4_1382400, - pbn_b1_8_1382400, - - pbn_b2_1_115200, - pbn_b2_2_115200, - pbn_b2_4_115200, - pbn_b2_8_115200, - - pbn_b2_1_460800, - pbn_b2_4_460800, - pbn_b2_8_460800, - pbn_b2_16_460800, - - pbn_b2_1_921600, - pbn_b2_4_921600, - pbn_b2_8_921600, - - pbn_b2_8_1152000, - - pbn_b2_bt_1_115200, - pbn_b2_bt_2_115200, - pbn_b2_bt_4_115200, - - pbn_b2_bt_2_921600, - pbn_b2_bt_4_921600, - - pbn_b3_2_115200, - pbn_b3_4_115200, - pbn_b3_8_115200, - - pbn_b4_bt_2_921600, - pbn_b4_bt_4_921600, - pbn_b4_bt_8_921600, - - /* - * Board-specific versions. - */ - pbn_panacom, - pbn_panacom2, - pbn_panacom4, - pbn_exsys_4055, - pbn_plx_romulus, - pbn_oxsemi, - pbn_oxsemi_1_4000000, - pbn_oxsemi_2_4000000, - pbn_oxsemi_4_4000000, - pbn_oxsemi_8_4000000, - pbn_intel_i960, - pbn_sgi_ioc3, - pbn_computone_4, - pbn_computone_6, - pbn_computone_8, - pbn_sbsxrsio, - pbn_exar_XR17C152, - pbn_exar_XR17C154, - pbn_exar_XR17C158, - pbn_exar_ibm_saturn, - pbn_pasemi_1682M, - pbn_ni8430_2, - pbn_ni8430_4, - pbn_ni8430_8, - pbn_ni8430_16, - pbn_ADDIDATA_PCIe_1_3906250, - pbn_ADDIDATA_PCIe_2_3906250, - pbn_ADDIDATA_PCIe_4_3906250, - pbn_ADDIDATA_PCIe_8_3906250, - pbn_ce4100_1_115200, - pbn_omegapci, - pbn_NETMOS9900_2s_115200, -}; - -/* - * uart_offset - the space between channels - * reg_shift - describes how the UART registers are mapped - * to PCI memory by the card. - * For example IER register on SBS, Inc. PMC-OctPro is located at - * offset 0x10 from the UART base, while UART_IER is defined as 1 - * in include/linux/serial_reg.h, - * see first lines of serial_in() and serial_out() in 8250.c -*/ - -static struct pciserial_board pci_boards[] __devinitdata = { - [pbn_default] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_1_115200] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_2_115200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_4_115200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_5_115200] = { - .flags = FL_BASE0, - .num_ports = 5, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_8_115200] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_1_921600] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_2_921600] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_4_921600] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b0_2_1130000] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1130000, - .uart_offset = 8, - }, - - [pbn_b0_4_1152000] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1152000, - .uart_offset = 8, - }, - - [pbn_b0_2_1843200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1843200, - .uart_offset = 8, - }, - [pbn_b0_4_1843200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1843200, - .uart_offset = 8, - }, - - [pbn_b0_2_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_4_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_8_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_1_4000000] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 4000000, - .uart_offset = 8, - }, - - [pbn_b0_bt_1_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_bt_2_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_bt_4_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_bt_8_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b0_bt_1_460800] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b0_bt_2_460800] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b0_bt_4_460800] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 460800, - .uart_offset = 8, - }, - - [pbn_b0_bt_1_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_bt_2_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_bt_4_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_bt_8_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b1_1_115200] = { - .flags = FL_BASE1, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_2_115200] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_4_115200] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_8_115200] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_16_115200] = { - .flags = FL_BASE1, - .num_ports = 16, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b1_1_921600] = { - .flags = FL_BASE1, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_2_921600] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_4_921600] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_8_921600] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_2_1250000] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 1250000, - .uart_offset = 8, - }, - - [pbn_b1_bt_1_115200] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_bt_2_115200] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_bt_4_115200] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b1_bt_2_921600] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b1_1_1382400] = { - .flags = FL_BASE1, - .num_ports = 1, - .base_baud = 1382400, - .uart_offset = 8, - }, - [pbn_b1_2_1382400] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 1382400, - .uart_offset = 8, - }, - [pbn_b1_4_1382400] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 1382400, - .uart_offset = 8, - }, - [pbn_b1_8_1382400] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 1382400, - .uart_offset = 8, - }, - - [pbn_b2_1_115200] = { - .flags = FL_BASE2, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_2_115200] = { - .flags = FL_BASE2, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_4_115200] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_8_115200] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b2_1_460800] = { - .flags = FL_BASE2, - .num_ports = 1, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b2_4_460800] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b2_8_460800] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b2_16_460800] = { - .flags = FL_BASE2, - .num_ports = 16, - .base_baud = 460800, - .uart_offset = 8, - }, - - [pbn_b2_1_921600] = { - .flags = FL_BASE2, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b2_4_921600] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b2_8_921600] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b2_8_1152000] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 1152000, - .uart_offset = 8, - }, - - [pbn_b2_bt_1_115200] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_bt_2_115200] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_bt_4_115200] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b2_bt_2_921600] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b2_bt_4_921600] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b3_2_115200] = { - .flags = FL_BASE3, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b3_4_115200] = { - .flags = FL_BASE3, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b3_8_115200] = { - .flags = FL_BASE3, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b4_bt_2_921600] = { - .flags = FL_BASE4, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b4_bt_4_921600] = { - .flags = FL_BASE4, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b4_bt_8_921600] = { - .flags = FL_BASE4, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - - /* - * Entries following this are board-specific. - */ - - /* - * Panacom - IOMEM - */ - [pbn_panacom] = { - .flags = FL_BASE2, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x400, - .reg_shift = 7, - }, - [pbn_panacom2] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x400, - .reg_shift = 7, - }, - [pbn_panacom4] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x400, - .reg_shift = 7, - }, - - [pbn_exsys_4055] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - - /* I think this entry is broken - the first_offset looks wrong --rmk */ - [pbn_plx_romulus] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8 << 2, - .reg_shift = 2, - .first_offset = 0x03, - }, - - /* - * This board uses the size of PCI Base region 0 to - * signal now many ports are available - */ - [pbn_oxsemi] = { - .flags = FL_BASE0|FL_REGION_SZ_CAP, - .num_ports = 32, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_oxsemi_1_4000000] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_oxsemi_2_4000000] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_oxsemi_4_4000000] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_oxsemi_8_4000000] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - - - /* - * EKF addition for i960 Boards form EKF with serial port. - * Max 256 ports. - */ - [pbn_intel_i960] = { - .flags = FL_BASE0, - .num_ports = 32, - .base_baud = 921600, - .uart_offset = 8 << 2, - .reg_shift = 2, - .first_offset = 0x10000, - }, - [pbn_sgi_ioc3] = { - .flags = FL_BASE0|FL_NOIRQ, - .num_ports = 1, - .base_baud = 458333, - .uart_offset = 8, - .reg_shift = 0, - .first_offset = 0x20178, - }, - - /* - * Computone - uses IOMEM. - */ - [pbn_computone_4] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x40, - .reg_shift = 2, - .first_offset = 0x200, - }, - [pbn_computone_6] = { - .flags = FL_BASE0, - .num_ports = 6, - .base_baud = 921600, - .uart_offset = 0x40, - .reg_shift = 2, - .first_offset = 0x200, - }, - [pbn_computone_8] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 0x40, - .reg_shift = 2, - .first_offset = 0x200, - }, - [pbn_sbsxrsio] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 460800, - .uart_offset = 256, - .reg_shift = 4, - }, - /* - * Exar Corp. XR17C15[248] Dual/Quad/Octal UART - * Only basic 16550A support. - * XR17C15[24] are not tested, but they should work. - */ - [pbn_exar_XR17C152] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17C154] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17C158] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_ibm_saturn] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 0x200, - }, - - /* - * PA Semi PWRficient PA6T-1682M on-chip UART - */ - [pbn_pasemi_1682M] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 8333333, - }, - /* - * National Instruments 843x - */ - [pbn_ni8430_16] = { - .flags = FL_BASE0, - .num_ports = 16, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - [pbn_ni8430_8] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - [pbn_ni8430_4] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - [pbn_ni8430_2] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - /* - * ADDI-DATA GmbH PCI-Express communication cards - */ - [pbn_ADDIDATA_PCIe_1_3906250] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ADDIDATA_PCIe_2_3906250] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ADDIDATA_PCIe_4_3906250] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ADDIDATA_PCIe_8_3906250] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ce4100_1_115200] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .reg_shift = 2, - }, - [pbn_omegapci] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 0x200, - }, - [pbn_NETMOS9900_2s_115200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 115200, - }, -}; - -static const struct pci_device_id softmodem_blacklist[] = { - { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ - { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ - { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ -}; - -/* - * Given a complete unknown PCI device, try to use some heuristics to - * guess what the configuration might be, based on the pitiful PCI - * serial specs. Returns 0 on success, 1 on failure. - */ -static int __devinit -serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) -{ - const struct pci_device_id *blacklist; - int num_iomem, num_port, first_port = -1, i; - - /* - * If it is not a communications device or the programming - * interface is greater than 6, give up. - * - * (Should we try to make guesses for multiport serial devices - * later?) - */ - if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && - ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || - (dev->class & 0xff) > 6) - return -ENODEV; - - /* - * Do not access blacklisted devices that are known not to - * feature serial ports. - */ - for (blacklist = softmodem_blacklist; - blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist); - blacklist++) { - if (dev->vendor == blacklist->vendor && - dev->device == blacklist->device) - return -ENODEV; - } - - num_iomem = num_port = 0; - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (pci_resource_flags(dev, i) & IORESOURCE_IO) { - num_port++; - if (first_port == -1) - first_port = i; - } - if (pci_resource_flags(dev, i) & IORESOURCE_MEM) - num_iomem++; - } - - /* - * If there is 1 or 0 iomem regions, and exactly one port, - * use it. We guess the number of ports based on the IO - * region size. - */ - if (num_iomem <= 1 && num_port == 1) { - board->flags = first_port; - board->num_ports = pci_resource_len(dev, first_port) / 8; - return 0; - } - - /* - * Now guess if we've got a board which indexes by BARs. - * Each IO BAR should be 8 bytes, and they should follow - * consecutively. - */ - first_port = -1; - num_port = 0; - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (pci_resource_flags(dev, i) & IORESOURCE_IO && - pci_resource_len(dev, i) == 8 && - (first_port == -1 || (first_port + num_port) == i)) { - num_port++; - if (first_port == -1) - first_port = i; - } - } - - if (num_port > 1) { - board->flags = first_port | FL_BASE_BARS; - board->num_ports = num_port; - return 0; - } - - return -ENODEV; -} - -static inline int -serial_pci_matches(const struct pciserial_board *board, - const struct pciserial_board *guessed) -{ - return - board->num_ports == guessed->num_ports && - board->base_baud == guessed->base_baud && - board->uart_offset == guessed->uart_offset && - board->reg_shift == guessed->reg_shift && - board->first_offset == guessed->first_offset; -} - -struct serial_private * -pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) -{ - struct uart_port serial_port; - struct serial_private *priv; - struct pci_serial_quirk *quirk; - int rc, nr_ports, i; - - nr_ports = board->num_ports; - - /* - * Find an init and setup quirks. - */ - quirk = find_quirk(dev); - - /* - * Run the new-style initialization function. - * The initialization function returns: - * <0 - error - * 0 - use board->num_ports - * >0 - number of ports - */ - if (quirk->init) { - rc = quirk->init(dev); - if (rc < 0) { - priv = ERR_PTR(rc); - goto err_out; - } - if (rc) - nr_ports = rc; - } - - priv = kzalloc(sizeof(struct serial_private) + - sizeof(unsigned int) * nr_ports, - GFP_KERNEL); - if (!priv) { - priv = ERR_PTR(-ENOMEM); - goto err_deinit; - } - - priv->dev = dev; - priv->quirk = quirk; - - memset(&serial_port, 0, sizeof(struct uart_port)); - serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - serial_port.uartclk = board->base_baud * 16; - serial_port.irq = get_pci_irq(dev, board); - serial_port.dev = &dev->dev; - - for (i = 0; i < nr_ports; i++) { - if (quirk->setup(priv, board, &serial_port, i)) - break; - -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", - serial_port.iobase, serial_port.irq, serial_port.iotype); -#endif - - priv->line[i] = serial8250_register_port(&serial_port); - if (priv->line[i] < 0) { - printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); - break; - } - } - priv->nr = i; - return priv; - -err_deinit: - if (quirk->exit) - quirk->exit(dev); -err_out: - return priv; -} -EXPORT_SYMBOL_GPL(pciserial_init_ports); - -void pciserial_remove_ports(struct serial_private *priv) -{ - struct pci_serial_quirk *quirk; - int i; - - for (i = 0; i < priv->nr; i++) - serial8250_unregister_port(priv->line[i]); - - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (priv->remapped_bar[i]) - iounmap(priv->remapped_bar[i]); - priv->remapped_bar[i] = NULL; - } - - /* - * Find the exit quirks. - */ - quirk = find_quirk(priv->dev); - if (quirk->exit) - quirk->exit(priv->dev); - - kfree(priv); -} -EXPORT_SYMBOL_GPL(pciserial_remove_ports); - -void pciserial_suspend_ports(struct serial_private *priv) -{ - int i; - - for (i = 0; i < priv->nr; i++) - if (priv->line[i] >= 0) - serial8250_suspend_port(priv->line[i]); -} -EXPORT_SYMBOL_GPL(pciserial_suspend_ports); - -void pciserial_resume_ports(struct serial_private *priv) -{ - int i; - - /* - * Ensure that the board is correctly configured. - */ - if (priv->quirk->init) - priv->quirk->init(priv->dev); - - for (i = 0; i < priv->nr; i++) - if (priv->line[i] >= 0) - serial8250_resume_port(priv->line[i]); -} -EXPORT_SYMBOL_GPL(pciserial_resume_ports); - -/* - * Probe one serial board. Unfortunately, there is no rhyme nor reason - * to the arrangement of serial ports on a PCI card. - */ -static int __devinit -pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) -{ - struct pci_serial_quirk *quirk; - struct serial_private *priv; - const struct pciserial_board *board; - struct pciserial_board tmp; - int rc; - - quirk = find_quirk(dev); - if (quirk->probe) { - rc = quirk->probe(dev); - if (rc) - return rc; - } - - if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { - printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", - ent->driver_data); - return -EINVAL; - } - - board = &pci_boards[ent->driver_data]; - - rc = pci_enable_device(dev); - pci_save_state(dev); - if (rc) - return rc; - - if (ent->driver_data == pbn_default) { - /* - * Use a copy of the pci_board entry for this; - * avoid changing entries in the table. - */ - memcpy(&tmp, board, sizeof(struct pciserial_board)); - board = &tmp; - - /* - * We matched one of our class entries. Try to - * determine the parameters of this board. - */ - rc = serial_pci_guess_board(dev, &tmp); - if (rc) - goto disable; - } else { - /* - * We matched an explicit entry. If we are able to - * detect this boards settings with our heuristic, - * then we no longer need this entry. - */ - memcpy(&tmp, &pci_boards[pbn_default], - sizeof(struct pciserial_board)); - rc = serial_pci_guess_board(dev, &tmp); - if (rc == 0 && serial_pci_matches(board, &tmp)) - moan_device("Redundant entry in serial pci_table.", - dev); - } - - priv = pciserial_init_ports(dev, board); - if (!IS_ERR(priv)) { - pci_set_drvdata(dev, priv); - return 0; - } - - rc = PTR_ERR(priv); - - disable: - pci_disable_device(dev); - return rc; -} - -static void __devexit pciserial_remove_one(struct pci_dev *dev) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - pci_set_drvdata(dev, NULL); - - pciserial_remove_ports(priv); - - pci_disable_device(dev); -} - -#ifdef CONFIG_PM -static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - if (priv) - pciserial_suspend_ports(priv); - - pci_save_state(dev); - pci_set_power_state(dev, pci_choose_state(dev, state)); - return 0; -} - -static int pciserial_resume_one(struct pci_dev *dev) -{ - int err; - struct serial_private *priv = pci_get_drvdata(dev); - - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - - if (priv) { - /* - * The device may have been disabled. Re-enable it. - */ - err = pci_enable_device(dev); - /* FIXME: We cannot simply error out here */ - if (err) - printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); - pciserial_resume_ports(priv); - } - return 0; -} -#endif - -static struct pci_device_id serial_pci_tbl[] = { - /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ - { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, - PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, - pbn_b1_2_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0, - pbn_b1_2_1250000 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0, - pbn_b0_2_1843200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0, - pbn_b0_4_1843200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_AFAVLAB, - PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, - pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, - 0, 0, pbn_exar_ibm_saturn }, - - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_1_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - /* - * VScom SPCOM800, from sl@s.pl - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_921600 }, - /* Unknown card - subdevice 0x1584 */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_KEYSPAN, - PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, - pbn_panacom }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom4 }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom2 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_VENDOR_ID_ESDGMBH, - PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_EXSYS, - PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, - pbn_exsys_4055 }, - /* - * Megawolf Romulus PCI Serial Card, from Mike Hudson - * (Exoray@isys.ca) - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, - 0x10b5, 0x106a, 0, 0, - pbn_plx_romulus }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, - 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, - 0, 0, - pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_OXSEMI, 0x9505, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - - /* - * The below card is a little controversial since it is the - * subject of a PCI vendor/device ID clash. (See - * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). - * For now just used the hex ID 0x950a. - */ - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0, - pbn_b0_2_115200 }, - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_1130000 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, - PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, - PCI_ANY_ID , PCI_ANY_ID, 0, 0, - pbn_b2_8_1152000 }, - - /* - * Oxford Semiconductor Inc. Tornado PCI express device range. - */ - { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - /* - * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado - */ - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, - pbn_oxsemi_8_4000000 }, - - /* - * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado - */ - { PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, - PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - - /* - * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, - * from skokodyn@yahoo.com - */ - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0, - pbn_sbsxrsio }, - - /* - * Digitan DS560-558, from jimd@esoft.com - */ - { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_115200 }, - - /* - * Titan Electronic cards - * The 400L and 800L have a custom setup quirk. - */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_8_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - - /* - * Computone devices submitted by Doug McNash dmcnash@computone.com - */ - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, - 0, 0, pbn_computone_4 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, - 0, 0, pbn_computone_8 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, - 0, 0, pbn_computone_6 }, - - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi }, - { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_921600 }, - - /* - * AFAVLAB serial card, from Harald Welte - */ - { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_115200 }, - { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_115200 }, - - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_460800 }, - - /* - * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408). - * Cards are identified by their subsystem vendor IDs, which - * (in hex) match the model number. - * - * Note that JC140x are RS422/485 cards which require ox950 - * ACR = 0x10, and as such are not currently fully supported. - */ - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1204, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, -/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1402, 0x0002, 0, 0, - pbn_b0_2_921600 }, */ -/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1404, 0x0004, 0, 0, - pbn_b0_4_921600 }, */ - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, - 0x1204, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - /* - * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com - */ - { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_1382400 }, - - /* - * Dell Remote Access Card III - Tim_T_Murphy@Dell.com - */ - { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_1382400 }, - - /* - * RAStel 2 port modem, gerg@moreton.com.au - */ - { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - - /* - * EKF addition for i960 Boards form EKF with serial port - */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, - 0xE4BF, PCI_ANY_ID, 0, 0, - pbn_intel_i960 }, - - /* - * Xircom Cardbus/Ethernet combos - */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - /* - * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) - */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - - /* - * Untested PCI modems, sent in from various folks... - */ - - /* - * Elsa Model 56K PCI Modem, from Andreas Rath - */ - { PCI_VENDOR_ID_ROCKWELL, 0x1004, - 0x1048, 0x1500, 0, 0, - pbn_b1_1_115200 }, - - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - 0xFF00, 0, 0, 0, - pbn_sgi_ioc3 }, - - /* - * HP Diva card - */ - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, - PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0, - pbn_b1_1_115200 }, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_5_115200 }, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_2_115200 }, - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_4_115200 }, - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_8_115200 }, - - /* - * Exar Corp. XR17C15[248] Dual/Quad/Octal UART - */ - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C152 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C154 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C158 }, - - /* - * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) - */ - { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - /* - * ITE - */ - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b1_bt_1_115200 }, - - /* - * IntaShield IS-200 - */ - { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0811 */ - pbn_b2_2_115200 }, - /* - * IntaShield IS-400 - */ - { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */ - pbn_b2_4_115200 }, - /* - * Perle PCI-RAS cards - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4, - 0, 0, pbn_b2_4_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8, - 0, 0, pbn_b2_8_921600 }, - - /* - * Mainpine series cards: Fairly standard layout but fools - * parts of the autodetect in some cases and uses otherwise - * unmatched communications subclasses in the PCI Express case - */ - - { /* RockForceDUO */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0200, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUATRO */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0300, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceDUO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0400, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUATRO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0500, - 0, 0, pbn_b0_4_115200 }, - { /* RockForce+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0600, - 0, 0, pbn_b0_2_115200 }, - { /* RockForce+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0700, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceOCTO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0800, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceDUO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0C00, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUARTRO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0D00, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceOCTO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x1D00, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceD1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2000, - 0, 0, pbn_b0_1_115200 }, - { /* RockForceF1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2100, - 0, 0, pbn_b0_1_115200 }, - { /* RockForceD2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2200, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceF2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2300, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceD4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2400, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceF4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2500, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceD8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2600, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceF8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2700, - 0, 0, pbn_b0_8_115200 }, - { /* IQ Express D1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3000, - 0, 0, pbn_b0_1_115200 }, - { /* IQ Express F1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3100, - 0, 0, pbn_b0_1_115200 }, - { /* IQ Express D2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3200, - 0, 0, pbn_b0_2_115200 }, - { /* IQ Express F2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3300, - 0, 0, pbn_b0_2_115200 }, - { /* IQ Express D4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3400, - 0, 0, pbn_b0_4_115200 }, - { /* IQ Express F4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3500, - 0, 0, pbn_b0_4_115200 }, - { /* IQ Express D8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3C00, - 0, 0, pbn_b0_8_115200 }, - { /* IQ Express F8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3D00, - 0, 0, pbn_b0_8_115200 }, - - - /* - * PA Semi PA6T-1682M on-chip UART - */ - { PCI_VENDOR_ID_PASEMI, 0xa004, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_pasemi_1682M }, - - /* - * National Instruments - */ - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_16_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_16_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_8 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_8 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_16 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_16 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - - /* - * ADDI-DATA GmbH communication cards - */ - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA_OLD, - PCI_DEVICE_ID_ADDIDATA_APCI7800, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b1_8_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7800_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_8_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_4_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7420, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_2_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7300, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_1_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7800, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_8_3906250 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_VENDOR_ID_IBM, 0x0299, - 0, 0, pbn_b0_bt_2_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - /* the 9901 is a rebranded 9912 */ - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, - 0xA000, 0x3002, - 0, 0, pbn_NETMOS9900_2s_115200 }, - - /* - * Best Connectivity and Rosewill PCI Multi I/O cards - */ - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x3002, - 0, 0, pbn_b0_bt_2_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x3004, - 0, 0, pbn_b0_bt_4_115200 }, - /* Intel CE4100 */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ce4100_1_115200 }, - - /* - * Cronyx Omega PCI - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_omegapci }, - - /* - * These entries match devices with class COMMUNICATION_SERIAL, - * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL - */ - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, - 0xffff00, pbn_default }, - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MODEM << 8, - 0xffff00, pbn_default }, - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, - 0xffff00, pbn_default }, - { 0, } -}; - -static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, - pci_channel_state_t state) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - if (state == pci_channel_io_perm_failure) - return PCI_ERS_RESULT_DISCONNECT; - - if (priv) - pciserial_suspend_ports(priv); - - pci_disable_device(dev); - - return PCI_ERS_RESULT_NEED_RESET; -} - -static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) -{ - int rc; - - rc = pci_enable_device(dev); - - if (rc) - return PCI_ERS_RESULT_DISCONNECT; - - pci_restore_state(dev); - pci_save_state(dev); - - return PCI_ERS_RESULT_RECOVERED; -} - -static void serial8250_io_resume(struct pci_dev *dev) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - if (priv) - pciserial_resume_ports(priv); -} - -static struct pci_error_handlers serial8250_err_handler = { - .error_detected = serial8250_io_error_detected, - .slot_reset = serial8250_io_slot_reset, - .resume = serial8250_io_resume, -}; - -static struct pci_driver serial_pci_driver = { - .name = "serial", - .probe = pciserial_init_one, - .remove = __devexit_p(pciserial_remove_one), -#ifdef CONFIG_PM - .suspend = pciserial_suspend_one, - .resume = pciserial_resume_one, -#endif - .id_table = serial_pci_tbl, - .err_handler = &serial8250_err_handler, -}; - -static int __init serial8250_pci_init(void) -{ - return pci_register_driver(&serial_pci_driver); -} - -static void __exit serial8250_pci_exit(void) -{ - pci_unregister_driver(&serial_pci_driver); -} - -module_init(serial8250_pci_init); -module_exit(serial8250_pci_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); -MODULE_DEVICE_TABLE(pci, serial_pci_tbl); diff --git a/drivers/tty/serial/8250_pnp.c b/drivers/tty/serial/8250_pnp.c deleted file mode 100644 index a2f2365..0000000 --- a/drivers/tty/serial/8250_pnp.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Probe module for 8250/16550-type ISAPNP serial ports. - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - * - * Ported to the Linux PnP Layer - (C) Adam Belay. - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "8250.h" - -#define UNKNOWN_DEV 0x3000 - - -static const struct pnp_device_id pnp_dev_table[] = { - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "AAC000F", 0 }, - /* Anchor Datacomm BV */ - /* SXPro 144 External Data Fax Modem Plug & Play */ - { "ADC0001", 0 }, - /* SXPro 288 External Data Fax Modem Plug & Play */ - { "ADC0002", 0 }, - /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ - { "AEI0250", 0 }, - /* Actiontec ISA PNP 56K X2 Fax Modem */ - { "AEI1240", 0 }, - /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, - /* AZT3005 PnP SOUND DEVICE */ - { "AZT4001", 0 }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { "BDP3336", 0 }, - /* Boca Research */ - /* Boca Complete Ofc Communicator 14.4 Data-FAX */ - { "BRI0A49", 0 }, - /* Boca Research 33,600 ACF Modem */ - { "BRI1400", 0 }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { "BRI3400", 0 }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { "BRI0A49", 0 }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { "BDP3336", 0 }, - /* Computer Peripherals Inc */ - /* EuroViVa CommCenter-33.6 SP PnP */ - { "CPI4050", 0 }, - /* Creative Labs */ - /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ - { "CTL3001", 0 }, - /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ - { "CTL3011", 0 }, - /* Davicom ISA 33.6K Modem */ - { "DAV0336", 0 }, - /* Creative */ - /* Creative Modem Blaster Flash56 DI5601-1 */ - { "DMB1032", 0 }, - /* Creative Modem Blaster V.90 DI5660 */ - { "DMB2001", 0 }, - /* E-Tech */ - /* E-Tech CyberBULLET PC56RVP */ - { "ETT0002", 0 }, - /* FUJITSU */ - /* Fujitsu 33600 PnP-I2 R Plug & Play */ - { "FUJ0202", 0 }, - /* Fujitsu FMV-FX431 Plug & Play */ - { "FUJ0205", 0 }, - /* Fujitsu 33600 PnP-I4 R Plug & Play */ - { "FUJ0206", 0 }, - /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ - { "FUJ0209", 0 }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "GVC000F", 0 }, - /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ - { "GVC0303", 0 }, - /* Hayes */ - /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ - { "HAY0001", 0 }, - /* Hayes Optima 336 V.34 + FAX + Voice PnP */ - { "HAY000C", 0 }, - /* Hayes Optima 336B V.34 + FAX + Voice PnP */ - { "HAY000D", 0 }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5670", 0 }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5674", 0 }, - /* Hayes Accura 56K Fax Modem PnP */ - { "HAY5675", 0 }, - /* Hayes 288, V.34 + FAX */ - { "HAYF000", 0 }, - /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ - { "HAYF001", 0 }, - /* IBM */ - /* IBM Thinkpad 701 Internal Modem Voice */ - { "IBM0033", 0 }, - /* Intermec */ - /* Intermec CV60 touchscreen port */ - { "PNP4972", 0 }, - /* Intertex */ - /* Intertex 28k8 33k6 Voice EXT PnP */ - { "IXDC801", 0 }, - /* Intertex 33k6 56k Voice EXT PnP */ - { "IXDC901", 0 }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDD801", 0 }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDD901", 0 }, - /* Intertex 28k8 33k6 Voice SP INT PnP */ - { "IXDF401", 0 }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDF801", 0 }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDF901", 0 }, - /* Kortex International */ - /* KORTEX 28800 Externe PnP */ - { "KOR4522", 0 }, - /* KXPro 33.6 Vocal ASVD PnP */ - { "KORF661", 0 }, - /* Lasat */ - /* LASAT Internet 33600 PnP */ - { "LAS4040", 0 }, - /* Lasat Safire 560 PnP */ - { "LAS4540", 0 }, - /* Lasat Safire 336 PnP */ - { "LAS5440", 0 }, - /* Microcom, Inc. */ - /* Microcom TravelPorte FAST V.34 Plug & Play */ - { "MNP0281", 0 }, - /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ - { "MNP0336", 0 }, - /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ - { "MNP0339", 0 }, - /* Microcom DeskPorte 28.8P Plug & Play */ - { "MNP0342", 0 }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0500", 0 }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0501", 0 }, - /* Microcom DeskPorte 28.8S Internal Plug & Play */ - { "MNP0502", 0 }, - /* Motorola */ - /* Motorola BitSURFR Plug & Play */ - { "MOT1105", 0 }, - /* Motorola TA210 Plug & Play */ - { "MOT1111", 0 }, - /* Motorola HMTA 200 (ISDN) Plug & Play */ - { "MOT1114", 0 }, - /* Motorola BitSURFR Plug & Play */ - { "MOT1115", 0 }, - /* Motorola Lifestyle 28.8 Internal */ - { "MOT1190", 0 }, - /* Motorola V.3400 Plug & Play */ - { "MOT1501", 0 }, - /* Motorola Lifestyle 28.8 V.34 Plug & Play */ - { "MOT1502", 0 }, - /* Motorola Power 28.8 V.34 Plug & Play */ - { "MOT1505", 0 }, - /* Motorola ModemSURFR External 28.8 Plug & Play */ - { "MOT1509", 0 }, - /* Motorola Premier 33.6 Desktop Plug & Play */ - { "MOT150A", 0 }, - /* Motorola VoiceSURFR 56K External PnP */ - { "MOT150F", 0 }, - /* Motorola ModemSURFR 56K External PnP */ - { "MOT1510", 0 }, - /* Motorola ModemSURFR 56K Internal PnP */ - { "MOT1550", 0 }, - /* Motorola ModemSURFR Internal 28.8 Plug & Play */ - { "MOT1560", 0 }, - /* Motorola Premier 33.6 Internal Plug & Play */ - { "MOT1580", 0 }, - /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ - { "MOT15B0", 0 }, - /* Motorola VoiceSURFR 56K Internal PnP */ - { "MOT15F0", 0 }, - /* Com 1 */ - /* Deskline K56 Phone System PnP */ - { "MVX00A1", 0 }, - /* PC Rider K56 Phone System PnP */ - { "MVX00F2", 0 }, - /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ - { "nEC8241", 0 }, - /* Pace 56 Voice Internal Plug & Play Modem */ - { "PMC2430", 0 }, - /* Generic */ - /* Generic standard PC COM port */ - { "PNP0500", 0 }, - /* Generic 16550A-compatible COM port */ - { "PNP0501", 0 }, - /* Compaq 14400 Modem */ - { "PNPC000", 0 }, - /* Compaq 2400/9600 Modem */ - { "PNPC001", 0 }, - /* Dial-Up Networking Serial Cable between 2 PCs */ - { "PNPC031", 0 }, - /* Dial-Up Networking Parallel Cable between 2 PCs */ - { "PNPC032", 0 }, - /* Standard 9600 bps Modem */ - { "PNPC100", 0 }, - /* Standard 14400 bps Modem */ - { "PNPC101", 0 }, - /* Standard 28800 bps Modem*/ - { "PNPC102", 0 }, - /* Standard Modem*/ - { "PNPC103", 0 }, - /* Standard 9600 bps Modem*/ - { "PNPC104", 0 }, - /* Standard 14400 bps Modem*/ - { "PNPC105", 0 }, - /* Standard 28800 bps Modem*/ - { "PNPC106", 0 }, - /* Standard Modem */ - { "PNPC107", 0 }, - /* Standard 9600 bps Modem */ - { "PNPC108", 0 }, - /* Standard 14400 bps Modem */ - { "PNPC109", 0 }, - /* Standard 28800 bps Modem */ - { "PNPC10A", 0 }, - /* Standard Modem */ - { "PNPC10B", 0 }, - /* Standard 9600 bps Modem */ - { "PNPC10C", 0 }, - /* Standard 14400 bps Modem */ - { "PNPC10D", 0 }, - /* Standard 28800 bps Modem */ - { "PNPC10E", 0 }, - /* Standard Modem */ - { "PNPC10F", 0 }, - /* Standard PCMCIA Card Modem */ - { "PNP2000", 0 }, - /* Rockwell */ - /* Modular Technology */ - /* Rockwell 33.6 DPF Internal PnP */ - /* Modular Technology 33.6 Internal PnP */ - { "ROK0030", 0 }, - /* Kortex International */ - /* KORTEX 14400 Externe PnP */ - { "ROK0100", 0 }, - /* Rockwell 28.8 */ - { "ROK4120", 0 }, - /* Viking Components, Inc */ - /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ - { "ROK4920", 0 }, - /* Rockwell */ - /* British Telecom */ - /* Modular Technology */ - /* Rockwell 33.6 DPF External PnP */ - /* BT Prologue 33.6 External PnP */ - /* Modular Technology 33.6 External PnP */ - { "RSS00A0", 0 }, - /* Viking 56K FAX INT */ - { "RSS0262", 0 }, - /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ - { "RSS0250", 0 }, - /* SupraExpress 28.8 Data/Fax PnP modem */ - { "SUP1310", 0 }, - /* SupraExpress 336i PnP Voice Modem */ - { "SUP1381", 0 }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1421", 0 }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1590", 0 }, - /* SupraExpress 336i Sp ASVD */ - { "SUP1620", 0 }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1760", 0 }, - /* SupraExpress 56i Sp Intl */ - { "SUP2171", 0 }, - /* Phoebe Micro */ - /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ - { "TEX0011", 0 }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "UAC000F", 0 }, - /* 3Com Corp. */ - /* Gateway Telepath IIvi 33.6 */ - { "USR0000", 0 }, - /* U.S. Robotics Sporster 33.6K Fax INT PnP */ - { "USR0002", 0 }, - /* Sportster Vi 14.4 PnP FAX Voicemail */ - { "USR0004", 0 }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR0006", 0 }, - /* U.S. Robotics 33.6K Voice EXT PnP */ - { "USR0007", 0 }, - /* U.S. Robotics Courier V.Everything INT PnP */ - { "USR0009", 0 }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR2002", 0 }, - /* U.S. Robotics 56K Voice INT PnP */ - { "USR2070", 0 }, - /* U.S. Robotics 56K Voice EXT PnP */ - { "USR2080", 0 }, - /* U.S. Robotics 56K FAX INT */ - { "USR3031", 0 }, - /* U.S. Robotics 56K FAX INT */ - { "USR3050", 0 }, - /* U.S. Robotics 56K Voice INT PnP */ - { "USR3070", 0 }, - /* U.S. Robotics 56K Voice EXT PnP */ - { "USR3080", 0 }, - /* U.S. Robotics 56K Voice INT PnP */ - { "USR3090", 0 }, - /* U.S. Robotics 56K Message */ - { "USR9100", 0 }, - /* U.S. Robotics 56K FAX EXT PnP*/ - { "USR9160", 0 }, - /* U.S. Robotics 56K FAX INT PnP*/ - { "USR9170", 0 }, - /* U.S. Robotics 56K Voice EXT PnP*/ - { "USR9180", 0 }, - /* U.S. Robotics 56K Voice INT PnP*/ - { "USR9190", 0 }, - /* Wacom tablets */ - { "WACFXXX", 0 }, - /* Compaq touchscreen */ - { "FPI2002", 0 }, - /* Fujitsu Stylistic touchscreens */ - { "FUJ02B2", 0 }, - { "FUJ02B3", 0 }, - /* Fujitsu Stylistic LT touchscreens */ - { "FUJ02B4", 0 }, - /* Passive Fujitsu Stylistic touchscreens */ - { "FUJ02B6", 0 }, - { "FUJ02B7", 0 }, - { "FUJ02B8", 0 }, - { "FUJ02B9", 0 }, - { "FUJ02BC", 0 }, - /* Fujitsu Wacom Tablet PC device */ - { "FUJ02E5", 0 }, - /* Fujitsu P-series tablet PC device */ - { "FUJ02E6", 0 }, - /* Fujitsu Wacom 2FGT Tablet PC device */ - { "FUJ02E7", 0 }, - /* Fujitsu Wacom 1FGT Tablet PC device */ - { "FUJ02E9", 0 }, - /* - * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in - * disguise) - */ - { "LTS0001", 0 }, - /* Rockwell's (PORALiNK) 33600 INT PNP */ - { "WCI0003", 0 }, - /* Unknown PnP modems */ - { "PNPCXXX", UNKNOWN_DEV }, - /* More unknown PnP modems */ - { "PNPDXXX", UNKNOWN_DEV }, - { "", 0 } -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -static char *modem_names[] __devinitdata = { - "MODEM", "Modem", "modem", "FAX", "Fax", "fax", - "56K", "56k", "K56", "33.6", "28.8", "14.4", - "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", - "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL -}; - -static int __devinit check_name(char *name) -{ - char **tmp; - - for (tmp = modem_names; *tmp; tmp++) - if (strstr(name, *tmp)) - return 1; - - return 0; -} - -static int __devinit check_resources(struct pnp_dev *dev) -{ - resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; - int i; - - for (i = 0; i < ARRAY_SIZE(base); i++) { - if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) - return 1; - } - - return 0; -} - -/* - * Given a complete unknown PnP device, try to use some heuristics to - * detect modems. Currently use such heuristic set: - * - dev->name or dev->bus->name must contain "modem" substring; - * - device must have only one IO region (8 byte long) with base address - * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. - * - * Such detection looks very ugly, but can detect at least some of numerous - * PnP modems, alternatively we must hardcode all modems in pnp_devices[] - * table. - */ -static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) -{ - if (!(check_name(pnp_dev_name(dev)) || - (dev->card && check_name(dev->card->name)))) - return -ENODEV; - - if (check_resources(dev)) - return 0; - - return -ENODEV; -} - -static int __devinit -serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) -{ - struct uart_port port; - int ret, line, flags = dev_id->driver_data; - - if (flags & UNKNOWN_DEV) { - ret = serial_pnp_guess_board(dev, &flags); - if (ret < 0) - return ret; - } - - memset(&port, 0, sizeof(struct uart_port)); - if (pnp_irq_valid(dev, 0)) - port.irq = pnp_irq(dev, 0); - if (pnp_port_valid(dev, 0)) { - port.iobase = pnp_port_start(dev, 0); - port.iotype = UPIO_PORT; - } else if (pnp_mem_valid(dev, 0)) { - port.mapbase = pnp_mem_start(dev, 0); - port.iotype = UPIO_MEM; - port.flags = UPF_IOREMAP; - } else - return -ENODEV; - -#ifdef SERIAL_DEBUG_PNP - printk(KERN_DEBUG - "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", - port.iobase, port.mapbase, port.irq, port.iotype); -#endif - - port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - port.flags |= UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &dev->dev; - - line = serial8250_register_port(&port); - if (line < 0) - return -ENODEV; - - pnp_set_drvdata(dev, (void *)((long)line + 1)); - return 0; -} - -static void __devexit serial_pnp_remove(struct pnp_dev *dev) -{ - long line = (long)pnp_get_drvdata(dev); - if (line) - serial8250_unregister_port(line - 1); -} - -#ifdef CONFIG_PM -static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state) -{ - long line = (long)pnp_get_drvdata(dev); - - if (!line) - return -ENODEV; - serial8250_suspend_port(line - 1); - return 0; -} - -static int serial_pnp_resume(struct pnp_dev *dev) -{ - long line = (long)pnp_get_drvdata(dev); - - if (!line) - return -ENODEV; - serial8250_resume_port(line - 1); - return 0; -} -#else -#define serial_pnp_suspend NULL -#define serial_pnp_resume NULL -#endif /* CONFIG_PM */ - -static struct pnp_driver serial_pnp_driver = { - .name = "serial", - .probe = serial_pnp_probe, - .remove = __devexit_p(serial_pnp_remove), - .suspend = serial_pnp_suspend, - .resume = serial_pnp_resume, - .id_table = pnp_dev_table, -}; - -static int __init serial8250_pnp_init(void) -{ - return pnp_register_driver(&serial_pnp_driver); -} - -static void __exit serial8250_pnp_exit(void) -{ - pnp_unregister_driver(&serial_pnp_driver); -} - -module_init(serial8250_pnp_init); -module_exit(serial8250_pnp_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver"); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index aca2386..0bff238 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -5,279 +5,7 @@ menu "Serial drivers" depends on HAS_IOMEM -# -# The new 8250/16550 serial drivers -config SERIAL_8250 - tristate "8250/16550 and compatible serial support" - select SERIAL_CORE - ---help--- - This selects whether you want to include the driver for the standard - serial ports. The standard answer is Y. People who might say N - here are those that are setting up dedicated Ethernet WWW/FTP - servers, or users that have one of the various bus mice instead of a - serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) - - To compile this driver as a module, choose M here: the - module will be called 8250. - [WARNING: Do not compile this driver as a module if you are using - non-standard serial ports, since the configuration information will - be lost when the driver is unloaded. This limitation may be lifted - in the future.] - - BTW1: If you have a mouseman serial mouse which is not recognized by - the X window system, try running gpm first. - - BTW2: If you intend to use a software modem (also called Winmodem) - under Linux, forget it. These modems are crippled and require - proprietary drivers which are only available under Windows. - - Most people will say Y or M here, so that they can use serial mice, - modems and similar devices connecting to the standard serial ports. - -config SERIAL_8250_CONSOLE - bool "Console on 8250/16550 and compatible serial port" - depends on SERIAL_8250=y - select SERIAL_CORE_CONSOLE - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). This could be useful if some terminal or printer is connected - to that serial port. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyS1". (Try "man bootparam" or see the documentation of - your boot loader (grub or lilo or loadlin) about how to pass options - to the kernel at boot time.) - - If you don't have a VGA card installed and you say Y here, the - kernel will automatically use the first serial line, /dev/ttyS0, as - system console. - - You can set that using a kernel command line option such as - "console=uart8250,io,0x3f8,9600n8" - "console=uart8250,mmio,0xff5e0000,115200n8". - and it will switch to normal serial console when the corresponding - port is ready. - "earlycon=uart8250,io,0x3f8,9600n8" - "earlycon=uart8250,mmio,0xff5e0000,115200n8". - it will not only setup early console. - - If unsure, say N. - -config FIX_EARLYCON_MEM - bool - depends on X86 - default y - -config SERIAL_8250_GSC - tristate - depends on SERIAL_8250 && GSC - default SERIAL_8250 - -config SERIAL_8250_PCI - tristate "8250/16550 PCI device support" if EXPERT - depends on SERIAL_8250 && PCI - default SERIAL_8250 - help - This builds standard PCI serial support. You may be able to - disable this feature if you only need legacy serial support. - Saves about 9K. - -config SERIAL_8250_PNP - tristate "8250/16550 PNP device support" if EXPERT - depends on SERIAL_8250 && PNP - default SERIAL_8250 - help - This builds standard PNP serial support. You may be able to - disable this feature if you only need legacy serial support. - -config SERIAL_8250_FSL - bool - depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 - default PPC - -config SERIAL_8250_HP300 - tristate - depends on SERIAL_8250 && HP300 - default SERIAL_8250 - -config SERIAL_8250_CS - tristate "8250/16550 PCMCIA device support" - depends on PCMCIA && SERIAL_8250 - ---help--- - Say Y here to enable support for 16-bit PCMCIA serial devices, - including serial port cards, modems, and the modem functions of - multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are - credit-card size devices often used with laptops.) - - To compile this driver as a module, choose M here: the - module will be called serial_cs. - - If unsure, say N. - -config SERIAL_8250_NR_UARTS - int "Maximum number of 8250/16550 serial ports" - depends on SERIAL_8250 - default "4" - help - Set this to the number of serial ports you want the driver - to support. This includes any ports discovered via ACPI or - PCI enumeration and any ports that may be added at run-time - via hot-plug, or any ISA multi-port serial cards. - -config SERIAL_8250_RUNTIME_UARTS - int "Number of 8250/16550 serial ports to register at runtime" - depends on SERIAL_8250 - range 0 SERIAL_8250_NR_UARTS - default "4" - help - Set this to the maximum number of serial ports you want - the kernel to register at boot time. This can be overridden - with the module parameter "nr_uarts", or boot-time parameter - 8250.nr_uarts - -config SERIAL_8250_EXTENDED - bool "Extended 8250/16550 serial driver options" - depends on SERIAL_8250 - help - If you wish to use any non-standard features of the standard "dumb" - driver, say Y here. This includes HUB6 support, shared serial - interrupts, special multiport support, support for more than the - four COM 1/2/3/4 boards, etc. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about serial driver options. If unsure, say N. - -config SERIAL_8250_MANY_PORTS - bool "Support more than 4 legacy serial ports" - depends on SERIAL_8250_EXTENDED && !IA64 - help - Say Y here if you have dumb serial boards other than the four - standard COM 1/2/3/4 ports. This may happen if you have an AST - FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available - from ), or other custom - serial port hardware which acts similar to standard serial port - hardware. If you only use the standard COM 1/2/3/4 ports, you can - say N here to save some memory. You can also say Y if you have an - "intelligent" multiport card such as Cyclades, Digiboards, etc. - -# -# Multi-port serial cards -# - -config SERIAL_8250_FOURPORT - tristate "Support Fourport cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have an AST FourPort serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_fourport. - -config SERIAL_8250_ACCENT - tristate "Support Accent cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have an Accent Async serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_accent. - -config SERIAL_8250_BOCA - tristate "Support Boca cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have a Boca serial board. Please read the Boca - mini-HOWTO, available from - - To compile this driver as a module, choose M here: the module - will be called 8250_boca. - -config SERIAL_8250_EXAR_ST16C554 - tristate "Support Exar ST16C554/554D Quad UART" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - The Uplogix Envoy TU301 uses this Exar Quad UART. If you are - tinkering with your Envoy TU301, or have a machine with this UART, - say Y here. - - To compile this driver as a module, choose M here: the module - will be called 8250_exar_st16c554. - -config SERIAL_8250_HUB6 - tristate "Support Hub6 cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have a HUB6 serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_hub6. - -config SERIAL_8250_SHARE_IRQ - bool "Support for sharing serial interrupts" - depends on SERIAL_8250_EXTENDED - help - Some serial boards have hardware support which allows multiple dumb - serial ports on the same board to share a single IRQ. To enable - support for this in the serial driver, say Y here. - -config SERIAL_8250_DETECT_IRQ - bool "Autodetect IRQ on standard ports (unsafe)" - depends on SERIAL_8250_EXTENDED - help - Say Y here if you want the kernel to try to guess which IRQ - to use for your serial port. - - This is considered unsafe; it is far better to configure the IRQ in - a boot script using the setserial command. - - If unsure, say N. - -config SERIAL_8250_RSA - bool "Support RSA serial ports" - depends on SERIAL_8250_EXTENDED - help - ::: To be written ::: - -config SERIAL_8250_MCA - tristate "Support 8250-type ports on MCA buses" - depends on SERIAL_8250 != n && MCA - help - Say Y here if you have a MCA serial ports. - - To compile this driver as a module, choose M here: the module - will be called 8250_mca. - -config SERIAL_8250_ACORN - tristate "Acorn expansion card serial port support" - depends on ARCH_ACORN && SERIAL_8250 - help - If you have an Atomwide Serial card or Serial Port card for an Acorn - system, say Y to this option. The driver can handle 1, 2, or 3 port - cards. If unsure, say N. - -config SERIAL_8250_RM9K - bool "Support for MIPS RM9xxx integrated serial port" - depends on SERIAL_8250 != n && SERIAL_RM9000 - select SERIAL_8250_SHARE_IRQ - help - Selecting this option will add support for the integrated serial - port hardware found on MIPS RM9122 and similar processors. - If unsure, say N. - -config SERIAL_8250_DW - tristate "Support for Synopsys DesignWare 8250 quirks" - depends on SERIAL_8250 && OF - help - Selecting this option will enable handling of the extra features - present in the Synopsys DesignWare APB UART. +source "drivers/tty/serial/8250/Kconfig" comment "Non-8250 serial port support" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index f5b01f2..a6d1ac0 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -14,22 +14,9 @@ obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o -obj-$(CONFIG_SERIAL_8250) += 8250.o -obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o -obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o -obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o -obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o -obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o -obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o -obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o -obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o -obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o -obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o -obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o -obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o -obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o -obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o -obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o +# Now bring in any enabled 8250/16450/16550 type drivers. +obj-$(CONFIG_SERIAL_8250) += 8250/ + obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c deleted file mode 100644 index 94a6792..0000000 --- a/drivers/tty/serial/m32r_sio.c +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * m32r_sio.c - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.c. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata - * - * 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. - */ - -/* - * A note about mapbase / membase - * - * mapbase is the physical address of the IO port. Currently, we don't - * support this very well, and it may well be dropped from this driver - * in future. As such, mapbase should be NULL. - * - * membase is an 'ioremapped' cookie. This is compatible with the old - * serial.c driver, and is currently the preferred form. - */ - -#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define PORT_M32R_BASE PORT_M32R_SIO -#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) -#define BAUD_RATE 115200 - -#include -#include "m32r_sio.h" -#include "m32r_sio_reg.h" - -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - -#define PASS_LIMIT 256 - -/* - * We default to IRQ0 for the "no irq" hack. Some - * machine types want others as well - they're free - * to redefine this in their header file. - */ -#define is_real_interrupt(irq) ((irq) != 0) - -#define BASE_BAUD 115200 - -/* Standard COM flags */ -#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) - -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ -#if defined(CONFIG_PLAT_USRV) - -#define SERIAL_PORT_DFNS \ - /* UART CLK PORT IRQ FLAGS */ \ - { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ - -#else /* !CONFIG_PLAT_USRV */ - -#if defined(CONFIG_SERIAL_M32R_PLDSIO) -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ - STD_COM_FLAGS }, /* ttyS0 */ -#else -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ - STD_COM_FLAGS }, /* ttyS0 */ -#endif - -#endif /* !CONFIG_PLAT_USRV */ - -static struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS -}; - -#define UART_NR ARRAY_SIZE(old_serial_port) - -struct uart_sio_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short rev; - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char lsr_break_flag; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); -}; - -struct irq_info { - spinlock_t lock; - struct list_head *head; -}; - -static struct irq_info irq_lists[NR_IRQS]; - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial_uart_config uart_config[] = { - [PORT_UNKNOWN] = { - .name = "unknown", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, - [PORT_INDEX(PORT_M32R_SIO)] = { - .name = "M32RSIO", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, -}; - -#ifdef CONFIG_SERIAL_M32R_PLDSIO - -#define __sio_in(x) inw((unsigned long)(x)) -#define __sio_out(v,x) outw((v),(unsigned long)(x)) - -static inline void sio_set_baud_rate(unsigned long baud) -{ - unsigned short sbaud; - sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1; - __sio_out(sbaud, PLD_ESIO0BAUR); -} - -static void sio_reset(void) -{ - unsigned short tmp; - - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0CR); - sio_set_baud_rate(BAUD_RATE); - __sio_out(0x0300, PLD_ESIO0CR); - __sio_out(0x0003, PLD_ESIO0CR); -} - -static void sio_init(void) -{ - unsigned short tmp; - - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0CR); - __sio_out(0x0300, PLD_ESIO0CR); - __sio_out(0x0003, PLD_ESIO0CR); -} - -static void sio_error(int *status) -{ - printk("SIO0 error[%04x]\n", *status); - do { - sio_init(); - } while ((*status = __sio_in(PLD_ESIO0CR)) != 3); -} - -#else /* not CONFIG_SERIAL_M32R_PLDSIO */ - -#define __sio_in(x) inl(x) -#define __sio_out(v,x) outl((v),(x)) - -static inline void sio_set_baud_rate(unsigned long baud) -{ - unsigned long i, j; - - i = boot_cpu_data.bus_clock / (baud * 16); - j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud; - i -= 1; - j = (j + 1) >> 1; - - __sio_out(i, M32R_SIO0_BAUR_PORTL); - __sio_out(j, M32R_SIO0_RBAUR_PORTL); -} - -static void sio_reset(void) -{ - __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */ - __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */ - __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */ - sio_set_baud_rate(BAUD_RATE); - __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL); - __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */ -} - -static void sio_init(void) -{ - unsigned int tmp; - - tmp = __sio_in(M32R_SIO0_RXB_PORTL); - tmp = __sio_in(M32R_SIO0_RXB_PORTL); - tmp = __sio_in(M32R_SIO0_STS_PORTL); - __sio_out(0x00000003, M32R_SIO0_CR_PORTL); -} - -static void sio_error(int *status) -{ - printk("SIO0 error[%04x]\n", *status); - do { - sio_init(); - } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3); -} - -#endif /* CONFIG_SERIAL_M32R_PLDSIO */ - -static unsigned int sio_in(struct uart_sio_port *up, int offset) -{ - return __sio_in(up->port.iobase + offset); -} - -static void sio_out(struct uart_sio_port *up, int offset, int value) -{ - __sio_out(value, up->port.iobase + offset); -} - -static unsigned int serial_in(struct uart_sio_port *up, int offset) -{ - if (!offset) - return 0; - - return __sio_in(offset); -} - -static void serial_out(struct uart_sio_port *up, int offset, int value) -{ - if (!offset) - return; - - __sio_out(value, offset); -} - -static void m32r_sio_stop_tx(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void m32r_sio_start_tx(struct uart_port *port) -{ -#ifdef CONFIG_SERIAL_M32R_PLDSIO - struct uart_sio_port *up = (struct uart_sio_port *)port; - struct circ_buf *xmit = &up->port.state->xmit; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - } - while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); -#else - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -#endif -} - -static void m32r_sio_stop_rx(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void m32r_sio_enable_ms(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void receive_chars(struct uart_sio_port *up, int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch; - unsigned char flag; - int max_count = 256; - - do { - ch = sio_in(up, SIORXB); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ingored. - */ - *status &= up->port.read_status_mask; - - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } - - if (*status & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); - - if (*status & UART_LSR_OE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - ignore_char: - *status = serial_in(up, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); -} - -static void transmit_chars(struct uart_sio_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { -#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */ - serial_out(up, UART_TX, up->port.x_char); -#endif - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - m32r_sio_stop_tx(&up->port); - return; - } - - count = up->port.fifosize; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - while (!(serial_in(up, UART_LSR) & UART_LSR_THRE)); - - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - DEBUG_INTR("THRE..."); - - if (uart_circ_empty(xmit)) - m32r_sio_stop_tx(&up->port); -} - -/* - * This handles the interrupt from one port. - */ -static inline void m32r_sio_handle_port(struct uart_sio_port *up, - unsigned int status) -{ - DEBUG_INTR("status = %x...", status); - - if (status & 0x04) - receive_chars(up, &status); - if (status & 0x01) - transmit_chars(up); -} - -/* - * This is the serial driver's interrupt routine. - * - * Arjan thinks the old way was overly complex, so it got simplified. - * Alan disagrees, saying that need the complexity to handle the weird - * nature of ISA shared interrupts. (This is a special exception.) - * - * In order to handle ISA shared interrupts properly, we need to check - * that all ports have been serviced, and therefore the ISA interrupt - * line has been de-asserted. - * - * This means we need to loop through all ports. checking that they - * don't have an interrupt pending. - */ -static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) -{ - struct irq_info *i = dev_id; - struct list_head *l, *end = NULL; - int pass_counter = 0; - - DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); - -#ifdef CONFIG_SERIAL_M32R_PLDSIO -// if (irq == PLD_IRQ_SIO0_SND) -// irq = PLD_IRQ_SIO0_RCV; -#else - if (irq == M32R_IRQ_SIO0_S) - irq = M32R_IRQ_SIO0_R; -#endif - - spin_lock(&i->lock); - - l = i->head; - do { - struct uart_sio_port *up; - unsigned int sts; - - up = list_entry(l, struct uart_sio_port, list); - - sts = sio_in(up, SIOSTS); - if (sts & 0x5) { - spin_lock(&up->port.lock); - m32r_sio_handle_port(up, sts); - spin_unlock(&up->port.lock); - - end = NULL; - } else if (end == NULL) - end = l; - - l = l->next; - - if (l == i->head && pass_counter++ > PASS_LIMIT) { - if (sts & 0xe0) - sio_error(&sts); - break; - } - } while (l != end); - - spin_unlock(&i->lock); - - DEBUG_INTR("end.\n"); - - return IRQ_HANDLED; -} - -/* - * To support ISA shared interrupts, we need to have one interrupt - * handler that ensures that the IRQ line has been deasserted - * before returning. Failing to do this will result in the IRQ - * line being stuck active, and, since ISA irqs are edge triggered, - * no more IRQs will be seen. - */ -static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up) -{ - spin_lock_irq(&i->lock); - - if (!list_empty(i->head)) { - if (i->head == &up->list) - i->head = i->head->next; - list_del(&up->list); - } else { - BUG_ON(i->head != &up->list); - i->head = NULL; - } - - spin_unlock_irq(&i->lock); -} - -static int serial_link_irq_chain(struct uart_sio_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - int ret, irq_flags = 0; - - spin_lock_irq(&i->lock); - - if (i->head) { - list_add(&up->list, i->head); - spin_unlock_irq(&i->lock); - - ret = 0; - } else { - INIT_LIST_HEAD(&up->list); - i->head = &up->list; - spin_unlock_irq(&i->lock); - - ret = request_irq(up->port.irq, m32r_sio_interrupt, - irq_flags, "SIO0-RX", i); - ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt, - irq_flags, "SIO0-TX", i); - if (ret < 0) - serial_do_unlink(i, up); - } - - return ret; -} - -static void serial_unlink_irq_chain(struct uart_sio_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - - BUG_ON(i->head == NULL); - - if (list_empty(i->head)) { - free_irq(up->port.irq, i); - free_irq(up->port.irq + 1, i); - } - - serial_do_unlink(i, up); -} - -/* - * This function is used to handle ports that do not have an interrupt. - */ -static void m32r_sio_timeout(unsigned long data) -{ - struct uart_sio_port *up = (struct uart_sio_port *)data; - unsigned int timeout; - unsigned int sts; - - sts = sio_in(up, SIOSTS); - if (sts & 0x5) { - spin_lock(&up->port.lock); - m32r_sio_handle_port(up, sts); - spin_unlock(&up->port.lock); - } - - timeout = up->port.timeout; - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - mod_timer(&up->timer, jiffies + timeout); -} - -static unsigned int m32r_sio_tx_empty(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int m32r_sio_get_mctrl(struct uart_port *port) -{ - return 0; -} - -static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - -} - -static void m32r_sio_break_ctl(struct uart_port *port, int break_state) -{ - -} - -static int m32r_sio_startup(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - int retval; - - sio_init(); - - /* - * If the "interrupt" for this port doesn't correspond with any - * hardware interrupt, we use a timer-based system. The original - * driver used to do this with IRQ0. - */ - if (!is_real_interrupt(up->port.irq)) { - unsigned int timeout = up->port.timeout; - - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + timeout); - } else { - retval = serial_link_irq_chain(up); - if (retval) - return retval; - } - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - * - M32R_SIO: 0x0c - * - M32R_PLDSIO: 0x04 - */ - up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - sio_out(up, SIOTRCR, up->ier); - - /* - * And clear the interrupt registers again for luck. - */ - sio_reset(); - - return 0; -} - -static void m32r_sio_shutdown(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - sio_out(up, SIOTRCR, 0); - - /* - * Disable break condition and FIFOs - */ - - sio_init(); - - if (!is_real_interrupt(up->port.irq)) - del_timer_sync(&up->timer); - else - serial_unlink_irq_chain(up); -} - -static unsigned int m32r_sio_get_divisor(struct uart_port *port, - unsigned int baud) -{ - return uart_get_divisor(port, baud); -} - -static void m32r_sio_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned char cval = 0; - unsigned long flags; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (termios->c_cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* - * Ask the core to calculate the divisor for us. - */ -#ifdef CONFIG_SERIAL_M32R_PLDSIO - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); -#else - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); -#endif - quot = m32r_sio_get_divisor(port, baud); - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - sio_set_baud_rate(baud); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - up->lcr = cval; /* Save LCR */ - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void m32r_sio_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (up->pm) - up->pm(port, state, oldstate); -} - -/* - * Resource handling. This is complicated by the fact that resources - * depend on the port type. Maybe we should be claiming the standard - * 8250 ports, and then trying to get other resources as necessary? - */ -static int -m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) -{ - unsigned int size = 8 << up->port.regshift; -#ifndef CONFIG_SERIAL_M32R_PLDSIO - unsigned long start; -#endif - int ret = 0; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { -#ifdef CONFIG_SERIAL_M32R_PLDSIO - *res = request_mem_region(up->port.mapbase, size, "serial"); -#else - start = up->port.mapbase; - *res = request_mem_region(start, size, "serial"); -#endif - if (!*res) - ret = -EBUSY; - } - break; - - case UPIO_PORT: - *res = request_region(up->port.iobase, size, "serial"); - if (!*res) - ret = -EBUSY; - break; - } - return ret; -} - -static void m32r_sio_release_port(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long start, offset = 0, size = 0; - - size <<= up->port.regshift; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { - /* - * Unmap the area. - */ - iounmap(up->port.membase); - up->port.membase = NULL; - - start = up->port.mapbase; - - if (size) - release_mem_region(start + offset, size); - release_mem_region(start, 8 << up->port.regshift); - } - break; - - case UPIO_PORT: - start = up->port.iobase; - - if (size) - release_region(start + offset, size); - release_region(start + offset, 8 << up->port.regshift); - break; - - default: - break; - } -} - -static int m32r_sio_request_port(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - struct resource *res = NULL; - int ret = 0; - - ret = m32r_sio_request_std_resource(up, &res); - - /* - * If we have a mapbase, then request that as well. - */ - if (ret == 0 && up->port.flags & UPF_IOREMAP) { - int size = resource_size(res); - - up->port.membase = ioremap(up->port.mapbase, size); - if (!up->port.membase) - ret = -ENOMEM; - } - - if (ret < 0) { - if (res) - release_resource(res); - } - - return ret; -} - -static void m32r_sio_config_port(struct uart_port *port, int unused) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int -m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if (ser->irq >= nr_irqs || ser->irq < 0 || - ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || - ser->type >= ARRAY_SIZE(uart_config)) - return -EINVAL; - return 0; -} - -static const char * -m32r_sio_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops m32r_sio_pops = { - .tx_empty = m32r_sio_tx_empty, - .set_mctrl = m32r_sio_set_mctrl, - .get_mctrl = m32r_sio_get_mctrl, - .stop_tx = m32r_sio_stop_tx, - .start_tx = m32r_sio_start_tx, - .stop_rx = m32r_sio_stop_rx, - .enable_ms = m32r_sio_enable_ms, - .break_ctl = m32r_sio_break_ctl, - .startup = m32r_sio_startup, - .shutdown = m32r_sio_shutdown, - .set_termios = m32r_sio_set_termios, - .pm = m32r_sio_pm, - .type = m32r_sio_type, - .release_port = m32r_sio_release_port, - .request_port = m32r_sio_request_port, - .config_port = m32r_sio_config_port, - .verify_port = m32r_sio_verify_port, -}; - -static struct uart_sio_port m32r_sio_ports[UART_NR]; - -static void __init m32r_sio_init_ports(void) -{ - struct uart_sio_port *up; - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); - i++, up++) { - up->port.iobase = old_serial_port[i].port; - up->port.irq = irq_canonicalize(old_serial_port[i].irq); - up->port.uartclk = old_serial_port[i].baud_base * 16; - up->port.flags = old_serial_port[i].flags; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; - up->port.ops = &m32r_sio_pops; - } -} - -static void __init m32r_sio_register_ports(struct uart_driver *drv) -{ - int i; - - m32r_sio_init_ports(); - - for (i = 0; i < UART_NR; i++) { - struct uart_sio_port *up = &m32r_sio_ports[i]; - - up->port.line = i; - up->port.ops = &m32r_sio_pops; - init_timer(&up->timer); - up->timer.function = m32r_sio_timeout; - - up->mcr_mask = ~0; - up->mcr_force = 0; - - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_sio_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = sio_in(up, SIOSTS); - - if (--tmout == 0) - break; - udelay(1); - } while ((status & UART_EMPTY) != UART_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout) - udelay(1); - } -} - -static void m32r_sio_console_putchar(struct uart_port *port, int ch) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - wait_for_xmitr(up); - sio_out(up, SIOTXB, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - */ -static void m32r_sio_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_sio_port *up = &m32r_sio_ports[co->index]; - unsigned int ier; - - /* - * First save the UER then disable the interrupts - */ - ier = sio_in(up, SIOTRCR); - sio_out(up, SIOTRCR, 0); - - uart_console_write(&up->port, s, count, m32r_sio_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - sio_out(up, SIOTRCR, ier); -} - -static int __init m32r_sio_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= UART_NR) - co->index = 0; - port = &m32r_sio_ports[co->index].port; - - /* - * Temporary fix. - */ - spin_lock_init(&port->lock); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver m32r_sio_reg; -static struct console m32r_sio_console = { - .name = "ttyS", - .write = m32r_sio_console_write, - .device = uart_console_device, - .setup = m32r_sio_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &m32r_sio_reg, -}; - -static int __init m32r_sio_console_init(void) -{ - sio_reset(); - sio_init(); - m32r_sio_init_ports(); - register_console(&m32r_sio_console); - return 0; -} -console_initcall(m32r_sio_console_init); - -#define M32R_SIO_CONSOLE &m32r_sio_console -#else -#define M32R_SIO_CONSOLE NULL -#endif - -static struct uart_driver m32r_sio_reg = { - .owner = THIS_MODULE, - .driver_name = "sio", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = UART_NR, - .cons = M32R_SIO_CONSOLE, -}; - -/** - * m32r_sio_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -void m32r_sio_suspend_port(int line) -{ - uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -/** - * m32r_sio_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -void m32r_sio_resume_port(int line) -{ - uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -static int __init m32r_sio_init(void) -{ - int ret, i; - - printk(KERN_INFO "Serial: M32R SIO driver\n"); - - for (i = 0; i < nr_irqs; i++) - spin_lock_init(&irq_lists[i].lock); - - ret = uart_register_driver(&m32r_sio_reg); - if (ret >= 0) - m32r_sio_register_ports(&m32r_sio_reg); - - return ret; -} - -static void __exit m32r_sio_exit(void) -{ - int i; - - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port); - - uart_unregister_driver(&m32r_sio_reg); -} - -module_init(m32r_sio_init); -module_exit(m32r_sio_exit); - -EXPORT_SYMBOL(m32r_sio_suspend_port); -EXPORT_SYMBOL(m32r_sio_resume_port); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/tty/serial/m32r_sio.h b/drivers/tty/serial/m32r_sio.h deleted file mode 100644 index e9b7e11..0000000 --- a/drivers/tty/serial/m32r_sio.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * m32r_sio.h - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.h. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata - * - * 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. - */ - - -struct m32r_sio_probe { - struct module *owner; - int (*pci_init_one)(struct pci_dev *dev); - void (*pci_remove_one)(struct pci_dev *dev); - void (*pnp_init)(void); -}; - -int m32r_sio_register_probe(struct m32r_sio_probe *probe); -void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); -void m32r_sio_get_irq_map(unsigned int *map); -void m32r_sio_suspend_port(int line); -void m32r_sio_resume_port(int line); - -struct old_serial_port { - unsigned int uart; - unsigned int baud_base; - unsigned int port; - unsigned int irq; - unsigned int flags; - unsigned char io_type; - unsigned char __iomem *iomem_base; - unsigned short iomem_reg_shift; -}; - -#define _INLINE_ inline - -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/tty/serial/m32r_sio_reg.h b/drivers/tty/serial/m32r_sio_reg.h deleted file mode 100644 index 4671473..0000000 --- a/drivers/tty/serial/m32r_sio_reg.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * m32r_sio_reg.h - * - * Copyright (C) 1992, 1994 by Theodore Ts'o. - * Copyright (C) 2004 Hirokazu Takata - * - * Redistribution of this file is permitted under the terms of the GNU - * Public License (GPL) - * - * These are the UART port assignments, expressed as offsets from the base - * register. These assignments should hold for any serial port based on - * a 8250, 16450, or 16550(A). - */ - -#ifndef _M32R_SIO_REG_H -#define _M32R_SIO_REG_H - - -#ifdef CONFIG_SERIAL_M32R_PLDSIO - -#define SIOCR 0x000 -#define SIOMOD0 0x002 -#define SIOMOD1 0x004 -#define SIOSTS 0x006 -#define SIOTRCR 0x008 -#define SIOBAUR 0x00a -// #define SIORBAUR 0x018 -#define SIOTXB 0x00c -#define SIORXB 0x00e - -#define UART_RX ((unsigned long) PLD_ESIO0RXB) - /* In: Receive buffer (DLAB=0) */ -#define UART_TX ((unsigned long) PLD_ESIO0TXB) - /* Out: Transmit buffer (DLAB=0) */ -#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ -#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx - * In: Fifo count - * Out: Fifo custom trigger levels - * XR16C85x only */ - -#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ -#define UART_IER ((unsigned long) PLD_ESIO0INTCR) - /* Out: Interrupt Enable Register */ -#define UART_FCTR 0 /* (LCR=BF) Feature Control Register - * XR16C85x only */ - -#define UART_IIR 0 /* In: Interrupt ID Register */ -#define UART_FCR 0 /* Out: FIFO Control Register */ -#define UART_EFR 0 /* I/O: Extended Features Register */ - /* (DLAB=1, 16C660 only) */ - -#define UART_LCR 0 /* Out: Line Control Register */ -#define UART_MCR 0 /* Out: Modem Control Register */ -#define UART_LSR ((unsigned long) PLD_ESIO0STS) - /* In: Line Status Register */ -#define UART_MSR 0 /* In: Modem Status Register */ -#define UART_SCR 0 /* I/O: Scratch Register */ -#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register - * FCTR bit 6 selects SCR or EMSR - * XR16c85x only */ - -#else /* not CONFIG_SERIAL_M32R_PLDSIO */ - -#define SIOCR 0x000 -#define SIOMOD0 0x004 -#define SIOMOD1 0x008 -#define SIOSTS 0x00c -#define SIOTRCR 0x010 -#define SIOBAUR 0x014 -#define SIORBAUR 0x018 -#define SIOTXB 0x01c -#define SIORXB 0x020 - -#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */ -#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */ -#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ -#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx - * In: Fifo count - * Out: Fifo custom trigger levels - * XR16C85x only */ - -#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ -#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */ -#define UART_FCTR 0 /* (LCR=BF) Feature Control Register - * XR16C85x only */ - -#define UART_IIR 0 /* In: Interrupt ID Register */ -#define UART_FCR 0 /* Out: FIFO Control Register */ -#define UART_EFR 0 /* I/O: Extended Features Register */ - /* (DLAB=1, 16C660 only) */ - -#define UART_LCR 0 /* Out: Line Control Register */ -#define UART_MCR 0 /* Out: Modem Control Register */ -#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */ -#define UART_MSR 0 /* In: Modem Status Register */ -#define UART_SCR 0 /* I/O: Scratch Register */ -#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register - * FCTR bit 6 selects SCR or EMSR - * XR16c85x only */ - -#endif /* CONFIG_SERIAL_M32R_PLDSIO */ - -#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * These are the definitions for the Line Control Register - * - * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting - * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. - */ -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ -#define UART_LCR_SBC 0x40 /* Set break control */ -#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ -#define UART_LCR_EPAR 0x10 /* Even parity select */ -#define UART_LCR_PARITY 0x08 /* Parity Enable */ -#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ -#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ -#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ -#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ -#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ - -/* - * These are the definitions for the Line Status Register - */ -#define UART_LSR_TEMT 0x02 /* Transmitter empty */ -#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x00 /* Break interrupt indicator */ -#define UART_LSR_FE 0x80 /* Frame error indicator */ -#define UART_LSR_PE 0x40 /* Parity error indicator */ -#define UART_LSR_OE 0x20 /* Overrun error indicator */ -#define UART_LSR_DR 0x04 /* Receiver data ready */ - -/* - * These are the definitions for the Interrupt Identification Register - */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ - -#define UART_IIR_MSI 0x00 /* Modem status interrupt */ -#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ -#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ - -/* - * These are the definitions for the Interrupt Enable Register - */ -#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */ -#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */ -#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */ -#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */ - -#endif /* _M32R_SIO_REG_H */ diff --git a/drivers/tty/serial/serial_cs.c b/drivers/tty/serial/serial_cs.c deleted file mode 100644 index 8609060..0000000 --- a/drivers/tty/serial/serial_cs.c +++ /dev/null @@ -1,870 +0,0 @@ -/*====================================================================== - - A driver for PCMCIA serial devices - - serial_cs.c 1.134 2002/05/04 05:48:53 - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "8250.h" - - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* Enable the speaker? */ -static int do_sound = 1; -/* Skip strict UART tests? */ -static int buggy_uart; - -module_param(do_sound, int, 0444); -module_param(buggy_uart, int, 0444); - -/*====================================================================*/ - -/* Table of multi-port card ID's */ - -struct serial_quirk { - unsigned int manfid; - unsigned int prodid; - int multi; /* 1 = multifunction, > 1 = # ports */ - void (*config)(struct pcmcia_device *); - void (*setup)(struct pcmcia_device *, struct uart_port *); - void (*wakeup)(struct pcmcia_device *); - int (*post)(struct pcmcia_device *); -}; - -struct serial_info { - struct pcmcia_device *p_dev; - int ndev; - int multi; - int slave; - int manfid; - int prodid; - int c950ctrl; - int line[4]; - const struct serial_quirk *quirk; -}; - -struct serial_cfg_mem { - tuple_t tuple; - cisparse_t parse; - u_char buf[256]; -}; - -/* - * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" - * manfid 0x0160, 0x0104 - * This card appears to have a 14.7456MHz clock. - */ -/* Generic Modem: MD55x (GPRS/EDGE) have - * Elan VPU16551 UART with 14.7456MHz oscillator - * manfid 0x015D, 0x4C45 - */ -static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) -{ - port->uartclk = 14745600; -} - -static int quirk_post_ibm(struct pcmcia_device *link) -{ - u8 val; - int ret; - - ret = pcmcia_read_config_byte(link, 0x800, &val); - if (ret) - goto failed; - - ret = pcmcia_write_config_byte(link, 0x800, val | 1); - if (ret) - goto failed; - return 0; - - failed: - return -ENODEV; -} - -/* - * Nokia cards are not really multiport cards. Shouldn't this - * be handled by setting the quirk entry .multi = 0 | 1 ? - */ -static void quirk_config_nokia(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - if (info->multi > 1) - info->multi = 1; -} - -static void quirk_wakeup_oxsemi(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - if (info->c950ctrl) - outb(12, info->c950ctrl + 1); -} - -/* request_region? oxsemi branch does no request_region too... */ -/* - * This sequence is needed to properly initialize MC45 attached to OXCF950. - * I tried decreasing these msleep()s, but it worked properly (survived - * 1000 stop/start operations) with these timeouts (or bigger). - */ -static void quirk_wakeup_possio_gcc(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - unsigned int ctrl = info->c950ctrl; - - outb(0xA, ctrl + 1); - msleep(100); - outb(0xE, ctrl + 1); - msleep(300); - outb(0xC, ctrl + 1); - msleep(100); - outb(0xE, ctrl + 1); - msleep(200); - outb(0xF, ctrl + 1); - msleep(100); - outb(0xE, ctrl + 1); - msleep(100); - outb(0xC, ctrl + 1); -} - -/* - * Socket Dual IO: this enables irq's for second port - */ -static void quirk_config_socket(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - if (info->multi) - link->config_flags |= CONF_ENABLE_ESR; -} - -static const struct serial_quirk quirks[] = { - { - .manfid = 0x0160, - .prodid = 0x0104, - .multi = -1, - .setup = quirk_setup_brainboxes_0104, - }, { - .manfid = 0x015D, - .prodid = 0x4C45, - .multi = -1, - .setup = quirk_setup_brainboxes_0104, - }, { - .manfid = MANFID_IBM, - .prodid = ~0, - .multi = -1, - .post = quirk_post_ibm, - }, { - .manfid = MANFID_INTEL, - .prodid = PRODID_INTEL_DUAL_RS232, - .multi = 2, - }, { - .manfid = MANFID_NATINST, - .prodid = PRODID_NATINST_QUAD_RS232, - .multi = 4, - }, { - .manfid = MANFID_NOKIA, - .prodid = ~0, - .multi = -1, - .config = quirk_config_nokia, - }, { - .manfid = MANFID_OMEGA, - .prodid = PRODID_OMEGA_QSP_100, - .multi = 4, - }, { - .manfid = MANFID_OXSEMI, - .prodid = ~0, - .multi = -1, - .wakeup = quirk_wakeup_oxsemi, - }, { - .manfid = MANFID_POSSIO, - .prodid = PRODID_POSSIO_GCC, - .multi = -1, - .wakeup = quirk_wakeup_possio_gcc, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_DUAL_RS232, - .multi = 2, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_DUAL_RS232_D1, - .multi = 2, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_DUAL_RS232_G, - .multi = 2, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_QUAD_RS232, - .multi = 4, - }, { - .manfid = MANFID_SOCKET, - .prodid = PRODID_SOCKET_DUAL_RS232, - .multi = 2, - .config = quirk_config_socket, - }, { - .manfid = MANFID_SOCKET, - .prodid = ~0, - .multi = -1, - .config = quirk_config_socket, - } -}; - - -static int serial_config(struct pcmcia_device * link); - - -static void serial_remove(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i; - - dev_dbg(&link->dev, "serial_release\n"); - - /* - * Recheck to see if the device is still configured. - */ - for (i = 0; i < info->ndev; i++) - serial8250_unregister_port(info->line[i]); - - if (!info->slave) - pcmcia_disable_device(link); -} - -static int serial_suspend(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i; - - for (i = 0; i < info->ndev; i++) - serial8250_suspend_port(info->line[i]); - - return 0; -} - -static int serial_resume(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i; - - for (i = 0; i < info->ndev; i++) - serial8250_resume_port(info->line[i]); - - if (info->quirk && info->quirk->wakeup) - info->quirk->wakeup(link); - - return 0; -} - -static int serial_probe(struct pcmcia_device *link) -{ - struct serial_info *info; - - dev_dbg(&link->dev, "serial_attach()\n"); - - /* Create new serial device */ - info = kzalloc(sizeof (*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - info->p_dev = link; - link->priv = info; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - if (do_sound) - link->config_flags |= CONF_ENABLE_SPKR; - - return serial_config(link); -} - -static void serial_detach(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - dev_dbg(&link->dev, "serial_detach\n"); - - /* - * Ensure that the ports have been released. - */ - serial_remove(link); - - /* free bits */ - kfree(info); -} - -/*====================================================================*/ - -static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, - unsigned int iobase, int irq) -{ - struct uart_port port; - int line; - - memset(&port, 0, sizeof (struct uart_port)); - port.iobase = iobase; - port.irq = irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &handle->dev; - if (buggy_uart) - port.flags |= UPF_BUGGY_UART; - - if (info->quirk && info->quirk->setup) - info->quirk->setup(handle, &port); - - line = serial8250_register_port(&port); - if (line < 0) { - printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " - "0x%04lx, irq %d failed\n", (u_long)iobase, irq); - return -EINVAL; - } - - info->line[info->ndev] = line; - info->ndev++; - - return 0; -} - -/*====================================================================*/ - -static int pfc_config(struct pcmcia_device *p_dev) -{ - unsigned int port = 0; - struct serial_info *info = p_dev->priv; - - if ((p_dev->resource[1]->end != 0) && - (resource_size(p_dev->resource[1]) == 8)) { - port = p_dev->resource[1]->start; - info->slave = 1; - } else if ((info->manfid == MANFID_OSITECH) && - (resource_size(p_dev->resource[0]) == 0x40)) { - port = p_dev->resource[0]->start + 0x28; - info->slave = 1; - } - if (info->slave) - return setup_serial(p_dev, info, port, p_dev->irq); - - dev_warn(&p_dev->dev, "no usable port range found, giving up\n"); - return -ENODEV; -} - -static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - static const int size_table[2] = { 8, 16 }; - int *try = priv_data; - - if (p_dev->resource[0]->start == 0) - return -ENODEV; - - if ((*try & 0x1) == 0) - p_dev->io_lines = 16; - - if (p_dev->resource[0]->end != size_table[(*try >> 1)]) - return -ENODEV; - - p_dev->resource[0]->end = 8; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - return pcmcia_request_io(p_dev); -} - -static int simple_config_check_notpicky(struct pcmcia_device *p_dev, - void *priv_data) -{ - static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - int j; - - if (p_dev->io_lines > 3) - return -ENODEV; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 8; - - for (j = 0; j < 5; j++) { - p_dev->resource[0]->start = base[j]; - p_dev->io_lines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; -} - -static int simple_config(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i = -ENODEV, try; - - /* First pass: look for a config entry that looks normal. - * Two tries: without IO aliases, then with aliases */ - link->config_flags |= CONF_AUTO_SET_VPP; - for (try = 0; try < 4; try++) - if (!pcmcia_loop_config(link, simple_config_check, &try)) - goto found_port; - - /* Second pass: try to find an entry that isn't picky about - its base address, then try to grab any standard serial port - address, and finally try to get any free port. */ - if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) - goto found_port; - - dev_warn(&link->dev, "no usable port range found, giving up\n"); - return -1; - -found_port: - if (info->multi && (info->manfid == MANFID_3COM)) - link->config_index &= ~(0x08); - - /* - * Apply any configuration quirks. - */ - if (info->quirk && info->quirk->config) - info->quirk->config(link); - - i = pcmcia_enable_device(link); - if (i != 0) - return -1; - return setup_serial(link, info, link->resource[0]->start, link->irq); -} - -static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - int *multi = priv_data; - - if (p_dev->resource[1]->end) - return -EINVAL; - - /* The quad port cards have bad CIS's, so just look for a - window larger than 8 ports and assume it will be right */ - if (p_dev->resource[0]->end <= 8) - return -EINVAL; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = *multi * 8; - - if (pcmcia_request_io(p_dev)) - return -ENODEV; - return 0; -} - -static int multi_config_check_notpicky(struct pcmcia_device *p_dev, - void *priv_data) -{ - int *base2 = priv_data; - - if (!p_dev->resource[0]->end || !p_dev->resource[1]->end || - p_dev->resource[0]->start + 8 != p_dev->resource[1]->start) - return -ENODEV; - - p_dev->resource[0]->end = p_dev->resource[1]->end = 8; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - if (pcmcia_request_io(p_dev)) - return -ENODEV; - - *base2 = p_dev->resource[0]->start + 8; - return 0; -} - -static int multi_config(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i, base2 = 0; - - /* First, look for a generic full-sized window */ - if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) - base2 = link->resource[0]->start + 8; - else { - /* If that didn't work, look for two windows */ - info->multi = 2; - if (pcmcia_loop_config(link, multi_config_check_notpicky, - &base2)) { - dev_warn(&link->dev, "no usable port range " - "found, giving up\n"); - return -ENODEV; - } - } - - if (!link->irq) - dev_warn(&link->dev, "no usable IRQ found, continuing...\n"); - - /* - * Apply any configuration quirks. - */ - if (info->quirk && info->quirk->config) - info->quirk->config(link); - - i = pcmcia_enable_device(link); - if (i != 0) - return -ENODEV; - - /* The Oxford Semiconductor OXCF950 cards are in fact single-port: - * 8 registers are for the UART, the others are extra registers. - * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. - */ - if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO && - info->prodid == PRODID_POSSIO_GCC)) { - int err; - - if (link->config_index == 1 || - link->config_index == 3) { - err = setup_serial(link, info, base2, - link->irq); - base2 = link->resource[0]->start; - } else { - err = setup_serial(link, info, link->resource[0]->start, - link->irq); - } - info->c950ctrl = base2; - - /* - * FIXME: We really should wake up the port prior to - * handing it over to the serial layer. - */ - if (info->quirk && info->quirk->wakeup) - info->quirk->wakeup(link); - - return 0; - } - - setup_serial(link, info, link->resource[0]->start, link->irq); - for (i = 0; i < info->multi - 1; i++) - setup_serial(link, info, base2 + (8 * i), - link->irq); - return 0; -} - -static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) -{ - struct serial_info *info = p_dev->priv; - - if (!p_dev->resource[0]->end) - return -EINVAL; - - if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) - info->multi = p_dev->resource[0]->end >> 3; - - if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) - && (p_dev->resource[1]->end == 8)) - info->multi = 2; - - return 0; /* break */ -} - - -static int serial_config(struct pcmcia_device * link) -{ - struct serial_info *info = link->priv; - int i; - - dev_dbg(&link->dev, "serial_config\n"); - - /* Is this a compliant multifunction card? */ - info->multi = (link->socket->functions > 1); - - /* Is this a multiport card? */ - info->manfid = link->manf_id; - info->prodid = link->card_id; - - for (i = 0; i < ARRAY_SIZE(quirks); i++) - if ((quirks[i].manfid == ~0 || - quirks[i].manfid == info->manfid) && - (quirks[i].prodid == ~0 || - quirks[i].prodid == info->prodid)) { - info->quirk = &quirks[i]; - break; - } - - /* Another check for dual-serial cards: look for either serial or - multifunction cards that ask for appropriate IO port ranges */ - if ((info->multi == 0) && - (link->has_func_id) && - (link->socket->pcmcia_pfc == 0) && - ((link->func_id == CISTPL_FUNCID_MULTI) || - (link->func_id == CISTPL_FUNCID_SERIAL))) - pcmcia_loop_config(link, serial_check_for_multi, info); - - /* - * Apply any multi-port quirk. - */ - if (info->quirk && info->quirk->multi != -1) - info->multi = info->quirk->multi; - - dev_info(&link->dev, - "trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n", - link->manf_id, link->card_id, - link->socket->pcmcia_pfc, info->multi, info->quirk); - if (link->socket->pcmcia_pfc) - i = pfc_config(link); - else if (info->multi > 1) - i = multi_config(link); - else - i = simple_config(link); - - if (i || info->ndev == 0) - goto failed; - - /* - * Apply any post-init quirk. FIXME: This should really happen - * before we register the port, since it might already be in use. - */ - if (info->quirk && info->quirk->post) - if (info->quirk->post(link)) - goto failed; - - return 0; - -failed: - dev_warn(&link->dev, "failed to initialize\n"); - serial_remove(link); - return -ENODEV; -} - -static const struct pcmcia_device_id serial_ids[] = { - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0b05), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), - PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), - PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), - PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070), - PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020), - PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), - PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), - PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), - PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), - PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), - PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), - PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), - PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), - PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */ - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */ - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */ - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), - PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */ - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), - PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */ - PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */ - PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */ - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ - PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ - PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), - PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), - PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), - PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), - PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), - PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e), - PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), - PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), - PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), - PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76), - PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), - PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), - PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), - PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b), - PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), - PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb), - PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447), - PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), - PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), - PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), - PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), - PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), - PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41), - PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), - PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), - PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), - PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38), - PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */ - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ - PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), - PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), - PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), - PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b), - /* too generic */ - /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ - /* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */ - PCMCIA_DEVICE_FUNC_ID(2), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, serial_ids); - -MODULE_FIRMWARE("cis/PCMLM28.cis"); -MODULE_FIRMWARE("cis/DP83903.cis"); -MODULE_FIRMWARE("cis/3CCFEM556.cis"); -MODULE_FIRMWARE("cis/3CXEM556.cis"); -MODULE_FIRMWARE("cis/SW_8xx_SER.cis"); -MODULE_FIRMWARE("cis/SW_7xx_SER.cis"); -MODULE_FIRMWARE("cis/SW_555_SER.cis"); -MODULE_FIRMWARE("cis/MT5634ZLX.cis"); -MODULE_FIRMWARE("cis/COMpad2.cis"); -MODULE_FIRMWARE("cis/COMpad4.cis"); -MODULE_FIRMWARE("cis/RS-COM-2P.cis"); - -static struct pcmcia_driver serial_cs_driver = { - .owner = THIS_MODULE, - .name = "serial_cs", - .probe = serial_probe, - .remove = serial_detach, - .id_table = serial_ids, - .suspend = serial_suspend, - .resume = serial_resume, -}; - -static int __init init_serial_cs(void) -{ - return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ - pcmcia_unregister_driver(&serial_cs_driver); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); - -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 791b7d7cf69de11275e4dccec2f538eec02cbff6 Mon Sep 17 00:00:00 2001 From: Renato Caldas Date: Fri, 6 Jan 2012 15:20:51 +0000 Subject: USB: serial: CP210x: Added USB-ID for the Link Instruments MSO-19 This device is a Oscilloscope/Logic Analizer/Pattern Generator/TDR, using a Silabs CP2103 USB to UART Bridge. Signed-off-by: Renato Caldas Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index fba1147..c2b5d4a 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -138,6 +138,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ + { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; -- cgit v0.10.2 From 55b2afbb92ad92e9f6b0aa4354eb1c94589280c3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:48 +0100 Subject: USB: cp210x: call generic open last in open Make sure port is fully initialised before calling generic open. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index c2b5d4a..bb4290b 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -410,13 +410,10 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } - result = usb_serial_generic_open(tty, port); - if (result) - return result; - /* Configure the termios structure */ cp210x_get_termios(tty, port); - return 0; + + return usb_serial_generic_open(tty, port); } static void cp210x_close(struct usb_serial_port *port) -- cgit v0.10.2 From 7f482fc88ac47662228d6b1f05759797c8936a30 Mon Sep 17 00:00:00 2001 From: Preston Fick Date: Mon, 16 Jan 2012 18:14:09 -0600 Subject: USB: cp210x: fix CP2104 baudrate usage This fix changes the way baudrates are set on the CP210x devices from Silicon Labs. The CP2101/2/3 will respond to both a GET/SET_BAUDDIV command, and GET/SET_BAUDRATE command, while CP2104 and higher devices only respond to GET/SET_BAUDRATE. The current cp210x.ko driver in kernel version 3.2.0 only implements the GET/SET_BAUDDIV command. This patch implements the two new codes for the GET/SET_BAUDRATE commands. Then there is a change in the way that the baudrate is assigned or retrieved. This is done according to the CP210x USB specification in AN571. This document can be found here: http://www.silabs.com/pages/DownloadDoc.aspx?FILEURL=Support%20Documents/TechnicalDocs/AN571.pdf&src=DocumentationWebPart Sections 5.3/5.4 describe the USB packets for the old baudrate method. Sections 5.5/5.6 describe the USB packets for the new method. This patch also implements the new request scheme, and eliminates the unnecessary baudrate calculations since it uses the "actual baudrate" method. This patch solves the problem reported for the CP2104 in bug 42586, and also keeps support for all other devices (CP2101/2/3). This patchfile is also attached to the bug report on bugzilla.kernel.org. This patch has been developed and test on the 3.2.0 mainline kernel version under Ubuntu 10.11. Signed-off-by: Preston Fick [duplicate patch also sent by Johan - gregkh] Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index bb4290b..f426788 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -202,6 +202,8 @@ static struct usb_serial_driver cp210x_device = { #define CP210X_EMBED_EVENTS 0x15 #define CP210X_GET_EVENTSTATE 0x16 #define CP210X_SET_CHARS 0x19 +#define CP210X_GET_BAUDRATE 0x1D +#define CP210X_SET_BAUDRATE 0x1E /* CP210X_IFC_ENABLE */ #define UART_ENABLE 0x0001 @@ -465,10 +467,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - cp210x_get_config(port, CP210X_GET_BAUDDIV, &baud, 2); - /* Convert to baudrate */ - if (baud) - baud = cp210x_quantise_baudrate((BAUD_RATE_GEN_FREQ + baud/2)/ baud); + cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4); dbg("%s - baud rate = %d", __func__, baud); *baudp = baud; @@ -597,8 +596,7 @@ static void cp210x_set_termios(struct tty_struct *tty, if (baud != tty_termios_baud_rate(old_termios) && baud != 0) { dbg("%s - Setting baud rate to %d baud", __func__, baud); - if (cp210x_set_config_single(port, CP210X_SET_BAUDDIV, - ((BAUD_RATE_GEN_FREQ + baud/2) / baud))) { + if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, 4)) { dbg("Baud rate requested not supported by device"); baud = tty_termios_baud_rate(old_termios); } -- cgit v0.10.2 From ba1960257c5980f9b58057995ce3394bd8e48ca3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 10 Jan 2012 15:19:54 +0200 Subject: mac80211: update oper_channel on ibss join Commit 13c40c5 ("mac80211: Add HT operation modes for IBSS") broke ibss operation by mistakenly removing the local->oper_channel update (causing ibss to start on the wrong channel). fix it. Signed-off-by: Eliad Peller Acked-by: Simon Wunderlich Signed-off-by: John W. Linville diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b3d76b7..a464396 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -106,6 +106,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; + local->oper_channel = chan; channel_type = ifibss->channel_type; if (channel_type > NL80211_CHAN_HT20 && !cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) -- cgit v0.10.2 From 405385f8ce7a2ed8f82e216d88b5282142e1288b Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 11 Jan 2012 13:11:50 +0200 Subject: mac80211: set bss_conf.idle when vif is connected __ieee80211_recalc_idle() iterates through the vifs, sets bss_conf.idle = true if they are disconnected, and increases "count" if they are not (which later gets evaluated in order to determine whether the device is idle). However, the loop doesn't set bss_conf.idle = false (along with increasing "count"), causing the device idle state and the vif idle state to get out of sync in some cases. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e47768c..01a21c2 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1314,6 +1314,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) continue; } /* count everything else */ + sdata->vif.bss_conf.idle = false; count++; } -- cgit v0.10.2 From b49ba04a3a0382e7314d990707c21094c410425a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 19 Jan 2012 08:20:57 -0800 Subject: iwlwifi: fix PCI-E transport "inta" race When an interrupt comes in, we read the reason bits and collect them into "trans_pcie->inta". This happens with the spinlock held. However, there's a bug resetting this variable -- that happens after the spinlock has been released. This means that it is possible for interrupts to be missed if the reset happens after some other interrupt reasons were already added to the variable. I found this by code inspection, looking for a reason that we sometimes see random commands time out. It seems possible that this causes such behaviour, but I can't say for sure right now since it happens extremely infrequently on my test systems. Cc: stable@vger.kernel.org [3.2] Signed-off-by: Johannes Berg Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 752493f..65d1f05 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -972,11 +972,11 @@ void iwl_irq_tasklet(struct iwl_trans *trans) } #endif - spin_unlock_irqrestore(&trans->shrd->lock, flags); - /* saved interrupt in inta variable now we can reset trans_pcie->inta */ trans_pcie->inta = 0; + spin_unlock_irqrestore(&trans->shrd->lock, flags); + /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { IWL_ERR(trans, "Hardware error detected. Restarting.\n"); -- cgit v0.10.2 From 34b76fcaee574017862ea3fa0efdcd77a9d0e57d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:49 +0100 Subject: USB: cp210x: fix up set_termios variables [Based on a patch from Johan, mangled by gregkh to keep things in line] Fix up the variable usage in the set_termios call. Signed-off-by: Johan Hovold Cc: Preston Fick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f426788..1270e02 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -580,7 +580,8 @@ static void cp210x_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; - unsigned int baud = 0, bits; + u32 baud; + unsigned int bits; unsigned int modem_ctl[4]; dbg("%s - port %d", __func__, port->number); @@ -596,7 +597,7 @@ static void cp210x_set_termios(struct tty_struct *tty, if (baud != tty_termios_baud_rate(old_termios) && baud != 0) { dbg("%s - Setting baud rate to %d baud", __func__, baud); - if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, 4)) { + if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, sizeof(baud))) { dbg("Baud rate requested not supported by device"); baud = tty_termios_baud_rate(old_termios); } -- cgit v0.10.2 From be125d9c8d59560e7cc2d6e2b65c8fd233498ab7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:50 +0100 Subject: USB: cp210x: do not map baud rates to B0 We do not implement B0 hangup yet so map low baudrates to 300bps. Signed-off-by: Johan Hovold Cc: Preston Fick Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 1270e02..ad7599e 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -363,8 +363,8 @@ static inline int cp210x_set_config_single(struct usb_serial_port *port, * Quantises the baud rate as per AN205 Table 1 */ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { - if (baud <= 56) baud = 0; - else if (baud <= 300) baud = 300; + if (baud <= 300) + baud = 300; else if (baud <= 600) baud = 600; else if (baud <= 1200) baud = 1200; else if (baud <= 1800) baud = 1800; -- cgit v0.10.2 From e5990874e511d5bbca23b3396419480cb2ca0ee7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:51 +0100 Subject: USB: cp210x: clean up, refactor and document speed handling Clean up and refactor speed handling. Document baud rate handling for CP210{1,2,4,5,10}. Signed-off-by: Johan Hovold Cc: Preston Fick Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index ad7599e..2d2d239 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -39,6 +39,8 @@ static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *port); static void cp210x_get_termios_port(struct usb_serial_port *port, unsigned int *cflagp, unsigned int *baudp); +static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *, + struct ktermios *); static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, struct ktermios*); static int cp210x_tiocmget(struct tty_struct *); @@ -576,11 +578,62 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, *cflagp = cflag; } +/* + * CP2101 supports the following baud rates: + * + * 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800, + * 38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600 + * + * CP2102 and CP2103 support the following additional rates: + * + * 4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000, + * 576000 + * + * The device will map a requested rate to a supported one, but the result + * of requests for rates greater than 1053257 is undefined (see AN205). + * + * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud, + * respectively, with an error less than 1%. The actual rates are determined + * by + * + * div = round(freq / (2 x prescale x request)) + * actual = freq / (2 x prescale x div) + * + * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps + * or 1 otherwise. + * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1 + * otherwise. + */ +static void cp210x_change_speed(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) +{ + u32 baud; + + baud = tty->termios->c_ospeed; + + /* This maps the requested rate to a rate valid on cp2102 or cp2103. + * + * NOTE: B0 is not implemented. + */ + baud = cp210x_quantise_baudrate(baud); + + dbg("%s - setting baud rate to %u", __func__, baud); + if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, + sizeof(baud))) { + dev_warn(&port->dev, "failed to set baud rate to %u\n", baud); + if (old_termios) + baud = old_termios->c_ospeed; + else + baud = 9600; + } + + tty_encode_baud_rate(tty, baud, baud); +} + static void cp210x_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; - u32 baud; unsigned int bits; unsigned int modem_ctl[4]; @@ -591,19 +644,9 @@ static void cp210x_set_termios(struct tty_struct *tty, cflag = tty->termios->c_cflag; old_cflag = old_termios->c_cflag; - baud = cp210x_quantise_baudrate(tty_get_baud_rate(tty)); - - /* If the baud rate is to be updated*/ - if (baud != tty_termios_baud_rate(old_termios) && baud != 0) { - dbg("%s - Setting baud rate to %d baud", __func__, - baud); - if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, sizeof(baud))) { - dbg("Baud rate requested not supported by device"); - baud = tty_termios_baud_rate(old_termios); - } - } - /* Report back the resulting baud rate */ - tty_encode_baud_rate(tty, baud, baud); + + if (tty->termios->c_ospeed != old_termios->c_ospeed) + cp210x_change_speed(tty, port, old_termios); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { -- cgit v0.10.2 From cdc32fd6f7b2b2580d7f1b74563f888e4dd9eb8a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:52 +0100 Subject: USB: cp210x: initialise baud rate at open The newer cp2104 devices require the baud rate to be initialised after power on. Make sure it is set when port is opened. Signed-off-by: Johan Hovold Cc: Preston Fick Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 2d2d239..5c3e859 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -417,6 +417,10 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) /* Configure the termios structure */ cp210x_get_termios(tty, port); + /* The baud rate must be initialised on cp2104 */ + if (tty) + cp210x_change_speed(tty, port, NULL); + return usb_serial_generic_open(tty, port); } -- cgit v0.10.2 From d1620ca9e7bb0030068c3b45b653defde8839dac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:53 +0100 Subject: USB: cp210x: allow more baud rates above 1Mbaud Allow more baud rates to be set in [1M,2M] baud. Signed-off-by: Johan Hovold Cc: Preston Fick Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 5c3e859..8dbf51a 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -394,10 +394,10 @@ static unsigned int cp210x_quantise_baudrate(unsigned int baud) { else if (baud <= 491520) baud = 460800; else if (baud <= 567138) baud = 500000; else if (baud <= 670254) baud = 576000; - else if (baud <= 1053257) baud = 921600; - else if (baud <= 1474560) baud = 1228800; - else if (baud <= 2457600) baud = 1843200; - else baud = 3686400; + else if (baud < 1000000) + baud = 921600; + else if (baud > 2000000) + baud = 2000000; return baud; } @@ -615,7 +615,8 @@ static void cp210x_change_speed(struct tty_struct *tty, baud = tty->termios->c_ospeed; - /* This maps the requested rate to a rate valid on cp2102 or cp2103. + /* This maps the requested rate to a rate valid on cp2102 or cp2103, + * or to an arbitrary rate in [1M,2M]. * * NOTE: B0 is not implemented. */ -- cgit v0.10.2 From 52a749992ca6a0fd304609af40ed3bfd6cef4660 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Jan 2012 12:02:38 -0800 Subject: Revert "USB: usb-skeleton.c: fix open/disconnect race" This reverts commit 26c71a79cade5ccad80e0752cd82f3518df48fb3. It's not needed, to quote Ming Lei: Looks you have queued the patch into your tree, but just now I find the patch is not needed at all, since we have had minor_rwsem(drivers/usb/core/file.c) for this purpose, please drop the patch, sorry for it. Cc: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 8efeae2..b4a7167 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -27,8 +27,6 @@ #define USB_SKEL_VENDOR_ID 0xfff0 #define USB_SKEL_PRODUCT_ID 0xfff0 -static DEFINE_MUTEX(skel_mutex); - /* table of devices that work with this driver */ static const struct usb_device_id skel_table[] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, @@ -101,25 +99,18 @@ static int skel_open(struct inode *inode, struct file *file) goto exit; } - mutex_lock(&skel_mutex); dev = usb_get_intfdata(interface); if (!dev) { - mutex_unlock(&skel_mutex); retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); - mutex_unlock(&skel_mutex); /* lock the device to allow correctly handling errors * in resumption */ mutex_lock(&dev->io_mutex); - if (!dev->interface) { - retval = -ENODEV; - goto out_err; - } retval = usb_autopm_get_interface(interface); if (retval) @@ -127,11 +118,7 @@ static int skel_open(struct inode *inode, struct file *file) /* save our object in the file's private structure */ file->private_data = dev; - -out_err: mutex_unlock(&dev->io_mutex); - if (retval) - kref_put(&dev->kref, skel_delete); exit: return retval; @@ -611,6 +598,7 @@ static void skel_disconnect(struct usb_interface *interface) int minor = interface->minor; dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &skel_class); @@ -622,12 +610,8 @@ static void skel_disconnect(struct usb_interface *interface) usb_kill_anchored_urbs(&dev->submitted); - mutex_lock(&skel_mutex); - usb_set_intfdata(interface, NULL); - /* decrement our usage count */ kref_put(&dev->kref, skel_delete); - mutex_unlock(&skel_mutex); dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor); } -- cgit v0.10.2 From 6d443d8499e4e59ffb949759cdded32730f8d2f6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 13 Jan 2012 21:32:06 -0800 Subject: usb: io_ti: Make edge_remove_sysfs_attrs the port_remove method. Calling edge_remove_sysfs_attrs from edge_disconnect is too late as the device has already been removed from sysfs. Do the simple and obvious thing and make edge_remove_sysfs_attrs the port_remove method. Signed-off-by: Eric W. Biederman Reported-by: Wolfgang Frisch Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 65bf06a..5818bfc 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2657,15 +2657,7 @@ cleanup: static void edge_disconnect(struct usb_serial *serial) { - int i; - struct edgeport_port *edge_port; - dbg("%s", __func__); - - for (i = 0; i < serial->num_ports; ++i) { - edge_port = usb_get_serial_port_data(serial->port[i]); - edge_remove_sysfs_attrs(edge_port->port); - } } static void edge_release(struct usb_serial *serial) @@ -2744,6 +2736,7 @@ static struct usb_serial_driver edgeport_1port_device = { .disconnect = edge_disconnect, .release = edge_release, .port_probe = edge_create_sysfs_attrs, + .port_remove = edge_remove_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -2775,6 +2768,7 @@ static struct usb_serial_driver edgeport_2port_device = { .disconnect = edge_disconnect, .release = edge_release, .port_probe = edge_create_sysfs_attrs, + .port_remove = edge_remove_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, -- cgit v0.10.2 From e423d7401fd0717cb56a6cf51dd8341cc3e800d2 Mon Sep 17 00:00:00 2001 From: Kentaro Matsuyama Date: Thu, 12 Jan 2012 23:07:51 +0900 Subject: USB: option: Add LG docomo L-02C Add vendor and product ID for USB 3G/LTE modem of docomo L-02C Signed-off-by: Kentaro Matsuyama Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 420d985..ea126a4 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -480,6 +480,10 @@ static void option_instat_callback(struct urb *urb); #define ZD_VENDOR_ID 0x0685 #define ZD_PRODUCT_7000 0x7000 +/* LG products */ +#define LG_VENDOR_ID 0x1004 +#define LG_PRODUCT_L02C 0x618f + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -1183,6 +1187,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CLU526) }, { USB_DEVICE_AND_INTERFACE_INFO(VIETTEL_VENDOR_ID, VIETTEL_PRODUCT_VT1000, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZD_VENDOR_ID, ZD_PRODUCT_7000, 0xff, 0xff, 0xff) }, + { USB_DEVICE(LG_VENDOR_ID, LG_PRODUCT_L02C) }, /* docomo L-02C modem */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v0.10.2 From 1097ccebe630170080c41df0edcf88e0626e9c75 Mon Sep 17 00:00:00 2001 From: Harrison Metzger Date: Sun, 15 Jan 2012 08:43:24 -0600 Subject: USB: usbsevseg: fix max length This changes the max length for the usb seven segment delcom device to 8 from 6. Delcom has both 6 and 8 variants and having 8 works fine with devices which are only 6. Signed-off-by: Harrison Metzger Signed-off-by: Stuart Pook Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 107bf13..b2d82b9 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -24,7 +24,7 @@ #define VENDOR_ID 0x0fc5 #define PRODUCT_ID 0x1227 -#define MAXLEN 6 +#define MAXLEN 8 /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { -- cgit v0.10.2 From ce597919361dcec97341151690e780eade2a9cf4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 13 Jan 2012 21:32:59 -0800 Subject: sysfs: Complain bitterly about attempts to remove files from nonexistent directories. Recently an OOPS was observed from the usb serial io_ti driver when it tried to remove sysfs directories. Upon investigation it turns out this driver was always buggy and that a recent sysfs change had stopped guarding itself against removing attributes from sysfs directories that had already been removed. :( Historically we have been silent about attempting to files from nonexistent sysfs directories and have politely returned error codes. That has resulted in people writing broken code that ignores the error codes. Issue a kernel WARNING and a stack backtrace to make it clear in no uncertain terms that abusing sysfs is not ok, and the callers need to fix their code. This change transforms the io_ti OOPS into a more comprehensible error message and stack backtrace. Signed-off-by: Eric W. Biederman Reported-by: Wolfgang Frisch Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 62f4fb3..00012e3 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -493,6 +493,12 @@ int sysfs_attr_ns(struct kobject *kobj, const struct attribute *attr, const void *ns = NULL; int err; + if (!dir_sd) { + WARN(1, KERN_ERR "sysfs: kobject %s without dirent\n", + kobject_name(kobj)); + return -ENOENT; + } + err = 0; if (!sysfs_ns_type(dir_sd)) goto out; diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 4a802b4..85eb816 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -318,8 +318,11 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const cha struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; - if (!dir_sd) + if (!dir_sd) { + WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", + name); return -ENOENT; + } sysfs_addrm_start(&acxt, dir_sd); -- cgit v0.10.2 From c428b70c1e115c5649707a602742e34130d19428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 12:41:47 +0100 Subject: USB: cdc-wdm: updating desc->length must be protected by spin_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wdm_in_callback() will also touch this field, so we cannot change it without locking Cc: stable@vger.kernel.org Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 1c50baf..1f6b5c8 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -467,7 +467,9 @@ retry: for (i = 0; i < desc->length - cntr; i++) desc->ubuf[i] = desc->ubuf[i + cntr]; + spin_lock_irq(&desc->iuspin); desc->length -= cntr; + spin_unlock_irq(&desc->iuspin); /* in case we had outstanding data */ if (!desc->length) clear_bit(WDM_READ, &desc->flags); -- cgit v0.10.2 From e8537bd2c4f325a4796da33564ddcef9489b7feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 12:41:48 +0100 Subject: USB: cdc-wdm: use two mutexes to allow simultaneous read and write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit using a separate read and write mutex for locking is sufficient to make the driver accept simultaneous read and write. This improves useability a lot. Signed-off-by: Bjørn Mork Cc: stable Cc: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 1f6b5c8..023d271 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -88,7 +88,8 @@ struct wdm_device { int count; dma_addr_t shandle; dma_addr_t ihandle; - struct mutex lock; + struct mutex wlock; + struct mutex rlock; wait_queue_head_t wait; struct work_struct rxwork; int werr; @@ -323,7 +324,7 @@ static ssize_t wdm_write } /* concurrent writes and disconnect */ - r = mutex_lock_interruptible(&desc->lock); + r = mutex_lock_interruptible(&desc->wlock); rv = -ERESTARTSYS; if (r) { kfree(buf); @@ -386,7 +387,7 @@ static ssize_t wdm_write out: usb_autopm_put_interface(desc->intf); outnp: - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); outnl: return rv < 0 ? rv : count; } @@ -399,7 +400,7 @@ static ssize_t wdm_read struct wdm_device *desc = file->private_data; - rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ + rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ if (rv < 0) return -ERESTARTSYS; @@ -476,7 +477,7 @@ retry: rv = cntr; err: - mutex_unlock(&desc->lock); + mutex_unlock(&desc->rlock); return rv; } @@ -542,7 +543,8 @@ static int wdm_open(struct inode *inode, struct file *file) } intf->needs_remote_wakeup = 1; - mutex_lock(&desc->lock); + /* using write lock to protect desc->count */ + mutex_lock(&desc->wlock); if (!desc->count++) { desc->werr = 0; desc->rerr = 0; @@ -555,7 +557,7 @@ static int wdm_open(struct inode *inode, struct file *file) } else { rv = 0; } - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); usb_autopm_put_interface(desc->intf); out: mutex_unlock(&wdm_mutex); @@ -567,9 +569,11 @@ static int wdm_release(struct inode *inode, struct file *file) struct wdm_device *desc = file->private_data; mutex_lock(&wdm_mutex); - mutex_lock(&desc->lock); + + /* using write lock to protect desc->count */ + mutex_lock(&desc->wlock); desc->count--; - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); if (!desc->count) { dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); @@ -667,7 +671,8 @@ next_desc: desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); if (!desc) goto out; - mutex_init(&desc->lock); + mutex_init(&desc->rlock); + mutex_init(&desc->wlock); spin_lock_init(&desc->iuspin); init_waitqueue_head(&desc->wait); desc->wMaxCommand = maxcom; @@ -781,10 +786,12 @@ static void wdm_disconnect(struct usb_interface *intf) /* to terminate pending flushes */ clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); - mutex_lock(&desc->lock); + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); + mutex_unlock(&desc->rlock); wake_up_all(&desc->wait); if (!desc->count) cleanup(desc); @@ -800,8 +807,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); /* if this is an autosuspend the caller does the locking */ - if (!PMSG_IS_AUTO(message)) - mutex_lock(&desc->lock); + if (!PMSG_IS_AUTO(message)) { + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); + } spin_lock_irq(&desc->iuspin); if (PMSG_IS_AUTO(message) && @@ -817,8 +826,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) kill_urbs(desc); cancel_work_sync(&desc->rxwork); } - if (!PMSG_IS_AUTO(message)) - mutex_unlock(&desc->lock); + if (!PMSG_IS_AUTO(message)) { + mutex_unlock(&desc->wlock); + mutex_unlock(&desc->rlock); + } return rv; } @@ -856,7 +867,8 @@ static int wdm_pre_reset(struct usb_interface *intf) { struct wdm_device *desc = usb_get_intfdata(intf); - mutex_lock(&desc->lock); + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); kill_urbs(desc); /* @@ -878,7 +890,8 @@ static int wdm_post_reset(struct usb_interface *intf) int rv; rv = recover_from_urb_loss(desc); - mutex_unlock(&desc->lock); + mutex_unlock(&desc->wlock); + mutex_unlock(&desc->rlock); return 0; } -- cgit v0.10.2 From 62aaf24dc125d7c55c93e313d15611f152b030c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 15:11:57 +0100 Subject: USB: cdc-wdm: call wake_up_all to allow driver to shutdown on device removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wdm_disconnect() waits for the mutex held by wdm_read() before calling wake_up_all(). This causes a deadlock, preventing device removal to complete. Do the wake_up_all() before we start waiting for the locks. Signed-off-by: Bjørn Mork Cc: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 023d271..07aa676 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -786,13 +786,13 @@ static void wdm_disconnect(struct usb_interface *intf) /* to terminate pending flushes */ clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); + wake_up_all(&desc->wait); mutex_lock(&desc->rlock); mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); - wake_up_all(&desc->wait); if (!desc->count) cleanup(desc); mutex_unlock(&wdm_mutex); -- cgit v0.10.2 From 655e247daf52b202a6c2d0f8a06dd2051e756ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 15:11:59 +0100 Subject: USB: cdc-wdm: better allocate a buffer that is at least as big as we tell the USB core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As it turns out, there was a mismatch between the allocated inbuf size (desc->bMaxPacketSize0, typically something like 64) and the length we specified in the URB (desc->wMaxCommand, typically something like 2048) Signed-off-by: Bjørn Mork Cc: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 07aa676..a940ad9 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -723,7 +723,7 @@ next_desc: goto err; desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), - desc->bMaxPacketSize0, + desc->wMaxCommand, GFP_KERNEL, &desc->response->transfer_dma); if (!desc->inbuf) -- cgit v0.10.2 From 2492c6e6454ff3edb11e273b071a6ea80a199c71 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 10:55:13 +0100 Subject: drivers/usb/host/ehci-fsl.c: add missing iounmap Add missing iounmap in error handling code, in a case where the function already preforms iounmap on some other execution path. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; statement S,S1; int ret; @@ e = \(ioremap\|ioremap_nocache\)(...) ... when != iounmap(e) if (<+...e...+>) S ... when any when != iounmap(e) *if (...) { ... when != iounmap(e) return ...; } ... when any iounmap(e); // Signed-off-by: Julia Lawall Acked-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index e90344a..b556a72 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -125,7 +125,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, */ if (pdata->init && pdata->init(pdev)) { retval = -ENODEV; - goto err3; + goto err4; } /* Enable USB controller, 83xx or 8536 */ -- cgit v0.10.2 From 3297f86a3d4158e052538c7b9a3dea9c855a1b42 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 24 Jan 2012 10:18:10 +0200 Subject: usb: serial: kobil_sct: fix compile warning: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following compile warning: drivers/usb/serial/kobil_sct.c: In function ‘__check_debug’: drivers/usb/serial/kobil_sct.c:719:1: warning: return from incompatible pointer type [enabled by default] Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 5d3beee..a92a3ef 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -38,7 +38,7 @@ #include #include "kobil_sct.h" -static int debug; +static bool debug; /* Version Information */ #define DRIVER_VERSION "21/05/2004" -- cgit v0.10.2 From 194b3af4eb4b7ba84e2e4274daf9f58aa958bd04 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 11:58:15 -0500 Subject: USB: OHCI: fix new compiler warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch (as1515) fixes some unavoidably dumb compiler warnings: CC [M] drivers/usb/renesas_usbhs/mod.o In file included from drivers/usb/host/ohci-hcd.c:101:0: drivers/usb/host/ohci-dbg.c: In function ‘fill_registers_buffer’: drivers/usb/host/ohci-dbg.c:656:2: warning: the comparison will always evaluate as ‘true’ for the address of ‘next’ will never be NULL [-Waddress] drivers/usb/host/ohci-dbg.c:675:3: warning: the comparison will always evaluate as ‘true’ for the address of ‘next’ will never be NULL [-Waddress] Instead of trying to fix the macro to work under all cirumstances, just add a second macro for use in cases where the "next" argument is the address of a local variable. Unfortunately the macro cannot be replaced by a real subroutine, because there's no va_list version of ohci_dbg() or dev_dbg(). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 5179fcd..e4bcb62 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -82,6 +82,14 @@ urb_print(struct urb * urb, char * str, int small, int status) ohci_dbg(ohci,format, ## arg ); \ } while (0); +/* Version for use where "next" is the address of a local variable */ +#define ohci_dbg_nosw(ohci, next, size, format, arg...) \ + do { \ + unsigned s_len; \ + s_len = scnprintf(*next, *size, format, ## arg); \ + *size -= s_len; *next += s_len; \ + } while (0); + static void ohci_dump_intr_mask ( struct ohci_hcd *ohci, @@ -653,7 +661,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) /* dump driver info, then registers in spec order */ - ohci_dbg_sw (ohci, &next, &size, + ohci_dbg_nosw(ohci, &next, &size, "bus %s, device %s\n" "%s\n" "%s\n", @@ -672,7 +680,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) /* hcca */ if (ohci->hcca) - ohci_dbg_sw (ohci, &next, &size, + ohci_dbg_nosw(ohci, &next, &size, "hcca frame 0x%04x\n", ohci_frame_no(ohci)); /* other registers mostly affect frame timings */ -- cgit v0.10.2 From 0fcd97789028e8ec286a4248c20a71eae239ba61 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:02:56 -0800 Subject: kernel-doc: fix new warning in usb.h Fix new kernel-doc warning: Warning(include/linux/usb.h:1251): No description found for parameter 'num_mapped_sgs' Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb.h b/include/linux/usb.h index 27a4e16..69d8457 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1073,6 +1073,7 @@ typedef void (*usb_complete_t)(struct urb *); * which the host controller driver should use in preference to the * transfer_buffer. * @sg: scatter gather buffer list + * @num_mapped_sgs: (internal) number of mapped sg entries * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may * be broken up into chunks according to the current maximum packet -- cgit v0.10.2 From 90b9a5454fd2e626aa1614fe9ece6b63a0dc37af Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Mon, 23 Jan 2012 23:26:48 +0000 Subject: stmmac: fix phy naming inconsistency After commit "db8857b stmmac: use an unique MDIO bus name" my device stopped being probed because two different names were being used in different places. This fixes the inconsistency. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Cc: Giuseppe Cavallaro Cc: Florian Fainelli Acked-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index da4a104..7319532 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -154,7 +154,7 @@ int stmmac_mdio_register(struct net_device *ndev) else irqlist = priv->mii_irq; - new_bus->name = "STMMAC MII Bus"; + new_bus->name = "stmmac"; new_bus->read = &stmmac_mdio_read; new_bus->write = &stmmac_mdio_write; new_bus->reset = &stmmac_mdio_reset; -- cgit v0.10.2 From 56ac11cf2f21366ad48b356f7a0d1af8cff3588e Mon Sep 17 00:00:00 2001 From: Radu Iliescu Date: Thu, 19 Jan 2012 03:57:57 +0000 Subject: llc: Fix race condition in llc_ui_recvmsg There is a race on sk_receive_queue between llc_ui_recvmsg and sock_queue_rcv_skb. Our current solution is to protect skb_eat in llc_ui_recvmsg with the queue spinlock. Signed-off-by: Radu Iliescu Signed-off-by: David S. Miller diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index a18e6c3..b9bef2c 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -713,6 +713,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb = NULL; struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); + unsigned long cpu_flags; size_t copied = 0; u32 peek_seq = 0; u32 *seq; @@ -838,7 +839,9 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, goto copy_uaddr; if (!(flags & MSG_PEEK)) { + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); sk_eat_skb(sk, skb, 0); + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); *seq = 0; } @@ -859,7 +862,9 @@ copy_uaddr: llc_cmsg_rcv(msg, skb); if (!(flags & MSG_PEEK)) { + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); sk_eat_skb(sk, skb, 0); + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); *seq = 0; } -- cgit v0.10.2 From 5437f4b2576f1763a27bc4c0e7f7c280220ba1aa Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Mon, 23 Jan 2012 23:08:56 +0000 Subject: stmmac: added PCI identifiers STM has a device ID within its own VENDOR space, and it is being used in the STA2X11 I/O Hub. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 54a819a..c796de9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -170,9 +170,9 @@ static int stmmac_pci_resume(struct pci_dev *pdev) #define STMMAC_DEVICE_ID 0x1108 static DEFINE_PCI_DEVICE_TABLE(stmmac_id_table) = { - { - PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)}, { - } + {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)}, + {} }; MODULE_DEVICE_TABLE(pci, stmmac_id_table); -- cgit v0.10.2 From 2bbba277a554431a426e81f37d5c50ab6216c07d Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 24 Jan 2012 10:41:40 +0000 Subject: drivers/net: dsa/mv88e6xxx.c files need linux/module.h An implicit instance of module.h leaked back into existence and was masking the fact that these drivers weren't calling out the include for itself. Fix the drivers before we remove the implicit include path via net/netprio_cgroup.h file. Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 7fc4e81..325391d 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index 6f23c95..c17c75b 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index e0eb6824..55888b0 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 5467c04..a2c62c2 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include -- cgit v0.10.2 From c11bf1c8baff170fa478adc04964da519d160e62 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 24 Jan 2012 10:21:28 +0000 Subject: net/hyperv: fix possible memory leak in do_set_multicast() do_set_multicast() may not free the memory malloc in netvsc_set_multicast_list(). Signed-off-by: Wei Yongjun Signed-off-by: Haiyang Zhang Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 462d05f..1a1ca6c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -68,11 +68,11 @@ static void do_set_multicast(struct work_struct *w) nvdev = hv_get_drvdata(ndevctx->device_ctx); if (nvdev == NULL) - return; + goto out; rdev = nvdev->extension; if (rdev == NULL) - return; + goto out; if (net->flags & IFF_PROMISC) rndis_filter_set_packet_filter(rdev, @@ -83,6 +83,7 @@ static void do_set_multicast(struct work_struct *w) NDIS_PACKET_TYPE_ALL_MULTICAST | NDIS_PACKET_TYPE_DIRECTED); +out: kfree(w); } -- cgit v0.10.2 From b82b9183d4f18f9b8c4bb31f223eb6c79b734eb0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 24 Jan 2012 05:16:00 +0000 Subject: team: send only changed options/ports via netlink This patch changes event message behaviour to send only updated records instead of whole list. This fixes bug on which userspace receives non-actual data in case multiple events occur in row. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ed2a862..6b678f3 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -92,9 +92,9 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) return NULL; } -int team_options_register(struct team *team, - const struct team_option *option, - size_t option_count) +int __team_options_register(struct team *team, + const struct team_option *option, + size_t option_count) { int i; struct team_option **dst_opts; @@ -116,8 +116,11 @@ int team_options_register(struct team *team, } } - for (i = 0; i < option_count; i++) + for (i = 0; i < option_count; i++) { + dst_opts[i]->changed = true; + dst_opts[i]->removed = false; list_add_tail(&dst_opts[i]->list, &team->option_list); + } kfree(dst_opts); return 0; @@ -130,10 +133,22 @@ rollback: return err; } -EXPORT_SYMBOL(team_options_register); +static void __team_options_mark_removed(struct team *team, + const struct team_option *option, + size_t option_count) +{ + int i; + + for (i = 0; i < option_count; i++, option++) { + struct team_option *del_opt; -static void __team_options_change_check(struct team *team, - struct team_option *changed_option); + del_opt = __team_find_option(team, option->name); + if (del_opt) { + del_opt->changed = true; + del_opt->removed = true; + } + } +} static void __team_options_unregister(struct team *team, const struct team_option *option, @@ -152,12 +167,29 @@ static void __team_options_unregister(struct team *team, } } +static void __team_options_change_check(struct team *team); + +int team_options_register(struct team *team, + const struct team_option *option, + size_t option_count) +{ + int err; + + err = __team_options_register(team, option, option_count); + if (err) + return err; + __team_options_change_check(team); + return 0; +} +EXPORT_SYMBOL(team_options_register); + void team_options_unregister(struct team *team, const struct team_option *option, size_t option_count) { + __team_options_mark_removed(team, option, option_count); + __team_options_change_check(team); __team_options_unregister(team, option, option_count); - __team_options_change_check(team, NULL); } EXPORT_SYMBOL(team_options_unregister); @@ -176,7 +208,8 @@ static int team_option_set(struct team *team, struct team_option *option, if (err) return err; - __team_options_change_check(team, option); + option->changed = true; + __team_options_change_check(team); return err; } @@ -653,6 +686,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) return -ENOENT; } + port->removed = true; __team_port_change_check(port, false); team_port_list_del_port(team, port); team_adjust_ops(team); @@ -1200,10 +1234,9 @@ err_fill: return err; } -static int team_nl_fill_options_get_changed(struct sk_buff *skb, - u32 pid, u32 seq, int flags, - struct team *team, - struct team_option *changed_option) +static int team_nl_fill_options_get(struct sk_buff *skb, + u32 pid, u32 seq, int flags, + struct team *team, bool fillall) { struct nlattr *option_list; void *hdr; @@ -1223,12 +1256,19 @@ static int team_nl_fill_options_get_changed(struct sk_buff *skb, struct nlattr *option_item; long arg; + /* Include only changed options if fill all mode is not on */ + if (!fillall && !option->changed) + continue; option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); if (!option_item) goto nla_put_failure; NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name); - if (option == changed_option) + if (option->changed) { NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED); + option->changed = false; + } + if (option->removed) + NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_REMOVED); switch (option->type) { case TEAM_OPTION_TYPE_U32: NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32); @@ -1255,13 +1295,13 @@ nla_put_failure: return -EMSGSIZE; } -static int team_nl_fill_options_get(struct sk_buff *skb, - struct genl_info *info, int flags, - struct team *team) +static int team_nl_fill_options_get_all(struct sk_buff *skb, + struct genl_info *info, int flags, + struct team *team) { - return team_nl_fill_options_get_changed(skb, info->snd_pid, - info->snd_seq, NLM_F_ACK, - team, NULL); + return team_nl_fill_options_get(skb, info->snd_pid, + info->snd_seq, NLM_F_ACK, + team, true); } static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) @@ -1273,7 +1313,7 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) if (!team) return -EINVAL; - err = team_nl_send_generic(info, team, team_nl_fill_options_get); + err = team_nl_send_generic(info, team, team_nl_fill_options_get_all); team_nl_team_put(team); @@ -1365,10 +1405,10 @@ team_put: return err; } -static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, - u32 pid, u32 seq, int flags, - struct team *team, - struct team_port *changed_port) +static int team_nl_fill_port_list_get(struct sk_buff *skb, + u32 pid, u32 seq, int flags, + struct team *team, + bool fillall) { struct nlattr *port_list; void *hdr; @@ -1387,12 +1427,19 @@ static int team_nl_fill_port_list_get_changed(struct sk_buff *skb, list_for_each_entry(port, &team->port_list, list) { struct nlattr *port_item; + /* Include only changed ports if fill all mode is not on */ + if (!fillall && !port->changed) + continue; port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); if (!port_item) goto nla_put_failure; NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex); - if (port == changed_port) + if (port->changed) { NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED); + port->changed = false; + } + if (port->removed) + NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_REMOVED); if (port->linkup) NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP); NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed); @@ -1408,13 +1455,13 @@ nla_put_failure: return -EMSGSIZE; } -static int team_nl_fill_port_list_get(struct sk_buff *skb, - struct genl_info *info, int flags, - struct team *team) +static int team_nl_fill_port_list_get_all(struct sk_buff *skb, + struct genl_info *info, int flags, + struct team *team) { - return team_nl_fill_port_list_get_changed(skb, info->snd_pid, - info->snd_seq, NLM_F_ACK, - team, NULL); + return team_nl_fill_port_list_get(skb, info->snd_pid, + info->snd_seq, NLM_F_ACK, + team, true); } static int team_nl_cmd_port_list_get(struct sk_buff *skb, @@ -1427,7 +1474,7 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb, if (!team) return -EINVAL; - err = team_nl_send_generic(info, team, team_nl_fill_port_list_get); + err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all); team_nl_team_put(team); @@ -1464,8 +1511,7 @@ static struct genl_multicast_group team_change_event_mcgrp = { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, }; -static int team_nl_send_event_options_get(struct team *team, - struct team_option *changed_option) +static int team_nl_send_event_options_get(struct team *team) { struct sk_buff *skb; int err; @@ -1475,8 +1521,7 @@ static int team_nl_send_event_options_get(struct team *team, if (!skb) return -ENOMEM; - err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team, - changed_option); + err = team_nl_fill_options_get(skb, 0, 0, 0, team, false); if (err < 0) goto err_fill; @@ -1489,18 +1534,17 @@ err_fill: return err; } -static int team_nl_send_event_port_list_get(struct team_port *port) +static int team_nl_send_event_port_list_get(struct team *team) { struct sk_buff *skb; int err; - struct net *net = dev_net(port->team->dev); + struct net *net = dev_net(team->dev); skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOMEM; - err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0, - port->team, port); + err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false); if (err < 0) goto err_fill; @@ -1544,12 +1588,11 @@ static void team_nl_fini(void) * Change checkers ******************/ -static void __team_options_change_check(struct team *team, - struct team_option *changed_option) +static void __team_options_change_check(struct team *team) { int err; - err = team_nl_send_event_options_get(team, changed_option); + err = team_nl_send_event_options_get(team); if (err) netdev_warn(team->dev, "Failed to send options change via netlink\n"); } @@ -1559,9 +1602,10 @@ static void __team_port_change_check(struct team_port *port, bool linkup) { int err; - if (port->linkup == linkup) + if (!port->removed && port->linkup == linkup) return; + port->changed = true; port->linkup = linkup; if (linkup) { struct ethtool_cmd ecmd; @@ -1577,7 +1621,7 @@ static void __team_port_change_check(struct team_port *port, bool linkup) port->duplex = 0; send_event: - err = team_nl_send_event_port_list_get(port); + err = team_nl_send_event_port_list_get(port->team); if (err) netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n", port->dev->name); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 828181f..58404b0 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -46,6 +46,10 @@ struct team_port { u32 speed; u8 duplex; + /* Custom gennetlink interface related flags */ + bool changed; + bool removed; + struct rcu_head rcu; }; @@ -72,6 +76,10 @@ struct team_option { enum team_option_type type; int (*getter)(struct team *team, void *arg); int (*setter)(struct team *team, void *arg); + + /* Custom gennetlink interface related flags */ + bool changed; + bool removed; }; struct team_mode { @@ -207,6 +215,7 @@ enum { TEAM_ATTR_OPTION_CHANGED, /* flag */ TEAM_ATTR_OPTION_TYPE, /* u8 */ TEAM_ATTR_OPTION_DATA, /* dynamic */ + TEAM_ATTR_OPTION_REMOVED, /* flag */ __TEAM_ATTR_OPTION_MAX, TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, @@ -227,6 +236,7 @@ enum { TEAM_ATTR_PORT_LINKUP, /* flag */ TEAM_ATTR_PORT_SPEED, /* u32 */ TEAM_ATTR_PORT_DUPLEX, /* u8 */ + TEAM_ATTR_PORT_REMOVED, /* flag */ __TEAM_ATTR_PORT_MAX, TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1, -- cgit v0.10.2 From 8dd5d2f15134c17302e67d9aedb0c51e00c354b0 Mon Sep 17 00:00:00 2001 From: Lucas Kannebley Tavares Date: Mon, 9 Jan 2012 17:39:24 -0200 Subject: Updated TTY MAINTAINERS info Greg Kroah-Hartman is the current TTY maintainer, however he wouldn't appear listed as such upon running get_maintainers.pl for drivers under drivers/tty/serial. Signed-off-by: Lucas Kannebley Tavares Signed-off-by: Greg Kroah-Hartman diff --git a/MAINTAINERS b/MAINTAINERS index 89b70df..a723385 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6664,7 +6664,7 @@ TTY LAYER M: Greg Kroah-Hartman S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6.git -F: drivers/tty/* +F: drivers/tty/ F: drivers/tty/serial/serial_core.c F: include/linux/serial_core.h F: include/linux/serial.h -- cgit v0.10.2 From 26aa38cafae0dbef3b2fe75ea487c83313c36d45 Mon Sep 17 00:00:00 2001 From: Lucas Kannebley Tavares Date: Mon, 9 Jan 2012 10:58:06 -0200 Subject: jsm: Fixed EEH recovery error There was an error on the jsm driver that would cause it to be unable to recover after a second error is detected. At the first error, the device recovers properly: [72521.485691] EEH: Detected PCI bus error on device 0003:02:00.0 [72521.485695] EEH: This PCI device has failed 1 times in the last hour: ... [72532.035693] ttyn3 at MMIO 0x0 (irq = 49) is a jsm [72532.105689] jsm: Port 3 added However, at the second error, it cascades until EEH disables the device: [72631.229549] Call Trace: ... [72641.725687] jsm: Port 3 added [72641.725695] EEH: Detected PCI bus error on device 0003:02:00.0 [72641.725698] EEH: This PCI device has failed 3 times in the last hour: It was caused because the PCI state was not being saved after the first restore. Therefore, at the second recovery the PCI state would not be restored. Signed-off-by: Lucas Kannebley Tavares Signed-off-by: Breno Leitao Acked-by: Thadeu Lima de Souza Cascardo Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 7c867a0..7545fe1 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -251,6 +251,7 @@ static void jsm_io_resume(struct pci_dev *pdev) struct jsm_board *brd = pci_get_drvdata(pdev); pci_restore_state(pdev); + pci_save_state(pdev); jsm_uart_port_init(brd); } -- cgit v0.10.2 From 0eee50af5b13e00b3fb7a5fe8480419a71b8235d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 12 Jan 2012 22:55:15 +0100 Subject: TTY: fix UV serial console regression Commit 74c2107759d (serial: Use block_til_ready helper) and its fixup 3f582b8c110 (serial: fix termios settings in open) introduced a regression on UV systems. The serial eventually freezes while being used. It's completely unpredictable and sometimes needs a heap of traffic to happen first. To reproduce this, yast installation was used as it turned out to be pretty reliable in reproducing. Especially during installation process where one doesn't have an SSH daemon running. And no monitor as the HW is completely headless. So this was fun to find. Given the machine doesn't boot on vanilla before 2.6.36 final. (And the commits above are older.) Unless there is some bad race in the code, the hardware seems to be pretty broken. Otherwise pure MSR read should not cause such a bug, or? So to prevent the bug, revert to the old behavior. I.e. read modem status only if we really have to -- for non-CLOCAL set serials. Non-CLOCAL works on this hardware OK, I tried. See? I don't. And document that shit. Signed-off-by: Jiri Slaby Cc: stable References: https://lkml.org/lkml/2011/12/6/573 References: https://bugzilla.novell.com/show_bug.cgi?id=718518 Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index ef9dd62..bf6e238 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -227,7 +227,6 @@ int tty_port_block_til_ready(struct tty_port *port, int do_clocal = 0, retval; unsigned long flags; DEFINE_WAIT(wait); - int cd; /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { @@ -284,11 +283,14 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - /* Probe the carrier. For devices with no carrier detect this - will always return true */ - cd = tty_port_carrier_raised(port); + /* + * Probe the carrier. For devices with no carrier detect + * tty_port_carrier_raised will always return true. + * Never ask drivers if CLOCAL is set, this causes troubles + * on some hardware. + */ if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || cd)) + (do_clocal || tty_port_carrier_raised(port))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; -- cgit v0.10.2 From c452ed70771cea3af73d21a5914989137fbd28b8 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 24 Jan 2012 16:03:33 -0500 Subject: net: flow_dissector.c missing include linux/export.h The file net/core/flow_dissector.c seems to be missing including linux/export.h. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 0985b9b..a225089 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1,4 +1,5 @@ #include +#include #include #include #include -- cgit v0.10.2 From fff24e21e17e438bf24791ed9cea7bbc02ad2dbb Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 23 Jan 2012 16:14:05 -0800 Subject: drm/i915: Correct debugfs printout for RC1e. We had two things in a row claiming to be RC6. Signed-off-by: Eric Anholt Reviewed-by: Keith Packard Reviewed-by: Ben Widawsky Reviewed-by: Eugeni Dodonov Reviewed-by: Kenneth Graunke Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a017b98..60dcee3 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1106,7 +1106,7 @@ static int gen6_drpc_info(struct seq_file *m) seq_printf(m, "SW control enabled: %s\n", yesno((rpmodectl1 & GEN6_RP_MEDIA_MODE_MASK) == GEN6_RP_MEDIA_SW_MODE)); - seq_printf(m, "RC6 Enabled: %s\n", + seq_printf(m, "RC1e Enabled: %s\n", yesno(rcctl1 & GEN6_RC_CTL_RC1e_ENABLE)); seq_printf(m, "RC6 Enabled: %s\n", yesno(rcctl1 & GEN6_RC_CTL_RC6_ENABLE)); -- cgit v0.10.2 From 04115a9dee110b52a8eaa556c574022fa3bf4704 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 23 Jan 2012 16:14:06 -0800 Subject: drm/i915: Re-enable gen7 RC6 and GPU turbo after resume. Signed-off-by: Eric Anholt Cc: stable@vger.kernel.org Reviewed-by: Keith Packard Reviewed-by: Eugeni Dodonov Reviewed-by: Kenneth Graunke Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 30d924f..2b5eb22 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -827,7 +827,7 @@ int i915_save_state(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) ironlake_disable_drps(dev); - if (IS_GEN6(dev)) + if (INTEL_INFO(dev)->gen >= 6) gen6_disable_rps(dev); /* Cache mode state */ @@ -886,7 +886,7 @@ int i915_restore_state(struct drm_device *dev) intel_init_emon(dev); } - if (IS_GEN6(dev)) { + if (INTEL_INFO(dev)->gen >= 6) { gen6_enable_rps(dev_priv); gen6_update_ring_freq(dev_priv); } -- cgit v0.10.2 From 075edca43b819c33bd755eaf7a3bd0e1b3279f70 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Jan 2012 09:44:28 +0100 Subject: drm/i915: allow userspace forcewake references also on gen7 We need this to correctly access registers in the gt power well from userspace. Signed-Off-by: Daniel Vetter Reviewed-by: Eugeni Dodonov Reviewed-by: Keith Packard Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 60dcee3..6b72046 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1669,7 +1669,7 @@ static int i915_forcewake_open(struct inode *inode, struct file *file) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (!IS_GEN6(dev)) + if (INTEL_INFO(dev)->gen < 6) return 0; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -1686,7 +1686,7 @@ int i915_forcewake_release(struct inode *inode, struct file *file) struct drm_device *dev = inode->i_private; struct drm_i915_private *dev_priv = dev->dev_private; - if (!IS_GEN6(dev)) + if (INTEL_INFO(dev)->gen < 6) return 0; /* -- cgit v0.10.2 From 48467a92215ced69a65c89c1b064dd84728a5ed0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Jan 2012 09:44:29 +0100 Subject: drm/i915: debugfs: show semaphore registers also on gen7 Corresponding changes to improve our error_state are pending some other patches to clean up things first. Signed-Off-by: Daniel Vetter Reviewed-by: Eugeni Dodonov Reviewed-by: Keith Packard Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 6b72046..99f1407 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -653,7 +653,7 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data) seq_printf(m, " Size : %08x\n", ring->size); seq_printf(m, " Active : %08x\n", intel_ring_get_active_head(ring)); seq_printf(m, " NOPID : %08x\n", I915_READ_NOPID(ring)); - if (IS_GEN6(dev)) { + if (IS_GEN6(dev) || IS_GEN7(dev)) { seq_printf(m, " Sync 0 : %08x\n", I915_READ_SYNC_0(ring)); seq_printf(m, " Sync 1 : %08x\n", I915_READ_SYNC_1(ring)); } -- cgit v0.10.2 From 171cf94ccb4b476d1d7d694a31d0820558375132 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 24 Jan 2012 21:33:26 +0000 Subject: PCMCIA: fix sa1111 oops on remove The sa1111 socket driver oopses when removed: Unable to handle kernel NULL pointer dereference at virtual address 000003b0 pgd = c1b40000 [000003b0] *pgd=00000000 Internal error: Oops: 41b43005 [#1] Modules linked in: CPU: 0 Not tainted (3.3.0-rc1+ #744) PC is at pcmcia_remove+0x3c/0x60 LR is at pcmcia_remove+0x34/0x60 This is because we try to dereference a NULL 's' to obtain the next pointer. Fix this. Signed-off-by: Russell King diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index 5986690..27f2fe3 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -205,7 +205,8 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) dev_set_drvdata(&dev->dev, NULL); - for (; next = s->next, s; s = next) { + for (; s; s = next) { + next = s->next; soc_pcmcia_remove_one(&s->soc); kfree(s); } -- cgit v0.10.2 From 36a1211970193ce215de50ed1e4e1272bc814df1 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 24 Jan 2012 11:33:19 +0000 Subject: netprio_cgroup.h: dont include module.h from other includes A considerable effort was invested in wiping out module.h from being present in all the other standard includes. This one leaked back in, but once again isn't strictly necessary, so remove it. Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h index e503b87..7b2d431 100644 --- a/include/net/netprio_cgroup.h +++ b/include/net/netprio_cgroup.h @@ -13,7 +13,6 @@ #ifndef _NETPRIO_CGROUP_H #define _NETPRIO_CGROUP_H -#include #include #include #include -- cgit v0.10.2 From f54367f9de1b52000c008d3f68512f44cc392816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Jan 2012 22:35:05 +0100 Subject: Documentation/pinctrl: fix a few syntax errors in code examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Stephen Warren Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 6727b92..5324d31 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -857,42 +857,41 @@ case), we define a mapping like this: ... { - .name "2bit" + .name = "2bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", .dev_name = "foo-mmc.0", }, { - .name "4bit" + .name = "4bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_1_grp", .dev_name = "foo-mmc.0", }, { - .name "4bit" + .name = "4bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", .dev_name = "foo-mmc.0", }, { - .name "8bit" + .name = "8bit" .ctrl_dev_name = "pinctrl-foo", - .function = "mmc0", .group = "mmc0_1_grp", .dev_name = "foo-mmc.0", }, { - .name "8bit" + .name = "8bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_2_grp", .dev_name = "foo-mmc.0", }, { - .name "8bit" + .name = "8bit" .ctrl_dev_name = "pinctrl-foo", .function = "mmc0", .group = "mmc0_3_grp", @@ -995,7 +994,7 @@ This is enabled by simply setting the .hog_on_boot field in the map to true, like this: { - .name "POWERMAP" + .name = "POWERMAP" .ctrl_dev_name = "pinctrl-foo", .function = "power_func", .hog_on_boot = true, -- cgit v0.10.2 From f9d41d7cb5a3a4fe9585d47e518d779d2aef8c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 19 Jan 2012 22:42:48 +0100 Subject: pinctrl: unbreak error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's better to not line break error messages to allow easier grepping for them even when the line gets >80 chars. Additionally some minor reformating is done. Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index a76a348..0b22037 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -152,8 +152,7 @@ static int pin_request(struct pinctrl_dev *pctldev, status = 0; if (status) - dev_err(pctldev->dev, "->request on device %s failed " - "for pin %d\n", + dev_err(pctldev->dev, "->request on device %s failed for pin %d\n", pctldev->desc->name, pin); out_free_pin: if (status) { @@ -355,21 +354,20 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, /* First sanity check the new mapping */ for (i = 0; i < num_maps; i++) { if (!maps[i].name) { - pr_err("failed to register map %d: " - "no map name given\n", i); + pr_err("failed to register map %d: no map name given\n", + i); return -EINVAL; } if (!maps[i].ctrl_dev && !maps[i].ctrl_dev_name) { - pr_err("failed to register map %s (%d): " - "no pin control device given\n", + pr_err("failed to register map %s (%d): no pin control device given\n", maps[i].name, i); return -EINVAL; } if (!maps[i].function) { - pr_err("failed to register map %s (%d): " - "no function ID given\n", maps[i].name, i); + pr_err("failed to register map %s (%d): no function ID given\n", + maps[i].name, i); return -EINVAL; } @@ -442,8 +440,7 @@ static int acquire_pins(struct pinctrl_dev *pctldev, ret = pin_request(pctldev, pins[i], func, NULL); if (ret) { dev_err(pctldev->dev, - "could not get pin %d for function %s " - "on device %s - conflicting mux mappings?\n", + "could not get pin %d for function %s on device %s - conflicting mux mappings?\n", pins[i], func ? : "(undefined)", pinctrl_dev_get_name(pctldev)); /* On error release all taken pins */ @@ -473,8 +470,7 @@ static void release_pins(struct pinctrl_dev *pctldev, ret = pctlops->get_group_pins(pctldev, group_selector, &pins, &num_pins); if (ret) { - dev_err(pctldev->dev, "could not get pins to release for " - "group selector %d\n", + dev_err(pctldev->dev, "could not get pins to release for group selector %d\n", group_selector); return; } @@ -526,8 +522,7 @@ static int pinmux_check_pin_group(struct pinctrl_dev *pctldev, ret = pinctrl_get_group_selector(pctldev, groups[0]); if (ret < 0) { dev_err(pctldev->dev, - "function %s wants group %s but the pin " - "controller does not seem to have that group\n", + "function %s wants group %s but the pin controller does not seem to have that group\n", pmxops->get_function_name(pctldev, func_selector), groups[0]); return ret; @@ -535,8 +530,7 @@ static int pinmux_check_pin_group(struct pinctrl_dev *pctldev, if (num_groups > 1) dev_dbg(pctldev->dev, - "function %s support more than one group, " - "default-selecting first group %s (%d)\n", + "function %s support more than one group, default-selecting first group %s (%d)\n", pmxops->get_function_name(pctldev, func_selector), groups[0], ret); @@ -628,10 +622,8 @@ static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev, if (pmx->pctldev && pmx->pctldev != pctldev) { dev_err(pctldev->dev, - "different pin control devices given for device %s, " - "function %s\n", - devname, - map->function); + "different pin control devices given for device %s, function %s\n", + devname, map->function); return -EINVAL; } pmx->dev = dev; @@ -695,7 +687,6 @@ static void pinmux_free_groups(struct pinmux *pmx) */ struct pinmux *pinmux_get(struct device *dev, const char *name) { - struct pinmux_map const *map = NULL; struct pinctrl_dev *pctldev = NULL; const char *devname = NULL; @@ -745,8 +736,7 @@ struct pinmux *pinmux_get(struct device *dev, const char *name) else if (map->ctrl_dev_name) devname = map->ctrl_dev_name; - pr_warning("could not find a pinctrl device for pinmux " - "function %s, fishy, they shall all have one\n", + pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n", map->function); pr_warning("given pinctrl device name: %s", devname ? devname : "UNDEFINED"); @@ -932,8 +922,8 @@ static int pinmux_hog_map(struct pinctrl_dev *pctldev, * without any problems, so then we can hog pinmuxes for * all devices that just want a static pin mux at this point. */ - dev_err(pctldev->dev, "map %s wants to hog a non-system " - "pinmux, this is not going to work\n", map->name); + dev_err(pctldev->dev, "map %s wants to hog a non-system pinmux, this is not going to work\n", + map->name); return -EINVAL; } @@ -1122,13 +1112,15 @@ static int pinmux_show(struct seq_file *s, void *what) seq_printf(s, "device: %s function: %s (%u),", pinctrl_dev_get_name(pmx->pctldev), - pmxops->get_function_name(pctldev, pmx->func_selector), + pmxops->get_function_name(pctldev, + pmx->func_selector), pmx->func_selector); seq_printf(s, " groups: ["); list_for_each_entry(grp, &pmx->groups, node) { seq_printf(s, " %s (%u)", - pctlops->get_group_name(pctldev, grp->group_selector), + pctlops->get_group_name(pctldev, + grp->group_selector), grp->group_selector); } seq_printf(s, " ]"); -- cgit v0.10.2 From 0215716083cac67ff7ea3e3efdc9943bdb462274 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 20 Jan 2012 08:17:22 -0800 Subject: pinctrl: free debugfs entries when unloading a pinmux driver We were not cleaning up properly after unloading a pinmux driver compiled as module. Signed-off-by: Tony Lindgren Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 569bdb3..d9d35fc 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -510,10 +510,12 @@ static struct dentry *debugfs_root; static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) { - static struct dentry *device_root; + struct dentry *device_root; device_root = debugfs_create_dir(dev_name(pctldev->dev), debugfs_root); + pctldev->device_root = device_root; + if (IS_ERR(device_root) || !device_root) { pr_warn("failed to create debugfs directory for %s\n", dev_name(pctldev->dev)); @@ -529,6 +531,11 @@ static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev) pinconf_init_device_debugfs(device_root, pctldev); } +static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) +{ + debugfs_remove_recursive(pctldev->device_root); +} + static void pinctrl_init_debugfs(void) { debugfs_root = debugfs_create_dir("pinctrl", NULL); @@ -553,6 +560,10 @@ static void pinctrl_init_debugfs(void) { } +static void pinctrl_remove_device_debugfs(struct pinctrl_dev *pctldev) +{ +} + #endif /** @@ -641,6 +652,7 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) if (pctldev == NULL) return; + pinctrl_remove_device_debugfs(pctldev); pinmux_unhog_maps(pctldev); /* TODO: check that no pinmuxes are still active? */ mutex_lock(&pinctrldev_list_mutex); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 177a331..cfa86da 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -41,6 +41,9 @@ struct pinctrl_dev { struct device *dev; struct module *owner; void *driver_data; +#ifdef CONFIG_DEBUG_FS + struct dentry *device_root; +#endif #ifdef CONFIG_PINMUX struct mutex pinmux_hogs_lock; struct list_head pinmux_hogs; -- cgit v0.10.2 From efc3dbc37412c027e363736b4f4c74ee5e8ecffc Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 24 Jan 2012 17:03:44 -0500 Subject: rds: Make rds_sock_lock BH rather than IRQ safe. rds_sock_info() triggers locking warnings because we try to perform a local_bh_enable() (via sock_i_ino()) while hardware interrupts are disabled (via taking rds_sock_lock). There is no reason for rds_sock_lock to be a hardware IRQ disabling lock, none of these access paths run in hardware interrupt context. Therefore making it a BH disabling lock is safe and sufficient to fix this bug. Reported-by: Kumar Sanghvi Reported-by: Josh Boyer Signed-off-by: David S. Miller diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index bb6ad81..424ff62 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -68,7 +68,6 @@ static int rds_release(struct socket *sock) { struct sock *sk = sock->sk; struct rds_sock *rs; - unsigned long flags; if (!sk) goto out; @@ -94,10 +93,10 @@ static int rds_release(struct socket *sock) rds_rdma_drop_keys(rs); rds_notify_queue_get(rs, NULL); - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); list_del_init(&rs->rs_item); rds_sock_count--; - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); rds_trans_put(rs->rs_transport); @@ -409,7 +408,6 @@ static const struct proto_ops rds_proto_ops = { static int __rds_create(struct socket *sock, struct sock *sk, int protocol) { - unsigned long flags; struct rds_sock *rs; sock_init_data(sock, sk); @@ -426,10 +424,10 @@ static int __rds_create(struct socket *sock, struct sock *sk, int protocol) spin_lock_init(&rs->rs_rdma_lock); rs->rs_rdma_keys = RB_ROOT; - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); list_add_tail(&rs->rs_item, &rds_sock_list); rds_sock_count++; - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); return 0; } @@ -471,12 +469,11 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, { struct rds_sock *rs; struct rds_incoming *inc; - unsigned long flags; unsigned int total = 0; len /= sizeof(struct rds_info_message); - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); list_for_each_entry(rs, &rds_sock_list, rs_item) { read_lock(&rs->rs_recv_lock); @@ -492,7 +489,7 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, read_unlock(&rs->rs_recv_lock); } - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); lens->nr = total; lens->each = sizeof(struct rds_info_message); @@ -504,11 +501,10 @@ static void rds_sock_info(struct socket *sock, unsigned int len, { struct rds_info_socket sinfo; struct rds_sock *rs; - unsigned long flags; len /= sizeof(struct rds_info_socket); - spin_lock_irqsave(&rds_sock_lock, flags); + spin_lock_bh(&rds_sock_lock); if (len < rds_sock_count) goto out; @@ -529,7 +525,7 @@ out: lens->nr = rds_sock_count; lens->each = sizeof(struct rds_info_socket); - spin_unlock_irqrestore(&rds_sock_lock, flags); + spin_unlock_bh(&rds_sock_lock); } static void rds_exit(void) -- cgit v0.10.2 From 3bc4f0d8f65b396d214a31195a91c0394c5bf628 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Mon, 16 Jan 2012 15:52:36 +0530 Subject: omap-serial :Make the suspend/resume functions depend on CONFIG_PM_SLEEP. The macro SET_SYSTEM_SLEEP_PM_OPS depends CONFIG_PM_SLEEP. The patch defines the suspend and resume functions for CONFIG_PM_SLEEP instead of CONFIG_SUSPEND. Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d192dcb..33e3360 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1160,7 +1160,7 @@ static struct uart_driver serial_omap_reg = { .cons = OMAP_CONSOLE, }; -#ifdef CONFIG_SUSPEND +#ifdef CONFIG_PM_SLEEP static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); -- cgit v0.10.2 From b5148856a2f732e7e99edad22bb8e2037af28ad3 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Mon, 16 Jan 2012 15:52:37 +0530 Subject: omap-serial: make serial_omap_restore_context depend on CONFIG_PM_RUNTIME The function serial_omap_restore_context is called only from serial_omap_runtime_resume which depends on CONFIG_PM_RUNTIME. Make serial_omap_restore_context also compile conditionally. if CONFIG_PM_RUNTIME is not defined below warn may be seen. LD net/xfrm/built-in.o drivers/tty/serial/omap-serial.c:1524: warning: 'serial_omap_restore_context' defined but not used CC drivers/tty/vt/selection.o Acked-by: Govindraj.R Signed-off-by: Shubhrajyoti D Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 33e3360..1c24269 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1521,6 +1521,7 @@ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1) } } +#ifdef CONFIG_PM_RUNTIME static void serial_omap_restore_context(struct uart_omap_port *up) { if (up->errata & UART_ERRATA_i202_MDR1_ACCESS) @@ -1550,7 +1551,6 @@ static void serial_omap_restore_context(struct uart_omap_port *up) serial_out(up, UART_OMAP_MDR1, up->mdr1); } -#ifdef CONFIG_PM_RUNTIME static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); -- cgit v0.10.2 From 0a697b22252c9d7208b5fb3e9fbd124dd229f1d2 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 21 Jan 2012 00:27:40 -0700 Subject: tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA mode Ensure FIFO levels are set correctly in non-DMA mode (the default). This patch will cause a receive FIFO threshold interrupt to be raised when there is at least one byte in the RX FIFO. It will also cause a transmit FIFO threshold interrupt when there is only one byte remaining in the TX FIFO. These changes fix the receive interrupt problem and part of the transmit interrupt problem. A separate set of issues must be worked around for the transmit path to have a basic level of functionality; a subsequent patch will address these. DMA operation is unaffected by this patch. Signed-off-by: Paul Walmsley Cc: Tomi Valkeinen Cc: Govindraj Raja Cc: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 1c24269..ca54f03 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -46,6 +46,18 @@ #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ +/* SCR register bitmasks */ +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) +#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6) + +/* FCR register bitmasks */ +#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 +#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) +#define OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT 4 + +/* TLR register bitmasks */ +#define OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT 0 + static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ @@ -694,6 +706,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, unsigned char efr = 0; unsigned long flags = 0; unsigned int baud, quot; + u32 tlr; switch (termios->c_cflag & CSIZE) { case CS5: @@ -811,14 +824,28 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->mcr = serial_in(up, UART_MCR); serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); /* FIFO ENABLE, DMA MODE */ - serial_out(up, UART_FCR, up->fcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + up->scr |= OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; + up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; if (up->use_dma) { - serial_out(up, UART_TI752_TLR, 0); - up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8); + tlr = 0; + } else { + up->scr &= ~OMAP_UART_SCR_TX_EMPTY; + + /* Set receive FIFO threshold to 1 */ + up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; + up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT); + + /* Set TX FIFO threshold to "63" (actually 1) */ + up->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT); + tlr = (0xf << OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT); } + serial_out(up, UART_TI752_TLR, tlr); + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_OMAP_SCR, up->scr); serial_out(up, UART_EFR, up->efr); -- cgit v0.10.2 From 43cf7c0bebf50d0b68aa42ae6d24cf08e3f24823 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 21 Jan 2012 00:27:41 -0700 Subject: tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip It seems that when the transmit FIFO threshold is reached on OMAP UARTs, it does not result in a PRCM wakeup. This appears to be a silicon bug. This means that if the MPU powerdomain is in a low-power state, the MPU will not be awakened to refill the FIFO until the next interrupt from another device. The best solution, at least for the short term, would be for the OMAP serial driver to call a OMAP subarchitecture function to prevent the MPU powerdomain from entering a low power state while the FIFO has data to transmit. However, we no longer have a clean way to do this, since patches that add platform_data function pointers have been deprecated by the OMAP maintainer. So we attempt to work around this as well. The workarounds depend on the setting of CONFIG_CPU_IDLE. When CONFIG_CPU_IDLE=n, the driver will now only transmit one byte at a time. This causes the transmit FIFO threshold interrupt to stay active until there is no more data to be sent. Thus, the MPU powerdomain stays on during transmits. Aside from that energy consumption penalty, each transmitted byte results in a huge number of UART interrupts -- about five per byte. This wastes CPU time and is quite inefficient, but is probably the most expedient workaround in this case. When CONFIG_CPU_IDLE=y, there is a slightly more direct workaround: the PM QoS constraint can be abused to keep the MPU powerdomain on. This results in a normal number of interrupts, but, similar to the above workaround, wastes power by preventing the MPU from entering WFI. Future patches are planned for the 3.4 merge window to implement more efficient, but also more disruptive, workarounds to these problems. DMA operation is unaffected by this patch. Signed-off-by: Paul Walmsley Cc: Tomi Valkeinen Cc: Govindraj Raja Cc: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h index 9ff4444..12a64eb 100644 --- a/arch/arm/plat-omap/include/plat/omap-serial.h +++ b/arch/arm/plat-omap/include/plat/omap-serial.h @@ -131,6 +131,7 @@ struct uart_omap_port { u32 context_loss_cnt; u32 errata; u8 wakeups_enabled; + u8 max_tx_count; struct pm_qos_request pm_qos_request; u32 latency; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index ca54f03..e00ac05 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -88,6 +88,49 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) serial_out(up, UART_FCR, 0); } +/** + * serial_omap_block_cpu_low_power_state - prevent MPU pwrdm from leaving ON + * @up: struct uart_omap_port * + * + * Prevent the MPU powerdomain from entering a power state lower than + * ON. (It should be sufficient to prevent it from entering INACTIVE, + * but there is presently no easy way to do this.) This works around + * a suspected silicon bug in the OMAP UART IP blocks. The UARTs should + * wake the PRCM when the transmit FIFO threshold interrupt is raised, but + * they do not. See also serial_omap_allow_cpu_low_power_state(). No + * return value. + */ +static void serial_omap_block_cpu_low_power_state(struct uart_omap_port *up) +{ +#ifdef CONFIG_CPU_IDLE + up->latency = 1; + schedule_work(&up->qos_work); +#else + up->max_tx_count = 1; +#endif +} + +/** + * serial_omap_allow_cpu_low_power_state - remove power state restriction on MPU + * @up: struct uart_omap_port * + * + * Cancel the effects of serial_omap_block_cpu_low_power_state(). + * This should allow the MPU powerdomain to enter a power state lower + * than ON, assuming the rest of the kernel is not restricting it. + * This works around a suspected silicon bug in the OMAP UART IP + * blocks. The UARTs should wake the PRCM when the transmit FIFO + * threshold interrupt is raised, but they do not. No return value. + */ +static void serial_omap_allow_cpu_low_power_state(struct uart_omap_port *up) +{ +#ifdef CONFIG_CPU_IDLE + up->latency = up->calc_latency; + schedule_work(&up->qos_work); +#else + up->max_tx_count = up->port.fifosize / 4; +#endif +} + /* * serial_omap_get_divisor - calculate divisor value * @port: uart port info @@ -163,6 +206,9 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } + if (!up->use_dma) + serial_omap_allow_cpu_low_power_state(up); + pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); } @@ -264,7 +310,7 @@ static void transmit_chars(struct uart_omap_port *up) serial_omap_stop_tx(&up->port); return; } - count = up->port.fifosize / 4; + count = up->max_tx_count; do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -297,6 +343,7 @@ static void serial_omap_start_tx(struct uart_port *port) if (!up->use_dma) { pm_runtime_get_sync(&up->pdev->dev); + serial_omap_block_cpu_low_power_state(up); serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); @@ -1421,6 +1468,8 @@ static int serial_omap_probe(struct platform_device *pdev) up->port.fifosize = 64; up->port.ops = &serial_omap_pops; + up->max_tx_count = up->port.fifosize / 4; + if (pdev->dev.of_node) up->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); else -- cgit v0.10.2 From 0dd2b62ada6f911fbd13e98e98f57f4edc42c604 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Jan 2012 13:13:50 -0200 Subject: drivers: usb: Fix dependency for USB_HWA_HCD Fix the following build warning: warning: (USB_HWA_HCD) selects UWB_HWA which has unmet direct dependencies (UWB && USB) Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 91413ca..cc12dc2 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -559,7 +559,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on USB + depends on USB && UWB select USB_WUSB select UWB_HWA help -- cgit v0.10.2 From 3a0bac0676d7f433c12389fc0bc574f048f921c3 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Fri, 6 Jan 2012 13:33:28 +0100 Subject: usb: add support for STA2X11 host driver Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Cc: Alan Cox Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index f4b627d..01bb7241d 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -276,6 +276,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); + if (pdev->vendor == PCI_VENDOR_ID_STMICRO + && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) + ehci->sbrn = 0x20; /* ConneXT has no sbrn register */ /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test @@ -526,6 +529,9 @@ static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0), .driver_data = (unsigned long) &ehci_pci_hc_driver, + }, { + PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST), + .driver_data = (unsigned long) &ehci_pci_hc_driver, }, { /* end: all zeroes */ } }; diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 6109810..1843bb6 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -397,6 +397,10 @@ static const struct pci_device_id pci_ids [] = { { /* handle any USB OHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0), .driver_data = (unsigned long) &ohci_pci_hc_driver, + }, { + /* The device in the ConneXT I/O hub has no class reg */ + PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI), + .driver_data = (unsigned long) &ohci_pci_hc_driver, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE (pci, pci_ids); -- cgit v0.10.2 From 15699e6fafc3a90e5fdc2ef30555a04dee62286f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Fri, 20 Jan 2012 01:49:57 +0100 Subject: USB: cdc-wdm: Avoid hanging on interface with no USB_CDC_DMM_TYPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe does not strictly require the USB_CDC_DMM_TYPE descriptor, which is a good thing as it makes the driver usable on non-conforming interfaces. A user could e.g. bind to it to a CDC ECM interface by using the new_id and bind sysfs files. But this would fail with a 0 buffer length due to the missing descriptor. Fix by defining a reasonable fallback size: The minimum device receive buffer size required by the CDC WMC standard, revision 1.1 Signed-off-by: Bjørn Mork Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a940ad9..d2b3cff 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -57,6 +57,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_MAX 16 +/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ +#define WDM_DEFAULT_BUFSIZE 256 static DEFINE_MUTEX(wdm_mutex); @@ -636,7 +638,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_cdc_dmm_desc *dmhd; u8 *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; - u16 maxcom = 0; + u16 maxcom = WDM_DEFAULT_BUFSIZE; if (!buffer) goto out; -- cgit v0.10.2 From f7aa554510f8ff4b1411c31332713ccd8c244ace Mon Sep 17 00:00:00 2001 From: Deepthi Dharwar Date: Thu, 12 Jan 2012 03:05:55 +0000 Subject: powerpc/cpuidle: Make it a bool, not a tristate As pointed out, asm/system.h has empty inline implementations for update_smt_snooze_delay and pseries_notify_cpuidle_add_cpu, which are used when CONFIG_PSERIES_IDLE is undefined. Since those two functions are used in core power architecture functions (store_smt_snooze_delay at kernel/sysfs.c and smp_xics_setup_cpu at platforms/pseries/smp.c), Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index ae7b6d4..31f22c1 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -122,7 +122,7 @@ config DTL Say N if you are unsure. config PSERIES_IDLE - tristate "Cpuidle driver for pSeries platforms" + bool "Cpuidle driver for pSeries platforms" depends on CPU_IDLE depends on PPC_PSERIES default y -- cgit v0.10.2 From 897e01a08c08d86bc76bebb0ca14588b406500e5 Mon Sep 17 00:00:00 2001 From: Christian Kujau Date: Tue, 17 Jan 2012 19:13:05 +0000 Subject: powerpc/crash: Fix build error without SMP I could not find cpus_in_crash anywhere in the sourcetree, except for arch/powerpc/kernel/crash.c. Moving the definition into the CONFIG_SMP fixes it. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c index 28be345..abef751 100644 --- a/arch/powerpc/kernel/crash.c +++ b/arch/powerpc/kernel/crash.c @@ -46,7 +46,6 @@ /* This keeps a track of which one is the crashing cpu. */ int crashing_cpu = -1; -static atomic_t cpus_in_crash; static int time_to_dump; #define CRASH_HANDLER_MAX 3 @@ -66,6 +65,7 @@ static int handle_fault(struct pt_regs *regs) #ifdef CONFIG_SMP +static atomic_t cpus_in_crash; void crash_ipi_callback(struct pt_regs *regs) { static cpumask_t cpus_state_saved = CPU_MASK_NONE; -- cgit v0.10.2 From 2053c2d1b116282038fde3d60965ac158b1b8ba2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 15 Jan 2012 12:36:16 +0100 Subject: usb: mv-otg - Fix build if CONFIG_USB is not set ERROR: "usb_remove_hcd" [drivers/usb/otg/mv_otg.ko] undefined! ERROR: "usb_add_hcd" [drivers/usb/otg/mv_otg.ko] undefined! Signed-off-by: Geert Uytterhoeven -- Inpired by drivers/usb/otg/msm_otg.c. Is this correct? drivers/usb/otg/mv_otg.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index db0d4fc..b5fbe14 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -202,6 +202,7 @@ static void mv_otg_init_irq(struct mv_otg *mvotg) static void mv_otg_start_host(struct mv_otg *mvotg, int on) { +#ifdef CONFIG_USB struct otg_transceiver *otg = &mvotg->otg; struct usb_hcd *hcd; @@ -216,6 +217,7 @@ static void mv_otg_start_host(struct mv_otg *mvotg, int on) usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); else usb_remove_hcd(hcd); +#endif /* CONFIG_USB */ } static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) -- cgit v0.10.2 From 0c8b92f7f25927808fb465474e344b759bade612 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Jan 2012 15:32:34 -0800 Subject: Revert "drivers: usb: Fix dependency for USB_HWA_HCD" This reverts commit 0dd2b62ada6f911fbd13e98e98f57f4edc42c604. It causes a bunch of Kconfig errors: drivers/usb/host/Kconfig:559:error: recursive dependency detected! drivers/usb/host/Kconfig:559: symbol USB_HWA_HCD depends on UWB drivers/uwb/Kconfig:5: symbol UWB is selected by USB_WUSB drivers/usb/wusbcore/Kconfig:4: symbol USB_WUSB is selected by USB_HWA_HCD showing that this really wasn't the correct fix at all. Cc: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index cc12dc2..91413ca 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -559,7 +559,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on USB && UWB + depends on USB select USB_WUSB select UWB_HWA help -- cgit v0.10.2 From 074cc73506f529f39fef32ad1c9e1d4cdd8acf6c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 24 Jan 2012 17:16:54 -0600 Subject: qcaux: add more Pantech UML190 and UML290 ports More ports we now know how to talk to. Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 30b73e6..a348198 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -36,6 +36,7 @@ #define UTSTARCOM_PRODUCT_UM175_V1 0x3712 #define UTSTARCOM_PRODUCT_UM175_V2 0x3714 #define UTSTARCOM_PRODUCT_UM175_ALLTEL 0x3715 +#define PANTECH_PRODUCT_UML190_VZW 0x3716 #define PANTECH_PRODUCT_UML290_VZW 0x3718 /* CMOTECH devices */ @@ -67,7 +68,11 @@ static struct usb_device_id id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) }, - { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xfe, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfd, 0xff) }, /* NMEA */ + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfe, 0xff) }, /* WMC */ + { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, /* DIAG */ { }, }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v0.10.2 From d8d8ffa477831b713ddfa2ad4d0ca545f3b567e5 Mon Sep 17 00:00:00 2001 From: Shreshtha Kumar Sahu Date: Wed, 18 Jan 2012 15:53:59 +0530 Subject: amba-pl011: do not disable RTS during shutdown In present driver, shutdown clears RTS and DTR in CR register. But the documentation "Documentation/serial/driver" suggests not to disable RTS and DTR in shutdown(). Also RTS and DTR is preserved between shutdown and startup calls, i.e. these are restored in startup if they were enabled while doing shutdown. So that if RTS and DTR are set using pl011_set_mctrl then it should continue even after shutdown->startup sequence. For throttling/unthrottling user should call pl011_set_mctrl. Signed-off-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Russell King Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 9ae0240..ce843d0 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -159,6 +159,7 @@ struct uart_amba_port { unsigned int fifosize; /* vendor-specific */ unsigned int lcrh_tx; /* vendor-specific */ unsigned int lcrh_rx; /* vendor-specific */ + unsigned int old_cr; /* state during shutdown */ bool autorts; char type[12]; bool interrupt_may_hang; /* vendor-specific */ @@ -1411,7 +1412,9 @@ static int pl011_startup(struct uart_port *port) while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); - cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; + /* restore RTS and DTR */ + cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR); + cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); /* Clear pending error interrupts */ @@ -1469,6 +1472,7 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; /* * disable all interrupts @@ -1488,9 +1492,16 @@ static void pl011_shutdown(struct uart_port *port) /* * disable the port + * disable the port. It should not disable RTS and DTR. + * Also RTS and DTR state should be preserved to restore + * it during startup(). */ uap->autorts = false; - writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR); + cr = readw(uap->port.membase + UART011_CR); + uap->old_cr = cr; + cr &= UART011_CR_RTS | UART011_CR_DTR; + cr |= UART01x_CR_UARTEN | UART011_CR_TXE; + writew(cr, uap->port.membase + UART011_CR); /* * disable break condition and fifos @@ -1905,6 +1916,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->vendor = vendor; uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; + uap->old_cr = 0; uap->fifosize = vendor->fifosize; uap->interrupt_may_hang = vendor->interrupt_may_hang; uap->port.dev = &dev->dev; -- cgit v0.10.2 From ef605fdb33883d687cff5ba75095a91b313b4966 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 17 Jan 2012 11:52:28 +0100 Subject: serial: amba-pl011: lock console writes against interrupts Protect against pl011_console_write() and the interrupt for the console UART running concurrently on different CPUs. Otherwise the console_write could spin for a long time waiting for the UART to become not busy, while the other CPU continuously services UART interrupts and keeps the UART busy. The checks for sysrq and oops_in_progress are taken from 8250.c. Cc: stable Signed-off-by: Rabin Vincent Reviewed-by: Srinidhi Kasagar Reviewed-by: Bibek Basu Reviewed-by: Shreshtha Kumar Sahu Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index ce843d0..6800f5f 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1751,9 +1751,19 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) { struct uart_amba_port *uap = amba_ports[co->index]; unsigned int status, old_cr, new_cr; + unsigned long flags; + int locked = 1; clk_enable(uap->clk); + local_irq_save(flags); + if (uap->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&uap->port.lock); + else + spin_lock(&uap->port.lock); + /* * First save the CR then disable the interrupts */ @@ -1773,6 +1783,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) } while (status & UART01x_FR_BUSY); writew(old_cr, uap->port.membase + UART011_CR); + if (locked) + spin_unlock(&uap->port.lock); + local_irq_restore(flags); + clk_disable(uap->clk); } -- cgit v0.10.2 From a5492e6591b8fdf171236046f9d6194f9bb4062b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:16 -0800 Subject: docbook: don't use serial_core.h in device-drivers book Fix new kernel-doc warning. This file no longer contains kernel-doc comments. Warning(include/linux/serial_core.h): no structured comments found Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index b638e50..b330b32 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -216,7 +216,6 @@ X!Isound/sound_firmware.c 16x50 UART Driver -!Iinclude/linux/serial_core.h !Edrivers/tty/serial/serial_core.c !Edrivers/tty/serial/8250.c -- cgit v0.10.2 From ecd9d34a674b671f09f55b3365d852f75a1f598b Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 22 Jan 2012 23:24:15 -0500 Subject: c2port: fix build error for duramar2150 due to missing header. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file needs the basic headers for resource management, otherwise we will see this build error: CC [M] drivers/misc/c2port/c2port-duramar2150.o drivers/misc/c2port/c2port-duramar2150.c: In function ‘duramar2150_c2port_init’: drivers/misc/c2port/c2port-duramar2150.c:125:2: error: implicit declaration of function ‘request_region’ [-Werror=implicit-function-declaration] drivers/misc/c2port/c2port-duramar2150.c:139:2: error: implicit declaration of function ‘release_region’ [-Werror=implicit-function-declaration] Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c index 778fc3f..5484301 100644 --- a/drivers/misc/c2port/c2port-duramar2150.c +++ b/drivers/misc/c2port/c2port-duramar2150.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define DATA_PORT 0x325 -- cgit v0.10.2 From 7c5763b8453a94871d356f20df30f350f8631e8b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 24 Jan 2012 02:11:25 -0200 Subject: drivers: misc: Remove MISC_DEVICES config option MISC_DEVICES option alone does not select any kernel code and can cause dependency build warnings, such as: warning: (KS8851 && AX88796_93CX6 && RTL8180 && RTL8187 && ADM8211 && RT2400PCI && RT2500PCI && RT61PCI && RT2800PCI && R8187SE) selects EEPROM_93CX6 which has unmet direct dependencies (MISC_DEVICES) As the current drivers/misc/Kconfig stands, it is only possible to select the drivers below if MISC_DEVICES option is selected: source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" source "drivers/misc/iwmc3200top/Kconfig" source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" So remove MISC_DEVICES option so that nothing is dependant on it. Signed-off-by: Fabio Estevam Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6a1a092..c779509 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -2,24 +2,14 @@ # Misc strange devices # -# This one has to live outside of the MISC_DEVICES conditional, -# because it may be selected by drivers/platform/x86/hp_accel. +menu "Misc devices" + config SENSORS_LIS3LV02D tristate depends on INPUT select INPUT_POLLDEV default n -menuconfig MISC_DEVICES - bool "Misc devices" - ---help--- - Say Y here to get to see options for device drivers from various - different categories. This option alone does not add any kernel code. - - If you say N, all options in this submenu will be skipped and disabled. - -if MISC_DEVICES - config AD525X_DPOT tristate "Analog Devices Digital Potentiometers" depends on (I2C || SPI) && SYSFS @@ -516,5 +506,4 @@ source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" - -endif # MISC_DEVICES +endmenu diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index cf444b0..00fcbed 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -477,7 +477,6 @@ config MMC_SDHI config MMC_CB710 tristate "ENE CB710 MMC/SD Interface support" depends on PCI - select MISC_DEVICES select CB710_CORE help This option enables support for MMC/SD part of ENE CB710/720 Flash diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig index 1ea811c..fe42fc0 100644 --- a/drivers/net/ethernet/micrel/Kconfig +++ b/drivers/net/ethernet/micrel/Kconfig @@ -42,7 +42,6 @@ config KS8851 select NET_CORE select MII select CRC32 - select MISC_DEVICES select EEPROM_93CX6 ---help--- SPI driver for Micrel KS8851 SPI attached network chip. -- cgit v0.10.2 From 773598357c0baf03081cf87f2b444f97744faf1e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 19 Jan 2012 11:28:56 -0800 Subject: serial: Fix wakeup init logic to speed up startup The synchronize_rcu() call resulting from making every serial driver wake-up capable (commit b3b708fa) slows boot down on my Tegra2x system (with CONFIG_PREEMPT disabled). But this is avoidable since it is the device_set_wakeup_enable() and then subsequence disable which causes the delay. We might as well just make the device wakeup capable but not actually enable it for wakeup until needed. Effectively the current code does this: device_set_wakeup_capable(dev, 1); device_set_wakeup_enable(dev, 1); device_set_wakeup_enable(dev, 0); We can just drop the last two lines. Before this change my boot log says: [ 0.227062] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled [ 0.702928] serial8250.0: ttyS0 at MMIO 0x70006040 (irq = 69) is a Tegra after: [ 0.227264] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled [ 0.227983] serial8250.0: ttyS0 at MMIO 0x70006040 (irq = 69) is a Tegra for saving of 450ms. Suggested-by: Rafael J. Wysocki Acked-by: Rafael J. Wysocki Signed-off-by: Simon Glass Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index c7bf31a..1305618 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2348,11 +2348,11 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) */ tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); if (likely(!IS_ERR(tty_dev))) { - device_init_wakeup(tty_dev, 1); - device_set_wakeup_enable(tty_dev, 0); - } else + device_set_wakeup_capable(tty_dev, 1); + } else { printk(KERN_ERR "Cannot register tty device on line %d\n", uport->line); + } /* * Ensure UPF_DEAD is not set. -- cgit v0.10.2 From f7ea82beb24f65e2058fa4029edefa0949e872b1 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 25 Jan 2012 13:31:56 +1100 Subject: powerpc/powernv: Fix PCI resource handling Recent changes to the handling of PCI resources for host bridges are breaking the PowerNV code for assigning resources on IODA. The root of the problem is that the pci_bus attached to a host bridge no longer has its "legacy" resource pointers populated but only uses the newer list instead. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index f31162c..5e155df 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -204,11 +204,10 @@ static void __devinit pnv_ioda_offset_bus(struct pci_bus *bus, pr_devel(" -> OBR %s [%x] +%016llx\n", bus->self ? pci_name(bus->self) : "root", flags, offset); - for (i = 0; i < 2; i++) { - r = bus->resource[i]; + pci_bus_for_each_resource(bus, r, i) { if (r && (r->flags & flags)) { - bus->resource[i]->start += offset; - bus->resource[i]->end += offset; + r->start += offset; + r->end += offset; } } list_for_each_entry(dev, &bus->devices, bus_list) @@ -288,12 +287,17 @@ static void __devinit pnv_ioda_calc_bus(struct pci_bus *bus, unsigned int flags, * assignment algorithm is going to be uber-trivial for now, we * can try to be smarter later at filling out holes. */ - start = bus->self ? 0 : bus->resource[bres]->start; - - /* Don't hand out IO 0 */ - if ((flags & IORESOURCE_IO) && !bus->self) - start += 0x1000; - + if (bus->self) { + /* No offset for downstream bridges */ + start = 0; + } else { + /* Offset from the root */ + if (flags & IORESOURCE_IO) + /* Don't hand out IO 0 */ + start = hose->io_resource.start + 0x1000; + else + start = hose->mem_resources[0].start; + } while(!list_empty(&head)) { w = list_first_entry(&head, struct resource_wrap, link); list_del(&w->link); @@ -321,13 +325,20 @@ static void __devinit pnv_ioda_calc_bus(struct pci_bus *bus, unsigned int flags, empty: /* Only setup P2P's, not the PHB itself */ if (bus->self) { - WARN_ON(bus->resource[bres] == NULL); - bus->resource[bres]->start = 0; - bus->resource[bres]->flags = (*size) ? flags : 0; - bus->resource[bres]->end = (*size) ? (*size - 1) : 0; + struct resource *res = bus->resource[bres]; + + if (WARN_ON(res == NULL)) + return; - /* Clear prefetch bus resources for now */ - bus->resource[2]->flags = 0; + /* + * FIXME: We should probably export and call + * pci_bridge_check_ranges() to properly re-initialize + * the PCI portion of the flags here, and to detect + * what the bridge actually supports. + */ + res->start = 0; + res->flags = (*size) ? flags : 0; + res->end = (*size) ? (*size - 1) : 0; } pr_devel("<- CBR %s [%x] *size=%016llx *align=%016llx\n", -- cgit v0.10.2 From 3493c85366ba09c9d0972c919e7123367a39982a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 25 Jan 2012 13:33:22 +1100 Subject: powerpc: Fix build on some non-freescale platforms Commit 9deaa53ac7fa373623123aa4f18828dd62292b1a broke build on platforms that use legacy_serial.c without also having CONFIG_SERIAL_8250_FSL enabled due to an unconditional code to a routine in that module. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 3fea368..bedd12e 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -442,8 +442,10 @@ static void __init fixup_port_irq(int index, port->irq = virq; +#ifdef CONFIG_SERIAL_8250_FSL if (of_device_is_compatible(np, "fsl,ns16550")) port->handle_irq = fsl8250_handle_irq; +#endif } static void __init fixup_port_pio(int index, -- cgit v0.10.2 From b30b3c60a25a4afbc49167ecb6210c291178ee5f Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 25 Jan 2012 10:02:46 +0200 Subject: usb: musb: omap2430: minor cleanups. 1/ remove incorrect comment (it is a non-blocking notifier) 2/ Use correct symbolic return value for notifier 3/ Make sure otg_notifier_work is cancelled before module exit. Signed-off-by: NeilBrown Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c27bbbf..df719ea 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -222,7 +222,6 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } -/* blocking notifier support */ static int musb_otg_notifications(struct notifier_block *nb, unsigned long event, void *unused) { @@ -231,7 +230,7 @@ static int musb_otg_notifications(struct notifier_block *nb, musb->xceiv_event = event; schedule_work(&musb->otg_notifier_work); - return 0; + return NOTIFY_OK; } static void musb_otg_notifier_work(struct work_struct *data_notifier_work) @@ -386,6 +385,7 @@ static void omap2430_musb_disable(struct musb *musb) static int omap2430_musb_exit(struct musb *musb) { del_timer_sync(&musb_idle_timer); + cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); otg_put_transceiver(musb->xceiv); -- cgit v0.10.2 From 3b25eb690e8c7424eecffe1458c02b87b32aa001 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 25 Jan 2012 09:55:46 +0100 Subject: ALSA: hda - Fix silent output on ASUS A6Rp The refactoring of Realtek codec driver in 3.2 kernel caused a regression for ASUS A6Rp laptop; it doesn't give any output. The reason was that this machine has a secret master mute (or EAPD) control via NID 0x0f VREF. Setting VREF50 on this node makes the sound working again. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42588 Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c95c8bd..a234799 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5586,6 +5586,7 @@ static const struct hda_amp_list alc861_loopbacks[] = { /* Pin config fixes */ enum { PINFIX_FSC_AMILO_PI1505, + PINFIX_ASUS_A6RP, }; static const struct alc_fixup alc861_fixups[] = { @@ -5597,9 +5598,18 @@ static const struct alc_fixup alc861_fixups[] = { { } } }, + [PINFIX_ASUS_A6RP] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* node 0x0f VREF seems controlling the master output */ + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + { } + }, + }, }; static const struct snd_pci_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), {} }; -- cgit v0.10.2 From a6a600d10aaddf1da38053c4c6b64f50f56176e6 Mon Sep 17 00:00:00 2001 From: Gustavo Maciel Dias Vieira Date: Tue, 24 Jan 2012 13:27:56 -0200 Subject: ALSA: hda: set mute led polarity for laptops with buggy BIOS based on SSID HP laptop models with buggy BIOS are apparently frequent, including machines with different codecs. Set the polarity of the mute led based on the SSID and include an entry for the HP Mini 110-3100. Signed-off-by: Gustavo Maciel Dias Vieira Tested-by: Predrag Ivanovic Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 336cfcd..948f0be 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4870,7 +4870,14 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) /* BIOS bug: unfilled OEM string */ if (strstr(dev->name, "HP_Mute_LED_P_G")) { set_hp_led_gpio(codec); - spec->gpio_led_polarity = 1; + switch (codec->subsystem_id) { + case 0x103c148a: + spec->gpio_led_polarity = 0; + break; + default: + spec->gpio_led_polarity = 1; + break; + } return 1; } } -- cgit v0.10.2 From 34ae6c96a6a7db4ed8ec0524bf7fa1086b9ab2ba Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 24 Jan 2012 11:56:02 +0100 Subject: ARM: 7298/1: realview: fix mapping of MPCore private memory region Since commit 0536bdf33faf (ARM: move iotable mappings within the vmalloc region), the RealView PB11MP cannot boot anymore. This is caused by the way the mappings are described on this platform (define replaced by hex values for clarity): { /* GIC CPU interface mapping */ .virtual = IO_ADDRESS(0x1F000100), .pfn = __phys_to_pfn(0x1F000100), .length = SZ_4K, .type = MT_DEVICE, }, { /* GIC distributor mapping */ .virtual = IO_ADDRESS(0x1F001000), .pfn = __phys_to_pfn(0x1F001000), .length = SZ_4K, .type = MT_DEVICE, } The first mapping ends up reserving two pages, and clashes with the second one, which triggers a BUG_ON in vm_area_add_early(). In order to solve this problem, treat the MPCore private memory region (containing the SCU, the GIC and the TWD) as a single region, as described in the TRM: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360f/CACGDJJC.html The EB11MP is converted the same way, even if it manages to avoid the problem. Tested on both PB11MP and EB11MP. Signed-off-by: Marc Zyngier Signed-off-by: Russell King diff --git a/arch/arm/mach-realview/include/mach/board-eb.h b/arch/arm/mach-realview/include/mach/board-eb.h index 794a8d9..124bce6 100644 --- a/arch/arm/mach-realview/include/mach/board-eb.h +++ b/arch/arm/mach-realview/include/mach/board-eb.h @@ -47,21 +47,23 @@ #define REALVIEW_EB_USB_BASE 0x4F000000 /* USB */ #ifdef CONFIG_REALVIEW_EB_ARM11MP_REVB -#define REALVIEW_EB11MP_SCU_BASE 0x10100000 /* SCU registers */ -#define REALVIEW_EB11MP_GIC_CPU_BASE 0x10100100 /* Generic interrupt controller CPU interface */ -#define REALVIEW_EB11MP_TWD_BASE 0x10100600 -#define REALVIEW_EB11MP_GIC_DIST_BASE 0x10101000 /* Generic interrupt controller distributor */ +#define REALVIEW_EB11MP_PRIV_MEM_BASE 0x1F000000 #define REALVIEW_EB11MP_L220_BASE 0x10102000 /* L220 registers */ #define REALVIEW_EB11MP_SYS_PLD_CTRL1 0xD8 /* Register offset for MPCore sysctl */ #else -#define REALVIEW_EB11MP_SCU_BASE 0x1F000000 /* SCU registers */ -#define REALVIEW_EB11MP_GIC_CPU_BASE 0x1F000100 /* Generic interrupt controller CPU interface */ -#define REALVIEW_EB11MP_TWD_BASE 0x1F000600 -#define REALVIEW_EB11MP_GIC_DIST_BASE 0x1F001000 /* Generic interrupt controller distributor */ +#define REALVIEW_EB11MP_PRIV_MEM_BASE 0x1F000000 #define REALVIEW_EB11MP_L220_BASE 0x1F002000 /* L220 registers */ #define REALVIEW_EB11MP_SYS_PLD_CTRL1 0x74 /* Register offset for MPCore sysctl */ #endif +#define REALVIEW_EB11MP_PRIV_MEM_SIZE SZ_8K +#define REALVIEW_EB11MP_PRIV_MEM_OFF(x) (REALVIEW_EB11MP_PRIV_MEM_BASE + (x)) + +#define REALVIEW_EB11MP_SCU_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0) /* SCU registers */ +#define REALVIEW_EB11MP_GIC_CPU_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0x0100) /* Generic interrupt controller CPU interface */ +#define REALVIEW_EB11MP_TWD_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0x0600) +#define REALVIEW_EB11MP_GIC_DIST_BASE REALVIEW_EB11MP_PRIV_MEM_OFF(0x1000) /* Generic interrupt controller distributor */ + /* * Core tile identification (REALVIEW_SYS_PROCID) */ diff --git a/arch/arm/mach-realview/include/mach/board-pb11mp.h b/arch/arm/mach-realview/include/mach/board-pb11mp.h index 7abf918..aa2d4e0 100644 --- a/arch/arm/mach-realview/include/mach/board-pb11mp.h +++ b/arch/arm/mach-realview/include/mach/board-pb11mp.h @@ -75,6 +75,8 @@ /* * Testchip peripheral and fpga gic regions */ +#define REALVIEW_TC11MP_PRIV_MEM_BASE 0x1F000000 +#define REALVIEW_TC11MP_PRIV_MEM_SIZE SZ_8K #define REALVIEW_TC11MP_SCU_BASE 0x1F000000 /* IRQ, Test chip */ #define REALVIEW_TC11MP_GIC_CPU_BASE 0x1F000100 /* Test chip interrupt controller CPU interface */ #define REALVIEW_TC11MP_TWD_BASE 0x1F000600 diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index e629621..9578145 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -91,14 +91,9 @@ static struct map_desc realview_eb_io_desc[] __initdata = { static struct map_desc realview_eb11mp_io_desc[] __initdata = { { - .virtual = IO_ADDRESS(REALVIEW_EB11MP_SCU_BASE), - .pfn = __phys_to_pfn(REALVIEW_EB11MP_SCU_BASE), - .length = SZ_4K, - .type = MT_DEVICE, - }, { - .virtual = IO_ADDRESS(REALVIEW_EB11MP_GIC_DIST_BASE), - .pfn = __phys_to_pfn(REALVIEW_EB11MP_GIC_DIST_BASE), - .length = SZ_4K, + .virtual = IO_ADDRESS(REALVIEW_EB11MP_PRIV_MEM_BASE), + .pfn = __phys_to_pfn(REALVIEW_EB11MP_PRIV_MEM_BASE), + .length = REALVIEW_EB11MP_PRIV_MEM_SIZE, .type = MT_DEVICE, }, { .virtual = IO_ADDRESS(REALVIEW_EB11MP_L220_BASE), diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c index 127a3fd..2147335 100644 --- a/arch/arm/mach-realview/realview_pb11mp.c +++ b/arch/arm/mach-realview/realview_pb11mp.c @@ -64,15 +64,10 @@ static struct map_desc realview_pb11mp_io_desc[] __initdata = { .pfn = __phys_to_pfn(REALVIEW_PB11MP_GIC_DIST_BASE), .length = SZ_4K, .type = MT_DEVICE, - }, { - .virtual = IO_ADDRESS(REALVIEW_TC11MP_GIC_CPU_BASE), - .pfn = __phys_to_pfn(REALVIEW_TC11MP_GIC_CPU_BASE), - .length = SZ_4K, - .type = MT_DEVICE, - }, { - .virtual = IO_ADDRESS(REALVIEW_TC11MP_GIC_DIST_BASE), - .pfn = __phys_to_pfn(REALVIEW_TC11MP_GIC_DIST_BASE), - .length = SZ_4K, + }, { /* Maps the SCU, GIC CPU interface, TWD, GIC DIST */ + .virtual = IO_ADDRESS(REALVIEW_TC11MP_PRIV_MEM_BASE), + .pfn = __phys_to_pfn(REALVIEW_TC11MP_PRIV_MEM_BASE), + .length = REALVIEW_TC11MP_PRIV_MEM_SIZE, .type = MT_DEVICE, }, { .virtual = IO_ADDRESS(REALVIEW_SCTL_BASE), -- cgit v0.10.2 From d68133b5a81bd9c4b673c2a731ac1a33a9dc0cb8 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 24 Jan 2012 16:52:52 +0100 Subject: ARM: 7299/1: ftrace: clear zero bit in reported IPs for Thumb-2 The dynamic ftrace ops startup test currently fails on Thumb-2 kernels: Testing tracer function: PASSED Testing dynamic ftrace: PASSED Testing dynamic ftrace ops #1: (0 0 0 0 0) FAILED! This is because while the addresses in the mcount records do not have the zero bit set, the IP reported by the mcount call does have it set (because it is copied from the LR). This mismatch causes the ops filtering in ftrace_ops_list_func() to not call the relevant tracers. Fix this by clearing the zero bit before adjusting the LR for the mcount instruction size. Also, combine the mov+sub into a single sub instruction. Acked-by: Dave Martin Signed-off-by: Rabin Vincent Signed-off-by: Russell King diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 520889c..9fd0ba9 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -149,6 +149,11 @@ ENDPROC(ret_from_fork) #endif #endif +.macro mcount_adjust_addr rd, rn + bic \rd, \rn, #1 @ clear the Thumb bit if present + sub \rd, \rd, #MCOUNT_INSN_SIZE +.endm + .macro __mcount suffix mcount_enter ldr r0, =ftrace_trace_function @@ -173,8 +178,7 @@ ENDPROC(ret_from_fork) mcount_exit 1: mcount_get_lr r1 @ lr of instrumented func - mov r0, lr @ instrumented function - sub r0, r0, #MCOUNT_INSN_SIZE + mcount_adjust_addr r0, lr @ instrumented function adr lr, BSYM(2f) mov pc, r2 2: mcount_exit @@ -184,8 +188,7 @@ ENDPROC(ret_from_fork) mcount_enter mcount_get_lr r1 @ lr of instrumented func - mov r0, lr @ instrumented function - sub r0, r0, #MCOUNT_INSN_SIZE + mcount_adjust_addr r0, lr @ instrumented function .globl ftrace_call\suffix ftrace_call\suffix: @@ -205,11 +208,11 @@ ftrace_graph_call\suffix: #ifdef CONFIG_DYNAMIC_FTRACE @ called from __ftrace_caller, saved in mcount_enter ldr r1, [sp, #16] @ instrumented routine (func) + mcount_adjust_addr r1, r1 #else @ called from __mcount, untouched in lr - mov r1, lr @ instrumented routine (func) + mcount_adjust_addr r1, lr @ instrumented routine (func) #endif - sub r1, r1, #MCOUNT_INSN_SIZE mov r2, fp @ frame pointer bl prepare_ftrace_return mcount_exit -- cgit v0.10.2 From 598781d71119827b454fd75d46f84755bca6f0c6 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 24 Jan 2012 18:54:21 +0100 Subject: drm: Fix authentication kernel crash If the master tries to authenticate a client using drm_authmagic and that client has already closed its drm file descriptor, either wilfully or because it was terminated, the call to drm_authmagic will dereference a stale pointer into kmalloc'ed memory and corrupt it. Typically this results in a hard system hang. This patch fixes that problem by removing any authentication tokens (struct drm_magic_entry) open for a file descriptor when that file descriptor is closed. Signed-off-by: Thomas Hellstrom Reviewed-by: Daniel Vetter Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 3f46772..ba23790 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -101,7 +101,7 @@ static int drm_add_magic(struct drm_master *master, struct drm_file *priv, * Searches and unlinks the entry in drm_device::magiclist with the magic * number hash key, while holding the drm_device::struct_mutex lock. */ -static int drm_remove_magic(struct drm_master *master, drm_magic_t magic) +int drm_remove_magic(struct drm_master *master, drm_magic_t magic) { struct drm_magic_entry *pt; struct drm_hash_item *hash; @@ -136,6 +136,8 @@ static int drm_remove_magic(struct drm_master *master, drm_magic_t magic) * If there is a magic number in drm_file::magic then use it, otherwise * searches an unique non-zero magic number and add it associating it with \p * file_priv. + * This ioctl needs protection by the drm_global_mutex, which protects + * struct drm_file::magic and struct drm_magic_entry::priv. */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -173,6 +175,8 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) * \return zero if authentication successed, or a negative number otherwise. * * Checks if \p file_priv is associated with the magic number passed in \arg. + * This ioctl needs protection by the drm_global_mutex, which protects + * struct drm_file::magic and struct drm_magic_entry::priv. */ int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index c00cf15..6263b01 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -487,6 +487,11 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->device), dev->open_count); + /* Release any auth tokens that might point to this file_priv, + (do that under the drm_global_mutex) */ + if (file_priv->magic) + (void) drm_remove_magic(file_priv->master, file_priv->magic); + /* if the master has gone away we can't do anything with the lock */ if (file_priv->minor->master) drm_master_release(dev, filp); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 76caa67..92f0981 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1328,6 +1328,7 @@ extern int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* Cache management (drm_cache.c) */ void drm_clflush_pages(struct page *pages[], unsigned long num_pages); -- cgit v0.10.2 From d54fbd49efe5c75bc7cf963bf065aef3fd22417a Mon Sep 17 00:00:00 2001 From: Jerome Glisse Date: Tue, 24 Jan 2012 12:08:52 -0500 Subject: drm/radeon: silence out possible lock dependency warning Silence out the lock dependency warning by moving bo allocation out of ib mutex protected section. Might lead to useless temporary allocation but it's not harmful as such things only happen at initialization. Signed-off-by: Jerome Glisse Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index e8bc709..1cb4b94 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -204,22 +204,25 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) int radeon_ib_pool_init(struct radeon_device *rdev) { + struct radeon_sa_manager tmp; int i, r; - mutex_lock(&rdev->ib_pool.mutex); - if (rdev->ib_pool.ready) { - mutex_unlock(&rdev->ib_pool.mutex); - return 0; - } - - r = radeon_sa_bo_manager_init(rdev, &rdev->ib_pool.sa_manager, + r = radeon_sa_bo_manager_init(rdev, &tmp, RADEON_IB_POOL_SIZE*64*1024, RADEON_GEM_DOMAIN_GTT); if (r) { - mutex_unlock(&rdev->ib_pool.mutex); return r; } + mutex_lock(&rdev->ib_pool.mutex); + if (rdev->ib_pool.ready) { + mutex_unlock(&rdev->ib_pool.mutex); + radeon_sa_bo_manager_fini(rdev, &tmp); + return 0; + } + + rdev->ib_pool.sa_manager = tmp; + INIT_LIST_HEAD(&rdev->ib_pool.sa_manager.sa_bo); for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { rdev->ib_pool.ibs[i].fence = NULL; rdev->ib_pool.ibs[i].idx = i; -- cgit v0.10.2 From 9fc04b503df9a34ec1a691225445c5b7dfd022e7 Mon Sep 17 00:00:00 2001 From: Jerome Glisse Date: Mon, 23 Jan 2012 11:52:15 -0500 Subject: drm/radeon: avoid deadlock if GPU lockup is detected in ib_pool_get If GPU lockup is detected in ib_pool get we are holding the ib_pool mutex that will be needed by the GPU reset code. As ib_pool code is safe to be reentrant from GPU reset code we should not block if we are trying to get the ib pool lock on the behalf of the same userspace caller, thus use the radeon_mutex_lock helper. Signed-off-by: Jerome Glisse Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 73e05cb..1668ec1 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -157,6 +157,47 @@ bool radeon_get_bios(struct radeon_device *rdev); /* + * Mutex which allows recursive locking from the same process. + */ +struct radeon_mutex { + struct mutex mutex; + struct task_struct *owner; + int level; +}; + +static inline void radeon_mutex_init(struct radeon_mutex *mutex) +{ + mutex_init(&mutex->mutex); + mutex->owner = NULL; + mutex->level = 0; +} + +static inline void radeon_mutex_lock(struct radeon_mutex *mutex) +{ + if (mutex_trylock(&mutex->mutex)) { + /* The mutex was unlocked before, so it's ours now */ + mutex->owner = current; + } else if (mutex->owner != current) { + /* Another process locked the mutex, take it */ + mutex_lock(&mutex->mutex); + mutex->owner = current; + } + /* Otherwise the mutex was already locked by this process */ + + mutex->level++; +} + +static inline void radeon_mutex_unlock(struct radeon_mutex *mutex) +{ + if (--mutex->level > 0) + return; + + mutex->owner = NULL; + mutex_unlock(&mutex->mutex); +} + + +/* * Dummy page */ struct radeon_dummy_page { @@ -598,7 +639,7 @@ struct radeon_ib { * mutex protects scheduled_ibs, ready, alloc_bm */ struct radeon_ib_pool { - struct mutex mutex; + struct radeon_mutex mutex; struct radeon_sa_manager sa_manager; struct radeon_ib ibs[RADEON_IB_POOL_SIZE]; bool ready; @@ -1355,47 +1396,6 @@ struct r600_vram_scratch { /* - * Mutex which allows recursive locking from the same process. - */ -struct radeon_mutex { - struct mutex mutex; - struct task_struct *owner; - int level; -}; - -static inline void radeon_mutex_init(struct radeon_mutex *mutex) -{ - mutex_init(&mutex->mutex); - mutex->owner = NULL; - mutex->level = 0; -} - -static inline void radeon_mutex_lock(struct radeon_mutex *mutex) -{ - if (mutex_trylock(&mutex->mutex)) { - /* The mutex was unlocked before, so it's ours now */ - mutex->owner = current; - } else if (mutex->owner != current) { - /* Another process locked the mutex, take it */ - mutex_lock(&mutex->mutex); - mutex->owner = current; - } - /* Otherwise the mutex was already locked by this process */ - - mutex->level++; -} - -static inline void radeon_mutex_unlock(struct radeon_mutex *mutex) -{ - if (--mutex->level > 0) - return; - - mutex->owner = NULL; - mutex_unlock(&mutex->mutex); -} - - -/* * Core structure, functions and helpers. */ typedef uint32_t (*radeon_rreg_t)(struct radeon_device*, uint32_t); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index a811bc6..cec51a5 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -720,7 +720,7 @@ int radeon_device_init(struct radeon_device *rdev, /* mutex initialization are all done here so we * can recall function without having locking issues */ radeon_mutex_init(&rdev->cs_mutex); - mutex_init(&rdev->ib_pool.mutex); + radeon_mutex_init(&rdev->ib_pool.mutex); for (i = 0; i < RADEON_NUM_RINGS; ++i) mutex_init(&rdev->ring[i].mutex); mutex_init(&rdev->dc_hw_i2c_mutex); diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 1cb4b94..30a4c50 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -109,12 +109,12 @@ int radeon_ib_get(struct radeon_device *rdev, int ring, return r; } - mutex_lock(&rdev->ib_pool.mutex); + radeon_mutex_lock(&rdev->ib_pool.mutex); idx = rdev->ib_pool.head_id; retry: if (cretry > 5) { dev_err(rdev->dev, "failed to get an ib after 5 retry\n"); - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); radeon_fence_unref(&fence); return -ENOMEM; } @@ -139,7 +139,7 @@ retry: */ rdev->ib_pool.head_id = (1 + idx); rdev->ib_pool.head_id &= (RADEON_IB_POOL_SIZE - 1); - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); return 0; } } @@ -158,7 +158,7 @@ retry: } idx = (idx + 1) & (RADEON_IB_POOL_SIZE - 1); } - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); radeon_fence_unref(&fence); return r; } @@ -171,12 +171,12 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib) if (tmp == NULL) { return; } - mutex_lock(&rdev->ib_pool.mutex); + radeon_mutex_lock(&rdev->ib_pool.mutex); if (tmp->fence && !tmp->fence->emitted) { radeon_sa_bo_free(rdev, &tmp->sa_bo); radeon_fence_unref(&tmp->fence); } - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); } int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib) @@ -214,9 +214,9 @@ int radeon_ib_pool_init(struct radeon_device *rdev) return r; } - mutex_lock(&rdev->ib_pool.mutex); + radeon_mutex_lock(&rdev->ib_pool.mutex); if (rdev->ib_pool.ready) { - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); radeon_sa_bo_manager_fini(rdev, &tmp); return 0; } @@ -239,7 +239,7 @@ int radeon_ib_pool_init(struct radeon_device *rdev) if (radeon_debugfs_ring_init(rdev)) { DRM_ERROR("Failed to register debugfs file for rings !\n"); } - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); return 0; } @@ -247,7 +247,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev) { unsigned i; - mutex_lock(&rdev->ib_pool.mutex); + radeon_mutex_lock(&rdev->ib_pool.mutex); if (rdev->ib_pool.ready) { for (i = 0; i < RADEON_IB_POOL_SIZE; i++) { radeon_sa_bo_free(rdev, &rdev->ib_pool.ibs[i].sa_bo); @@ -256,7 +256,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev) radeon_sa_bo_manager_fini(rdev, &rdev->ib_pool.sa_manager); rdev->ib_pool.ready = false; } - mutex_unlock(&rdev->ib_pool.mutex); + radeon_mutex_unlock(&rdev->ib_pool.mutex); } int radeon_ib_pool_start(struct radeon_device *rdev) -- cgit v0.10.2 From 4e7682d077d693e34a993ae7a2831b522930ebcb Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 25 Jan 2012 11:38:13 +0100 Subject: ARM: 7301/1: Rename the T() macro to TUSER() to avoid namespace conflicts This macro is used to generate unprivileged accesses (LDRT/STRT) to user space. Signed-off-by: Catalin Marinas Acked-by: Nicolas Pitre Signed-off-by: Russell King diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index b6e65de..62f8095 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -237,7 +237,7 @@ */ #ifdef CONFIG_THUMB2_KERNEL - .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=T() + .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=TUSER() 9999: .if \inc == 1 \instr\cond\()b\()\t\().w \reg, [\ptr, #\off] @@ -277,7 +277,7 @@ #else /* !CONFIG_THUMB2_KERNEL */ - .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=T() + .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=TUSER() .rept \rept 9999: .if \inc == 1 diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h index af18cea..b5dc173 100644 --- a/arch/arm/include/asm/domain.h +++ b/arch/arm/include/asm/domain.h @@ -83,9 +83,9 @@ * instructions (inline assembly) */ #ifdef CONFIG_CPU_USE_DOMAINS -#define T(instr) #instr "t" +#define TUSER(instr) #instr "t" #else -#define T(instr) #instr +#define TUSER(instr) #instr #endif #else /* __ASSEMBLY__ */ @@ -95,9 +95,9 @@ * instructions */ #ifdef CONFIG_CPU_USE_DOMAINS -#define T(instr) instr ## t +#define TUSER(instr) instr ## t #else -#define T(instr) instr +#define TUSER(instr) instr #endif #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 253cc86..7be5469 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -75,9 +75,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ __asm__ __volatile__( \ - "1: " T(ldr) " %1, [%3]\n" \ + "1: " TUSER(ldr) " %1, [%3]\n" \ " " insn "\n" \ - "2: " T(str) " %0, [%3]\n" \ + "2: " TUSER(str) " %0, [%3]\n" \ " mov %0, #0\n" \ __futex_atomic_ex_table("%5") \ : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ @@ -95,10 +95,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, return -EFAULT; __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: " T(ldr) " %1, [%4]\n" + "1: " TUSER(ldr) " %1, [%4]\n" " teq %1, %2\n" " it eq @ explicit IT needed for the 2b label\n" - "2: " T(streq) " %3, [%4]\n" + "2: " TUSER(streq) " %3, [%4]\n" __futex_atomic_ex_table("%5") : "+r" (ret), "=&r" (val) : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index b293616..2958976 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -227,7 +227,7 @@ do { \ #define __get_user_asm_byte(x,addr,err) \ __asm__ __volatile__( \ - "1: " T(ldrb) " %1,[%2],#0\n" \ + "1: " TUSER(ldrb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -263,7 +263,7 @@ do { \ #define __get_user_asm_word(x,addr,err) \ __asm__ __volatile__( \ - "1: " T(ldr) " %1,[%2],#0\n" \ + "1: " TUSER(ldr) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -308,7 +308,7 @@ do { \ #define __put_user_asm_byte(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: " T(strb) " %1,[%2],#0\n" \ + "1: " TUSER(strb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -341,7 +341,7 @@ do { \ #define __put_user_asm_word(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: " T(str) " %1,[%2],#0\n" \ + "1: " TUSER(str) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -366,10 +366,10 @@ do { \ #define __put_user_asm_dword(x,__pu_addr,err) \ __asm__ __volatile__( \ - ARM( "1: " T(str) " " __reg_oper1 ", [%1], #4\n" ) \ - ARM( "2: " T(str) " " __reg_oper0 ", [%1]\n" ) \ - THUMB( "1: " T(str) " " __reg_oper1 ", [%1]\n" ) \ - THUMB( "2: " T(str) " " __reg_oper0 ", [%1, #4]\n" ) \ + ARM( "1: " TUSER(str) " " __reg_oper1 ", [%1], #4\n" ) \ + ARM( "2: " TUSER(str) " " __reg_oper0 ", [%1]\n" ) \ + THUMB( "1: " TUSER(str) " " __reg_oper1 ", [%1]\n" ) \ + THUMB( "2: " TUSER(str) " " __reg_oper0 ", [%1, #4]\n" ) \ "3:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S index 1b049cd..11093a7 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -31,18 +31,18 @@ #include ENTRY(__get_user_1) -1: T(ldrb) r2, [r0] +1: TUSER(ldrb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_1) ENTRY(__get_user_2) #ifdef CONFIG_THUMB2_KERNEL -2: T(ldrb) r2, [r0] -3: T(ldrb) r3, [r0, #1] +2: TUSER(ldrb) r2, [r0] +3: TUSER(ldrb) r3, [r0, #1] #else -2: T(ldrb) r2, [r0], #1 -3: T(ldrb) r3, [r0] +2: TUSER(ldrb) r2, [r0], #1 +3: TUSER(ldrb) r3, [r0] #endif #ifndef __ARMEB__ orr r2, r2, r3, lsl #8 @@ -54,7 +54,7 @@ ENTRY(__get_user_2) ENDPROC(__get_user_2) ENTRY(__get_user_4) -4: T(ldr) r2, [r0] +4: TUSER(ldr) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_4) diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S index c023fc1..7db2599 100644 --- a/arch/arm/lib/putuser.S +++ b/arch/arm/lib/putuser.S @@ -31,7 +31,7 @@ #include ENTRY(__put_user_1) -1: T(strb) r2, [r0] +1: TUSER(strb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_1) @@ -40,19 +40,19 @@ ENTRY(__put_user_2) mov ip, r2, lsr #8 #ifdef CONFIG_THUMB2_KERNEL #ifndef __ARMEB__ -2: T(strb) r2, [r0] -3: T(strb) ip, [r0, #1] +2: TUSER(strb) r2, [r0] +3: TUSER(strb) ip, [r0, #1] #else -2: T(strb) ip, [r0] -3: T(strb) r2, [r0, #1] +2: TUSER(strb) ip, [r0] +3: TUSER(strb) r2, [r0, #1] #endif #else /* !CONFIG_THUMB2_KERNEL */ #ifndef __ARMEB__ -2: T(strb) r2, [r0], #1 -3: T(strb) ip, [r0] +2: TUSER(strb) r2, [r0], #1 +3: TUSER(strb) ip, [r0] #else -2: T(strb) ip, [r0], #1 -3: T(strb) r2, [r0] +2: TUSER(strb) ip, [r0], #1 +3: TUSER(strb) r2, [r0] #endif #endif /* CONFIG_THUMB2_KERNEL */ mov r0, #0 @@ -60,18 +60,18 @@ ENTRY(__put_user_2) ENDPROC(__put_user_2) ENTRY(__put_user_4) -4: T(str) r2, [r0] +4: TUSER(str) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_4) ENTRY(__put_user_8) #ifdef CONFIG_THUMB2_KERNEL -5: T(str) r2, [r0] -6: T(str) r3, [r0, #4] +5: TUSER(str) r2, [r0] +6: TUSER(str) r3, [r0, #4] #else -5: T(str) r2, [r0], #4 -6: T(str) r3, [r0] +5: TUSER(str) r2, [r0], #4 +6: TUSER(str) r3, [r0] #endif mov r0, #0 mov pc, lr diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S index d0ece2a..5c908b1 100644 --- a/arch/arm/lib/uaccess.S +++ b/arch/arm/lib/uaccess.S @@ -32,11 +32,11 @@ rsb ip, ip, #4 cmp ip, #2 ldrb r3, [r1], #1 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault sub r2, r2, ip b .Lc2u_dest_aligned @@ -59,7 +59,7 @@ ENTRY(__copy_to_user) addmi ip, r2, #4 bmi .Lc2u_0nowords ldr r3, [r1], #4 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -88,18 +88,18 @@ USER( T(str) r3, [r0], #4) @ May fault stmneia r0!, {r3 - r4} @ Shouldnt fault tst ip, #4 ldrne r3, [r1], #4 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_0fupi .Lc2u_0nowords: teq ip, #0 beq .Lc2u_finished .Lc2u_nowords: cmp ip, #2 ldrb r3, [r1], #1 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_not_enough: @@ -120,7 +120,7 @@ USER( T(strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #8 ldr r7, [r1], #4 orr r3, r3, r7, push #24 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -155,18 +155,18 @@ USER( T(str) r3, [r0], #4) @ May fault movne r3, r7, pull #8 ldrne r7, [r1], #4 orrne r3, r3, r7, push #24 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_1fupi .Lc2u_1nowords: mov r3, r7, get_byte_1 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_2 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault movgt r3, r7, get_byte_3 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_2fupi: subs r2, r2, #4 @@ -175,7 +175,7 @@ USER( T(strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #16 ldr r7, [r1], #4 orr r3, r3, r7, push #16 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -210,18 +210,18 @@ USER( T(str) r3, [r0], #4) @ May fault movne r3, r7, pull #16 ldrne r7, [r1], #4 orrne r3, r3, r7, push #16 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_2fupi .Lc2u_2nowords: mov r3, r7, get_byte_2 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_3 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_3fupi: subs r2, r2, #4 @@ -230,7 +230,7 @@ USER( T(strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #24 ldr r7, [r1], #4 orr r3, r3, r7, push #8 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -265,18 +265,18 @@ USER( T(str) r3, [r0], #4) @ May fault movne r3, r7, pull #24 ldrne r7, [r1], #4 orrne r3, r3, r7, push #8 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_3fupi .Lc2u_3nowords: mov r3, r7, get_byte_3 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished ENDPROC(__copy_to_user) @@ -295,11 +295,11 @@ ENDPROC(__copy_to_user) .Lcfu_dest_not_aligned: rsb ip, ip, #4 cmp ip, #2 -USER( T(ldrb) r3, [r1], #1) @ May fault +USER( TUSER( ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( T(ldrgeb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 sub r2, r2, ip b .Lcfu_dest_aligned @@ -322,7 +322,7 @@ ENTRY(__copy_from_user) .Lcfu_0fupi: subs r2, r2, #4 addmi ip, r2, #4 bmi .Lcfu_0nowords -USER( T(ldr) r3, [r1], #4) +USER( TUSER( ldr) r3, [r1], #4) str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 @@ -351,18 +351,18 @@ USER( T(ldr) r3, [r1], #4) ldmneia r1!, {r3 - r4} @ Shouldnt fault stmneia r0!, {r3 - r4} tst ip, #4 - T(ldrne) r3, [r1], #4 @ Shouldnt fault + TUSER( ldrne) r3, [r1], #4 @ Shouldnt fault strne r3, [r0], #4 ands ip, ip, #3 beq .Lcfu_0fupi .Lcfu_0nowords: teq ip, #0 beq .Lcfu_finished .Lcfu_nowords: cmp ip, #2 -USER( T(ldrb) r3, [r1], #1) @ May fault +USER( TUSER( ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( T(ldrgeb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -375,7 +375,7 @@ USER( T(ldrgtb) r3, [r1], #1) @ May fault .Lcfu_src_not_aligned: bic r1, r1, #3 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault cmp ip, #2 bgt .Lcfu_3fupi beq .Lcfu_2fupi @@ -383,7 +383,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_1nowords mov r3, r7, pull #8 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #24 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -418,7 +418,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #8 -USER( T(ldrne) r7, [r1], #4) @ May fault +USER( TUSER( ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #24 strne r3, [r0], #4 ands ip, ip, #3 @@ -438,7 +438,7 @@ USER( T(ldrne) r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_2nowords mov r3, r7, pull #16 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #16 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -474,7 +474,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #16 -USER( T(ldrne) r7, [r1], #4) @ May fault +USER( TUSER( ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #16 strne r3, [r0], #4 ands ip, ip, #3 @@ -486,7 +486,7 @@ USER( T(ldrne) r7, [r1], #4) @ May fault strb r3, [r0], #1 movge r3, r7, get_byte_3 strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #0) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -494,7 +494,7 @@ USER( T(ldrgtb) r3, [r1], #0) @ May fault addmi ip, r2, #4 bmi .Lcfu_3nowords mov r3, r7, pull #24 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #8 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -529,7 +529,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #24 -USER( T(ldrne) r7, [r1], #4) @ May fault +USER( TUSER( ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #8 strne r3, [r0], #4 ands ip, ip, #3 @@ -539,9 +539,9 @@ USER( T(ldrne) r7, [r1], #4) @ May fault beq .Lcfu_finished cmp ip, #2 strb r3, [r0], #1 -USER( T(ldrgeb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished ENDPROC(__copy_from_user) -- cgit v0.10.2 From 9fb83526a898f14adbd3f6f52fa7126f528f15ac Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 15:19:20 +0000 Subject: ASoC: wm5100: Make sure we switch to button reporting mode When we have identified an accessory make sure we've flagged that we've done so in order to make sure we always report buttons and don't continue to polarity flip. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 66f0611..3f8fd3c 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2183,6 +2183,7 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec) if (wm5100->jack_detecting) { dev_dbg(codec->dev, "Microphone detected\n"); wm5100->jack_mic = true; + wm5100->jack_detecting = false; snd_soc_jack_report(wm5100->jack, SND_JACK_HEADSET, SND_JACK_HEADSET | SND_JACK_BTN_0); @@ -2221,6 +2222,7 @@ static void wm5100_micd_irq(struct snd_soc_codec *codec) SND_JACK_BTN_0); } else if (wm5100->jack_detecting) { dev_dbg(codec->dev, "Headphone detected\n"); + wm5100->jack_detecting = false; snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); -- cgit v0.10.2 From 9b025eb3a89e041bab6698e3858706be2385d692 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 11 Jan 2012 18:52:10 +0000 Subject: xfs: Fix missing xfs_iunlock() on error recovery path in xfs_readlink() Commit b52a360b forgot to call xfs_iunlock() when it detected corrupted symplink and bailed out. Fix it by jumping to 'out' instead of doing return. CC: stable@kernel.org CC: Carlos Maiolino Signed-off-by: Jan Kara Reviewed-by: Alex Elder Reviewed-by: Dave Chinner Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 0cf52da..ebdb888 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -131,7 +131,8 @@ xfs_readlink( __func__, (unsigned long long) ip->i_ino, (long long) pathlen); ASSERT(0); - return XFS_ERROR(EFSCORRUPTED); + error = XFS_ERROR(EFSCORRUPTED); + goto out; } -- cgit v0.10.2 From 93b525dccf212e50a895792d79d64bdb53312f5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Jan 2012 13:52:43 +0100 Subject: drm/i915: fixup forcewake spinlock fallout in drpc debugfs function My forcewake spinlock patches have a functional conflict with Ben Widawsky's gen6 drpc support for debugfs. Result was a benign warning about trying to read an non-atomic variabla with atomic_read. Note that the entire check is racy anyway and purely informational. Also update it to reflect the forcewake voodoo changes, the kernel can now also hold onto a forcewake reference for longer times. Signed-Off-by: Daniel Vetter Reviewed-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 99f1407..deaa657 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1075,6 +1075,7 @@ static int gen6_drpc_info(struct seq_file *m) struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 rpmodectl1, gt_core_status, rcctl1; + unsigned forcewake_count; int count=0, ret; @@ -1082,9 +1083,13 @@ static int gen6_drpc_info(struct seq_file *m) if (ret) return ret; - if (atomic_read(&dev_priv->forcewake_count)) { - seq_printf(m, "RC information inaccurate because userspace " - "holds a reference \n"); + spin_lock_irq(&dev_priv->gt_lock); + forcewake_count = dev_priv->forcewake_count; + spin_unlock_irq(&dev_priv->gt_lock); + + if (forcewake_count) { + seq_printf(m, "RC information inaccurate because somebody " + "holds a forcewake reference \n"); } else { /* NB: we cannot use forcewake, else we read the wrong values */ while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1)) -- cgit v0.10.2 From a188fcba73837f83a78dc90a44998a978f50ac83 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 17:57:16 +0000 Subject: ASoC: wm5100: Fix microphone configuration We need to write the configuration for each microphone to a different register. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 3f8fd3c..fb757af 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2612,6 +2612,13 @@ static const struct regmap_config wm5100_regmap = { .cache_type = REGCACHE_RBTREE, }; +static const unsigned int wm5100_mic_ctrl_reg[] = { + WM5100_IN1L_CONTROL, + WM5100_IN2L_CONTROL, + WM5100_IN3L_CONTROL, + WM5100_IN4L_CONTROL, +}; + static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2744,7 +2751,7 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, } for (i = 0; i < ARRAY_SIZE(wm5100->pdata.in_mode); i++) { - regmap_update_bits(wm5100->regmap, WM5100_IN1L_CONTROL, + regmap_update_bits(wm5100->regmap, wm5100_mic_ctrl_reg[i], WM5100_IN1_MODE_MASK | WM5100_IN1_DMIC_SUP_MASK, (wm5100->pdata.in_mode[i] << -- cgit v0.10.2 From 9f1feed2e16652a6e599ed4a73b4c501bb3d4568 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 25 Jan 2012 15:34:22 +1000 Subject: drm/ttm: fix two regressions since move_notify changes Both changes in dc97b3409a790d2a21aac6e5cdb99558b5944119 cause serious regressions in the nouveau driver. move_notify() was originally able to presume that bo->mem is the old node, and new_mem is the new node. The above commit moves the call to move_notify() to after move() has been done, which means that now, sometimes, new_mem isn't the new node at all, bo->mem is, and new_mem points at a stale, possibly-just-been-killed-by-move node. This is clearly not a good situation. This patch reverts this change, and replaces it with a cleanup in the move() failure path instead. The second issue is that the call to move_notify() from cleanup_memtype_use() causes the TTM ghost objects to get passed into the driver. This is clearly bad as the driver knows nothing about these "fake" TTM BOs, and ends up accessing uninitialised memory. I worked around this in nouveau's move_notify() hook by ensuring the BO destructor was nouveau's. I don't particularly like this solution, and would rather TTM never pass the driver these objects. However, I don't clearly understand the reason why we're calling move_notify() here anyway and am happy to work around the problem in nouveau instead of breaking the behaviour expected by other drivers. Signed-off-by: Ben Skeggs Reviewed-by: Thomas Hellstrom Cc: Jerome Glisse Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 724b41a..ec54364 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -812,6 +812,10 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_vma *vma; + /* ttm can now (stupidly) pass the driver bos it didn't create... */ + if (bo->destroy != nouveau_bo_del_ttm) + return; + list_for_each_entry(vma, &nvbo->vma_list, head) { if (new_mem && new_mem->mem_type == TTM_PL_VRAM) { nouveau_vm_map(vma, new_mem->mm_node); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 2f0eab6..7c3a57d 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -404,6 +404,9 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, } } + if (bdev->driver->move_notify) + bdev->driver->move_notify(bo, mem); + if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) ret = ttm_bo_move_ttm(bo, evict, no_wait_reserve, no_wait_gpu, mem); @@ -413,11 +416,17 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, else ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, mem); - if (ret) - goto out_err; + if (ret) { + if (bdev->driver->move_notify) { + struct ttm_mem_reg tmp_mem = *mem; + *mem = bo->mem; + bo->mem = tmp_mem; + bdev->driver->move_notify(bo, mem); + bo->mem = *mem; + } - if (bdev->driver->move_notify) - bdev->driver->move_notify(bo, mem); + goto out_err; + } moved: if (bo->evicted) { -- cgit v0.10.2 From 1ac6d46e43a52a901dadde2a341204e9a1c9e147 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 23 Jan 2012 14:15:28 +0200 Subject: ARM: OMAP2+: hwmod data: split omap2/3 dispc hwmod class Currently OMAP2 and 3 share the same omap_hwmod_class and omap_hwmod_class_sysconfig for dispc. However, OMAP3 has sysconfig bits that OMAP2 doesn't have, so we need to split those structs into OMAP2 and OMAP3 specific versions. This patch only splits the structs, without changing the contents. This is a prerequisite for a subsequent fix. Signed-off-by: Tomi Valkeinen [paul@pwsan.com: added commit note] Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c index c11273d..f08e442 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c @@ -56,27 +56,6 @@ struct omap_hwmod_class omap2_dss_hwmod_class = { }; /* - * 'dispc' class - * display controller - */ - -static struct omap_hwmod_class_sysconfig omap2_dispc_sysc = { - .rev_offs = 0x0000, - .sysc_offs = 0x0010, - .syss_offs = 0x0014, - .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | - SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), - .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | - MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), - .sysc_fields = &omap_hwmod_sysc_type1, -}; - -struct omap_hwmod_class omap2_dispc_hwmod_class = { - .name = "dispc", - .sysc = &omap2_dispc_sysc, -}; - -/* * 'rfbi' class * remote frame buffer interface */ diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c index 177dee2..2a67297 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -28,6 +28,28 @@ struct omap_hwmod_dma_info omap2xxx_dss_sdma_chs[] = { { .name = "dispc", .dma_req = 5 }, { .dma_req = -1 } }; + +/* + * 'dispc' class + * display controller + */ + +static struct omap_hwmod_class_sysconfig omap2_dispc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_dispc_hwmod_class = { + .name = "dispc", + .sysc = &omap2_dispc_sysc, +}; + /* OMAP2xxx Timer Common */ static struct omap_hwmod_class_sysconfig omap2xxx_timer_sysc = { .rev_offs = 0x0000, diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 5324e8d..c965309 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -1480,6 +1480,27 @@ static struct omap_hwmod omap3xxx_dss_core_hwmod = { .masters_cnt = ARRAY_SIZE(omap3xxx_dss_masters), }; +/* + * 'dispc' class + * display controller + */ + +static struct omap_hwmod_class_sysconfig omap3_dispc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3_dispc_hwmod_class = { + .name = "dispc", + .sysc = &omap3_dispc_sysc, +}; + /* l4_core -> dss_dispc */ static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_dispc = { .master = &omap3xxx_l4_core_hwmod, @@ -1503,7 +1524,7 @@ static struct omap_hwmod_ocp_if *omap3xxx_dss_dispc_slaves[] = { static struct omap_hwmod omap3xxx_dss_dispc_hwmod = { .name = "dss_dispc", - .class = &omap2_dispc_hwmod_class, + .class = &omap3_dispc_hwmod_class, .mpu_irqs = omap2_dispc_irqs, .main_clk = "dss1_alwon_fck", .prcm = { -- cgit v0.10.2 From b0a85faf0bf11862a2a466daa1b7dc1d45527e64 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 23 Jan 2012 14:15:29 +0200 Subject: ARM: OMAP3: hwmod data: add SYSC_HAS_ENAWAKEUP for dispc dispc's sysc_flags is missing SYSC_HAS_ENAWAKEUP flag. This seems to cause SYNC_LOST errors from the DSS when the power management is enabled. This patch adds the missing SYSC_HAS_ENAWAKEUP flag. Note that there are other flags missing also (clock activity, DSI's sysc flags), but as they are not critical, they will be fixed in the next merge window. Signed-off-by: Tomi Valkeinen Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index c965309..b176d44 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -1490,7 +1490,8 @@ static struct omap_hwmod_class_sysconfig omap3_dispc_sysc = { .sysc_offs = 0x0010, .syss_offs = 0x0014, .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | - SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSC_HAS_ENAWAKEUP), .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), .sysc_fields = &omap_hwmod_sysc_type1, -- cgit v0.10.2 From 6af486e2b3d45efca7c680aa7ce3bacebe22d7aa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 28 Nov 2011 15:45:39 +0200 Subject: ARM: OMAP4: hwmod data: Add names for DMIC memory address space To be able to get the memory resources by name from the DMIC driver (for MPU and for DMA). Without this patch, functionality that was working in 3.2 breaks in 3.3-rc1. This patch should have gone in as part of the 3.3 merge window, but was inadvertently missed. Signed-off-by: Peter Ujfalusi [paul@pwsan.com: added commit message note] Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index f9f1510..ef0524c 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -1031,6 +1031,7 @@ static struct omap_hwmod_dma_info omap44xx_dmic_sdma_reqs[] = { static struct omap_hwmod_addr_space omap44xx_dmic_addrs[] = { { + .name = "mpu", .pa_start = 0x4012e000, .pa_end = 0x4012e07f, .flags = ADDR_TYPE_RT @@ -1049,6 +1050,7 @@ static struct omap_hwmod_ocp_if omap44xx_l4_abe__dmic = { static struct omap_hwmod_addr_space omap44xx_dmic_dma_addrs[] = { { + .name = "dma", .pa_start = 0x4902e000, .pa_end = 0x4902e07f, .flags = ADDR_TYPE_RT -- cgit v0.10.2 From 161107981df333955218bb0894f53b05b31da2ff Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 25 Jan 2012 12:57:46 -0700 Subject: ARM: OMAP2+: io: fix compilation breakage on 2420-only configs Commit 7b250aff1ce346b6c7bc0329a2350334d1c66525 ("ARM: OMAP: Avoid cpu_is_omapxxxx usage until map_io is done") breaks the build on a 2420-only config on v3.3-rc1: arch/arm/mach-omap2/built-in.o: In function `omap2430_init_early': arch/arm/mach-omap2/io.c:406: undefined reference to `omap2_set_globals_243x' arch/arm/mach-omap2/io.c:410: undefined reference to `omap243x_clockdomains_init' arch/arm/mach-omap2/io.c:411: undefined reference to `omap2430_hwmod_init' Fix by only compiling omap2420_init_early() when CONFIG_SOC_OMAP2420 is selected, and only compiling omap2430_init_early() when CONFIG_SOC_OMAP2430 is selected. Signed-off-by: Paul Walmsley Cc: Tony Lindgren diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index 3f174d5..eb50c29 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -388,7 +388,7 @@ static void __init omap_hwmod_init_postsetup(void) omap_pm_if_early_init(); } -#ifdef CONFIG_ARCH_OMAP2 +#ifdef CONFIG_SOC_OMAP2420 void __init omap2420_init_early(void) { omap2_set_globals_242x(); @@ -400,7 +400,9 @@ void __init omap2420_init_early(void) omap_hwmod_init_postsetup(); omap2420_clk_init(); } +#endif +#ifdef CONFIG_SOC_OMAP2430 void __init omap2430_init_early(void) { omap2_set_globals_243x(); -- cgit v0.10.2 From d19e8f2e44a34b2a461f67ce9d0cb5bd43197c1e Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 25 Jan 2012 12:57:49 -0700 Subject: ARM: OMAP2/3: PRM: fix missing plat/irqs.h build breakage Commit 22f51371f8c35869ed850f46aa76b6cc2b502110 ("ARM: OMAP3: pm: use prcm chain handler") breaks the build on a 2420-only config, due to a missing include for plat/irqs.h: CC arch/arm/mach-omap2/prm2xxx_3xxx.o arch/arm/mach-omap2/prm2xxx_3xxx.c:41:11: error: 'INT_34XX_PRCM_MPU_IRQ' undeclared here (not in a function) Fix by explicitly including it. Signed-off-by: Paul Walmsley Cc: Tero Kristo Cc: Kevin Hilman diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.c b/arch/arm/mach-omap2/prm2xxx_3xxx.c index c1c4d86..9ce7654 100644 --- a/arch/arm/mach-omap2/prm2xxx_3xxx.c +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.c @@ -19,6 +19,7 @@ #include "common.h" #include #include +#include #include "vp.h" -- cgit v0.10.2 From 140941987f8f85ee7c7d4592ecb6667b32f3485d Mon Sep 17 00:00:00 2001 From: Dustin Kirkland Date: Wed, 7 Dec 2011 08:56:49 -0600 Subject: MAINTAINERS: Update eCryptfs maintainer address Update my email address in MAINTAINERS. Signed-off-by: Dustin Kirkland Signed-off-by: Tyler Hicks diff --git a/MAINTAINERS b/MAINTAINERS index 89b70df..b63c181 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2391,7 +2391,7 @@ F: net/bridge/netfilter/ebt*.c ECRYPT FILE SYSTEM M: Tyler Hicks -M: Dustin Kirkland +M: Dustin Kirkland L: ecryptfs@vger.kernel.org W: https://launchpad.net/ecryptfs S: Supported -- cgit v0.10.2 From 30373dc0c87ffef68d5628e77d56ffb1fa22e1ee Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Thu, 12 Jan 2012 16:31:55 +0100 Subject: ecryptfs: Improve metadata read failure logging Print inode on metadata read failure. The only real way of dealing with metadata read failures is to delete the underlying file system file. Having the inode allows one to 'find . -inum INODE`. [tyhicks@canonical.com: Removed some minor not-for-stable parts] Signed-off-by: Tim Gardner Reviewed-by: Kees Cook Cc: stable@vger.kernel.org Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 2a83425..2bf5203 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1620,7 +1620,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_inode); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " - "file header region or xattr region\n"); + "file header region or xattr region, inode %lu\n", + ecryptfs_inode->i_ino); rc = -EINVAL; goto out; } @@ -1629,7 +1630,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) ECRYPTFS_DONT_VALIDATE_HEADER_SIZE); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " - "file xattr region either\n"); + "file xattr region either, inode %lu\n", + ecryptfs_inode->i_ino); rc = -EINVAL; } if (crypt_stat->mount_crypt_stat->flags @@ -1640,7 +1642,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) "crypto metadata only in the extended attribute " "region, but eCryptfs was mounted without " "xattr support enabled. eCryptfs will not treat " - "this like an encrypted file.\n"); + "this like an encrypted file, inode %lu\n", + ecryptfs_inode->i_ino); rc = -EINVAL; } } -- cgit v0.10.2 From bb4503615d95d6826b7907986ad574e3157877e8 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Thu, 12 Jan 2012 16:31:55 +0100 Subject: ecryptfs: Remove unnecessary variable initialization Removes unneeded variable initialization in ecryptfs_read_metadata(). Also adds a small comment to help explain metadata reading logic. [tyhicks@canonical.com: Pulled out of for-stable patch and wrote commit msg] Signed-off-by: Tim Gardner Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 2bf5203..ff98150 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1590,8 +1590,8 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry, */ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) { - int rc = 0; - char *page_virt = NULL; + int rc; + char *page_virt; struct inode *ecryptfs_inode = ecryptfs_dentry->d_inode; struct ecryptfs_crypt_stat *crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat; @@ -1616,6 +1616,7 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) ecryptfs_dentry, ECRYPTFS_VALIDATE_HEADER_SIZE); if (rc) { + /* metadata is not in the file header, so try xattrs */ memset(page_virt, 0, PAGE_CACHE_SIZE); rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_inode); if (rc) { -- cgit v0.10.2 From db10e556518eb9d21ee92ff944530d84349684f4 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 12 Jan 2012 11:30:44 +0100 Subject: eCryptfs: Sanitize write counts of /dev/ecryptfs A malicious count value specified when writing to /dev/ecryptfs may result in a a very large kernel memory allocation. This patch peeks at the specified packet payload size, adds that to the size of the packet headers and compares the result with the write count value. The resulting maximum memory allocation size is approximately 532 bytes. Signed-off-by: Tyler Hicks Reported-by: Sasha Levin Cc: diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 940a82e..0dc5a3d 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -409,11 +409,47 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, ssize_t sz = 0; char *data; uid_t euid = current_euid(); + unsigned char packet_size_peek[3]; int rc; - if (count == 0) + if (count == 0) { goto out; + } else if (count == (1 + 4)) { + /* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */ + goto memdup; + } else if (count < (1 + 4 + 1) + || count > (1 + 4 + 2 + sizeof(struct ecryptfs_message) + 4 + + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES)) { + printk(KERN_WARNING "%s: Acceptable packet size range is " + "[%d-%lu], but amount of data written is [%zu].", + __func__, (1 + 4 + 1), + (1 + 4 + 2 + sizeof(struct ecryptfs_message) + 4 + + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES), count); + return -EINVAL; + } + + if (copy_from_user(packet_size_peek, (buf + 1 + 4), + sizeof(packet_size_peek))) { + printk(KERN_WARNING "%s: Error while inspecting packet size\n", + __func__); + return -EFAULT; + } + + rc = ecryptfs_parse_packet_length(packet_size_peek, &packet_size, + &packet_size_length); + if (rc) { + printk(KERN_WARNING "%s: Error parsing packet length; " + "rc = [%d]\n", __func__, rc); + return rc; + } + + if ((1 + 4 + packet_size_length + packet_size) != count) { + printk(KERN_WARNING "%s: Invalid packet size [%zu]\n", __func__, + packet_size); + return -EINVAL; + } +memdup: data = memdup_user(buf, count); if (IS_ERR(data)) { printk(KERN_ERR "%s: memdup_user returned error [%ld]\n", @@ -435,23 +471,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, } memcpy(&counter_nbo, &data[i], 4); seq = be32_to_cpu(counter_nbo); - i += 4; - rc = ecryptfs_parse_packet_length(&data[i], &packet_size, - &packet_size_length); - if (rc) { - printk(KERN_WARNING "%s: Error parsing packet length; " - "rc = [%d]\n", __func__, rc); - goto out_free; - } - i += packet_size_length; - if ((1 + 4 + packet_size_length + packet_size) != count) { - printk(KERN_WARNING "%s: (1 + packet_size_length([%zd])" - " + packet_size([%zd]))([%zd]) != " - "count([%zd]). Invalid packet format.\n", - __func__, packet_size_length, packet_size, - (1 + packet_size_length + packet_size), count); - goto out_free; - } + i += 4 + packet_size_length; rc = ecryptfs_miscdev_response(&data[i], packet_size, euid, current_user_ns(), task_pid(current), seq); -- cgit v0.10.2 From 7f133504249afa48618becac546ce3c35c9f0185 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Sat, 14 Jan 2012 15:51:37 +0100 Subject: eCryptfs: Report errors in writes to /dev/ecryptfs Errors in writes to /dev/ecryptfs were being incorrectly reported by returning 0 or the value of the original write count. This patch clears up the return code assignment in error paths. Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 0dc5a3d..1145c58 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -406,14 +406,13 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, __be32 counter_nbo; u32 seq; size_t packet_size, packet_size_length, i; - ssize_t sz = 0; char *data; uid_t euid = current_euid(); unsigned char packet_size_peek[3]; - int rc; + ssize_t rc; if (count == 0) { - goto out; + return 0; } else if (count == (1 + 4)) { /* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */ goto memdup; @@ -439,7 +438,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, &packet_size_length); if (rc) { printk(KERN_WARNING "%s: Error parsing packet length; " - "rc = [%d]\n", __func__, rc); + "rc = [%zd]\n", __func__, rc); return rc; } @@ -454,9 +453,8 @@ memdup: if (IS_ERR(data)) { printk(KERN_ERR "%s: memdup_user returned error [%ld]\n", __func__, PTR_ERR(data)); - goto out; + return PTR_ERR(data); } - sz = count; i = 0; switch (data[i++]) { case ECRYPTFS_MSG_RESPONSE: @@ -467,6 +465,7 @@ memdup: __func__, (1 + 4 + 1 + sizeof(struct ecryptfs_message)), count); + rc = -EINVAL; goto out_free; } memcpy(&counter_nbo, &data[i], 4); @@ -475,10 +474,12 @@ memdup: rc = ecryptfs_miscdev_response(&data[i], packet_size, euid, current_user_ns(), task_pid(current), seq); - if (rc) + if (rc) { printk(KERN_WARNING "%s: Failed to deliver miscdev " - "response to requesting operation; rc = [%d]\n", + "response to requesting operation; rc = [%zd]\n", __func__, rc); + goto out_free; + } break; case ECRYPTFS_MSG_HELO: case ECRYPTFS_MSG_QUIT: @@ -487,12 +488,13 @@ memdup: ecryptfs_printk(KERN_WARNING, "Dropping miscdev " "message of unrecognized type [%d]\n", data[0]); - break; + rc = -EINVAL; + goto out_free; } + rc = count; out_free: kfree(data); -out: - return sz; + return rc; } -- cgit v0.10.2 From 48399c0b0e6172888a2e2e36df1595ab1e049ba8 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Sat, 14 Jan 2012 16:46:46 +0100 Subject: eCryptfs: Replace miscdev read/write magic numbers ecryptfs_miscdev_read() and ecryptfs_miscdev_write() contained many magic numbers for specifying packet header field sizes and offsets. This patch defines those values and replaces the magic values. Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index a9f29b1..a2362df 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -151,6 +151,11 @@ ecryptfs_get_key_payload_data(struct key *key) * dentry name */ #define ECRYPTFS_TAG_73_PACKET_TYPE 0x49 /* FEK-encrypted filename as * metadata */ +#define ECRYPTFS_MIN_PKT_LEN_SIZE 1 /* Min size to specify packet length */ +#define ECRYPTFS_MAX_PKT_LEN_SIZE 2 /* Pass at least this many bytes to + * ecryptfs_parse_packet_length() and + * ecryptfs_write_packet_length() + */ /* Constraint: ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES >= * ECRYPTFS_MAX_IV_BYTES */ #define ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES 16 diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index ac1ad48..8e3b943 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -109,7 +109,7 @@ int ecryptfs_parse_packet_length(unsigned char *data, size_t *size, (*size) += ((unsigned char)(data[1]) + 192); (*length_size) = 2; } else if (data[0] == 255) { - /* Five-byte length; we're not supposed to see this */ + /* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */ ecryptfs_printk(KERN_ERR, "Five-byte packet length not " "supported\n"); rc = -EINVAL; @@ -126,7 +126,7 @@ out: /** * ecryptfs_write_packet_length * @dest: The byte array target into which to write the length. Must - * have at least 5 bytes allocated. + * have at least ECRYPTFS_MAX_PKT_LEN_SIZE bytes allocated. * @size: The length to write. * @packet_size_length: The number of bytes used to encode the packet * length is written to this address. @@ -146,6 +146,7 @@ int ecryptfs_write_packet_length(char *dest, size_t size, dest[1] = ((size - 192) % 256); (*packet_size_length) = 2; } else { + /* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */ rc = -EINVAL; ecryptfs_printk(KERN_WARNING, "Unsupported packet size: [%zd]\n", size); diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 1145c58..349209d 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -218,6 +218,29 @@ out_unlock: return rc; } +/* + * miscdevfs packet format: + * Octet 0: Type + * Octets 1-4: network byte order msg_ctx->counter + * Octets 5-N0: Size of struct ecryptfs_message to follow + * Octets N0-N1: struct ecryptfs_message (including data) + * + * Octets 5-N1 not written if the packet type does not include a message + */ +#define PKT_TYPE_SIZE 1 +#define PKT_CTR_SIZE 4 +#define MIN_NON_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE) +#define MIN_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \ + + ECRYPTFS_MIN_PKT_LEN_SIZE) +/* 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES comes from tag 65 packet format */ +#define MAX_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \ + + ECRYPTFS_MAX_PKT_LEN_SIZE \ + + sizeof(struct ecryptfs_message) \ + + 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) +#define PKT_TYPE_OFFSET 0 +#define PKT_CTR_OFFSET PKT_TYPE_SIZE +#define PKT_LEN_OFFSET (PKT_TYPE_SIZE + PKT_CTR_SIZE) + /** * ecryptfs_miscdev_read - format and send message from queue * @file: fs/ecryptfs/euid miscdevfs handle (ignored) @@ -237,7 +260,7 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count, struct ecryptfs_daemon *daemon; struct ecryptfs_msg_ctx *msg_ctx; size_t packet_length_size; - char packet_length[3]; + char packet_length[ECRYPTFS_MAX_PKT_LEN_SIZE]; size_t i; size_t total_length; uid_t euid = current_euid(); @@ -305,15 +328,8 @@ check_list: packet_length_size = 0; msg_ctx->msg_size = 0; } - /* miscdevfs packet format: - * Octet 0: Type - * Octets 1-4: network byte order msg_ctx->counter - * Octets 5-N0: Size of struct ecryptfs_message to follow - * Octets N0-N1: struct ecryptfs_message (including data) - * - * Octets 5-N1 not written if the packet type does not - * include a message */ - total_length = (1 + 4 + packet_length_size + msg_ctx->msg_size); + total_length = (PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_length_size + + msg_ctx->msg_size); if (count < total_length) { rc = 0; printk(KERN_WARNING "%s: Only given user buffer of " @@ -324,9 +340,10 @@ check_list: rc = -EFAULT; if (put_user(msg_ctx->type, buf)) goto out_unlock_msg_ctx; - if (put_user(cpu_to_be32(msg_ctx->counter), (__be32 __user *)(buf + 1))) + if (put_user(cpu_to_be32(msg_ctx->counter), + (__be32 __user *)(&buf[PKT_CTR_OFFSET]))) goto out_unlock_msg_ctx; - i = 5; + i = PKT_TYPE_SIZE + PKT_CTR_SIZE; if (msg_ctx->msg) { if (copy_to_user(&buf[i], packet_length, packet_length_size)) goto out_unlock_msg_ctx; @@ -391,12 +408,6 @@ out: * @count: Amount of data in @buf * @ppos: Pointer to offset in file (ignored) * - * miscdevfs packet format: - * Octet 0: Type - * Octets 1-4: network byte order msg_ctx->counter (0's for non-response) - * Octets 5-N0: Size of struct ecryptfs_message to follow - * Octets N0-N1: struct ecryptfs_message (including data) - * * Returns the number of bytes read from @buf */ static ssize_t @@ -405,29 +416,25 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, { __be32 counter_nbo; u32 seq; - size_t packet_size, packet_size_length, i; + size_t packet_size, packet_size_length; char *data; uid_t euid = current_euid(); - unsigned char packet_size_peek[3]; + unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE]; ssize_t rc; if (count == 0) { return 0; - } else if (count == (1 + 4)) { + } else if (count == MIN_NON_MSG_PKT_SIZE) { /* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */ goto memdup; - } else if (count < (1 + 4 + 1) - || count > (1 + 4 + 2 + sizeof(struct ecryptfs_message) + 4 - + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES)) { + } else if (count < MIN_MSG_PKT_SIZE || count > MAX_MSG_PKT_SIZE) { printk(KERN_WARNING "%s: Acceptable packet size range is " "[%d-%lu], but amount of data written is [%zu].", - __func__, (1 + 4 + 1), - (1 + 4 + 2 + sizeof(struct ecryptfs_message) + 4 - + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES), count); + __func__, MIN_MSG_PKT_SIZE, MAX_MSG_PKT_SIZE, count); return -EINVAL; } - if (copy_from_user(packet_size_peek, (buf + 1 + 4), + if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET], sizeof(packet_size_peek))) { printk(KERN_WARNING "%s: Error while inspecting packet size\n", __func__); @@ -442,7 +449,8 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, return rc; } - if ((1 + 4 + packet_size_length + packet_size) != count) { + if ((PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_size_length + packet_size) + != count) { printk(KERN_WARNING "%s: Invalid packet size [%zu]\n", __func__, packet_size); return -EINVAL; @@ -455,25 +463,25 @@ memdup: __func__, PTR_ERR(data)); return PTR_ERR(data); } - i = 0; - switch (data[i++]) { + switch (data[PKT_TYPE_OFFSET]) { case ECRYPTFS_MSG_RESPONSE: - if (count < (1 + 4 + 1 + sizeof(struct ecryptfs_message))) { + if (count < (MIN_MSG_PKT_SIZE + + sizeof(struct ecryptfs_message))) { printk(KERN_WARNING "%s: Minimum acceptable packet " "size is [%zd], but amount of data written is " "only [%zd]. Discarding response packet.\n", __func__, - (1 + 4 + 1 + sizeof(struct ecryptfs_message)), - count); + (MIN_MSG_PKT_SIZE + + sizeof(struct ecryptfs_message)), count); rc = -EINVAL; goto out_free; } - memcpy(&counter_nbo, &data[i], 4); + memcpy(&counter_nbo, &data[PKT_CTR_OFFSET], PKT_CTR_SIZE); seq = be32_to_cpu(counter_nbo); - i += 4 + packet_size_length; - rc = ecryptfs_miscdev_response(&data[i], packet_size, - euid, current_user_ns(), - task_pid(current), seq); + rc = ecryptfs_miscdev_response( + &data[PKT_LEN_OFFSET + packet_size_length], + packet_size, euid, current_user_ns(), + task_pid(current), seq); if (rc) { printk(KERN_WARNING "%s: Failed to deliver miscdev " "response to requesting operation; rc = [%zd]\n", -- cgit v0.10.2 From 684a3ff7e69acc7c678d1a1394fe9e757993fd34 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Thu, 19 Jan 2012 09:44:36 +0800 Subject: eCryptfs: Infinite loop due to overflow in ecryptfs_write() ecryptfs_write() can enter an infinite loop when truncating a file to a size larger than 4G. This only happens on architectures where size_t is represented by 32 bits. This was caused by a size_t overflow due to it incorrectly being used to store the result of a calculation which uses potentially large values of type loff_t. [tyhicks@canonical.com: rewrite subject and commit message] Signed-off-by: Li Wang Signed-off-by: Yunchuan Wen Reviewed-by: Cong Wang Cc: Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index 3745f7c..ec3d936 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -130,13 +130,13 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT); size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK); size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); - size_t total_remaining_bytes = ((offset + size) - pos); + loff_t total_remaining_bytes = ((offset + size) - pos); if (num_bytes > total_remaining_bytes) num_bytes = total_remaining_bytes; if (pos < offset) { /* remaining zeros to write, up to destination offset */ - size_t total_remaining_zeros = (offset - pos); + loff_t total_remaining_zeros = (offset - pos); if (num_bytes > total_remaining_zeros) num_bytes = total_remaining_zeros; -- cgit v0.10.2 From 5e6f0d769017cc49207ef56996e42363ec26c1f0 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Wed, 18 Jan 2012 18:30:04 -0600 Subject: eCryptfs: Make truncate path killable ecryptfs_write() handles the truncation of eCryptfs inodes. It grabs a page, zeroes out the appropriate portions, and then encrypts the page before writing it to the lower filesystem. It was unkillable and due to the lack of sparse file support could result in tying up a large portion of system resources, while encrypting pages of zeros, with no way for the truncate operation to be stopped from userspace. This patch adds the ability for ecryptfs_write() to detect a pending fatal signal and return as gracefully as possible. The intent is to leave the lower file in a useable state, while still allowing a user to break out of the encryption loop. If a pending fatal signal is detected, the eCryptfs inode size is updated to reflect the modified inode size and then -EINTR is returned. Signed-off-by: Tyler Hicks Cc: diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index ec3d936..608c1c3 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -132,6 +132,11 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); loff_t total_remaining_bytes = ((offset + size) - pos); + if (fatal_signal_pending(current)) { + rc = -EINTR; + break; + } + if (num_bytes > total_remaining_bytes) num_bytes = total_remaining_bytes; if (pos < offset) { @@ -193,15 +198,19 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, } pos += num_bytes; } - if ((offset + size) > ecryptfs_file_size) { - i_size_write(ecryptfs_inode, (offset + size)); + if (pos > ecryptfs_file_size) { + i_size_write(ecryptfs_inode, pos); if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) { - rc = ecryptfs_write_inode_size_to_metadata( + int rc2; + + rc2 = ecryptfs_write_inode_size_to_metadata( ecryptfs_inode); - if (rc) { + if (rc2) { printk(KERN_ERR "Problem with " "ecryptfs_write_inode_size_to_metadata; " - "rc = [%d]\n", rc); + "rc = [%d]\n", rc2); + if (!rc) + rc = rc2; goto out; } } -- cgit v0.10.2 From a261a03904849c3df50bd0300efb7fb3f865137d Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 19 Jan 2012 20:33:44 -0600 Subject: eCryptfs: Check inode changes in setattr Most filesystems call inode_change_ok() very early in ->setattr(), but eCryptfs didn't call it at all. It allowed the lower filesystem to make the call in its ->setattr() function. Then, eCryptfs would copy the appropriate inode attributes from the lower inode to the eCryptfs inode. This patch changes that and actually calls inode_change_ok() on the eCryptfs inode, fairly early in ecryptfs_setattr(). Ideally, the call would happen earlier in ecryptfs_setattr(), but there are some possible inode initialization steps that must happen first. Since the call was already being made on the lower inode, the change in functionality should be minimal, except for the case of a file extending truncate call. In that case, inode_newsize_ok() was never being called on the eCryptfs inode. Rather than inode_newsize_ok() catching maximum file size errors early on, eCryptfs would encrypt zeroed pages and write them to the lower filesystem until the lower filesystem's write path caught the error in generic_write_checks(). This patch introduces a new function, called ecryptfs_inode_newsize_ok(), which checks if the new lower file size is within the appropriate limits when the truncate operation will be growing the lower file. In summary this change prevents eCryptfs truncate operations (and the resulting page encryptions), which would exceed the lower filesystem limits or FSIZE rlimits, from ever starting. Signed-off-by: Tyler Hicks Reviewed-by: Li Wang Cc: diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 19a8ca4..19892d7 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -822,18 +822,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, size_t num_zeros = (PAGE_CACHE_SIZE - (ia->ia_size & ~PAGE_CACHE_MASK)); - - /* - * XXX(truncate) this should really happen at the begginning - * of ->setattr. But the code is too messy to that as part - * of a larger patch. ecryptfs is also totally missing out - * on the inode_change_ok check at the beginning of - * ->setattr while would include this. - */ - rc = inode_newsize_ok(inode, ia->ia_size); - if (rc) - goto out; - if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { truncate_setsize(inode, ia->ia_size); lower_ia->ia_size = ia->ia_size; @@ -883,6 +871,28 @@ out: return rc; } +static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset) +{ + struct ecryptfs_crypt_stat *crypt_stat; + loff_t lower_oldsize, lower_newsize; + + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + lower_oldsize = upper_size_to_lower_size(crypt_stat, + i_size_read(inode)); + lower_newsize = upper_size_to_lower_size(crypt_stat, offset); + if (lower_newsize > lower_oldsize) { + /* + * The eCryptfs inode and the new *lower* size are mixed here + * because we may not have the lower i_mutex held and/or it may + * not be appropriate to call inode_newsize_ok() with inodes + * from other filesystems. + */ + return inode_newsize_ok(inode, lower_newsize); + } + + return 0; +} + /** * ecryptfs_truncate * @dentry: The ecryptfs layer dentry @@ -899,6 +909,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) struct iattr lower_ia = { .ia_valid = 0 }; int rc; + rc = ecryptfs_inode_newsize_ok(dentry->d_inode, new_length); + if (rc) + return rc; + rc = truncate_upper(dentry, &ia, &lower_ia); if (!rc && lower_ia.ia_valid & ATTR_SIZE) { struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); @@ -978,6 +992,16 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) } } mutex_unlock(&crypt_stat->cs_mutex); + + rc = inode_change_ok(inode, ia); + if (rc) + goto out; + if (ia->ia_valid & ATTR_SIZE) { + rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); + if (rc) + goto out; + } + if (S_ISREG(inode->i_mode)) { rc = filemap_write_and_wait(inode->i_mapping); if (rc) -- cgit v0.10.2 From f2cb933501ebc066bf3c4b1836fd8428f8fe9863 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Wed, 18 Jan 2012 15:09:43 -0600 Subject: eCryptfs: Remove unused ecryptfs_read() ecryptfs_read() has been ifdef'ed out for years now and it was apparently unused before then. It is time to get rid of it for good. Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index 608c1c3..5c0106f 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -282,76 +282,3 @@ int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs, flush_dcache_page(page_for_ecryptfs); return rc; } - -#if 0 -/** - * ecryptfs_read - * @data: The virtual address into which to write the data read (and - * possibly decrypted) from the lower file - * @offset: The offset in the decrypted view of the file from which to - * read into @data - * @size: The number of bytes to read into @data - * @ecryptfs_file: The eCryptfs file from which to read - * - * Read an arbitrary amount of data from an arbitrary location in the - * eCryptfs page cache. This is done on an extent-by-extent basis; - * individual extents are decrypted and read from the lower page - * cache (via VFS reads). This function takes care of all the - * address translation to locations in the lower filesystem. - * - * Returns zero on success; non-zero otherwise - */ -int ecryptfs_read(char *data, loff_t offset, size_t size, - struct file *ecryptfs_file) -{ - struct inode *ecryptfs_inode = ecryptfs_file->f_dentry->d_inode; - struct page *ecryptfs_page; - char *ecryptfs_page_virt; - loff_t ecryptfs_file_size = i_size_read(ecryptfs_inode); - loff_t data_offset = 0; - loff_t pos; - int rc = 0; - - if ((offset + size) > ecryptfs_file_size) { - rc = -EINVAL; - printk(KERN_ERR "%s: Attempt to read data past the end of the " - "file; offset = [%lld]; size = [%td]; " - "ecryptfs_file_size = [%lld]\n", - __func__, offset, size, ecryptfs_file_size); - goto out; - } - pos = offset; - while (pos < (offset + size)) { - pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT); - size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK); - size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page); - size_t total_remaining_bytes = ((offset + size) - pos); - - if (num_bytes > total_remaining_bytes) - num_bytes = total_remaining_bytes; - ecryptfs_page = ecryptfs_get_locked_page(ecryptfs_inode, - ecryptfs_page_idx); - if (IS_ERR(ecryptfs_page)) { - rc = PTR_ERR(ecryptfs_page); - printk(KERN_ERR "%s: Error getting page at " - "index [%ld] from eCryptfs inode " - "mapping; rc = [%d]\n", __func__, - ecryptfs_page_idx, rc); - goto out; - } - ecryptfs_page_virt = kmap_atomic(ecryptfs_page, KM_USER0); - memcpy((data + data_offset), - ((char *)ecryptfs_page_virt + start_offset_in_page), - num_bytes); - kunmap_atomic(ecryptfs_page_virt, KM_USER0); - flush_dcache_page(ecryptfs_page); - SetPageUptodate(ecryptfs_page); - unlock_page(ecryptfs_page); - page_cache_release(ecryptfs_page); - pos += num_bytes; - data_offset += num_bytes; - } -out: - return rc; -} -#endif /* 0 */ -- cgit v0.10.2 From 58ded24f0fcb85bddb665baba75892f6ad0f4b8a Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 24 Jan 2012 10:02:22 -0600 Subject: eCryptfs: Fix oops when printing debug info in extent crypto functions If pages passed to the eCryptfs extent-based crypto functions are not mapped and the module parameter ecryptfs_verbosity=1 was specified at loading time, a NULL pointer dereference will occur. Note that this wouldn't happen on a production system, as you wouldn't pass ecryptfs_verbosity=1 on a production system. It leaks private information to the system logs and is for debugging only. The debugging info printed in these messages is no longer very useful and rather than doing a kmap() in these debugging paths, it will be better to simply remove the debugging paths completely. https://launchpad.net/bugs/913651 Signed-off-by: Tyler Hicks Reported-by: Daniel DeFreez Cc: diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index ff98150..63ab245 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -417,17 +417,6 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Encrypting extent " - "with iv:\n"); - ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " - "encryption:\n"); - ecryptfs_dump_hex((char *) - (page_address(page) - + (extent_offset * crypt_stat->extent_size)), - 8); - } rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0, page, (extent_offset * crypt_stat->extent_size), @@ -440,14 +429,6 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, goto out; } rc = 0; - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16llx]; " - "rc = [%d]\n", - (unsigned long long)(extent_base + extent_offset), rc); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " - "encryption:\n"); - ecryptfs_dump_hex((char *)(page_address(enc_extent_page)), 8); - } out: return rc; } @@ -543,17 +524,6 @@ static int ecryptfs_decrypt_extent(struct page *page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Decrypting extent " - "with iv:\n"); - ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " - "decryption:\n"); - ecryptfs_dump_hex((char *) - (page_address(enc_extent_page) - + (extent_offset * crypt_stat->extent_size)), - 8); - } rc = ecryptfs_decrypt_page_offset(crypt_stat, page, (extent_offset * crypt_stat->extent_size), @@ -567,16 +537,6 @@ static int ecryptfs_decrypt_extent(struct page *page, goto out; } rc = 0; - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Decrypt extent [0x%.16llx]; " - "rc = [%d]\n", - (unsigned long long)(extent_base + extent_offset), rc); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " - "decryption:\n"); - ecryptfs_dump_hex((char *)(page_address(page) - + (extent_offset - * crypt_stat->extent_size)), 8); - } out: return rc; } -- cgit v0.10.2 From cf840551a884360841bd3d3ce1ad0868ff0b759a Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 18 Jan 2012 17:47:12 +0800 Subject: xHCI: Cleanup isoc transfer ring when TD length mismatch found When a TD length mismatch is found during isoc TRB enqueue, it directly returns -EINVAL. However, isoc transfer is partially enqueued at this time, and the ring should be cleared. This should be backported to kernels as old as 2.6.36, which contain the commit 522989a27c7badb608155b1f1dea3487ed431f74 "xhci: Fix failed enqueue in the middle of isoch TD." Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5a818cb..b62037b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3324,7 +3324,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Check TD length */ if (running_total != td_len) { xhci_err(xhci, "ISOC TD length unmatch\n"); - return -EINVAL; + ret = -EINVAL; + goto cleanup; } } -- cgit v0.10.2 From 1d2f56c84f100890476e62d83062cfe9965fc7b4 Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Wed, 28 Dec 2011 00:31:33 +0100 Subject: ARM: OMAP3: hwmod data: register dss hwmods after dss_core dss_core has to be initialized before any other DSS hwmod. Currently this is broken as dss_core is listed in chip/revision specific hwmod lists while other DSS hwmods are listed in common list which is registered first. This patch moves DSS hwmods (except for dss_core) to the separate list which is registered last to ensure that dss_core is already registered. This solves the problem with BUG() in L3 interrupt handler on boards with DSS enabled in bootloader. The long-term fix to this is to ensure modules are set up in dependency order in the hwmod core code. CC: Tomi Valkeinen CC: Archit Taneja CC: Paul Walmsley Signed-off-by: Ilya Yanok [paul@pwsan.com: add notes that this is just a temporary workaround until hwmod dependencies are added] Signed-off-by: Paul Walmsley diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index b176d44..3c8dd92 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -3545,12 +3545,6 @@ static __initdata struct omap_hwmod *omap3xxx_hwmods[] = { &omap3xxx_uart2_hwmod, &omap3xxx_uart3_hwmod, - /* dss class */ - &omap3xxx_dss_dispc_hwmod, - &omap3xxx_dss_dsi1_hwmod, - &omap3xxx_dss_rfbi_hwmod, - &omap3xxx_dss_venc_hwmod, - /* i2c class */ &omap3xxx_i2c1_hwmod, &omap3xxx_i2c2_hwmod, @@ -3657,6 +3651,15 @@ static __initdata struct omap_hwmod *am35xx_hwmods[] = { NULL }; +static __initdata struct omap_hwmod *omap3xxx_dss_hwmods[] = { + /* dss class */ + &omap3xxx_dss_dispc_hwmod, + &omap3xxx_dss_dsi1_hwmod, + &omap3xxx_dss_rfbi_hwmod, + &omap3xxx_dss_venc_hwmod, + NULL +}; + int __init omap3xxx_hwmod_init(void) { int r; @@ -3730,6 +3733,21 @@ int __init omap3xxx_hwmod_init(void) if (h) r = omap_hwmod_register(h); + if (r < 0) + return r; + + /* + * DSS code presumes that dss_core hwmod is handled first, + * _before_ any other DSS related hwmods so register common + * DSS hwmods last to ensure that dss_core is already registered. + * Otherwise some change things may happen, for ex. if dispc + * is handled before dss_core and DSS is enabled in bootloader + * DIPSC will be reset with outputs enabled which sometimes leads + * to unrecoverable L3 error. + * XXX The long-term fix to this is to ensure modules are set up + * in dependency order in the hwmod core code. + */ + r = omap_hwmod_register(omap3xxx_dss_hwmods); return r; } -- cgit v0.10.2 From 1b76d2ee4012f325ae14e0e71dad1a0835195906 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 21:10:07 +0000 Subject: ASoC: wm8996: Mark register cache as dirty when regulators are disabled Otherwise we won't resync later. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 13aa2bd..61f7daa 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -108,7 +108,7 @@ static int wm8996_regulator_event_##n(struct notifier_block *nb, \ struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ - regcache_cache_only(wm8996->regmap, true); \ + regcache_mark_dirty(wm8996->regmap); \ } \ return 0; \ } -- cgit v0.10.2 From 5539a102882d5ddd1bb95ea9f6f43130a789cb7f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 21:10:21 +0000 Subject: ASoC: wm8962: Mark register cache as dirty when regulators are disabled Otherwise we won't resync later. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 296de4e..bda3da8 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -96,7 +96,7 @@ static int wm8962_regulator_event_##n(struct notifier_block *nb, \ struct wm8962_priv *wm8962 = container_of(nb, struct wm8962_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ - regcache_cache_only(wm8962->regmap, true); \ + regcache_mark_dirty(wm8962->regmap); \ } \ return 0; \ } -- cgit v0.10.2 From 5c1b136b7bf702e550039cb0039ec9c790c48f99 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 25 Jan 2012 21:10:33 +0000 Subject: ASoC: wm5100: Mark register cache as dirty when regulators are disabled Otherwise we won't resync later. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index fb757af..89f2af7 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1405,6 +1405,7 @@ static int wm5100_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: regcache_cache_only(wm5100->regmap, true); + regcache_mark_dirty(wm5100->regmap); if (wm5100->pdata.ldo_ena) gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), -- cgit v0.10.2 From 1589cb1a94c381579a0235ca708d9e2dca6d3a39 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Wed, 25 Jan 2012 15:40:31 +0800 Subject: eCryptfs: move misleading function comments The data encryption was moved from ecryptfs_write_end into ecryptfs_writepage, this patch moves the corresponding function comments to be consistent with the modification. Signed-off-by: Li Wang Signed-off-by: Linus Torvalds diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 6a44148..10ec695 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -57,6 +57,10 @@ struct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index) * @page: Page that is locked before this call is made * * Returns zero on success; non-zero otherwise + * + * This is where we encrypt the data and pass the encrypted data to + * the lower filesystem. In OpenPGP-compatible mode, we operate on + * entire underlying packets. */ static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc) { @@ -481,10 +485,6 @@ int ecryptfs_write_inode_size_to_metadata(struct inode *ecryptfs_inode) * @copied: The amount of data copied * @page: The eCryptfs page * @fsdata: The fsdata (unused) - * - * This is where we encrypt the data and pass the encrypted data to - * the lower filesystem. In OpenPGP-compatible mode, we operate on - * entire underlying packets. */ static int ecryptfs_write_end(struct file *file, struct address_space *mapping, -- cgit v0.10.2 From 68315801dbf3ab2001679fd2074c9dc5dcf87dfa Mon Sep 17 00:00:00 2001 From: James Chapman Date: Wed, 25 Jan 2012 02:39:05 +0000 Subject: l2tp: l2tp_ip - fix possible oops on packet receive When a packet is received on an L2TP IP socket (L2TPv3 IP link encapsulation), the l2tpip socket's backlog_rcv function calls xfrm4_policy_check(). This is not necessary, since it was called before the skb was added to the backlog. With CONFIG_NET_NS enabled, xfrm4_policy_check() will oops if skb->dev is null, so this trivial patch removes the call. This bug has always been present, but only when CONFIG_NET_NS is enabled does it cause problems. Most users are probably using UDP encapsulation for L2TP, hence the problem has only recently surfaced. EIP: 0060:[] EFLAGS: 00210246 CPU: 0 EIP is at l2tp_ip_recvmsg+0xd4/0x2a7 EAX: 00000001 EBX: d77b5180 ECX: 00000000 EDX: 00200246 ESI: 00000000 EDI: d63cbd30 EBP: d63cbd18 ESP: d63cbcf4 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 Call Trace: [] sock_common_recvmsg+0x31/0x46 [] __sock_recvmsg_nosec+0x45/0x4d [] __sock_recvmsg+0x31/0x3b [] sock_recvmsg+0x96/0xab [] ? might_fault+0x47/0x81 [] ? might_fault+0x47/0x81 [] ? _copy_from_user+0x31/0x115 [] ? copy_from_user+0x8/0xa [] ? verify_iovec+0x3e/0x78 [] __sys_recvmsg+0x10a/0x1aa [] ? sock_recvmsg+0x0/0xab [] ? __lock_acquire+0xbdf/0xbee [] ? do_page_fault+0x193/0x375 [] ? fcheck_files+0x9b/0xca [] ? fget_light+0x2a/0x9c [] sys_recvmsg+0x2b/0x43 [] sys_socketcall+0x16d/0x1a5 [] ? trace_hardirqs_on_thunk+0xc/0x10 [] sysenter_do_call+0x12/0x38 Code: c6 05 8c ea a8 c1 01 e8 0c d4 d9 ff 85 f6 74 07 3e ff 86 80 00 00 00 b9 17 b6 2b c1 ba 01 00 00 00 b8 78 ed 48 c1 e8 23 f6 d9 ff 76 0c 68 28 e3 30 c1 68 2d 44 41 c1 e8 89 57 01 00 83 c4 0c Signed-off-by: James Chapman Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index d21e7eb..55670ec 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -393,11 +393,6 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) { int rc; - if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) - goto drop; - - nf_reset(skb); - /* Charge it to the socket, dropping if the queue is full. */ rc = sock_queue_rcv_skb(sk, skb); if (rc < 0) -- cgit v0.10.2 From 2b05ad33e1e624e7f08b8676d270dc7725403b7e Mon Sep 17 00:00:00 2001 From: Flavio Leitner Date: Wed, 25 Jan 2012 08:34:51 +0000 Subject: tcp: bind() fix autoselection to share ports The current code checks for conflicts when the application requests a specific port. If there is no conflict, then the request is granted. On the other hand, the port autoselection done by the kernel fails when all ports are bound even when there is a port with no conflict available. The fix changes port autoselection to check if there is a conflict and use it if not. Signed-off-by: Flavio Leitner Signed-off-by: Marcelo Ricardo Leitner Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 2e4e244..ecd19b5 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -128,6 +128,11 @@ again: goto have_snum; } } + if (!inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) { + spin_unlock(&head->lock); + snum = rover; + goto have_snum; + } goto next; } break; -- cgit v0.10.2 From fddb7b5761f104f034a0e708ece756d9b2eb2cac Mon Sep 17 00:00:00 2001 From: Flavio Leitner Date: Wed, 25 Jan 2012 08:34:52 +0000 Subject: tcp: bind() optimize port allocation Port autoselection finds a port and then drop the lock, then right after that, gets the hash bucket again and lock it. Fix it to go direct. Signed-off-by: Flavio Leitner Signed-off-by: Marcelo Ricardo Leitner Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index ecd19b5..19d66ce 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -123,15 +123,13 @@ again: smallest_size = tb->num_owners; smallest_rover = rover; if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) { - spin_unlock(&head->lock); snum = smallest_rover; - goto have_snum; + goto tb_found; } } if (!inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) { - spin_unlock(&head->lock); snum = rover; - goto have_snum; + goto tb_found; } goto next; } -- cgit v0.10.2 From 58d7d18b5268febb8b1391c6dffc8e2aaa751fcd Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 26 Jan 2012 15:03:16 +1100 Subject: crypto: sha512 - Use binary and instead of modulus The previous patch used the modulus operator over a power of 2 unnecessarily which may produce suboptimal binary code. This patch changes changes them to binary ands instead. Signed-off-by: Herbert Xu diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 88f160b..3edebfd 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -78,7 +78,7 @@ static inline void LOAD_OP(int I, u64 *W, const u8 *input) static inline void BLEND_OP(int I, u64 *W) { - W[I % 16] += s1(W[(I-2) % 16]) + W[(I-7) % 16] + s0(W[(I-15) % 16]); + W[I & 15] += s1(W[(I-2) & 15]) + W[(I-7) & 15] + s0(W[(I-15) & 15]); } static void @@ -105,7 +105,7 @@ sha512_transform(u64 *state, const u8 *input) #define SHA512_16_79(i, a, b, c, d, e, f, g, h) \ BLEND_OP(i, W); \ - t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)%16]; \ + t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)&15]; \ t2 = e0(a) + Maj(a, b, c); \ d += t1; \ h = t1 + t2 -- cgit v0.10.2 From 5a51467b146ab7948d2f6812892eac120a30529c Mon Sep 17 00:00:00 2001 From: Russ Anderson Date: Wed, 18 Jan 2012 20:07:54 -0600 Subject: x86/uv: Fix uv_gpa_to_soc_phys_ram() shift uv_gpa_to_soc_phys_ram() was inadvertently ignoring the shift values. This fix takes the shift into account. Signed-off-by: Russ Anderson Cc: Link: http://lkml.kernel.org/r/20120119020753.GA7228@sgi.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h index 54a13aa..21f7385 100644 --- a/arch/x86/include/asm/uv/uv_hub.h +++ b/arch/x86/include/asm/uv/uv_hub.h @@ -318,13 +318,13 @@ uv_gpa_in_mmr_space(unsigned long gpa) /* UV global physical address --> socket phys RAM */ static inline unsigned long uv_gpa_to_soc_phys_ram(unsigned long gpa) { - unsigned long paddr = gpa & uv_hub_info->gpa_mask; + unsigned long paddr; unsigned long remap_base = uv_hub_info->lowmem_remap_base; unsigned long remap_top = uv_hub_info->lowmem_remap_top; gpa = ((gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | ((gpa >> uv_hub_info->n_lshift) << uv_hub_info->m_val); - gpa = gpa & uv_hub_info->gpa_mask; + paddr = gpa & uv_hub_info->gpa_mask; if (paddr >= remap_base && paddr < remap_base + remap_top) paddr -= remap_base; return paddr; -- cgit v0.10.2 From d2ebc71d472020bc30e29afe8c4d2a85a5b41f56 Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Wed, 18 Jan 2012 09:40:47 -0600 Subject: x86/uv: Fix uninitialized spinlocks Initialize two spinlocks in tlb_uv.c and also properly define/initialize the uv_irq_lock. The lack of explicit initialization seems to be functionally harmless, but it is diagnosed when these are turned on: CONFIG_DEBUG_SPINLOCK=y CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_LOCKDEP=y Signed-off-by: Cliff Wickman Cc: Cc: Dimitri Sivanich Link: http://lkml.kernel.org/r/E1RnXd1-0003wU-PM@eag09.americas.sgi.com [ Added the uv_irq_lock initialization fix by Dimitri Sivanich ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 9be4cff..3ae0e61 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1851,6 +1851,8 @@ static void __init init_per_cpu_tunables(void) bcp->cong_reps = congested_reps; bcp->cong_period = congested_period; bcp->clocks_per_100_usec = usec_2_cycles(100); + spin_lock_init(&bcp->queue_lock); + spin_lock_init(&bcp->uvhub_lock); } } diff --git a/arch/x86/platform/uv/uv_irq.c b/arch/x86/platform/uv/uv_irq.c index 374a05d..f25c276 100644 --- a/arch/x86/platform/uv/uv_irq.c +++ b/arch/x86/platform/uv/uv_irq.c @@ -25,7 +25,7 @@ struct uv_irq_2_mmr_pnode{ int irq; }; -static spinlock_t uv_irq_lock; +static DEFINE_SPINLOCK(uv_irq_lock); static struct rb_root uv_irq_root; static int uv_set_irq_affinity(struct irq_data *, const struct cpumask *, bool); -- cgit v0.10.2 From 3fe54564a61f72982032423d24041dca30617ca2 Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Wed, 25 Jan 2012 14:35:49 +0800 Subject: x86/numachip: Drop unnecessary conflict with EDAC EDAC detection no longer crashes multi-node systems, so don't conflict on it with NumaChip. Signed-off-by: Daniel J Blueman Cc: Steffen Persvold Link: http://lkml.kernel.org/r/1327473349-28395-1-git-send-email-daniel@numascale-asia.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 864cc6e..5bed94e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -360,7 +360,6 @@ config X86_NUMACHIP depends on NUMA depends on SMP depends on X86_X2APIC - depends on !EDAC_AMD64 ---help--- Adds support for Numascale NumaChip large-SMP systems. Needed to enable more than ~168 cores. -- cgit v0.10.2 From 5067cf53cac9b36d42ebb3a45bb12259d0bc1e68 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 23 Jan 2012 23:34:59 +0100 Subject: x86/boot-image: Don't leak phdrs in arch/x86/boot/compressed/misc.c::Parse_elf() We allocate memory with malloc(), but neglect to free it before the variable 'phdrs' goes out of scope --> leak. Signed-off-by: Jesper Juhl Link: http://lkml.kernel.org/r/alpine.LNX.2.00.1201232332590.8772@swampdragon.chaosbits.net [ Mostly harmless. ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 3a19d04..7116dcb 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -321,6 +321,8 @@ static void parse_elf(void *output) default: /* Ignore other PT_* */ break; } } + + free(phdrs); } asmlinkage void decompress_kernel(void *rmode, memptr heap, -- cgit v0.10.2 From 8a093049c604ab32d94bcc5baa24f7939d5e3f7b Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 25 Jan 2012 10:31:45 +0100 Subject: regulator: Set apply_uV only when min and max voltages are defined apply_uV is errornously set when regulator is instantiated from device tree, even when it doesn't contain any voltage constraints. This commit fixes error: machine_constraints_voltage: CHARGER: failed to apply 0uV constraint for following regulator description in DTS: CHARGER { regulator-min-microamp = <100000>; regulator-max-microamp = <200000>; } Signed-off-by: Karol Lewandowski Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index f1651eb..679734d 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -35,7 +35,7 @@ static void of_get_regulation_constraints(struct device_node *np, if (constraints->min_uV != constraints->max_uV) constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; /* Only one voltage? Then make sure it's set. */ - if (constraints->min_uV == constraints->max_uV) + if (min_uV && max_uV && constraints->min_uV == constraints->max_uV) constraints->apply_uV = true; uV_offset = of_get_property(np, "regulator-microvolt-offset", NULL); -- cgit v0.10.2 From 652847aa449cfe364d40018849223f57f31a38e2 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 20 Jan 2012 17:38:23 +0100 Subject: x86/amd: Add missing feature flag for fam15h models 10h-1fh processors That is the last one missing for those CPUs. Others were recently added with commits fb215366b3c7320ac25dca766a0152df16534932 (KVM: expose latest Intel cpu new features (BMI1/BMI2/FMA/AVX2) to guest) and commit 969df4b82904a30fef19a67398a0c854d223ea67 (x86: Report cpb and eff_freq_ro flags correctly) Signed-off-by: Andreas Herrmann Link: http://lkml.kernel.org/r/20120120163823.GC24508@alberich.amd.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 17c5d4b..8d67d42 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -159,6 +159,7 @@ #define X86_FEATURE_WDT (6*32+13) /* Watchdog timer */ #define X86_FEATURE_LWP (6*32+15) /* Light Weight Profiling */ #define X86_FEATURE_FMA4 (6*32+16) /* 4 operands MAC instructions */ +#define X86_FEATURE_TCE (6*32+17) /* translation cache extension */ #define X86_FEATURE_NODEID_MSR (6*32+19) /* NodeId MSR */ #define X86_FEATURE_TBM (6*32+21) /* trailing bit manipulations */ #define X86_FEATURE_TOPOEXT (6*32+22) /* topology extensions CPUID leafs */ -- cgit v0.10.2 From 5b68edc91cdc972c46f76f85eded7ffddc3ff5c2 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 20 Jan 2012 17:44:12 +0100 Subject: x86/microcode_amd: Add support for CPU family specific container files We've decided to provide CPU family specific container files (starting with CPU family 15h). E.g. for family 15h we have to load microcode_amd_fam15h.bin instead of microcode_amd.bin Rationale is that starting with family 15h patch size is larger than 2KB which was hard coded as maximum patch size in various microcode loaders (not just Linux). Container files which include patches larger than 2KB cause different kinds of trouble with such old patch loaders. Thus we have to ensure that the default container file provides only patches with size less than 2KB. Signed-off-by: Andreas Herrmann Cc: Borislav Petkov Cc: Link: http://lkml.kernel.org/r/20120120164412.GD24508@alberich.amd.com [ documented the naming convention and tidied the code a bit. ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index fe86493..ac0417b 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -311,13 +311,33 @@ out: return state; } +/* + * AMD microcode firmware naming convention, up to family 15h they are in + * the legacy file: + * + * amd-ucode/microcode_amd.bin + * + * This legacy file is always smaller than 2K in size. + * + * Starting at family 15h they are in family specific firmware files: + * + * amd-ucode/microcode_amd_fam15h.bin + * amd-ucode/microcode_amd_fam16h.bin + * ... + * + * These might be larger than 2K. + */ static enum ucode_state request_microcode_amd(int cpu, struct device *device) { - const char *fw_name = "amd-ucode/microcode_amd.bin"; + char fw_name[36] = "amd-ucode/microcode_amd.bin"; const struct firmware *fw; enum ucode_state ret = UCODE_NFOUND; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86 >= 0x15) + snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86); - if (request_firmware(&fw, fw_name, device)) { + if (request_firmware(&fw, (const char *)fw_name, device)) { pr_err("failed to load file %s\n", fw_name); goto out; } -- cgit v0.10.2 From 0eaf9f52e94f756147dbfe1faf1f77a02378dbf9 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 23 Jan 2012 13:23:08 +0200 Subject: OMAPDSS: use sync versions of pm_runtime_put omapdss doesn't work properly on system suspend. The problem seems to be the fact that omapdss uses pm_runtime_put() functions when turning off the hardware, and when system suspend is in process only sync versions are allowed. Using non-sync versions normally and sync versions when suspending would need rather ugly hacks to convey the information of suspending/not-suspending to different functions. Optimally the driver wouldn't even need to care about this, and the PM layer would handle syncing when suspend is in process. This patch changes all omapdss's pm_runtime_put calls to pm_runtime_put_sync. This fixes the suspend problem, and probably the performance penalty of always using sync versions is negligible. Signed-off-by: Tomi Valkeinen Acked-by: Kevin Hilman diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index a5ec7f3..e1626a1 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -401,7 +401,7 @@ void dispc_runtime_put(void) DSSDBG("dispc_runtime_put\n"); - r = pm_runtime_put(&dispc.pdev->dev); + r = pm_runtime_put_sync(&dispc.pdev->dev); WARN_ON(r < 0); } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 511ae2a..04a89a7 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -1079,7 +1079,7 @@ void dsi_runtime_put(struct platform_device *dsidev) DSSDBG("dsi_runtime_put\n"); - r = pm_runtime_put(&dsi->pdev->dev); + r = pm_runtime_put_sync(&dsi->pdev->dev); WARN_ON(r < 0); } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 1703345..77c2b5a 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -720,7 +720,7 @@ void dss_runtime_put(void) DSSDBG("dss_runtime_put\n"); - r = pm_runtime_put(&dss.pdev->dev); + r = pm_runtime_put_sync(&dss.pdev->dev); WARN_ON(r < 0); } diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index b4c270e..c39f9c3 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -176,7 +176,7 @@ static void hdmi_runtime_put(void) DSSDBG("hdmi_runtime_put\n"); - r = pm_runtime_put(&hdmi.pdev->dev); + r = pm_runtime_put_sync(&hdmi.pdev->dev); WARN_ON(r < 0); } diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 814bb95..55f3980 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -140,7 +140,7 @@ static void rfbi_runtime_put(void) DSSDBG("rfbi_runtime_put\n"); - r = pm_runtime_put(&rfbi.pdev->dev); + r = pm_runtime_put_sync(&rfbi.pdev->dev); WARN_ON(r < 0); } diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index b3e9f90..5c3d0f9 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -401,7 +401,7 @@ static void venc_runtime_put(void) DSSDBG("venc_runtime_put\n"); - r = pm_runtime_put(&venc.pdev->dev); + r = pm_runtime_put_sync(&venc.pdev->dev); WARN_ON(r < 0); } -- cgit v0.10.2 From 575753e3bea3b67eef8e454fb87f719e3f7da599 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 17 Jan 2012 11:04:53 +0200 Subject: OMAP: 4430SDP/Panda: use gpio_free_array to free HDMI gpios Instead of freeing the GPIOs individually, use gpio_free_array(). Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index e1fe304..7bbe23e 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -614,8 +614,7 @@ static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev) static void sdp4430_panel_disable_hdmi(struct omap_dss_device *dssdev) { - gpio_free(HDMI_GPIO_LS_OE); - gpio_free(HDMI_GPIO_HPD); + gpio_free_array(sdp4430_hdmi_gpios, ARRAY_SIZE(sdp4430_hdmi_gpios)); } static struct nokia_dsi_panel_data dsi1_panel = { diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 3e1c507..aeb9e88 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -497,8 +497,7 @@ static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev) static void omap4_panda_panel_disable_hdmi(struct omap_dss_device *dssdev) { - gpio_free(HDMI_GPIO_LS_OE); - gpio_free(HDMI_GPIO_HPD); + gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios)); } static struct omap_dss_device omap4_panda_hdmi_device = { -- cgit v0.10.2 From 3932a32fcf5393f8be70ac99dc718ad7ad0a415b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 17 Jan 2012 10:49:38 +0200 Subject: OMAP: 4430SDP/Panda: rename HPD GPIO to CT_CP_HPD The GPIO 60 on 4430sdp and Panda is not HPD GPIO, as currently marked in the board files, but CT_CP_HPD, which is used to enable/disable HPD functionality. This patch renames the GPIO. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 7bbe23e..02a4d5f 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -52,7 +52,7 @@ #define ETH_KS8851_QUART 138 #define OMAP4_SFH7741_SENSOR_OUTPUT_GPIO 184 #define OMAP4_SFH7741_ENABLE_GPIO 188 -#define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */ +#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ #define DISPLAY_SEL_GPIO 59 /* LCD2/PicoDLP switch */ #define DLP_POWER_ON_GPIO 40 @@ -596,7 +596,7 @@ static void __init omap_sfh7741prox_init(void) } static struct gpio sdp4430_hdmi_gpios[] = { - { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, + { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, }; diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index aeb9e88..844e285 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -51,7 +51,7 @@ #define GPIO_HUB_NRESET 62 #define GPIO_WIFI_PMENA 43 #define GPIO_WIFI_IRQ 53 -#define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */ +#define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ /* wl127x BT, FM, GPS connectivity chip */ @@ -479,7 +479,7 @@ int __init omap4_panda_dvi_init(void) } static struct gpio panda_hdmi_gpios[] = { - { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, + { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, }; -- cgit v0.10.2 From 7bb122d155f742fe2d79849090c825be7b4a247e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 17 Jan 2012 10:59:00 +0200 Subject: OMAPDSS: remove wrong HDMI HPD muxing "hdmi_hpd" pin is muxed to INPUT and PULLUP, but the pin is not currently used, and in the future when it is used, the pin is used as a GPIO and is board specific, not an OMAP4 wide thing. So remove the muxing for now. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index ffd9bd9..d6e65e2 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -102,12 +102,8 @@ static void omap4_hdmi_mux_pads(enum omap_hdmi_flags flags) u32 reg; u16 control_i2c_1; - /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ - omap_mux_init_signal("hdmi_hpd", - OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_cec", OMAP_PIN_INPUT_PULLUP); - /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ omap_mux_init_signal("hdmi_ddc_scl", OMAP_PIN_INPUT_PULLUP); omap_mux_init_signal("hdmi_ddc_sda", -- cgit v0.10.2 From 78a1ad8f12db70b8b0a4548b90704de08ee216ce Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 17 Jan 2012 11:02:36 +0200 Subject: OMAP: 4430SDP/Panda: setup HDMI GPIO muxes The HDMI GPIO pins LS_OE and CT_CP_HPD are not currently configured. This patch configures them as output pins. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 02a4d5f..95766cd 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -821,6 +821,9 @@ static void omap_4430sdp_display_init(void) omap_hdmi_init(OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP); else omap_hdmi_init(0); + + omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); } #ifdef CONFIG_OMAP_MUX diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 844e285..9823972 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -538,6 +538,9 @@ void omap4_panda_display_init(void) omap_hdmi_init(OMAP_HDMI_SDA_SCL_EXTERNAL_PULLUP); else omap_hdmi_init(0); + + omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); } static void __init omap4_panda_init(void) -- cgit v0.10.2 From aa74274b464d4aa24703963ac89a0ee942d5d267 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 17 Jan 2012 11:05:32 +0200 Subject: OMAP: 4430SDP/Panda: add HDMI HPD gpio Both Panda and 4430SDP use GPIO 63 as HDMI hot-plug-detect. Configure this GPIO in the board files. Signed-off-by: Tomi Valkeinen Acked-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 95766cd..cd6ec51 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -54,6 +54,7 @@ #define OMAP4_SFH7741_ENABLE_GPIO 188 #define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ +#define HDMI_GPIO_HPD 63 /* Hotplug detect */ #define DISPLAY_SEL_GPIO 59 /* LCD2/PicoDLP switch */ #define DLP_POWER_ON_GPIO 40 @@ -598,6 +599,7 @@ static void __init omap_sfh7741prox_init(void) static struct gpio sdp4430_hdmi_gpios[] = { { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, + { HDMI_GPIO_HPD, GPIOF_DIR_IN, "hdmi_gpio_hpd" }, }; static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev) @@ -824,6 +826,7 @@ static void omap_4430sdp_display_init(void) omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT_PULLDOWN); } #ifdef CONFIG_OMAP_MUX diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 9823972..e1b1963 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -53,6 +53,7 @@ #define GPIO_WIFI_IRQ 53 #define HDMI_GPIO_CT_CP_HPD 60 /* HPD mode enable/disable */ #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ +#define HDMI_GPIO_HPD 63 /* Hotplug detect */ /* wl127x BT, FM, GPS connectivity chip */ static int wl1271_gpios[] = {46, -1, -1}; @@ -481,6 +482,7 @@ int __init omap4_panda_dvi_init(void) static struct gpio panda_hdmi_gpios[] = { { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ct_cp_hpd" }, { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, + { HDMI_GPIO_HPD, GPIOF_DIR_IN, "hdmi_gpio_hpd" }, }; static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev) @@ -541,6 +543,7 @@ void omap4_panda_display_init(void) omap_mux_init_gpio(HDMI_GPIO_LS_OE, OMAP_PIN_OUTPUT); omap_mux_init_gpio(HDMI_GPIO_CT_CP_HPD, OMAP_PIN_OUTPUT); + omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT_PULLDOWN); } static void __init omap4_panda_init(void) -- cgit v0.10.2 From c49d005b6cc8491fad5b24f82805be2d6bcbd3dd Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 17 Jan 2012 11:09:57 +0200 Subject: OMAPDSS: HDMI: PHY burnout fix A hardware bug in the OMAP4 HDMI PHY causes physical damage to the board if the HDMI PHY is kept powered on when the cable is not connected. This patch solves the problem by adding hot-plug-detection into the HDMI IP driver. This is not a real HPD support in the sense that nobody else than the IP driver gets to know about the HPD events, but is only meant to fix the HW bug. The strategy is simple: If the display device is turned off by the user, the PHY power is set to OFF. When the display device is turned on by the user, the PHY power is set either to LDOON or TXON, depending on whether the HDMI cable is connected. The reason to avoid PHY OFF when the display device is on, but the cable is disconnected, is that when the PHY is turned OFF, the HDMI IP is not "ticking" and thus the DISPC does not receive pixel clock from the HDMI IP. This would, for example, prevent any VSYNCs from happening, and would thus affect the users of omapdss. By using LDOON when the cable is disconnected we'll avoid the HW bug, but keep the HDMI working as usual from the user's point of view. Signed-off-by: Tomi Valkeinen diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index cd6ec51..0ce758e 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -732,6 +732,10 @@ static void sdp4430_lcd_init(void) pr_err("%s: Could not get lcd2_reset_gpio\n", __func__); } +static struct omap_dss_hdmi_data sdp4430_hdmi_data = { + .hpd_gpio = HDMI_GPIO_HPD, +}; + static struct omap_dss_device sdp4430_hdmi_device = { .name = "hdmi", .driver_name = "hdmi_panel", @@ -739,6 +743,7 @@ static struct omap_dss_device sdp4430_hdmi_device = { .platform_enable = sdp4430_panel_enable_hdmi, .platform_disable = sdp4430_panel_disable_hdmi, .channel = OMAP_DSS_CHANNEL_DIGIT, + .data = &sdp4430_hdmi_data, }; static struct picodlp_panel_data sdp4430_picodlp_pdata = { diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index e1b1963..370c4b4 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -502,6 +502,10 @@ static void omap4_panda_panel_disable_hdmi(struct omap_dss_device *dssdev) gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios)); } +static struct omap_dss_hdmi_data omap4_panda_hdmi_data = { + .hpd_gpio = HDMI_GPIO_HPD, +}; + static struct omap_dss_device omap4_panda_hdmi_device = { .name = "hdmi", .driver_name = "hdmi_panel", @@ -509,6 +513,7 @@ static struct omap_dss_device omap4_panda_hdmi_device = { .platform_enable = omap4_panda_panel_enable_hdmi, .platform_disable = omap4_panda_panel_disable_hdmi, .channel = OMAP_DSS_CHANNEL_DIGIT, + .data = &omap4_panda_hdmi_data, }; static struct omap_dss_device *omap4_panda_dss_devices[] = { diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index c39f9c3..d7aa3b0 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -497,6 +497,7 @@ bool omapdss_hdmi_detect(void) int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) { + struct omap_dss_hdmi_data *priv = dssdev->data; int r = 0; DSSDBG("ENTER hdmi_display_enable\n"); @@ -509,6 +510,8 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } + hdmi.ip_data.hpd_gpio = priv->hpd_gpio; + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 7503f7f..50dadba 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -126,6 +126,10 @@ struct hdmi_ip_data { const struct ti_hdmi_ip_ops *ops; struct hdmi_config cfg; struct hdmi_pll_info pll_data; + + /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */ + int hpd_gpio; + bool phy_tx_enabled; }; int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index 9af81f1..2d72334 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "ti_hdmi_4xxx_ip.h" #include "dss.h" @@ -223,6 +224,49 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data) hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF); } +static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data) +{ + unsigned long flags; + bool hpd; + int r; + /* this should be in ti_hdmi_4xxx_ip private data */ + static DEFINE_SPINLOCK(phy_tx_lock); + + spin_lock_irqsave(&phy_tx_lock, flags); + + hpd = gpio_get_value(ip_data->hpd_gpio); + + if (hpd == ip_data->phy_tx_enabled) { + spin_unlock_irqrestore(&phy_tx_lock, flags); + return 0; + } + + if (hpd) + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); + else + r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON); + + if (r) { + DSSERR("Failed to %s PHY TX power\n", + hpd ? "enable" : "disable"); + goto err; + } + + ip_data->phy_tx_enabled = hpd; +err: + spin_unlock_irqrestore(&phy_tx_lock, flags); + return r; +} + +static irqreturn_t hpd_irq_handler(int irq, void *data) +{ + struct hdmi_ip_data *ip_data = data; + + hdmi_check_hpd_state(ip_data); + + return IRQ_HANDLED; +} + int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) { u16 r = 0; @@ -232,10 +276,6 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) if (r) return r; - r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON); - if (r) - return r; - /* * Read address 0 in order to get the SCP reset done completed * Dummy access performed to make sure reset is done @@ -257,12 +297,32 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) /* Write to phy address 3 to change the polarity control */ REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); + r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), + NULL, hpd_irq_handler, + IRQF_DISABLED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, "hpd", ip_data); + if (r) { + DSSERR("HPD IRQ request failed\n"); + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); + return r; + } + + r = hdmi_check_hpd_state(ip_data); + if (r) { + free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); + return r; + } + return 0; } void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data) { + free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data); + hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); + ip_data->phy_tx_enabled = false; } static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data) diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 062b3b2..483f67c 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -590,6 +590,11 @@ struct omap_dss_device { int (*get_backlight)(struct omap_dss_device *dssdev); }; +struct omap_dss_hdmi_data +{ + int hpd_gpio; +}; + struct omap_dss_driver { struct device_driver driver; -- cgit v0.10.2 From 7c0c34544d71b10914f29383c119d80631f367b7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 5 Jan 2012 19:33:08 -0200 Subject: ARM: imx: iomux-v1.h: Fix build error due to __init annotation Fix the following build error found when building imx_v4_v5_defconfig: CC arch/arm/mach-imx/mach-imx27ipcam.o In file included from arch/arm/plat-mxc/include/mach/iomux-mx27.h:23, from arch/arm/mach-imx/mach-imx27ipcam.c:22: arch/arm/plat-mxc/include/mach/iomux-v1.h:99: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'imx_iomuxv1_init' Signed-off-by: Fabio Estevam Signed-off-by: Sascha Hauer diff --git a/arch/arm/plat-mxc/include/mach/iomux-v1.h b/arch/arm/plat-mxc/include/mach/iomux-v1.h index 6fa8a70..f7d1804 100644 --- a/arch/arm/plat-mxc/include/mach/iomux-v1.h +++ b/arch/arm/plat-mxc/include/mach/iomux-v1.h @@ -96,6 +96,6 @@ extern int mxc_gpio_mode(int gpio_mode); extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count, const char *label); -extern int __init imx_iomuxv1_init(void __iomem *base, int numports); +extern int imx_iomuxv1_init(void __iomem *base, int numports); #endif /* __MACH_IOMUX_V1_H__ */ -- cgit v0.10.2 From 945f82f25f9c49b93c315e0acc6d965cb37e137f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 10:55:12 +0100 Subject: arch/arm/mach-imx/mach-mx53_ard.c: add missing iounmap Add missing iounmap in error handling code, in a case where the function already preforms iounmap on some other execution path. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; statement S,S1; int ret; @@ e = \(ioremap\|ioremap_nocache\)(...) ... when != iounmap(e) if (<+...e...+>) S ... when any when != iounmap(e) *if (...) { ... when != iounmap(e) return ...; } ... when any iounmap(e); // Signed-off-by: Julia Lawall Signed-off-by: Sascha Hauer diff --git a/arch/arm/mach-mx5/board-mx53_ard.c b/arch/arm/mach-mx5/board-mx53_ard.c index 5f224f1..d4aac81 100644 --- a/arch/arm/mach-mx5/board-mx53_ard.c +++ b/arch/arm/mach-mx5/board-mx53_ard.c @@ -189,8 +189,10 @@ static int weim_cs_config(void) return -ENOMEM; iomuxc_base = ioremap(MX53_IOMUXC_BASE_ADDR, SZ_4K); - if (!iomuxc_base) + if (!iomuxc_base) { + iounmap(weim_base); return -ENOMEM; + } /* CS1 timings for LAN9220 */ writel(0x20001, (weim_base + 0x18)); -- cgit v0.10.2 From de849eecd0addaa6bf60f2f7be36b30abf9ff2ae Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 20 Jan 2012 08:17:33 -0800 Subject: pinctrl: fix some pinmux typos Fix some pinmux typos so implementing pinmux drivers is a bit easier. Signed-off-by: Tony Lindgren Signed-off-by: Linus Walleij diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 5324d31..150fd38 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -1024,7 +1024,7 @@ it, disables and releases it, and muxes it in on the pins defined by group B: foo_switch() { - struct pinmux pmx; + struct pinmux *pmx; /* Enable on position A */ pmx = pinmux_get(&device, "spi0-pos-A"); diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 0b22037..f4f8c7e 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -53,11 +53,6 @@ struct pinmux_group { * @dev: the device using this pinmux * @usecount: the number of active users of this mux setting, used to keep * track of nested use cases - * @pins: an array of discrete physical pins used in this mapping, taken - * from the global pin enumeration space (copied from pinmux map) - * @num_pins: the number of pins in this mapping array, i.e. the number of - * elements in .pins so we can iterate over that array (copied from - * pinmux map) * @pctldev: pin control device handling this pinmux * @func_selector: the function selector for the pinmux device handling * this pinmux @@ -409,7 +404,7 @@ int __init pinmux_register_mappings(struct pinmux_map const *maps, } /** - * acquire_pins() - acquire all the pins for a certain funcion on a pinmux + * acquire_pins() - acquire all the pins for a certain function on a pinmux * @pctldev: the device to take the pins on * @func_selector: the function selector to acquire the pins for * @group_selector: the group selector containing the pins to acquire @@ -455,7 +450,7 @@ static int acquire_pins(struct pinctrl_dev *pctldev, /** * release_pins() - release pins taken by earlier acquirement - * @pctldev: the device to free the pinx on + * @pctldev: the device to free the pins on * @group_selector: the group selector containing the pins to free */ static void release_pins(struct pinctrl_dev *pctldev, -- cgit v0.10.2 From 9e2551e10b5c7ba550849bd9ed519e498cc30e68 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 20 Jan 2012 07:43:53 -0800 Subject: pinctrl: fix pinmux_hog_maps when ctrl_dev_name is not set The ctrl_dev_name is optional for struct pinmux_map assuming that ctrl_dev is set. Without this patch we can get: Unable to handle kernel NULL pointer dereference at virtual address 00000000 ... (pinmux_hog_maps+0xa4/0x20c) (pinctrl_register+0x2a4/0x378) ... Fix this by adding adding a test for map->ctrl_dev. Additionally move the test for map->ctrl_dev earlier to optimize out the loop a bit. Signed-off-by: Tony Lindgren Acked-by: Stephen Warren Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index f4f8c7e..3ffa932 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -978,9 +978,12 @@ int pinmux_hog_maps(struct pinctrl_dev *pctldev) for (i = 0; i < pinmux_maps_num; i++) { struct pinmux_map const *map = &pinmux_maps[i]; - if (((map->ctrl_dev == dev) || - !strcmp(map->ctrl_dev_name, devname)) && - map->hog_on_boot) { + if (!map->hog_on_boot) + continue; + + if ((map->ctrl_dev == dev) || + (map->ctrl_dev_name && + !strcmp(map->ctrl_dev_name, devname))) { /* OK time to hog! */ ret = pinmux_hog_map(pctldev, map); if (ret) -- cgit v0.10.2 From b9130b776ee481acbc27a7e56d98df75680de369 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 24 Jan 2012 16:28:08 -0800 Subject: pinctrl: add checks for empty function names This is needed as otherwise we can get the following when dealing with buggy data in a pinmux driver for pinmux_search_function: Unable to handle kernel NULL pointer dereference at virtual address 00000000 ... PC is at strcmp+0xc/0x34 LR is at pinmux_get+0x350/0x8f4 ... As we need pctldev initialized to call ops->list_functions, let's initialize it before check_ops calls and pass the pctldev to the check_ops functions. Do this for both pinmux and pinconf check_ops functions. Signed-off-by: Tony Lindgren Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index d9d35fc..8fe15cf 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -583,40 +583,40 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, if (pctldesc->name == NULL) return NULL; + pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); + if (pctldev == NULL) + return NULL; + + /* Initialize pin control device struct */ + pctldev->owner = pctldesc->owner; + pctldev->desc = pctldesc; + pctldev->driver_data = driver_data; + INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); + spin_lock_init(&pctldev->pin_desc_tree_lock); + INIT_LIST_HEAD(&pctldev->gpio_ranges); + mutex_init(&pctldev->gpio_ranges_lock); + pctldev->dev = dev; + /* If we're implementing pinmuxing, check the ops for sanity */ if (pctldesc->pmxops) { - ret = pinmux_check_ops(pctldesc->pmxops); + ret = pinmux_check_ops(pctldev); if (ret) { pr_err("%s pinmux ops lacks necessary functions\n", pctldesc->name); - return NULL; + goto out_err; } } /* If we're implementing pinconfig, check the ops for sanity */ if (pctldesc->confops) { - ret = pinconf_check_ops(pctldesc->confops); + ret = pinconf_check_ops(pctldev); if (ret) { pr_err("%s pin config ops lacks necessary functions\n", pctldesc->name); - return NULL; + goto out_err; } } - pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL); - if (pctldev == NULL) - return NULL; - - /* Initialize pin control device struct */ - pctldev->owner = pctldesc->owner; - pctldev->desc = pctldesc; - pctldev->driver_data = driver_data; - INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL); - spin_lock_init(&pctldev->pin_desc_tree_lock); - INIT_LIST_HEAD(&pctldev->gpio_ranges); - mutex_init(&pctldev->gpio_ranges_lock); - pctldev->dev = dev; - /* Register all the pins */ pr_debug("try to register %d pins on %s...\n", pctldesc->npins, pctldesc->name); diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 1892a37..9fb7545 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -205,8 +205,10 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, } EXPORT_SYMBOL(pin_config_group_set); -int pinconf_check_ops(const struct pinconf_ops *ops) +int pinconf_check_ops(struct pinctrl_dev *pctldev) { + const struct pinconf_ops *ops = pctldev->desc->confops; + /* We must be able to read out pin status */ if (!ops->pin_config_get && !ops->pin_config_group_get) return -EINVAL; diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index e7dc616..006b77f 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -13,7 +13,7 @@ #ifdef CONFIG_PINCONF -int pinconf_check_ops(const struct pinconf_ops *ops); +int pinconf_check_ops(struct pinctrl_dev *pctldev); void pinconf_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, @@ -23,7 +23,7 @@ int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, #else -static inline int pinconf_check_ops(const struct pinconf_ops *ops) +static inline int pinconf_check_ops(struct pinctrl_dev *pctldev) { return 0; } diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 3ffa932..7c3193f 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -889,8 +889,11 @@ void pinmux_disable(struct pinmux *pmx) } EXPORT_SYMBOL_GPL(pinmux_disable); -int pinmux_check_ops(const struct pinmux_ops *ops) +int pinmux_check_ops(struct pinctrl_dev *pctldev) { + const struct pinmux_ops *ops = pctldev->desc->pmxops; + unsigned selector = 0; + /* Check that we implement required operations */ if (!ops->list_functions || !ops->get_function_name || @@ -899,6 +902,18 @@ int pinmux_check_ops(const struct pinmux_ops *ops) !ops->disable) return -EINVAL; + /* Check that all functions registered have names */ + while (ops->list_functions(pctldev, selector) >= 0) { + const char *fname = ops->get_function_name(pctldev, + selector); + if (!fname) { + pr_err("pinmux ops has no name for function%u\n", + selector); + return -EINVAL; + } + selector++; + } + return 0; } diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h index 844500b..97f5222 100644 --- a/drivers/pinctrl/pinmux.h +++ b/drivers/pinctrl/pinmux.h @@ -12,7 +12,7 @@ */ #ifdef CONFIG_PINMUX -int pinmux_check_ops(const struct pinmux_ops *ops); +int pinmux_check_ops(struct pinctrl_dev *pctldev); void pinmux_init_device_debugfs(struct dentry *devroot, struct pinctrl_dev *pctldev); void pinmux_init_debugfs(struct dentry *subsys_root); @@ -21,7 +21,7 @@ void pinmux_unhog_maps(struct pinctrl_dev *pctldev); #else -static inline int pinmux_check_ops(const struct pinmux_ops *ops) +static inline int pinmux_check_ops(struct pinctrl_dev *pctldev) { return 0; } -- cgit v0.10.2 From b3a81520bd37a28f77cb0f7002086fb14061824d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 26 Jan 2012 15:56:16 +0100 Subject: ALSA: hda - Fix silent output on Haier W18 laptop The very same problem is seen on Haier W18 laptop with ALC861 as seen on ASUS A6Rp, which was fixed by the commit 3b25eb69. Now we just need to add a new SSID entry pointing to the same fixup. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42656 Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a234799..0db1dc4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5610,6 +5610,7 @@ static const struct alc_fixup alc861_fixups[] = { static const struct snd_pci_quirk alc861_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", PINFIX_ASUS_A6RP), + SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), {} }; -- cgit v0.10.2 From 073862ba5d249c20bd5c49fc6d904ff0e1f6a672 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Jan 2012 00:41:38 +0000 Subject: netns: fix net_alloc_generic() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a new net namespace is created, we should attach to it a "struct net_generic" with enough slots (even empty), or we can hit the following BUG_ON() : [ 200.752016] kernel BUG at include/net/netns/generic.h:40! ... [ 200.752016] [] ? get_cfcnfg+0x3a/0x180 [ 200.752016] [] ? lockdep_rtnl_is_held+0x10/0x20 [ 200.752016] [] caif_device_notify+0x2e/0x530 [ 200.752016] [] notifier_call_chain+0x67/0x110 [ 200.752016] [] raw_notifier_call_chain+0x11/0x20 [ 200.752016] [] call_netdevice_notifiers+0x32/0x60 [ 200.752016] [] register_netdevice+0x196/0x300 [ 200.752016] [] register_netdev+0x19/0x30 [ 200.752016] [] loopback_net_init+0x4a/0xa0 [ 200.752016] [] ops_init+0x42/0x180 [ 200.752016] [] setup_net+0x6b/0x100 [ 200.752016] [] copy_net_ns+0x86/0x110 [ 200.752016] [] create_new_namespaces+0xd9/0x190 net_alloc_generic() should take into account the maximum index into the ptr array, as a subsystem might use net_generic() anytime. This also reduces number of reallocations in net_assign_generic() Reported-by: Sasha Levin Tested-by: Sasha Levin Signed-off-by: Eric Dumazet Cc: Sjur Brændeland Cc: Eric W. Biederman Cc: Pavel Emelyanov Signed-off-by: David S. Miller diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index aefcd7a..0e950fd 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -30,6 +30,20 @@ EXPORT_SYMBOL(init_net); #define INITIAL_NET_GEN_PTRS 13 /* +1 for len +2 for rcu_head */ +static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS; + +static struct net_generic *net_alloc_generic(void) +{ + struct net_generic *ng; + size_t generic_size = offsetof(struct net_generic, ptr[max_gen_ptrs]); + + ng = kzalloc(generic_size, GFP_KERNEL); + if (ng) + ng->len = max_gen_ptrs; + + return ng; +} + static int net_assign_generic(struct net *net, int id, void *data) { struct net_generic *ng, *old_ng; @@ -43,8 +57,7 @@ static int net_assign_generic(struct net *net, int id, void *data) if (old_ng->len >= id) goto assign; - ng = kzalloc(sizeof(struct net_generic) + - id * sizeof(void *), GFP_KERNEL); + ng = net_alloc_generic(); if (ng == NULL) return -ENOMEM; @@ -59,7 +72,6 @@ static int net_assign_generic(struct net *net, int id, void *data) * the old copy for kfree after a grace period. */ - ng->len = id; memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); rcu_assign_pointer(net->gen, ng); @@ -161,18 +173,6 @@ out_undo: goto out; } -static struct net_generic *net_alloc_generic(void) -{ - struct net_generic *ng; - size_t generic_size = sizeof(struct net_generic) + - INITIAL_NET_GEN_PTRS * sizeof(void *); - - ng = kzalloc(generic_size, GFP_KERNEL); - if (ng) - ng->len = INITIAL_NET_GEN_PTRS; - - return ng; -} #ifdef CONFIG_NET_NS static struct kmem_cache *net_cachep; @@ -483,6 +483,7 @@ again: } return error; } + max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id); } error = __register_pernet_operations(list, ops); if (error) { -- cgit v0.10.2 From 590dfe2f3bbbbeee806ee91bef68ba2a6afc16d2 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Wed, 25 Jan 2012 16:51:05 +0100 Subject: agp: fix scratch page cleanup In error cleanup of agp_backend_initialize() and in agp_backend_cleanup(), agp_destroy_page() is passed virtual address of the scratch page. This leads to a kernel warning if the initialization fails (or upon regular cleanup) as pointer to struct page should be passed instead. Signed-off-by: Michal Kubecek Signed-off-by: Dave Airlie diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 4b71647..317c28c 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -194,10 +194,10 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) err_out: if (bridge->driver->needs_scratch_page) { - void *va = page_address(bridge->scratch_page_page); + struct page *page = bridge->scratch_page_page; - bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); - bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); + bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_UNMAP); + bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_FREE); } if (got_gatt) bridge->driver->free_gatt_table(bridge); @@ -221,10 +221,10 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) if (bridge->driver->agp_destroy_page && bridge->driver->needs_scratch_page) { - void *va = page_address(bridge->scratch_page_page); + struct page *page = bridge->scratch_page_page; - bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); - bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); + bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_UNMAP); + bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_FREE); } } -- cgit v0.10.2 From 4ca9b72b71f10147bd21969c1805f5b2c4ca7b7b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 25 Jan 2012 11:50:51 +0100 Subject: sched: Fix rq->nr_uninterruptible update race KOSAKI Motohiro noticed the following race: > CPU0 CPU1 > -------------------------------------------------------- > deactivate_task() > task->state = TASK_UNINTERRUPTIBLE; > activate_task() > rq->nr_uninterruptible--; > > schedule() > deactivate_task() > rq->nr_uninterruptible++; > Kosaki-San's scenario is possible when CPU0 runs __sched_setscheduler() against CPU1's current @task. __sched_setscheduler() does a dequeue/enqueue in order to move the task to its new queue (position) to reflect the newly provided scheduling parameters. However it should be completely invariant to nr_uninterruptible accounting, sched_setscheduler() doesn't affect readyness to run, merely policy on when to run. So convert the inappropriate activate/deactivate_task usage to enqueue/dequeue_task, which avoids the nr_uninterruptible accounting. Also convert the two other sites: __migrate_task() and normalize_task() that still use activate/deactivate_task. These sites aren't really a problem since __migrate_task() will only be called on non-running task (and therefore are immume to the described problem) and normalize_task() isn't ever used on regular systems. Also remove the comments from activate/deactivate_task since they're misleading at best. Reported-by: KOSAKI Motohiro Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1327486224.2614.45.camel@laptop Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index df00cb0..e067df1 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -723,9 +723,6 @@ static void dequeue_task(struct rq *rq, struct task_struct *p, int flags) p->sched_class->dequeue_task(rq, p, flags); } -/* - * activate_task - move a task to the runqueue. - */ void activate_task(struct rq *rq, struct task_struct *p, int flags) { if (task_contributes_to_load(p)) @@ -734,9 +731,6 @@ void activate_task(struct rq *rq, struct task_struct *p, int flags) enqueue_task(rq, p, flags); } -/* - * deactivate_task - remove a task from the runqueue. - */ void deactivate_task(struct rq *rq, struct task_struct *p, int flags) { if (task_contributes_to_load(p)) @@ -4134,7 +4128,7 @@ recheck: on_rq = p->on_rq; running = task_current(rq, p); if (on_rq) - deactivate_task(rq, p, 0); + dequeue_task(rq, p, 0); if (running) p->sched_class->put_prev_task(rq, p); @@ -4147,7 +4141,7 @@ recheck: if (running) p->sched_class->set_curr_task(rq); if (on_rq) - activate_task(rq, p, 0); + enqueue_task(rq, p, 0); check_class_changed(rq, p, prev_class, oldprio); task_rq_unlock(rq, p, &flags); @@ -4998,9 +4992,9 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) * placed properly. */ if (p->on_rq) { - deactivate_task(rq_src, p, 0); + dequeue_task(rq_src, p, 0); set_task_cpu(p, dest_cpu); - activate_task(rq_dest, p, 0); + enqueue_task(rq_dest, p, 0); check_preempt_curr(rq_dest, p, 0); } done: @@ -7032,10 +7026,10 @@ static void normalize_task(struct rq *rq, struct task_struct *p) on_rq = p->on_rq; if (on_rq) - deactivate_task(rq, p, 0); + dequeue_task(rq, p, 0); __setscheduler(rq, p, SCHED_NORMAL, 0); if (on_rq) { - activate_task(rq, p, 0); + enqueue_task(rq, p, 0); resched_task(rq->curr); } -- cgit v0.10.2 From db7e527da41560f597ccdc4417cefa6b7657c0c0 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 11 Jan 2012 08:58:16 +0100 Subject: sched/s390: Fix compile error in sched/core.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 029632fbb7b7c9d85063cc9eb470de6c54873df3 ("sched: Make separate sched*.c translation units") removed the include of asm/mutex.h from sched.c. This breaks the combination of: CONFIG_MUTEX_SPIN_ON_OWNER=yes CONFIG_HAVE_ARCH_MUTEX_CPU_RELAX=yes like s390 without mutex debugging: CC kernel/sched/core.o kernel/sched/core.c: In function ‘mutex_spin_on_owner’: kernel/sched/core.c:3287: error: implicit declaration of function ‘arch_mutex_cpu_relax’ Lets re-add the include to kernel/sched/core.c Signed-off-by: Christian Borntraeger Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1326268696-30904-1-git-send-email-borntraeger@de.ibm.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e067df1..5255c9d 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -74,6 +74,7 @@ #include #include +#include #ifdef CONFIG_PARAVIRT #include #endif -- cgit v0.10.2 From 71325960d16cd68ea0e22a8da15b2495b0f363f7 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 19 Jan 2012 18:28:57 -0800 Subject: sched/nohz: Fix nohz cpu idle load balancing state with cpu hotplug With the recent nohz scheduler changes, rq's nohz flag 'NOHZ_TICK_STOPPED' and its associated state doesn't get cleared immediately after the cpu exits idle. This gets cleared as part of the next tick seen on that cpu. For the cpu offline support, we need to clear this state manually. Fix it by registering a cpu notifier, which clears the nohz idle load balance state for this rq explicitly during the CPU_DYING notification. There won't be any nohz updates for that cpu, after the CPU_DYING notification. But lets be extra paranoid and skip updating the nohz state in the select_nohz_load_balancer() if the cpu is not in active state anymore. Reported-by: Srivatsa S. Bhat Reviewed-and-tested-by: Srivatsa S. Bhat Tested-by: Sergey Senozhatsky Signed-off-by: Suresh Siddha Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1327026538.16150.40.camel@sbsiddha-desk.sc.intel.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 84adb2d..7c6414f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4866,6 +4866,15 @@ static void nohz_balancer_kick(int cpu) return; } +static inline void clear_nohz_tick_stopped(int cpu) +{ + if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { + cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); + atomic_dec(&nohz.nr_cpus); + clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); + } +} + static inline void set_cpu_sd_state_busy(void) { struct sched_domain *sd; @@ -4904,6 +4913,12 @@ void select_nohz_load_balancer(int stop_tick) { int cpu = smp_processor_id(); + /* + * If this cpu is going down, then nothing needs to be done. + */ + if (!cpu_active(cpu)) + return; + if (stop_tick) { if (test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu))) return; @@ -4914,6 +4929,18 @@ void select_nohz_load_balancer(int stop_tick) } return; } + +static int __cpuinit sched_ilb_notifier(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_DYING: + clear_nohz_tick_stopped(smp_processor_id()); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} #endif static DEFINE_SPINLOCK(balancing); @@ -5070,11 +5097,7 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu) * busy tick after returning from idle, we will update the busy stats. */ set_cpu_sd_state_busy(); - if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { - clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); - cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); - atomic_dec(&nohz.nr_cpus); - } + clear_nohz_tick_stopped(cpu); /* * None are in tickless mode and hence no need for NOHZ idle load @@ -5590,6 +5613,7 @@ __init void init_sched_fair_class(void) #ifdef CONFIG_NO_HZ zalloc_cpumask_var(&nohz.idle_cpus_mask, GFP_NOWAIT); + cpu_notifier(sched_ilb_notifier, 0); #endif #endif /* SMP */ -- cgit v0.10.2 From 40206dd98f066d596d4280558fc5f798165861c7 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 26 Jan 2012 07:23:23 +0000 Subject: xen-netfront: correct MAX_TX_TARGET calculation. Signed-off-by: Wei Liu Signed-off-by: David S. Miller diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index fa67905..698b905 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -68,7 +68,7 @@ struct netfront_cb { #define NET_TX_RING_SIZE __CONST_RING_SIZE(xen_netif_tx, PAGE_SIZE) #define NET_RX_RING_SIZE __CONST_RING_SIZE(xen_netif_rx, PAGE_SIZE) -#define TX_MAX_TARGET min_t(int, NET_RX_RING_SIZE, 256) +#define TX_MAX_TARGET min_t(int, NET_TX_RING_SIZE, 256) struct netfront_stats { u64 rx_packets; -- cgit v0.10.2 From 77231abe55433aa17eca712718745275853fa66d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Jan 2012 12:19:43 +0000 Subject: ASoC: wm_hubs: Enable line out VMID buffer for single ended line outputs For optimal performance the single ended line outputs require that the line output VMID buffer be enabled. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2a61094..9ccc416 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -613,6 +613,8 @@ SND_SOC_DAPM_INPUT("IN2RP:VXRP"), SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("LINEOUT_VMID_BUF", WM8993_ANTIPOP1, 7, 0, NULL, 0), + SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0, in1l_pga, ARRAY_SIZE(in1l_pga)), SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0, @@ -834,9 +836,11 @@ static const struct snd_soc_dapm_route lineout1_diff_routes[] = { }; static const struct snd_soc_dapm_route lineout1_se_routes[] = { + { "LINEOUT1N Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" }, + { "LINEOUT1P Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, @@ -853,9 +857,11 @@ static const struct snd_soc_dapm_route lineout2_diff_routes[] = { }; static const struct snd_soc_dapm_route lineout2_se_routes[] = { + { "LINEOUT2N Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" }, + { "LINEOUT2P Mixer", NULL, "LINEOUT_VMID_BUF" }, { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, -- cgit v0.10.2 From af681cad3f79ad8f7bd6cb170b70990aeef74233 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 26 Jan 2012 11:14:42 -0800 Subject: Revert "tty: serial: OMAP: transmit FIFO threshold interrupts don't wake the chip" This reverts commit 43cf7c0bebf50d0b68aa42ae6d24cf08e3f24823 as Paul wants to redo it. Cc: Paul Walmsley Cc: Tomi Valkeinen Cc: Govindraj Raja Cc: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h index 12a64eb..9ff4444 100644 --- a/arch/arm/plat-omap/include/plat/omap-serial.h +++ b/arch/arm/plat-omap/include/plat/omap-serial.h @@ -131,7 +131,6 @@ struct uart_omap_port { u32 context_loss_cnt; u32 errata; u8 wakeups_enabled; - u8 max_tx_count; struct pm_qos_request pm_qos_request; u32 latency; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index e00ac05..ca54f03 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -88,49 +88,6 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) serial_out(up, UART_FCR, 0); } -/** - * serial_omap_block_cpu_low_power_state - prevent MPU pwrdm from leaving ON - * @up: struct uart_omap_port * - * - * Prevent the MPU powerdomain from entering a power state lower than - * ON. (It should be sufficient to prevent it from entering INACTIVE, - * but there is presently no easy way to do this.) This works around - * a suspected silicon bug in the OMAP UART IP blocks. The UARTs should - * wake the PRCM when the transmit FIFO threshold interrupt is raised, but - * they do not. See also serial_omap_allow_cpu_low_power_state(). No - * return value. - */ -static void serial_omap_block_cpu_low_power_state(struct uart_omap_port *up) -{ -#ifdef CONFIG_CPU_IDLE - up->latency = 1; - schedule_work(&up->qos_work); -#else - up->max_tx_count = 1; -#endif -} - -/** - * serial_omap_allow_cpu_low_power_state - remove power state restriction on MPU - * @up: struct uart_omap_port * - * - * Cancel the effects of serial_omap_block_cpu_low_power_state(). - * This should allow the MPU powerdomain to enter a power state lower - * than ON, assuming the rest of the kernel is not restricting it. - * This works around a suspected silicon bug in the OMAP UART IP - * blocks. The UARTs should wake the PRCM when the transmit FIFO - * threshold interrupt is raised, but they do not. No return value. - */ -static void serial_omap_allow_cpu_low_power_state(struct uart_omap_port *up) -{ -#ifdef CONFIG_CPU_IDLE - up->latency = up->calc_latency; - schedule_work(&up->qos_work); -#else - up->max_tx_count = up->port.fifosize / 4; -#endif -} - /* * serial_omap_get_divisor - calculate divisor value * @port: uart port info @@ -206,9 +163,6 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } - if (!up->use_dma) - serial_omap_allow_cpu_low_power_state(up); - pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); } @@ -310,7 +264,7 @@ static void transmit_chars(struct uart_omap_port *up) serial_omap_stop_tx(&up->port); return; } - count = up->max_tx_count; + count = up->port.fifosize / 4; do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -343,7 +297,6 @@ static void serial_omap_start_tx(struct uart_port *port) if (!up->use_dma) { pm_runtime_get_sync(&up->pdev->dev); - serial_omap_block_cpu_low_power_state(up); serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); @@ -1468,8 +1421,6 @@ static int serial_omap_probe(struct platform_device *pdev) up->port.fifosize = 64; up->port.ops = &serial_omap_pops; - up->max_tx_count = up->port.fifosize / 4; - if (pdev->dev.of_node) up->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); else -- cgit v0.10.2 From 8a74e9ffd97dc9de063de8c02ae32db79dd60436 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 26 Jan 2012 11:15:18 -0800 Subject: Revert "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA mode" This reverts commit 0a697b22252c9d7208b5fb3e9fbd124dd229f1d2 as Paul wants to rework it. Cc: Paul Walmsley Cc: Tomi Valkeinen Cc: Govindraj Raja Cc: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index ca54f03..1c24269 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -46,18 +46,6 @@ #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ -/* SCR register bitmasks */ -#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) -#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6) - -/* FCR register bitmasks */ -#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 -#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) -#define OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT 4 - -/* TLR register bitmasks */ -#define OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT 0 - static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ @@ -706,7 +694,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, unsigned char efr = 0; unsigned long flags = 0; unsigned int baud, quot; - u32 tlr; switch (termios->c_cflag & CSIZE) { case CS5: @@ -824,28 +811,14 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->mcr = serial_in(up, UART_MCR); serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); /* FIFO ENABLE, DMA MODE */ - - up->scr |= OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; - up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); if (up->use_dma) { - tlr = 0; - } else { - up->scr &= ~OMAP_UART_SCR_TX_EMPTY; - - /* Set receive FIFO threshold to 1 */ - up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; - up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT); - - /* Set TX FIFO threshold to "63" (actually 1) */ - up->fcr |= (0x3 << OMAP_UART_FCR_TX_FIFO_TRIG_SHIFT); - tlr = (0xf << OMAP_UART_TLR_TX_FIFO_TRIG_DMA_SHIFT); + serial_out(up, UART_TI752_TLR, 0); + up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8); } - serial_out(up, UART_TI752_TLR, tlr); - serial_out(up, UART_FCR, up->fcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_OMAP_SCR, up->scr); serial_out(up, UART_EFR, up->efr); -- cgit v0.10.2 From 523b82e3734908fc9eff5d48de46c83e76e51641 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 26 Jan 2012 17:45:39 +0000 Subject: serial: Kill off Moorestown code All production devices operate in the Oaktrail configuration with legacy PC elements present and an ACPI BIOS. Continue stripping out the Moorestown elements from the tree leaving Medfield. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0bff238..2de9924 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -264,15 +264,6 @@ config SERIAL_MAX3107 help MAX3107 chip support -config SERIAL_MAX3107_AAVA - tristate "MAX3107 AAVA platform support" - depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB - select SERIAL_CORE - help - Support for the MAX3107 chip configuration found on the AAVA - platform. Includes the extra initialisation and GPIO support - neded for this device. - config SERIAL_DZ bool "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index a6d1ac0..fef32e1 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_MAX3107) += max3107.o -obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o diff --git a/drivers/tty/serial/max3107-aava.c b/drivers/tty/serial/max3107-aava.c deleted file mode 100644 index aae772a..0000000 --- a/drivers/tty/serial/max3107-aava.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * max3107.c - spi uart protocol driver for Maxim 3107 - * Based on max3100.c - * by Christian Pellegrin - * and max3110.c - * by Feng Tang - * - * Copyright (C) Aavamobile 2009 - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "max3107.h" - -/* GPIO direction to input function */ -static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[1]; /* Buffer for SPI transfer */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return -EINVAL; - } - - /* Read current GPIO configuration register */ - buf[0] = MAX3107_GPIOCFG_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n"); - return -EIO; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - - /* Set GPIO to input */ - buf[0] &= ~(0x0001 << offset); - - /* Write new GPIO configuration register value */ - buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 2)) { - dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n"); - return -EIO; - } - return 0; -} - -/* GPIO direction to output function */ -static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[2]; /* Buffer for SPI transfers */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return -EINVAL; - } - - /* Read current GPIO configuration and data registers */ - buf[0] = MAX3107_GPIOCFG_REG; - buf[1] = MAX3107_GPIODATA_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { - dev_err(&s->spi->dev, "SPI transfer gpio failed\n"); - return -EIO; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - buf[1] &= MAX3107_SPI_RX_DATA_MASK; - - /* Set GPIO to output */ - buf[0] |= (0x0001 << offset); - /* Set value */ - if (value) - buf[1] |= (0x0001 << offset); - else - buf[1] &= ~(0x0001 << offset); - - /* Write new GPIO configuration and data register values */ - buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); - buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 4)) { - dev_err(&s->spi->dev, - "SPI transfer for GPIO conf data w failed\n"); - return -EIO; - } - return 0; -} - -/* GPIO value query function */ -static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[1]; /* Buffer for SPI transfer */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return -EINVAL; - } - - /* Read current GPIO data register */ - buf[0] = MAX3107_GPIODATA_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n"); - return -EIO; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - - /* Return value */ - return buf[0] & (0x0001 << offset); -} - -/* GPIO value set function */ -static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[2]; /* Buffer for SPI transfers */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return; - } - - /* Read current GPIO configuration registers*/ - buf[0] = MAX3107_GPIODATA_REG; - buf[1] = MAX3107_GPIOCFG_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { - dev_err(&s->spi->dev, - "SPI transfer for GPIO data and config read failed\n"); - return; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - buf[1] &= MAX3107_SPI_RX_DATA_MASK; - - if (!(buf[1] & (0x0001 << offset))) { - /* Configured as input, can't set value */ - dev_warn(&s->spi->dev, - "Trying to set value for input GPIO\n"); - return; - } - - /* Set value */ - if (value) - buf[0] |= (0x0001 << offset); - else - buf[0] &= ~(0x0001 << offset); - - /* Write new GPIO data register value */ - buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n"); -} - -/* GPIO chip data */ -static struct gpio_chip max3107_gpio_chip = { - .owner = THIS_MODULE, - .direction_input = max3107_gpio_direction_in, - .direction_output = max3107_gpio_direction_out, - .get = max3107_gpio_get, - .set = max3107_gpio_set, - .can_sleep = 1, - .base = MAX3107_GPIO_BASE, - .ngpio = MAX3107_GPIO_COUNT, -}; - -/** - * max3107_aava_reset - reset on AAVA systems - * @spi: The SPI device we are probing - * - * Reset the device ready for probing. - */ - -static int max3107_aava_reset(struct spi_device *spi) -{ - /* Reset the chip */ - if (gpio_request(MAX3107_RESET_GPIO, "max3107")) { - pr_err("Requesting RESET GPIO failed\n"); - return -EIO; - } - if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) { - pr_err("Setting RESET GPIO to 0 failed\n"); - gpio_free(MAX3107_RESET_GPIO); - return -EIO; - } - msleep(MAX3107_RESET_DELAY); - if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) { - pr_err("Setting RESET GPIO to 1 failed\n"); - gpio_free(MAX3107_RESET_GPIO); - return -EIO; - } - gpio_free(MAX3107_RESET_GPIO); - msleep(MAX3107_WAKEUP_DELAY); - return 0; -} - -static int max3107_aava_configure(struct max3107_port *s) -{ - int retval; - - /* Initialize GPIO chip data */ - s->chip = max3107_gpio_chip; - s->chip.label = s->spi->modalias; - s->chip.dev = &s->spi->dev; - - /* Add GPIO chip */ - retval = gpiochip_add(&s->chip); - if (retval) { - dev_err(&s->spi->dev, "Adding GPIO chip failed\n"); - return retval; - } - - /* Temporary fix for EV2 boot problems, set modem reset to 0 */ - max3107_gpio_direction_out(&s->chip, 3, 0); - return 0; -} - -#if 0 -/* This will get enabled once we have the board stuff merged for this - specific case */ - -static const struct baud_table brg13_ext[] = { - { 300, MAX3107_BRG13_B300 }, - { 600, MAX3107_BRG13_B600 }, - { 1200, MAX3107_BRG13_B1200 }, - { 2400, MAX3107_BRG13_B2400 }, - { 4800, MAX3107_BRG13_B4800 }, - { 9600, MAX3107_BRG13_B9600 }, - { 19200, MAX3107_BRG13_B19200 }, - { 57600, MAX3107_BRG13_B57600 }, - { 115200, MAX3107_BRG13_B115200 }, - { 230400, MAX3107_BRG13_B230400 }, - { 460800, MAX3107_BRG13_B460800 }, - { 921600, MAX3107_BRG13_B921600 }, - { 0, 0 } -}; - -static void max3107_aava_init(struct max3107_port *s) -{ - /*override for AAVA SC specific*/ - if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) { - if (get_koski_build_id() <= KOSKI_EV2) - if (s->ext_clk) { - s->brg_cfg = MAX3107_BRG13_B9600; - s->baud_tbl = (struct baud_table *)brg13_ext; - } - } -} -#endif - -static int __devexit max3107_aava_remove(struct spi_device *spi) -{ - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - /* Remove GPIO chip */ - if (gpiochip_remove(&s->chip)) - dev_warn(&spi->dev, "Removing GPIO chip failed\n"); - - /* Then do the default remove */ - return max3107_remove(spi); -} - -/* Platform data */ -static struct max3107_plat aava_plat_data = { - .loopback = 0, - .ext_clk = 1, -/* .init = max3107_aava_init, */ - .configure = max3107_aava_configure, - .hw_suspend = max3107_hw_susp, - .polled_mode = 0, - .poll_time = 0, -}; - - -static int __devinit max3107_probe_aava(struct spi_device *spi) -{ - int err = max3107_aava_reset(spi); - if (err < 0) - return err; - return max3107_probe(spi, &aava_plat_data); -} - -/* Spi driver data */ -static struct spi_driver max3107_driver = { - .driver = { - .name = "aava-max3107", - .owner = THIS_MODULE, - }, - .probe = max3107_probe_aava, - .remove = __devexit_p(max3107_aava_remove), - .suspend = max3107_suspend, - .resume = max3107_resume, -}; - -/* Driver init function */ -static int __init max3107_init(void) -{ - return spi_register_driver(&max3107_driver); -} - -/* Driver exit function */ -static void __exit max3107_exit(void) -{ - spi_unregister_driver(&max3107_driver); -} - -module_init(max3107_init); -module_exit(max3107_exit); - -MODULE_DESCRIPTION("MAX3107 driver"); -MODULE_AUTHOR("Aavamobile"); -MODULE_ALIAS("spi:aava-max3107"); -MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 2353f806c97020d4c7709f15eebb49b591f7306d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 26 Jan 2012 17:41:34 +0000 Subject: USB: ftdi_sio: Add more identifiers 0x04d8, 0x000a: Hornby Elite Signed-off-by: Alan Cox Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 104aff5..ad654f8 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -797,6 +797,7 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(HORNBY_VID, HORNBY_ELITE_PID) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 09237ff..f994503 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -532,6 +532,12 @@ #define ADI_GNICEPLUS_PID 0xF001 /* + * Hornby Elite + */ +#define HORNBY_VID 0x04D8 +#define HORNBY_ELITE_PID 0x000A + +/* * RATOC REX-USB60F */ #define RATOC_VENDOR_ID 0x0584 -- cgit v0.10.2 From b3ef051db763b640d1ff724b616ffba940896b44 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 26 Jan 2012 12:29:42 +0100 Subject: USB: Realtek cr: fix autopm scheduling while atomic Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=784345 Reported-by: Francis Moreau Reported-and-tested-by: Christian D Reported-and-tested-by: Jimmy Dorff Reported-and-tested-by: collura@ieee.org Cc: stable@vger.kernel.org # 3.2+ Signed-off-by: Stanislaw Gruszka Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 1f62723..d32f720 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -789,7 +789,7 @@ static void rts51x_suspend_timer_fn(unsigned long data) rts51x_set_stat(chip, RTS51X_STAT_SS); /* ignore mass storage interface's children */ pm_suspend_ignore_children(&us->pusb_intf->dev, true); - usb_autopm_put_interface(us->pusb_intf); + usb_autopm_put_interface_async(us->pusb_intf); US_DEBUGP("%s: RTS51X_STAT_SS 01," "intf->pm_usage_cnt:%d, power.usage:%d\n", __func__, -- cgit v0.10.2 From a0701f04846eee9976e6b3eafca09f2a9d2744ef Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 25 Jan 2012 13:52:12 -0800 Subject: uwb & wusb: fix kconfig error Fix UWB/WUSB kconfig error by changing 'select' to 'depends on'. drivers/usb/wusbcore/Kconfig:4:error: recursive dependency detected! drivers/usb/wusbcore/Kconfig:4: symbol USB_WUSB is selected by USB_HWA_HCD drivers/usb/host/Kconfig:559: symbol USB_HWA_HCD depends on UWB drivers/uwb/Kconfig:5: symbol UWB is selected by USB_WUSB Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig index 0ead882..f29fdd7 100644 --- a/drivers/usb/wusbcore/Kconfig +++ b/drivers/usb/wusbcore/Kconfig @@ -6,7 +6,7 @@ config USB_WUSB depends on EXPERIMENTAL depends on USB depends on PCI - select UWB + depends on UWB select CRYPTO select CRYPTO_BLKCIPHER select CRYPTO_CBC -- cgit v0.10.2 From b1375d64c539c5b76794be759b62d3f178e67c32 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 26 Jan 2012 15:01:11 -0500 Subject: Btrfs: fix uninit warning in backref.c Added initialization with the declaration of ret. It isn't set later on the switch-default branch (which should never be taken). Signed-off-by: Jan Schmidt Signed-off-by: Chris Mason diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index b9a8432..633c701 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -297,7 +297,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq, struct btrfs_delayed_extent_op *extent_op = head->extent_op; struct rb_node *n = &head->node.rb_node; int sgn; - int ret; + int ret = 0; if (extent_op && extent_op->update_key) btrfs_disk_key_to_cpu(info_key, &extent_op->key); @@ -392,7 +392,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info, struct btrfs_key *info_key, int *info_level, struct list_head *prefs) { - int ret; + int ret = 0; int slot; struct extent_buffer *leaf; struct btrfs_key key; -- cgit v0.10.2 From 357b9784b79924a31ccded5d9a0c688f48cc28f2 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 26 Jan 2012 15:01:11 -0500 Subject: Btrfs: make sure a bitmap has enough bytes We have only been checking for min_bytes available in bitmap entries, but we won't successfully setup a bitmap cluster unless it has at least bytes in the bitmap, so in the common case min_bytes is 4k and we want something like 2MB, so if there are a bunch of bitmap entries with less than 2mb's in them, we'll search all them anyway, which is suboptimal. Fix this check. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index efe2003..6e74069 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2475,7 +2475,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group, } list_for_each_entry(entry, bitmaps, list) { - if (entry->bytes < min_bytes) + if (entry->bytes < bytes) continue; ret = btrfs_bitmap_cluster(block_group, entry, cluster, offset, bytes, cont1_bytes, min_bytes); -- cgit v0.10.2 From 6dd70ce4eb7429c2ba6dd9fa46f78a0a2a254038 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Jan 2012 15:01:11 -0500 Subject: btrfs: Fix busyloops in transaction waiting code wait_log_commit() and wait_for_writer() were using slightly different conditions for deciding whether they should call schedule() and whether they should continue in the wait loop. Thus it could happen that we busylooped when the first condition was not true while the second one was. That is burning CPU cycles needlessly and is deadly on UP machines... Signed-off-by: Jan Kara Signed-off-by: Chris Mason diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index cb877e0..966cc74 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1957,7 +1957,8 @@ static int wait_log_commit(struct btrfs_trans_handle *trans, finish_wait(&root->log_commit_wait[index], &wait); mutex_lock(&root->log_mutex); - } while (root->log_transid < transid + 2 && + } while (root->fs_info->last_trans_log_full_commit != + trans->transid && root->log_transid < transid + 2 && atomic_read(&root->log_commit[index])); return 0; } @@ -1966,7 +1967,8 @@ static int wait_for_writer(struct btrfs_trans_handle *trans, struct btrfs_root *root) { DEFINE_WAIT(wait); - while (atomic_read(&root->log_writers)) { + while (root->fs_info->last_trans_log_full_commit != + trans->transid && atomic_read(&root->log_writers)) { prepare_to_wait(&root->log_writer_wait, &wait, TASK_UNINTERRUPTIBLE); mutex_unlock(&root->log_mutex); -- cgit v0.10.2 From 8bedd51b6121c4607784d75f852828d25d119c52 Mon Sep 17 00:00:00 2001 From: Mitch Harder Date: Thu, 26 Jan 2012 15:01:11 -0500 Subject: Btrfs: Check for NULL page in extent_range_uptodate A user has encountered a NULL pointer kernel oops in btrfs when encountering media errors. The problem has been identified as an unhandled NULL pointer returned from find_get_page(). This modification simply checks for a NULL page, and returns with an error if found (the extent_range_uptodate() function returns 1 on errors). After testing this patch, the user reported that the error with the NULL pointer oops was solved. However, there is still a remaining problem with a thread becoming stuck in wait_on_page_locked(page) in the read_extent_buffer_pages(...) function in extent_io.c for (i = start_i; i < num_pages; i++) { page = extent_buffer_page(eb, i); wait_on_page_locked(page); if (!PageUptodate(page)) ret = -EIO; } This patch leaves the issue with the locked page yet to be resolved. Signed-off-by: Mitch Harder Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 9d09a4f..fcf77e1 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3909,6 +3909,8 @@ int extent_range_uptodate(struct extent_io_tree *tree, while (start <= end) { index = start >> PAGE_CACHE_SHIFT; page = find_get_page(tree->mapping, index); + if (!page) + return 1; uptodate = PageUptodate(page); page_cache_release(page); if (!uptodate) { -- cgit v0.10.2 From 0b4a9d248f88e6773312f262e8185f23863d984a Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 26 Jan 2012 15:01:11 -0500 Subject: Btrfs: use cluster->window_start when allocating from a cluster bitmap We specifically set window_start in the cluster struct to indicate where the cluster starts in a bitmap, but we've been using min_start to indicate where we're searching from. This is usually the start of the blockgroup, so essentially means we're constantly searching from the start of any bitmap we find, which completely negates all the trouble we go to in order to setup a cluster. So start using window_start to make sure we actually use the area we found. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 6e74069..61447a5 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2242,7 +2242,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, if (entry->bitmap) { ret = btrfs_alloc_from_bitmap(block_group, cluster, entry, bytes, - min_start); + cluster->window_start); if (ret == 0) { node = rb_next(&entry->offset_index); if (!node) -- cgit v0.10.2 From 0b485143d835c019cddc45f46e4b3873dcc9aa4e Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Thu, 26 Jan 2012 15:01:11 -0500 Subject: Btrfs: fix warning for 32-bit build of fs/btrfs/check-integrity.c There have been 4 warnings on 32-bit build, they are herewith fixed. Signed-off-by: Stefan Behrens Signed-off-by: Chris Mason diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index ad0b3ba..b669a7d 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c @@ -1662,7 +1662,7 @@ static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state, block = btrfsic_block_hashtable_lookup(bdev, dev_bytenr, &state->block_hashtable); if (NULL != block) { - u64 bytenr; + u64 bytenr = 0; struct list_head *elem_ref_to; struct list_head *tmp_ref_to; @@ -2777,9 +2777,10 @@ int btrfsic_submit_bh(int rw, struct buffer_head *bh) printk(KERN_INFO "submit_bh(rw=0x%x, blocknr=%lu (bytenr %llu)," " size=%lu, data=%p, bdev=%p)\n", - rw, bh->b_blocknr, - (unsigned long long)dev_bytenr, bh->b_size, - bh->b_data, bh->b_bdev); + rw, (unsigned long)bh->b_blocknr, + (unsigned long long)dev_bytenr, + (unsigned long)bh->b_size, bh->b_data, + bh->b_bdev); btrfsic_process_written_block(dev_state, dev_bytenr, bh->b_data, bh->b_size, NULL, NULL, bh, rw); @@ -2844,7 +2845,7 @@ void btrfsic_submit_bio(int rw, struct bio *bio) printk(KERN_INFO "submit_bio(rw=0x%x, bi_vcnt=%u," " bi_sector=%lu (bytenr %llu), bi_bdev=%p)\n", - rw, bio->bi_vcnt, bio->bi_sector, + rw, bio->bi_vcnt, (unsigned long)bio->bi_sector, (unsigned long long)dev_bytenr, bio->bi_bdev); -- cgit v0.10.2 From 7ec31b548a17f773ab6289e795ed3a6820e8b56e Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Thu, 26 Jan 2012 15:01:12 -0500 Subject: Btrfs: do not defrag a file partially xfstests 218 complains that btrfs defrags a file partially: After: 1 Write backwards sync, but contiguous - should defrag to 1 extent Before: 10 -After: 1 +After: 2 To fix this, we need to set max_to_defrag count properly. Signed-off-by: Liu Bo Signed-off-by: Chris Mason diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 6834be4..0b06a5c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1066,7 +1066,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, i = range->start >> PAGE_CACHE_SHIFT; } if (!max_to_defrag) - max_to_defrag = last_index; + max_to_defrag = last_index + 1; /* * make writeback starts from i, so the defrag range can be -- cgit v0.10.2 From 9e622d6bea0202e9fe267955362c01918562c09b Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 26 Jan 2012 15:01:12 -0500 Subject: Btrfs: fix enospc error caused by wrong checks of the chunk When we did sysbench test for inline files, enospc error happened easily though there was lots of free disk space which could be allocated for new chunks. Reproduce steps: # mkfs.btrfs -b $((2 * 1024 * 1024 * 1024)) # mount /mnt # ulimit -n 102400 # cd /mnt # sysbench --num-threads=1 --test=fileio --file-num=81920 \ > --file-total-size=80M --file-block-size=1K --file-io-mode=sync \ > --file-test-mode=seqwr prepare # sysbench --num-threads=1 --test=fileio --file-num=81920 \ > --file-total-size=80M --file-block-size=1K --file-io-mode=sync \ > --file-test-mode=seqwr run The reason of this bug is: Now, we can reserve space which is larger than the free space in the chunks if we have enough free disk space which can be used for new chunks. By this way, the space allocator should allocate a new chunk by force if there is no free space in the free space cache. But there are two wrong checks which break this operation. One is if (ret == -ENOSPC && num_bytes > min_alloc_size) in btrfs_reserve_extent(), it is wrong, we should try to allocate a new chunk even we fail to allocate free space by minimum allocable size. The other is if (space_info->force_alloc) force = space_info->force_alloc; in do_chunk_alloc(). It makes the allocator ignore CHUNK_ALLOC_FORCE If someone sets ->force_alloc to CHUNK_ALLOC_LIMITED, and makes the enospc error happen. Fix these two wrong checks. Especially the second one, we fix it by changing the value of CHUNK_ALLOC_LIMITED and CHUNK_ALLOC_FORCE, and make CHUNK_ALLOC_FORCE greater than CHUNK_ALLOC_LIMITED since CHUNK_ALLOC_FORCE has higher priority. And if the value which is passed in by the caller is greater than ->force_alloc, use the passed value. Signed-off-by: Miao Xie Signed-off-by: Chris Mason diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 700879e..283af7a 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -34,23 +34,24 @@ #include "locking.h" #include "free-space-cache.h" -/* control flags for do_chunk_alloc's force field +/* + * control flags for do_chunk_alloc's force field * CHUNK_ALLOC_NO_FORCE means to only allocate a chunk * if we really need one. * - * CHUNK_ALLOC_FORCE means it must try to allocate one - * * CHUNK_ALLOC_LIMITED means to only try and allocate one * if we have very few chunks already allocated. This is * used as part of the clustering code to help make sure * we have a good pool of storage to cluster in, without * filling the FS with empty chunks * + * CHUNK_ALLOC_FORCE means it must try to allocate one + * */ enum { CHUNK_ALLOC_NO_FORCE = 0, - CHUNK_ALLOC_FORCE = 1, - CHUNK_ALLOC_LIMITED = 2, + CHUNK_ALLOC_LIMITED = 1, + CHUNK_ALLOC_FORCE = 2, }; /* @@ -3414,7 +3415,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, again: spin_lock(&space_info->lock); - if (space_info->force_alloc) + if (force < space_info->force_alloc) force = space_info->force_alloc; if (space_info->full) { spin_unlock(&space_info->lock); @@ -5794,6 +5795,7 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, u64 search_end, struct btrfs_key *ins, u64 data) { + bool final_tried = false; int ret; u64 search_start = 0; @@ -5813,22 +5815,25 @@ again: search_start, search_end, hint_byte, ins, data); - if (ret == -ENOSPC && num_bytes > min_alloc_size) { - num_bytes = num_bytes >> 1; - num_bytes = num_bytes & ~(root->sectorsize - 1); - num_bytes = max(num_bytes, min_alloc_size); - do_chunk_alloc(trans, root->fs_info->extent_root, - num_bytes, data, CHUNK_ALLOC_FORCE); - goto again; - } - if (ret == -ENOSPC && btrfs_test_opt(root, ENOSPC_DEBUG)) { - struct btrfs_space_info *sinfo; - - sinfo = __find_space_info(root->fs_info, data); - printk(KERN_ERR "btrfs allocation failed flags %llu, " - "wanted %llu\n", (unsigned long long)data, - (unsigned long long)num_bytes); - dump_space_info(sinfo, num_bytes, 1); + if (ret == -ENOSPC) { + if (!final_tried) { + num_bytes = num_bytes >> 1; + num_bytes = num_bytes & ~(root->sectorsize - 1); + num_bytes = max(num_bytes, min_alloc_size); + do_chunk_alloc(trans, root->fs_info->extent_root, + num_bytes, data, CHUNK_ALLOC_FORCE); + if (num_bytes == min_alloc_size) + final_tried = true; + goto again; + } else if (btrfs_test_opt(root, ENOSPC_DEBUG)) { + struct btrfs_space_info *sinfo; + + sinfo = __find_space_info(root->fs_info, data); + printk(KERN_ERR "btrfs allocation failed flags %llu, " + "wanted %llu\n", (unsigned long long)data, + (unsigned long long)num_bytes); + dump_space_info(sinfo, num_bytes, 1); + } } trace_btrfs_reserved_extent_alloc(root, ins->objectid, ins->offset); -- cgit v0.10.2 From 0c4e538bccc106872d31b1514570b4dac95fb7f2 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 26 Jan 2012 15:01:12 -0500 Subject: btrfs: mask out gfp flags in releasepage btree_releasepage is a callback and can be passed unknown gfp flags and then they may end up in kmem_cache_alloc called from alloc_extent_state, slab allocator will BUG_ON when there is HIGHMEM or DMA32 flag set. This may happen when btrfs is mounted from a loop device, which masks out __GFP_IO flag. The check in try_release_extent_state 3399 if ((mask & GFP_NOFS) == GFP_NOFS) 3400 mask = GFP_NOFS; will not work and passes unfiltered flags further resulting in crash at mm/slab.c:2963 [<000000000024ae4c>] cache_alloc_refill+0x3b4/0x5c8 [<000000000024c810>] kmem_cache_alloc+0x204/0x294 [<00000000001fd3c2>] mempool_alloc+0x52/0x170 [<000003c000ced0b0>] alloc_extent_state+0x40/0xd4 [btrfs] [<000003c000cee5ae>] __clear_extent_bit+0x38a/0x4cc [btrfs] [<000003c000cee78c>] try_release_extent_state+0x9c/0xd4 [btrfs] [<000003c000cc4c66>] btree_releasepage+0x7e/0xd0 [btrfs] [<0000000000210d84>] shrink_page_list+0x6a0/0x724 [<0000000000211394>] shrink_inactive_list+0x230/0x578 [<0000000000211bb8>] shrink_list+0x6c/0x120 [<0000000000211e4e>] shrink_zone+0x1e2/0x228 [<0000000000211f24>] shrink_zones+0x90/0x254 [<0000000000213410>] do_try_to_free_pages+0xac/0x420 [<0000000000213ae0>] try_to_free_pages+0x13c/0x1b0 [<0000000000204e6c>] __alloc_pages_nodemask+0x5b4/0x9a8 [<00000000001fb04a>] grab_cache_page_write_begin+0x7e/0xe8 Signed-off-by: David Sterba Signed-off-by: Chris Mason diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index da4457f..4c86711 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -961,6 +961,13 @@ static int btree_releasepage(struct page *page, gfp_t gfp_flags) tree = &BTRFS_I(page->mapping->host)->io_tree; map = &BTRFS_I(page->mapping->host)->extent_tree; + /* + * We need to mask out eg. __GFP_HIGHMEM and __GFP_DMA32 as we're doing + * slab allocation from alloc_extent_state down the callchain where + * it'd hit a BUG_ON as those flags are not allowed. + */ + gfp_flags &= ~GFP_SLAB_BUG_MASK; + ret = try_release_extent_state(map, tree, page, gfp_flags); if (!ret) return 0; -- cgit v0.10.2 From 9b23062840e7c685ef0a0b561285d6e3a3b6811b Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 26 Jan 2012 15:01:12 -0500 Subject: Btrfs: advance window_start if we're using a bitmap If we span a long area in a bitmap we could end up taking a lot of time searching to the next free area if we're searching from the original window_start, so advance window_start in order to make sure we don't do any superficial searching. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 61447a5..5802b147 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -2251,6 +2251,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, offset_index); continue; } + cluster->window_start += bytes; } else { ret = entry->offset; -- cgit v0.10.2 From fc395b9291925b1880e0afc61274fe2f6ddc1269 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 26 Jan 2012 15:47:37 +0000 Subject: x86: Properly parenthesize cmpxchg() macro arguments Quite oddly, all of the arguments passed through from the top level macros to the second level which didn't need parentheses had them, while the only expression (involving a parameter) needing them didn't. Very recently I got bitten by the lack thereof when using something like "array + index" for the first operand, with "array" being an array more narrow than int. Signed-off-by: Jan Beulich Cc: Linus Torvalds Link: http://lkml.kernel.org/r/4F2183A9020000780006F3E6@nat28.tlf.novell.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h index 0c9fa27..b3b7332 100644 --- a/arch/x86/include/asm/cmpxchg.h +++ b/arch/x86/include/asm/cmpxchg.h @@ -145,13 +145,13 @@ extern void __add_wrong_size(void) #ifdef __HAVE_ARCH_CMPXCHG #define cmpxchg(ptr, old, new) \ - __cmpxchg((ptr), (old), (new), sizeof(*ptr)) + __cmpxchg(ptr, old, new, sizeof(*(ptr))) #define sync_cmpxchg(ptr, old, new) \ - __sync_cmpxchg((ptr), (old), (new), sizeof(*ptr)) + __sync_cmpxchg(ptr, old, new, sizeof(*(ptr))) #define cmpxchg_local(ptr, old, new) \ - __cmpxchg_local((ptr), (old), (new), sizeof(*ptr)) + __cmpxchg_local(ptr, old, new, sizeof(*(ptr))) #endif /* -- cgit v0.10.2 From b0f4c4b32c8e3aa0d44fc4dd6c40a9a9a8d66b63 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 26 Jan 2012 08:55:34 -0500 Subject: bugs, x86: Fix printk levels for panic, softlockups and stack dumps rsyslog will display KERN_EMERG messages on a connected terminal. However, these messages are useless/undecipherable for a general user. For example, after a softlockup we get: Message from syslogd@intel-s3e37-04 at Jan 25 14:18:06 ... kernel:Stack: Message from syslogd@intel-s3e37-04 at Jan 25 14:18:06 ... kernel:Call Trace: Message from syslogd@intel-s3e37-04 at Jan 25 14:18:06 ... kernel:Code: ff ff a8 08 75 25 31 d2 48 8d 86 38 e0 ff ff 48 89 d1 0f 01 c8 0f ae f0 48 8b 86 38 e0 ff ff a8 08 75 08 b1 01 4c 89 e0 0f 01 c9 ea 69 dd ff 4c 29 e8 48 89 c7 e8 0f bc da ff 49 89 c4 49 89 This happens because the printk levels for these messages are incorrect. Only an informational message should be displayed on a terminal. I modified the printk levels for various messages in the kernel and tested the output by using the drivers/misc/lkdtm.c kernel modules (ie, softlockups, panics, hard lockups, etc.) and confirmed that the console output was still the same and that the output to the terminals was correct. For example, in the case of a softlockup we now see the much more informative: Message from syslogd@intel-s3e37-04 at Jan 25 10:18:06 ... BUG: soft lockup - CPU4 stuck for 60s! instead of the above confusing messages. AFAICT, the messages no longer have to be KERN_EMERG. In the most important case of a panic we set console_verbose(). As for the other less severe cases the correct data is output to the console and /var/log/messages. Successfully tested by me using the drivers/misc/lkdtm.c module. Signed-off-by: Prarit Bhargava Cc: dzickus@redhat.com Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/r/1327586134-11926-1-git-send-email-prarit@redhat.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 1aae78f..4025fe4 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -252,7 +252,8 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err) unsigned short ss; unsigned long sp; #endif - printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); + printk(KERN_DEFAULT + "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); #ifdef CONFIG_PREEMPT printk("PREEMPT "); #endif diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index 6d728d9..42b2bca 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c @@ -269,11 +269,11 @@ void show_registers(struct pt_regs *regs) unsigned char c; u8 *ip; - printk(KERN_EMERG "Stack:\n"); + printk(KERN_DEFAULT "Stack:\n"); show_stack_log_lvl(NULL, regs, (unsigned long *)sp, - 0, KERN_EMERG); + 0, KERN_DEFAULT); - printk(KERN_EMERG "Code: "); + printk(KERN_DEFAULT "Code: "); ip = (u8 *)regs->ip - code_prologue; if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 9d74824..f0b4caf 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -673,7 +673,7 @@ no_context(struct pt_regs *regs, unsigned long error_code, stackend = end_of_stack(tsk); if (tsk != &init_task && *stackend != STACK_END_MAGIC) - printk(KERN_ALERT "Thread overran stack, or stack corrupted\n"); + printk(KERN_EMERG "Thread overran stack, or stack corrupted\n"); tsk->thread.cr2 = address; tsk->thread.trap_no = 14; @@ -684,7 +684,7 @@ no_context(struct pt_regs *regs, unsigned long error_code, sig = 0; /* Executive summary in case the body of the oops scrolled away */ - printk(KERN_EMERG "CR2: %016lx\n", address); + printk(KERN_DEFAULT "CR2: %016lx\n", address); oops_end(flags, regs, sig); } diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 1d7bca7..d117262 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -296,7 +296,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) if (__this_cpu_read(soft_watchdog_warn) == true) return HRTIMER_RESTART; - printk(KERN_ERR "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", + printk(KERN_EMERG "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", smp_processor_id(), duration, current->comm, task_pid_nr(current)); print_modules(); diff --git a/lib/bug.c b/lib/bug.c index 1955209..a28c141 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -169,7 +169,7 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) return BUG_TRAP_TYPE_WARN; } - printk(KERN_EMERG "------------[ cut here ]------------\n"); + printk(KERN_DEFAULT "------------[ cut here ]------------\n"); if (file) printk(KERN_CRIT "kernel BUG at %s:%u!\n", -- cgit v0.10.2 From f2b3ee9e4200b32d113b1bd3c93f9a836c97357c Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 26 Jan 2012 10:34:35 +0000 Subject: ipv6: Fix ip_gre lockless xmits. Tunnel devices set NETIF_F_LLTX to bypass HARD_TX_LOCK. Sit and ipip set this unconditionally in ops->setup, but gre enables it conditionally after parameter passing in ops->newlink. This is not called during tunnel setup as below, however, so GRE tunnels are still taking the lock. modprobe ip_gre ip tunnel add test0 mode gre remote 10.5.1.1 dev lo ip link set test0 up ip addr add 10.6.0.1 dev test0 # cat /sys/class/net/test0/features # $DIR/test_tunnel_xmit 10 10.5.2.1 ip route add 10.5.2.0/24 dev test0 ip tunnel del test0 The newlink callback is only called in rtnl_netlink, and only if the device is new, as it calls register_netdevice internally. Gre tunnels are created at 'ip tunnel add' with ioctl SIOCADDTUNNEL, which calls ipgre_tunnel_locate, which calls register_netdev. rtnl_newlink is called at 'ip link set', but skips ops->newlink and the device is up with locking still enabled. The equivalent ipip tunnel works fine, btw (just substitute 'method gre' for 'method ipip'). On kernels before /sys/class/net/*/features was removed [1], the first commented out line returns 0x6000 with method gre, which indicates that NETIF_F_LLTX (0x1000) is not set. With ipip, it reports 0x7000. This test cannot be used on recent kernels where the sysfs file is removed (and ETHTOOL_GFEATURES does not currently work for tunnel devices, because they lack dev->ethtool_ops). The second commented out line calls a simple transmission test [2] that sends on 24 cores at maximum rate. Results of a single run: ipip: 19,372,306 gre before patch: 4,839,753 gre after patch: 19,133,873 This patch replicates the condition check in ipgre_newlink to ipgre_tunnel_locate. It works for me, both with oseq on and off. This is the first time I looked at rtnetlink and iproute2 code, though, so someone more knowledgeable should probably check the patch. Thanks. The tail of both functions is now identical, by the way. To avoid code duplication, I'll be happy to rework this and merge the two. [1] http://patchwork.ozlabs.org/patch/104610/ [2] http://kernel.googlecode.com/files/xmit_udp_parallel.c Signed-off-by: Willem de Bruijn Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 2b53a1f..6b3ca5b 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -422,6 +422,10 @@ static struct ip_tunnel *ipgre_tunnel_locate(struct net *net, if (register_netdevice(dev) < 0) goto failed_free; + /* Can use a lockless transmit, unless we generate output sequences */ + if (!(nt->parms.o_flags & GRE_SEQ)) + dev->features |= NETIF_F_LLTX; + dev_hold(dev); ipgre_tunnel_link(ign, nt); return nt; -- cgit v0.10.2 From f18da14565819ba43b8321237e2426a2914cc2ef Mon Sep 17 00:00:00 2001 From: Stefan Gula Date: Thu, 26 Jan 2012 11:01:06 +0000 Subject: net: RTNETLINK adjusting values of min_ifinfo_dump_size Setting link parameters on a netdevice changes the value of if_nlmsg_size(), therefore it is necessary to recalculate min_ifinfo_dump_size. Signed-off-by: Stefan Gula Signed-off-by: David S. Miller diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index f16444b..65aebd4 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1509,6 +1509,9 @@ errout: if (send_addr_notify) call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + min_ifinfo_dump_size = max_t(u16, if_nlmsg_size(dev), + min_ifinfo_dump_size); + return err; } -- cgit v0.10.2 From d1bb399ad03c11e792f6dea198d3b1e23061f094 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 26 Jan 2012 22:05:58 +0100 Subject: firewire: ohci: add reset packet quirk for SB Audigy The Audigy's SB1394 controller is actually from Texas Instruments and has the same bus reset packet generation bug, so it needs the same quirk entry. Signed-off-by: Clemens Ladisch Cc: 2.6.36+ Signed-off-by: Stefan Richter diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 6628fea..21250ec 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -263,6 +263,7 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) static char ohci_driver_name[] = KBUILD_MODNAME; #define PCI_DEVICE_ID_AGERE_FW643 0x5901 +#define PCI_DEVICE_ID_CREATIVE_SB1394 0x4001 #define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380 #define PCI_DEVICE_ID_TI_TSB12LV22 0x8009 #define PCI_DEVICE_ID_TI_TSB12LV26 0x8020 @@ -289,6 +290,9 @@ static const struct { {PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_AGERE_FW643, 6, QUIRK_NO_MSI}, + {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_SB1394, PCI_ANY_ID, + QUIRK_RESET_PACKET}, + {PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_FW, PCI_ANY_ID, QUIRK_NO_MSI}, -- cgit v0.10.2 From 9018e93948c6f8f95fbcc9fa05f6c403d6adb406 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Thu, 26 Jan 2012 12:09:28 +0000 Subject: net: explicitly add jump_label.h header to sock.h Commit 36a1211970193ce215de50ed1e4e1272bc814df1 removed linux/module.h include statement from one of the headers that end up in net/sock.h. It was providing us with static_branch() definition implicitly, so after its removal the build got broken. To fix this, and avoid having this happening in the future, let me do the right thing and include linux/jump_label.h explicitly in sock.h. Signed-off-by: Glauber Costa Reported-by: Randy Dunlap CC: David S. Miller Signed-off-by: David S. Miller diff --git a/include/net/sock.h b/include/net/sock.h index 4c69ac1..91c1c8b 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -55,6 +55,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From dbc3982ae286e934e71f0b44e78d14844a9ca83b Mon Sep 17 00:00:00 2001 From: Vaibhav Hiremath Date: Mon, 23 Jan 2012 12:18:14 +0530 Subject: ARM: OMAP2+: timer: Fix crash due to wrong arg to __omap_dm_timer_read_counter Commit 2f0778af (ARM: 7205/2: sched_clock: allow sched_clock to be selected at runtime) had a typo for the case when CONFIG_OMAP_32K_TIMER is not set. In dmtimer_read_sched_clock(), wrong argument was getting passed to __omap_dm_timer_read_counter() function call; instead of "&clksrc", we were passing "clksrc.io_base", which results into kernel crash. To reproduce kernel crash, just disable the CONFIG_OMAP_32K_TIMER config option (and DEBUG_LL) and build/boot the kernel. This will use dmtimer as a kernel clocksource and lead to kernel crash during boot - [ 0.000000] OMAP clocksource: GPTIMER2 at 26000000 Hz [ 0.000000] sched_clock: 32 bits at 26MHz, resolution 38ns, wraps every 165191ms [ 0.000000] Unable to handle kernel paging request at virtual address 00030ef1 [ 0.000000] pgd = c0004000 [ 0.000000] [00030ef1] *pgd=00000000 [ 0.000000] Internal error: Oops: 5 [#1] SMP [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 Not tainted (3.3.0-rc1-11574-g0c76665-dirty #3) [ 0.000000] PC is at dmtimer_read_sched_clock+0x18/0x4c [ 0.000000] LR is at update_sched_clock+0x10/0x84 [ 0.000000] pc : [] lr : [] psr: 200001d3 [ 0.000000] sp : c0641f38 ip : c0641e18 fp : 0000000a [ 0.000000] r10: 151c3303 r9 : 00000026 r8 : 76276259 [ 0.000000] r7 : 00028547 r6 : c065ac80 r5 : 431bde82 r4 : c0655968 [ 0.000000] r3 : 00030ef1 r2 : fb032000 r1 : 00000028 r0 : 00000001 Signed-off-by: Vaibhav Hiremath [tony@atomide.com: updated comments] Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index 6eeff0e..5c9acea 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c @@ -270,7 +270,7 @@ static struct clocksource clocksource_gpt = { static u32 notrace dmtimer_read_sched_clock(void) { if (clksrc.reserved) - return __omap_dm_timer_read_counter(clksrc.io_base, 1); + return __omap_dm_timer_read_counter(&clksrc, 1); return 0; } -- cgit v0.10.2 From 8ef5d844cc3a644ea6f7665932a4307e9fad01fa Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Mon, 23 Jan 2012 08:32:23 +0100 Subject: ARM: OMAP2+: GPMC: fix device size setup following statement can only change device size from 8-bit(0) to 16-bit(1), but not vice versa: regval |= GPMC_CONFIG1_DEVICESIZE(wval); so as this field has 1 reserved bit, that could be used in future, just clear both bits and then OR with the desired value Signed-off-by: Yegor Yefremov Cc: stable@vger.kernel.org Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 130034b..dfffbbf 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -528,7 +528,13 @@ int gpmc_cs_configure(int cs, int cmd, int wval) case GPMC_CONFIG_DEV_SIZE: regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + + /* clear 2 target bits */ + regval &= ~GPMC_CONFIG1_DEVICESIZE(3); + + /* set the proper value */ regval |= GPMC_CONFIG1_DEVICESIZE(wval); + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); break; -- cgit v0.10.2 From ffa1e4ede453cf92cfcd9f96f9140c21aeb319f7 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sun, 18 Dec 2011 02:35:47 +0200 Subject: ARM: OMAP: fix erroneous mmc2 clock change on mmc3 setup hsmmc23_before_set_reg() can set MMCSDIO2ADPCLKISEL bit, which enables internal clock for MMC2. Currently this function is also called by code handling MMC3, and if .internal_clock is set in platform data (by default it currently is), it will set MMCSDIO2ADPCLKISEL for MMC2 instead of MMC3 (MMC3 doesn't have such bit so nothing actually needs to be done). This breaks 2nd SD slot on pandora. Fix this by changing hsmmc23_before_set_reg() to only handle MMC2. Note that this removes .remux() call for MMC3, but no board currently needs it and it's also not called for MMC4 and MMC5. Signed-off-by: Grazvydas Ignotas Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index bd844af..8658bd7 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -182,7 +182,7 @@ static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc) } } -static void hsmmc23_before_set_reg(struct device *dev, int slot, +static void hsmmc2_before_set_reg(struct device *dev, int slot, int power_on, int vdd) { struct omap_mmc_platform_data *mmc = dev->platform_data; @@ -407,14 +407,13 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, c->caps &= ~MMC_CAP_8_BIT_DATA; c->caps |= MMC_CAP_4_BIT_DATA; } - /* FALLTHROUGH */ - case 3: if (mmc->slots[0].features & HSMMC_HAS_PBIAS) { /* off-chip level shifting, or none */ - mmc->slots[0].before_set_reg = hsmmc23_before_set_reg; + mmc->slots[0].before_set_reg = hsmmc2_before_set_reg; mmc->slots[0].after_set_reg = NULL; } break; + case 3: case 4: case 5: mmc->slots[0].before_set_reg = NULL; -- cgit v0.10.2 From d82e5190da9057e31d6f0fe5771dfbe55ff9125f Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Thu, 12 Jan 2012 16:26:45 +0200 Subject: ARM: OMAP: fix MMC2 loopback clock handling Currently MMC2 setup code can only enable loopback clock and relies on reset value for boards that need to have it disabled. This causes a problem with certain bootloaders that always enable that clock, resulting with unwanted bootloader dependencies. Fix this by making it disable the clock if board data says so. Signed-off-by: Grazvydas Ignotas Acked-by: Igor Grinberg Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index 8658bd7..ad0adb5 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -175,11 +175,12 @@ static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc) { u32 reg; - if (mmc->slots[0].internal_clock) { - reg = omap_ctrl_readl(control_devconf1_offset); + reg = omap_ctrl_readl(control_devconf1_offset); + if (mmc->slots[0].internal_clock) reg |= OMAP2_MMCSDIO2ADPCLKISEL; - omap_ctrl_writel(reg, control_devconf1_offset); - } + else + reg &= ~OMAP2_MMCSDIO2ADPCLKISEL; + omap_ctrl_writel(reg, control_devconf1_offset); } static void hsmmc2_before_set_reg(struct device *dev, int slot, -- cgit v0.10.2 From e0feca899c9785316ec67954f1a8e84102eaaca7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 23 Dec 2011 18:39:31 +0100 Subject: ARM: OMAP2+: arch/arm/mach-omap2/devices.c: introduce missing kfree pdata needs to be freed before leaving the function in an error case. A simplified version of the semantic match that finds the problem is as follows: (http://coccinelle.lip6.fr) // @r exists@ local idexpression x; statement S; identifier f1; position p1,p2; expression *ptr != NULL; @@ x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S <... when != x when != if (...) { <+...x...+> } x->f1 ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 0b510ad..283d11e 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -405,6 +405,7 @@ static int omap_mcspi_init(struct omap_hwmod *oh, void *unused) break; default: pr_err("Invalid McSPI Revision value\n"); + kfree(pdata); return -EINVAL; } -- cgit v0.10.2 From 14ea960164ecb25f7617eba33e7c7f9163e93e7c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 12 Jan 2012 10:55:17 +0100 Subject: ARM: OMAP2+: arch/arm/mach-omap2/smartreflex.c: add missing iounmap Add missing iounmap in error handling code, in a case where the function already preforms iounmap on some other execution path. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; statement S,S1; int ret; @@ e = \(ioremap\|ioremap_nocache\)(...) ... when != iounmap(e) if (<+...e...+>) S ... when any when != iounmap(e) *if (...) { ... when != iounmap(e) return ...; } ... when any iounmap(e); // Signed-off-by: Julia Lawall Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index 9dd9345..7e755bb 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -897,7 +897,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = sr_late_init(sr_info); if (ret) { pr_warning("%s: Error in SR late init\n", __func__); - return ret; + goto err_iounmap; } } -- cgit v0.10.2 From 485bc54c3360e9c1d595c48c9c82dbd3a51e133e Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Thu, 22 Dec 2011 11:30:09 +0900 Subject: drm/exynos: use release_mem_region instead of release_resource To make a api pair of request_mem_region and release_mem_region, release_mem_region is used instead of release_resource. Signed-off-by: Seung-Woo Kim Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index f48f7ce..3429d3f 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1116,8 +1116,8 @@ err_ddc: err_iomap: iounmap(hdata->regs); err_req_region: - release_resource(hdata->regs_res); - kfree(hdata->regs_res); + release_mem_region(hdata->regs_res->start, + resource_size(hdata->regs_res)); err_resource: hdmi_resources_cleanup(hdata); err_data: @@ -1145,8 +1145,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev) iounmap(hdata->regs); - release_resource(hdata->regs_res); - kfree(hdata->regs_res); + release_mem_region(hdata->regs_res->start, + resource_size(hdata->regs_res)); /* hdmiphy i2c driver */ i2c_del_driver(&hdmiphy_driver); -- cgit v0.10.2 From 2363dc636df34abb795c31668eeadc659e815fbd Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Wed, 4 Jan 2012 15:34:32 +0900 Subject: drm/exynos: fix build dependency for DRM_EXYNOS_HDMI DRM_EXYNOS_HDMI driver and VIDEO_SAMSUNG_S5P_TV driver should be not enabled at once because they use same HW blocks. So dependency for DRM_EXYNOS_HDMI is fixed to check VIDEO_SAMSUNG_S5P_TV=n. Signed-off-by: Seung-Woo Kim Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index f9aaa56..4ab915e 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -21,7 +21,7 @@ config DRM_EXYNOS_FIMD config DRM_EXYNOS_HDMI tristate "Exynos DRM HDMI" - depends on DRM_EXYNOS + depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV help Choose this option if you want to use Exynos HDMI for DRM. If M is selected, the module will be called exynos_drm_hdmi -- cgit v0.10.2 From a4b42dab293afdabc3e4ae57cbc743ad05af0e4b Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Mon, 16 Jan 2012 18:55:02 +0900 Subject: drm/exynos: fixed build dependency for DRM_EXYNOS_FIMD FB based FIMD and DRM based FIMD drivers use same hardware so with this patch, only one of them would be selected. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 4ab915e..b9e5266 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -13,7 +13,7 @@ config DRM_EXYNOS config DRM_EXYNOS_FIMD tristate "Exynos DRM FIMD" - depends on DRM_EXYNOS + depends on DRM_EXYNOS && !FB_S3C default n help Choose this option if you want to use Exynos FIMD for DRM. -- cgit v0.10.2 From f15013033e2dd363b3ad181bfd27fa4e8e8ffda8 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Tue, 17 Jan 2012 14:08:55 +0900 Subject: MAINTAINERS: added maintainer entry for Exynos DRM Driver. I'd like to add my colleagues who dedicated to developing and improving our driver to maintainer entry. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park diff --git a/MAINTAINERS b/MAINTAINERS index 89b70df..2387cc3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2339,6 +2339,9 @@ F: include/drm/i915* DRM DRIVERS FOR EXYNOS M: Inki Dae +M: Joonyoung Shim +M: Seung-Woo Kim +M: Kyungmin Park L: dri-devel@lists.freedesktop.org S: Supported F: drivers/gpu/drm/exynos -- cgit v0.10.2 From d22b086970c3ee2d327d7dfdcb436254f7f72204 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 21 Jan 2012 13:29:27 -0500 Subject: MAINTAINERS: Add hwmon entries for Wolfson The actual driver code seems to have been lost in the shuffle. Signed-off-by: Mark Brown Signed-off-by: Guenter Roeck diff --git a/MAINTAINERS b/MAINTAINERS index 89b70df..8cc2d45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7357,6 +7357,7 @@ S: Supported F: Documentation/hwmon/wm83?? F: arch/arm/mach-s3c64xx/mach-crag6410* F: drivers/leds/leds-wm83*.c +F: drivers/hwmon/wm83??-hwmon.c F: drivers/input/misc/wm831x-on.c F: drivers/input/touchscreen/wm831x-ts.c F: drivers/input/touchscreen/wm97*.c -- cgit v0.10.2 From cba9384b3c53d1a302206f68134a6cbfbae1d686 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 20 Jan 2012 02:01:11 -0800 Subject: MAINTAINERS: Drop maintainer for MAX1668 hwmon driver David no longer has access to MAX1688 hardware, so drop him from the maintainers list. Cc: David George Signed-off-by: Guenter Roeck Acked-by: David George Acked-by: Jean Delvare diff --git a/MAINTAINERS b/MAINTAINERS index 8cc2d45..686c652 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4267,13 +4267,6 @@ S: Orphan F: drivers/video/matrox/matroxfb_* F: include/linux/matroxfb.h -MAX1668 TEMPERATURE SENSOR DRIVER -M: "David George" -L: lm-sensors@lm-sensors.org -S: Maintained -F: Documentation/hwmon/max1668 -F: drivers/hwmon/max1668.c - MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER M: "Hans J. Koch" L: lm-sensors@lm-sensors.org -- cgit v0.10.2 From 373af0c0c539b109ea978e96f217df0fc20aa261 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Fri, 27 Jan 2012 11:54:58 +0900 Subject: drm/exynos: fixed pm feature for fimd module. this patch separates fimd specific power on/off function from pm function and the pm interfaces will call that function for power on or off. and also removes unnecessary codes of resume function. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index ca83139..b6a737d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -158,7 +158,8 @@ static void fimd_dpms(struct device *subdrv_dev, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: - pm_runtime_put_sync(subdrv_dev); + if (!ctx->suspended) + pm_runtime_put_sync(subdrv_dev); break; default: DRM_DEBUG_KMS("unspecified mode %d\n", mode); @@ -734,6 +735,46 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) writel(val, ctx->regs + SHADOWCON); } +static int fimd_power_on(struct fimd_context *ctx, bool enable) +{ + struct exynos_drm_subdrv *subdrv = &ctx->subdrv; + struct device *dev = subdrv->manager.dev; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (enable != false && enable != true) + return -EINVAL; + + if (enable) { + int ret; + + ret = clk_enable(ctx->bus_clk); + if (ret < 0) + return ret; + + ret = clk_enable(ctx->lcd_clk); + if (ret < 0) { + clk_disable(ctx->bus_clk); + return ret; + } + + ctx->suspended = false; + + /* if vblank was enabled status, enable it again. */ + if (test_and_clear_bit(0, &ctx->irq_flags)) + fimd_enable_vblank(dev); + + fimd_apply(dev); + } else { + clk_disable(ctx->lcd_clk); + clk_disable(ctx->bus_clk); + + ctx->suspended = true; + } + + return 0; +} + static int __devinit fimd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -911,39 +952,30 @@ out: #ifdef CONFIG_PM_SLEEP static int fimd_suspend(struct device *dev) { - int ret; + struct fimd_context *ctx = get_fimd_context(dev); if (pm_runtime_suspended(dev)) return 0; - ret = pm_runtime_suspend(dev); - if (ret < 0) - return ret; - - return 0; + /* + * do not use pm_runtime_suspend(). if pm_runtime_suspend() is + * called here, an error would be returned by that interface + * because the usage_count of pm runtime is more than 1. + */ + return fimd_power_on(ctx, false); } static int fimd_resume(struct device *dev) { - int ret; - - ret = pm_runtime_resume(dev); - if (ret < 0) { - DRM_ERROR("failed to resume runtime pm.\n"); - return ret; - } - - pm_runtime_disable(dev); - - ret = pm_runtime_set_active(dev); - if (ret < 0) { - DRM_ERROR("failed to active runtime pm.\n"); - pm_runtime_enable(dev); - pm_runtime_suspend(dev); - return ret; - } + struct fimd_context *ctx = get_fimd_context(dev); - pm_runtime_enable(dev); + /* + * if entered to sleep when lcd panel was on, the usage_count + * of pm runtime would still be 1 so in this case, fimd driver + * should be on directly not drawing on pm runtime interface. + */ + if (!pm_runtime_suspended(dev)) + return fimd_power_on(ctx, true); return 0; } @@ -956,39 +988,16 @@ static int fimd_runtime_suspend(struct device *dev) DRM_DEBUG_KMS("%s\n", __FILE__); - clk_disable(ctx->lcd_clk); - clk_disable(ctx->bus_clk); - - ctx->suspended = true; - return 0; + return fimd_power_on(ctx, false); } static int fimd_runtime_resume(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); - int ret; DRM_DEBUG_KMS("%s\n", __FILE__); - ret = clk_enable(ctx->bus_clk); - if (ret < 0) - return ret; - - ret = clk_enable(ctx->lcd_clk); - if (ret < 0) { - clk_disable(ctx->bus_clk); - return ret; - } - - ctx->suspended = false; - - /* if vblank was enabled status, enable it again. */ - if (test_and_clear_bit(0, &ctx->irq_flags)) - fimd_enable_vblank(dev); - - fimd_apply(dev); - - return 0; + return fimd_power_on(ctx, true); } #endif -- cgit v0.10.2 From b7c9705cb799adabc0ae430943843e6c6e82617d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Jan 2012 14:41:20 +0900 Subject: ARM: S3C64XX: Make s3c64xx_init_uarts() static Now that it's in common.c it's not used in multiple source files. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c64xx/common.c b/arch/arm/mach-s3c64xx/common.c index 4a7394d..bee7dcd 100644 --- a/arch/arm/mach-s3c64xx/common.c +++ b/arch/arm/mach-s3c64xx/common.c @@ -49,7 +49,7 @@ /* uart registration process */ -void __init s3c64xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) +static void __init s3c64xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) { s3c24xx_init_uartdevs("s3c6400-uart", s3c64xx_uart_resources, cfg, no); } -- cgit v0.10.2 From 5d3a21990c58b68a31dcbf4c378231781c53bfc1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Jan 2012 14:43:44 +0900 Subject: ARM: S3C6410: Use device names for both I2C clocks When the S3C64xx CPUs were converted to clkdev mappings were added for the I2C controllers on them. On S3C6410 a device name is specified for I2C controller 1 but not for controller 0 which makes the code less robust as we'll falsely return the clock for controller 0 if there's an error in the request for controller 1. Improve things by registering a device name for controller 0 as well. Due to the fact that we change the numbering for controller 0 depending on if we've registered controller 1 this requires an ifdef to choose the name. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c64xx/clock.c b/arch/arm/mach-s3c64xx/clock.c index 31bb27d..aebbcc2 100644 --- a/arch/arm/mach-s3c64xx/clock.c +++ b/arch/arm/mach-s3c64xx/clock.c @@ -138,6 +138,11 @@ static struct clk init_clocks_off[] = { .ctrlbit = S3C_CLKCON_PCLK_TSADC, }, { .name = "i2c", +#ifdef CONFIG_S3C_DEV_I2C1 + .devname = "s3c2440-i2c.0", +#else + .devname = "s3c2440-i2c", +#endif .parent = &clk_p, .enable = s3c64xx_pclk_ctrl, .ctrlbit = S3C_CLKCON_PCLK_IIC, -- cgit v0.10.2 From 556ef3e474dd87d627655dafdf1e7eaf4747e388 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 27 Jan 2012 14:47:45 +0900 Subject: ARM: EXYNOS: fix non-SMP builds for EXYNOS4 This patch fixes the following build issue, which happens only if SMP has been disabled: arch/arm/mach-exynos/built-in.o: In function `exynos4_pm_resume': arch/arm/mach-exynos/pm.c:387: undefined reference to `scu_enable' Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index a4f61a4..2521b23 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -384,7 +384,9 @@ static void exynos4_pm_resume(void) exynos4_restore_pll(); +#ifdef CONFIG_SMP scu_enable(S5P_VA_SCU); +#endif #ifdef CONFIG_CACHE_L2X0 s3c_pm_do_restore_core(exynos4_l2cc_save, ARRAY_SIZE(exynos4_l2cc_save)); -- cgit v0.10.2 From 693cec9755e5198e0c291a4bfea6ebb1c7054550 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 27 Jan 2012 14:51:21 +0900 Subject: ARM: SAMSUNG: Fix platform data setup for I2C adapter 0 The common static default_i2c_data structure gets bus_num set by each s3c_i2c?_set_platdata() call, except for s3c_i2c0_set_platdata(). Thus if for instance s3c_i2c1_set_platdata() is called prior to s3c_i2c0_set_platdata() the I2C0 controller has bus_num set to wrong value of 1, i.e. the one from previous set_platdata call. Fix this by also setting bus_num for I2C0. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 32a6e39..f10768e 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -468,8 +468,10 @@ void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) { struct s3c2410_platform_i2c *npd; - if (!pd) + if (!pd) { pd = &default_i2c_data; + pd->bus_num = 0; + } npd = s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c), &s3c_device_i2c0); -- cgit v0.10.2 From 7cdf04d7d4c0b5b205817ceb7a21c6e07d09ce11 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 27 Jan 2012 14:56:17 +0900 Subject: ARM: EXYNOS: Remove build warning without enabling PM Fixed following build warning with exynos4_defconfig. arch/arm/mach-exynos/clock.c:33: warning: 'exynos4_clock_save' defined but not used arch/arm/mach-exynos/clock-exynos4210.c:35: warning: 'exynos4210_clock_save' defined but not used arch/arm/mach-exynos/clock-exynos4212.c:35: warning: 'exynos4212_clock_save' defined but not used Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/clock-exynos4210.c b/arch/arm/mach-exynos/clock-exynos4210.c index a5823a7..13312cc 100644 --- a/arch/arm/mach-exynos/clock-exynos4210.c +++ b/arch/arm/mach-exynos/clock-exynos4210.c @@ -32,6 +32,7 @@ #include "common.h" +#ifdef CONFIG_PM_SLEEP static struct sleep_save exynos4210_clock_save[] = { SAVE_ITEM(S5P_CLKSRC_IMAGE), SAVE_ITEM(S5P_CLKSRC_LCD1), @@ -42,6 +43,7 @@ static struct sleep_save exynos4210_clock_save[] = { SAVE_ITEM(S5P_CLKGATE_IP_LCD1), SAVE_ITEM(S5P_CLKGATE_IP_PERIR_4210), }; +#endif static struct clksrc_clk *sysclks[] = { /* nothing here yet */ diff --git a/arch/arm/mach-exynos/clock-exynos4212.c b/arch/arm/mach-exynos/clock-exynos4212.c index 26a668b..48af285 100644 --- a/arch/arm/mach-exynos/clock-exynos4212.c +++ b/arch/arm/mach-exynos/clock-exynos4212.c @@ -32,12 +32,14 @@ #include "common.h" +#ifdef CONFIG_PM_SLEEP static struct sleep_save exynos4212_clock_save[] = { SAVE_ITEM(S5P_CLKSRC_IMAGE), SAVE_ITEM(S5P_CLKDIV_IMAGE), SAVE_ITEM(S5P_CLKGATE_IP_IMAGE_4212), SAVE_ITEM(S5P_CLKGATE_IP_PERIR_4212), }; +#endif static struct clk *clk_src_mpll_user_list[] = { [0] = &clk_fin_mpll, diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c index 5a8c42e..187287a 100644 --- a/arch/arm/mach-exynos/clock.c +++ b/arch/arm/mach-exynos/clock.c @@ -30,6 +30,7 @@ #include "common.h" +#ifdef CONFIG_PM_SLEEP static struct sleep_save exynos4_clock_save[] = { SAVE_ITEM(S5P_CLKDIV_LEFTBUS), SAVE_ITEM(S5P_CLKGATE_IP_LEFTBUS), @@ -93,6 +94,7 @@ static struct sleep_save exynos4_clock_save[] = { SAVE_ITEM(S5P_CLKGATE_SCLKCPU), SAVE_ITEM(S5P_CLKGATE_IP_CPU), }; +#endif struct clk clk_sclk_hdmi27m = { .name = "sclk_hdmi27m", -- cgit v0.10.2 From 706212f3321a542db6847116563a46a96ef31816 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Sat, 21 Jan 2012 16:32:42 +0900 Subject: ARM: S5PV210: Fix the name of exynos4_clk_hdmiphy_ctrl() for S5PV210 Should be s5pv210_clk_hdmiphy_ctrl() in mach-s5pv210/. Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index c78dfdd..b9ec0c3 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -175,7 +175,7 @@ static int s5pv210_clk_mask1_ctrl(struct clk *clk, int enable) return s5p_gatectrl(S5P_CLK_SRC_MASK1, clk, enable); } -static int exynos4_clk_hdmiphy_ctrl(struct clk *clk, int enable) +static int s5pv210_clk_hdmiphy_ctrl(struct clk *clk, int enable) { return s5p_gatectrl(S5P_HDMI_PHY_CONTROL, clk, enable); } @@ -372,7 +372,7 @@ static struct clk init_clocks_off[] = { }, { .name = "hdmiphy", .devname = "s5pv210-hdmi", - .enable = exynos4_clk_hdmiphy_ctrl, + .enable = s5pv210_clk_hdmiphy_ctrl, .ctrlbit = (1 << 0), }, { .name = "dacphy", -- cgit v0.10.2 From 9a60571ecf918162553592ef8c4b4450155394a0 Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Fri, 27 Jan 2012 15:30:48 +0900 Subject: ARM: EXYNOS: Fix "warning: initialization from incompatible pointer type" Fix the wrong function prototype. Signed-off-by: Jonghwan Choi Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index 2521b23..e190130 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -206,7 +206,7 @@ static void exynos4_pm_prepare(void) } -static int exynos4_pm_add(struct device *dev) +static int exynos4_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = exynos4_pm_prepare; pm_cpu_sleep = exynos4_cpu_suspend; -- cgit v0.10.2 From 04511a6faeed146030dcc5b892beff30fa0a7c9b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 27 Jan 2012 15:35:25 +0900 Subject: ARM: SAMSUNG: Fix missing api-change from subsys_interface change Commit 4a858cfc9a (arm: convert sysdev_class to a regular subsystem) converted the samsung sysdevs into subsys_interface instances. While the original add-function only had a (struct sys_device *) parameter, the dev_add from subsys_interface needs (struct device *, struct subsys_interface *) leading to "initialized from incompatible pointer type" warnings. Signed-off-by: Heiko Stuebner Cc: Greg Kroah-Hartman Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-s3c2410/cpu-freq.c b/arch/arm/mach-s3c2410/cpu-freq.c index 7dc6c46..5404535 100644 --- a/arch/arm/mach-s3c2410/cpu-freq.c +++ b/arch/arm/mach-s3c2410/cpu-freq.c @@ -115,7 +115,8 @@ static struct s3c_cpufreq_info s3c2410_cpufreq_info = { .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs), }; -static int s3c2410_cpufreq_add(struct device *dev) +static int s3c2410_cpufreq_add(struct device *dev, + struct subsys_interface *sif) { return s3c_cpufreq_register(&s3c2410_cpufreq_info); } @@ -133,7 +134,8 @@ static int __init s3c2410_cpufreq_init(void) arch_initcall(s3c2410_cpufreq_init); -static int s3c2410a_cpufreq_add(struct device *dev) +static int s3c2410a_cpufreq_add(struct device *dev, + struct subsys_interface *sif) { /* alter the maximum freq settings for S3C2410A. If a board knows * it only has a maximum of 200, then it should register its own @@ -144,7 +146,7 @@ static int s3c2410a_cpufreq_add(struct device *dev) s3c2410_cpufreq_info.max.pclk = 66500000; s3c2410_cpufreq_info.name = "s3c2410a"; - return s3c2410_cpufreq_add(dev); + return s3c2410_cpufreq_add(dev, sif); } static struct subsys_interface s3c2410a_cpufreq_interface = { diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c index 2afd000..4803338 100644 --- a/arch/arm/mach-s3c2410/dma.c +++ b/arch/arm/mach-s3c2410/dma.c @@ -132,7 +132,8 @@ static struct s3c24xx_dma_order __initdata s3c2410_dma_order = { }, }; -static int __init s3c2410_dma_add(struct device *dev) +static int __init s3c2410_dma_add(struct device *dev, + struct subsys_interface *sif) { s3c2410_dma_init(); s3c24xx_dma_order_set(&s3c2410_dma_order); @@ -148,7 +149,7 @@ static struct subsys_interface s3c2410_dma_interface = { static int __init s3c2410_dma_drvinit(void) { - return subsys_interface_register(&s3c2410_interface); + return subsys_interface_register(&s3c2410_dma_interface); } arch_initcall(s3c2410_dma_drvinit); diff --git a/arch/arm/mach-s3c2410/pll.c b/arch/arm/mach-s3c2410/pll.c index c07438b..e0b3b34 100644 --- a/arch/arm/mach-s3c2410/pll.c +++ b/arch/arm/mach-s3c2410/pll.c @@ -66,7 +66,7 @@ static struct cpufreq_frequency_table pll_vals_12MHz[] = { { .frequency = 270000000, .index = PLLVAL(127, 1, 1), }, }; -static int s3c2410_plls_add(struct device *dev) +static int s3c2410_plls_add(struct device *dev, struct subsys_interface *sif) { return s3c_plltab_register(pll_vals_12MHz, ARRAY_SIZE(pll_vals_12MHz)); } diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c index fda5385..03f706d 100644 --- a/arch/arm/mach-s3c2410/pm.c +++ b/arch/arm/mach-s3c2410/pm.c @@ -111,7 +111,7 @@ struct syscore_ops s3c2410_pm_syscore_ops = { .resume = s3c2410_pm_resume, }; -static int s3c2410_pm_add(struct device *dev) +static int s3c2410_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = s3c2410_pm_prepare; pm_cpu_sleep = s3c2410_cpu_suspend; diff --git a/arch/arm/mach-s3c2412/cpu-freq.c b/arch/arm/mach-s3c2412/cpu-freq.c index d8664b7..125be7d 100644 --- a/arch/arm/mach-s3c2412/cpu-freq.c +++ b/arch/arm/mach-s3c2412/cpu-freq.c @@ -194,7 +194,8 @@ static struct s3c_cpufreq_info s3c2412_cpufreq_info = { .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs), }; -static int s3c2412_cpufreq_add(struct device *dev) +static int s3c2412_cpufreq_add(struct device *dev, + struct subsys_interface *sif) { unsigned long fclk_rate; diff --git a/arch/arm/mach-s3c2412/dma.c b/arch/arm/mach-s3c2412/dma.c index 142acd3..38472ac 100644 --- a/arch/arm/mach-s3c2412/dma.c +++ b/arch/arm/mach-s3c2412/dma.c @@ -159,7 +159,8 @@ static struct s3c24xx_dma_selection __initdata s3c2412_dma_sel = { .map_size = ARRAY_SIZE(s3c2412_dma_mappings), }; -static int __init s3c2412_dma_add(struct device *dev) +static int __init s3c2412_dma_add(struct device *dev, + struct subsys_interface *sif) { s3c2410_dma_init(); return s3c24xx_dma_init_map(&s3c2412_dma_sel); diff --git a/arch/arm/mach-s3c2412/irq.c b/arch/arm/mach-s3c2412/irq.c index a8a46c1..e65619d 100644 --- a/arch/arm/mach-s3c2412/irq.c +++ b/arch/arm/mach-s3c2412/irq.c @@ -170,7 +170,7 @@ static int s3c2412_irq_rtc_wake(struct irq_data *data, unsigned int state) static struct irq_chip s3c2412_irq_rtc_chip; -static int s3c2412_irq_add(struct device *dev) +static int s3c2412_irq_add(struct device *dev, struct subsys_interface *sif) { unsigned int irqno; diff --git a/arch/arm/mach-s3c2412/pm.c b/arch/arm/mach-s3c2412/pm.c index d1adfa6..d045885 100644 --- a/arch/arm/mach-s3c2412/pm.c +++ b/arch/arm/mach-s3c2412/pm.c @@ -56,7 +56,7 @@ static void s3c2412_pm_prepare(void) { } -static int s3c2412_pm_add(struct device *dev) +static int s3c2412_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = s3c2412_pm_prepare; pm_cpu_sleep = s3c2412_cpu_suspend; diff --git a/arch/arm/mach-s3c2416/irq.c b/arch/arm/mach-s3c2416/irq.c index 36df761..fd49f35 100644 --- a/arch/arm/mach-s3c2416/irq.c +++ b/arch/arm/mach-s3c2416/irq.c @@ -213,7 +213,8 @@ static int __init s3c2416_add_sub(unsigned int base, return 0; } -static int __init s3c2416_irq_add(struct device *dev) +static int __init s3c2416_irq_add(struct device *dev, + struct subsys_interface *sif) { printk(KERN_INFO "S3C2416: IRQ Support\n"); diff --git a/arch/arm/mach-s3c2416/pm.c b/arch/arm/mach-s3c2416/pm.c index 3bdb15a..1bd4817 100644 --- a/arch/arm/mach-s3c2416/pm.c +++ b/arch/arm/mach-s3c2416/pm.c @@ -48,7 +48,7 @@ static void s3c2416_pm_prepare(void) __raw_writel(virt_to_phys(s3c_cpu_resume), S3C2412_INFORM1); } -static int s3c2416_pm_add(struct device *dev) +static int s3c2416_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = s3c2416_pm_prepare; pm_cpu_sleep = s3c2416_cpu_suspend; diff --git a/arch/arm/mach-s3c2440/clock.c b/arch/arm/mach-s3c2440/clock.c index bedbc87..414364e 100644 --- a/arch/arm/mach-s3c2440/clock.c +++ b/arch/arm/mach-s3c2440/clock.c @@ -149,7 +149,7 @@ static struct clk_lookup s3c2440_clk_lookup[] = { CLKDEV_INIT(NULL, "clk_uart_baud3", &s3c2440_clk_fclk_n), }; -static int s3c2440_clk_add(struct device *dev) +static int s3c2440_clk_add(struct device *dev, struct subsys_interface *sif) { struct clk *clock_upll; struct clk *clock_h; diff --git a/arch/arm/mach-s3c2440/dma.c b/arch/arm/mach-s3c2440/dma.c index 15b1ddf..5f0a0c8 100644 --- a/arch/arm/mach-s3c2440/dma.c +++ b/arch/arm/mach-s3c2440/dma.c @@ -174,7 +174,8 @@ static struct s3c24xx_dma_order __initdata s3c2440_dma_order = { }, }; -static int __init s3c2440_dma_add(struct device *dev) +static int __init s3c2440_dma_add(struct device *dev, + struct subsys_interface *sif) { s3c2410_dma_init(); s3c24xx_dma_order_set(&s3c2440_dma_order); diff --git a/arch/arm/mach-s3c2440/irq.c b/arch/arm/mach-s3c2440/irq.c index 4fee9bc..4a18cde 100644 --- a/arch/arm/mach-s3c2440/irq.c +++ b/arch/arm/mach-s3c2440/irq.c @@ -92,7 +92,7 @@ static struct irq_chip s3c_irq_wdtac97 = { .irq_ack = s3c_irq_wdtac97_ack, }; -static int s3c2440_irq_add(struct device *dev) +static int s3c2440_irq_add(struct device *dev, struct subsys_interface *sif) { unsigned int irqno; diff --git a/arch/arm/mach-s3c2440/s3c2440-cpufreq.c b/arch/arm/mach-s3c2440/s3c2440-cpufreq.c index cf75966..6177676 100644 --- a/arch/arm/mach-s3c2440/s3c2440-cpufreq.c +++ b/arch/arm/mach-s3c2440/s3c2440-cpufreq.c @@ -270,7 +270,8 @@ struct s3c_cpufreq_info s3c2440_cpufreq_info = { .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs), }; -static int s3c2440_cpufreq_add(struct device *dev) +static int s3c2440_cpufreq_add(struct device *dev, + struct subsys_interface *sif) { xtal = s3c_cpufreq_clk_get(NULL, "xtal"); hclk = s3c_cpufreq_clk_get(NULL, "hclk"); diff --git a/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c b/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c index b5368ae..551fb43 100644 --- a/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c +++ b/arch/arm/mach-s3c2440/s3c2440-pll-12000000.c @@ -51,7 +51,7 @@ static struct cpufreq_frequency_table s3c2440_plls_12[] __initdata = { { .frequency = 400000000, .index = PLLVAL(0x5c, 1, 1), }, /* FVco 800.000000 */ }; -static int s3c2440_plls12_add(struct device *dev) +static int s3c2440_plls12_add(struct device *dev, struct subsys_interface *sif) { struct clk *xtal_clk; unsigned long xtal; diff --git a/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c b/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c index 42f2b5c..3f15bcf 100644 --- a/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c +++ b/arch/arm/mach-s3c2440/s3c2440-pll-16934400.c @@ -79,7 +79,8 @@ static struct cpufreq_frequency_table s3c2440_plls_169344[] __initdata = { { .frequency = 402192000, .index = PLLVAL(87, 2, 1), }, /* FVco 804.384000 */ }; -static int s3c2440_plls169344_add(struct device *dev) +static int s3c2440_plls169344_add(struct device *dev, + struct subsys_interface *sif) { struct clk *xtal_clk; unsigned long xtal; diff --git a/arch/arm/mach-s3c2440/s3c2442.c b/arch/arm/mach-s3c2440/s3c2442.c index 8004e04..22cb7c9 100644 --- a/arch/arm/mach-s3c2440/s3c2442.c +++ b/arch/arm/mach-s3c2440/s3c2442.c @@ -122,7 +122,7 @@ static struct clk s3c2442_clk_cam_upll = { }, }; -static int s3c2442_clk_add(struct device *dev) +static int s3c2442_clk_add(struct device *dev, struct subsys_interface *sif) { struct clk *clock_upll; struct clk *clock_h; diff --git a/arch/arm/mach-s3c2440/s3c244x-clock.c b/arch/arm/mach-s3c2440/s3c244x-clock.c index b3fdbdd..6d9b688 100644 --- a/arch/arm/mach-s3c2440/s3c244x-clock.c +++ b/arch/arm/mach-s3c2440/s3c244x-clock.c @@ -72,7 +72,7 @@ static struct clk clk_arm = { }, }; -static int s3c244x_clk_add(struct device *dev) +static int s3c244x_clk_add(struct device *dev, struct subsys_interface *sif) { unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN); unsigned long clkdivn; diff --git a/arch/arm/mach-s3c2440/s3c244x-irq.c b/arch/arm/mach-s3c2440/s3c244x-irq.c index 74d3dcf..5fe8e58 100644 --- a/arch/arm/mach-s3c2440/s3c244x-irq.c +++ b/arch/arm/mach-s3c2440/s3c244x-irq.c @@ -91,7 +91,7 @@ static struct irq_chip s3c_irq_cam = { .irq_ack = s3c_irq_cam_ack, }; -static int s3c244x_irq_add(struct device *dev) +static int s3c244x_irq_add(struct device *dev, struct subsys_interface *sif) { unsigned int irqno; diff --git a/arch/arm/mach-s3c2443/dma.c b/arch/arm/mach-s3c2443/dma.c index de6b4a2..1422451 100644 --- a/arch/arm/mach-s3c2443/dma.c +++ b/arch/arm/mach-s3c2443/dma.c @@ -135,7 +135,8 @@ static struct s3c24xx_dma_selection __initdata s3c2443_dma_sel = { .map_size = ARRAY_SIZE(s3c2443_dma_mappings), }; -static int __init s3c2443_dma_add(struct device *dev) +static int __init s3c2443_dma_add(struct device *dev, + struct subsys_interface *sif) { s3c24xx_dma_init(6, IRQ_S3C2443_DMA0, 0x100); return s3c24xx_dma_init_map(&s3c2443_dma_sel); diff --git a/arch/arm/mach-s3c2443/irq.c b/arch/arm/mach-s3c2443/irq.c index 35e4ff2..ac2829f 100644 --- a/arch/arm/mach-s3c2443/irq.c +++ b/arch/arm/mach-s3c2443/irq.c @@ -241,7 +241,8 @@ static int __init s3c2443_add_sub(unsigned int base, return 0; } -static int __init s3c2443_irq_add(struct device *dev) +static int __init s3c2443_irq_add(struct device *dev, + struct subsys_interface *sif) { printk("S3C2443: IRQ Support\n"); diff --git a/arch/arm/mach-s5p64x0/pm.c b/arch/arm/mach-s5p64x0/pm.c index 23f9b22..9cba18b 100644 --- a/arch/arm/mach-s5p64x0/pm.c +++ b/arch/arm/mach-s5p64x0/pm.c @@ -160,7 +160,7 @@ static void s5p64x0_pm_prepare(void) } -static int s5p64x0_pm_add(struct device *dev) +static int s5p64x0_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = s5p64x0_pm_prepare; pm_cpu_sleep = s5p64x0_cpu_suspend; diff --git a/arch/arm/mach-s5pv210/pm.c b/arch/arm/mach-s5pv210/pm.c index 677c71c..736bfb1 100644 --- a/arch/arm/mach-s5pv210/pm.c +++ b/arch/arm/mach-s5pv210/pm.c @@ -133,7 +133,7 @@ static void s5pv210_pm_prepare(void) s3c_pm_do_save(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save)); } -static int s5pv210_pm_add(struct device *dev) +static int s5pv210_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = s5pv210_pm_prepare; pm_cpu_sleep = s5pv210_cpu_suspend; -- cgit v0.10.2 From 5d32d4868ad87a5be8571b22b3d732576504dfb0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 27 Dec 2011 15:01:28 +0100 Subject: watchdog: imx2_wdt.c: use devm_request_and_ioremap Reimplement a call to devm_request_mem_region followed by a call to ioremap or ioremap_nocache by a call to devm_request_and_ioremap. The variable res_size is then no longer needed. The semantic patch that makes this transformation is as follows: (http://coccinelle.lip6.fr/) // @nm@ expression myname; identifier i; @@ struct platform_driver i = { .driver = { .name = myname } }; @@ expression dev,res,size; expression nm.myname; @@ -if (!devm_request_mem_region(dev, res->start, size, - \(res->name\|dev_name(dev)\|myname\))) { - ... - return ...; -} ... when != res->start ( -devm_ioremap(dev,res->start,size) +devm_request_and_ioremap(dev,res) | -devm_ioremap_nocache(dev,res->start,size) +devm_request_and_ioremap(dev,res) ) ... when any when != res->start // Signed-off-by: Julia Lawall Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index b8ef2c6..c44c333 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -247,7 +247,6 @@ static struct miscdevice imx2_wdt_miscdev = { static int __init imx2_wdt_probe(struct platform_device *pdev) { int ret; - int res_size; struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -256,15 +255,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) return -ENODEV; } - res_size = resource_size(res); - if (!devm_request_mem_region(&pdev->dev, res->start, res_size, - res->name)) { - dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", - res_size, res->start); - return -ENOMEM; - } - - imx2_wdt.base = devm_ioremap_nocache(&pdev->dev, res->start, res_size); + imx2_wdt.base = devm_request_and_ioremap(&pdev->dev, res); if (!imx2_wdt.base) { dev_err(&pdev->dev, "ioremap failed\n"); return -ENOMEM; -- cgit v0.10.2 From 52ea9a7d7946c03aa1f9eeb71adb0191402724dd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 27 Dec 2011 15:01:29 +0100 Subject: watchdog: dw_wdt.c: use devm_request_and_ioremap Reimplement a call to devm_request_mem_region followed by a call to ioremap or ioremap_nocache by a call to devm_request_and_ioremap. The semantic patch that makes this transformation is as follows: (http://coccinelle.lip6.fr/) // @nm@ expression myname; identifier i; @@ struct platform_driver i = { .driver = { .name = myname } }; @@ expression dev,res,size; expression nm.myname; @@ -if (!devm_request_mem_region(dev, res->start, size, - \(res->name\|dev_name(dev)\|myname\))) { - ... - return ...; -} ... when != res->start ( -devm_ioremap(dev,res->start,size) +devm_request_and_ioremap(dev,res) | -devm_ioremap_nocache(dev,res->start,size) +devm_request_and_ioremap(dev,res) ) ... when any when != res->start // Signed-off-by: Julia Lawall Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 1b0e3dd..63d7b58 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -300,11 +300,7 @@ static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) if (!mem) return -EINVAL; - if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), - "dw_wdt")) - return -ENOMEM; - - dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + dw_wdt.regs = devm_request_and_ioremap(&pdev->dev, mem); if (!dw_wdt.regs) return -ENOMEM; -- cgit v0.10.2 From 12c583d8dca3320f36afe51e514dfa709ac0662e Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 11 Jan 2012 19:50:18 +0530 Subject: watchdog: omap_wdt.c: Fix the mismatch of pm_runtime enable and disable Currently the watchdog driver calls the pm_runtime_enable and never the disable. This may cause a warning when pm_runtime_enable checks for the count match. Also fixes the error /build/watchdog # insmod omap_wdt.ko [ 44.999389] omap_wdt omap_wdt: Unbalanced pm_runtime_enable! [ 45.011047] OMAP Watchdog Timer Rev 0x00: initial timeout 60 sec /build/watchdog # Attempting to fix the same by calling pm_runtime_disable. Signed-off-by: Shubhrajyoti D Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 4b33e3f..d19ff51 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -339,6 +339,7 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) return 0; err_misc: + pm_runtime_disable(wdev->dev); platform_set_drvdata(pdev, NULL); iounmap(wdev->base); @@ -371,6 +372,7 @@ static int __devexit omap_wdt_remove(struct platform_device *pdev) struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pm_runtime_disable(wdev->dev); if (!res) return -ENOENT; -- cgit v0.10.2 From 0318e286f9a73547dedfd9f733671f4941efb0e2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 14 Jan 2012 19:34:34 +0800 Subject: watchdog: via_wdt: Staticise wdt_pci_table It is only used in this driver, so no need to make the symbol global. Signed-off-by: Axel Lin Acked-by: Marc Vertes Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index 026b4bb..8b24b00 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -233,7 +233,7 @@ static void __devexit wdt_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = { +static DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) }, -- cgit v0.10.2 From e352829a67c1a80c73dfad33ba9dca8ddf2ef0fd Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 18 Jan 2012 10:45:20 +0800 Subject: watchdog: Staticise nuc900_wdt It is only used in this driver, so no need to make the symbol global. Signed-off-by: Axel Lin Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c index 50359ba..2cce856 100644 --- a/drivers/watchdog/nuc900_wdt.c +++ b/drivers/watchdog/nuc900_wdt.c @@ -72,7 +72,7 @@ struct nuc900_wdt { }; static unsigned long nuc900wdt_busy; -struct nuc900_wdt *nuc900_wdt; +static struct nuc900_wdt *nuc900_wdt; static inline void nuc900_wdt_keepalive(void) { -- cgit v0.10.2 From 2865e770c9dddd40676eadf7c3dfe80aee7628e4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 18 Jan 2012 10:46:52 +0800 Subject: watchdog: Return proper error in nuc900wdt_probe if misc_register fails Return proper error instead of 0 if misc_register fails Signed-off-by: Axel Lin Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c index 2cce856..529085b 100644 --- a/drivers/watchdog/nuc900_wdt.c +++ b/drivers/watchdog/nuc900_wdt.c @@ -287,7 +287,8 @@ static int __devinit nuc900wdt_probe(struct platform_device *pdev) setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0); - if (misc_register(&nuc900wdt_miscdev)) { + ret = misc_register(&nuc900wdt_miscdev); + if (ret) { dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n", WATCHDOG_MINOR, ret); goto err_clk; -- cgit v0.10.2 From ebe06e826faed9d710c3d49fe336c74b5f3a5f2a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 18 Jan 2012 19:25:01 +0800 Subject: watchdog: wm8350_wdt: Fix handling WDIOS_DISABLECARD/WDIOS_ENABLECARD options While receiving WDIOS_DISABLECARD option for WDIOC_SETOPTIONS command, call wm8350_wdt_stop() to disable watchdog. Call wm8350_wdt_start() while receiving WDIOS_ENABLECARD option. Current code has reverse behavior. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c index 909c786..5d7113c 100644 --- a/drivers/watchdog/wm8350_wdt.c +++ b/drivers/watchdog/wm8350_wdt.c @@ -212,10 +212,10 @@ static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd, /* Setting both simultaneously means at least one must fail */ if (options == WDIOS_DISABLECARD) - ret = wm8350_wdt_start(wm8350); + ret = wm8350_wdt_stop(wm8350); if (options == WDIOS_ENABLECARD) - ret = wm8350_wdt_stop(wm8350); + ret = wm8350_wdt_start(wm8350); break; } -- cgit v0.10.2 From 8a062ac693d5597b3c5d0e347ce7d1d82b967698 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 18 Jan 2012 19:26:43 +0800 Subject: watchdog: wafer5823wdt: Fix handling WDIOS_DISABLECARD/WDIOS_ENABLECARD options While receiving WDIOS_DISABLECARD option for WDIOC_SETOPTIONS command, call wafwdt_stop() to disable watchdog. Call wafwdt_start() while receiving WDIOS_ENABLECARD option. Current code has reverse behavior. Signed-off-by: Axel Lin Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c index 42e940c..c3c3188 100644 --- a/drivers/watchdog/wafer5823wdt.c +++ b/drivers/watchdog/wafer5823wdt.c @@ -152,12 +152,12 @@ static long wafwdt_ioctl(struct file *file, unsigned int cmd, return -EFAULT; if (options & WDIOS_DISABLECARD) { - wafwdt_start(); + wafwdt_stop(); retval = 0; } if (options & WDIOS_ENABLECARD) { - wafwdt_stop(); + wafwdt_start(); retval = 0; } -- cgit v0.10.2 From b1785dfd4fcd4011834f914810c1acb46b007a44 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 20 Jan 2012 23:56:19 +0900 Subject: watchdog: Fix typo "unexpectdly" Correct typo "unexpectdly" to "unexpectedly" in pnx4008_wdt.c and stmp3xxx_wdt.c Signed-off-by: Masanari Iida Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index bd143c9..8e210aa 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -226,7 +226,7 @@ static long pnx4008_wdt_ioctl(struct file *file, unsigned int cmd, static int pnx4008_wdt_release(struct inode *inode, struct file *file) { if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n"); + printk(KERN_WARNING "WATCHDOG: Device closed unexpectedly\n"); wdt_disable(); clk_disable(wdt_clk); diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c index 4c2a4e8..e37d811 100644 --- a/drivers/watchdog/stmp3xxx_wdt.c +++ b/drivers/watchdog/stmp3xxx_wdt.c @@ -174,7 +174,7 @@ static int stmp3xxx_wdt_release(struct inode *inode, struct file *file) if (!nowayout) { if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { wdt_ping(); - pr_debug("%s: Device closed unexpectdly\n", __func__); + pr_debug("%s: Device closed unexpectedly\n", __func__); ret = -EINVAL; } else { wdt_disable(); -- cgit v0.10.2 From f6dd94f8194408e11df4e33e1f7113612e84ca17 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 21 Jan 2012 15:08:38 +0800 Subject: watchdog: via_wdt: Set min_timeout and max_timeout for wdt_dev Let the watchdog core to check the valid value range of min_timeout/max_timeout. Signed-off-by: Axel Lin Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index 8b24b00..8f07dd4 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -124,8 +124,6 @@ static int wdt_stop(struct watchdog_device *wdd) static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout) { - if (new_timeout < 1 || new_timeout > WDT_TIMEOUT_MAX) - return -EINVAL; writel(new_timeout, wdt_mem + VIA_WDT_COUNT); timeout = new_timeout; return 0; @@ -150,6 +148,8 @@ static const struct watchdog_ops wdt_ops = { static struct watchdog_device wdt_dev = { .info = &wdt_info, .ops = &wdt_ops, + .min_timeout = 1, + .max_timeout = WDT_TIMEOUT_MAX, }; static int __devinit wdt_probe(struct pci_dev *pdev, -- cgit v0.10.2 From 84e83c2846ffb42772056a0f825d8578dc92d586 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Mon, 23 Jan 2012 16:40:55 -0800 Subject: watchdog: iTCO_wdt: add Intel Lynx Point DeviceIDs This patch adds the TCO Watchdog DeviceIDs for the Intel Lynx Point PCH. Signed-off-by: Seth Heasley Signed-off-by: Wim Van Sebroeck diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 99796c5..bdf401b 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -36,6 +36,7 @@ * document number TBD : Patsburg (PBG) * document number TBD : DH89xxCC * document number TBD : Panther Point + * document number TBD : Lynx Point */ /* @@ -126,6 +127,7 @@ enum iTCO_chipsets { TCO_PBG, /* Patsburg */ TCO_DH89XXCC, /* DH89xxCC */ TCO_PPT, /* Panther Point */ + TCO_LPT, /* Lynx Point */ }; static struct { @@ -189,6 +191,7 @@ static struct { {"Patsburg", 2}, {"DH89xxCC", 2}, {"Panther Point", 2}, + {"Lynx Point", 2}, {NULL, 0} }; @@ -331,6 +334,38 @@ static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT}, + { PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); -- cgit v0.10.2 From b5740f4b2cb3503b436925eb2242bc3d75cd3dfe Mon Sep 17 00:00:00 2001 From: Yasunori Goto Date: Tue, 17 Jan 2012 17:40:31 +0900 Subject: sched: Fix ancient race in do_exit() try_to_wake_up() has a problem which may change status from TASK_DEAD to TASK_RUNNING in race condition with SMI or guest environment of virtual machine. As a result, exited task is scheduled() again and panic occurs. Here is the sequence how it occurs: ----------------------------------+----------------------------- | CPU A | CPU B ----------------------------------+----------------------------- TASK A calls exit().... do_exit() exit_mm() down_read(mm->mmap_sem); rwsem_down_failed_common() set TASK_UNINTERRUPTIBLE set waiter.task <= task A list_add to sem->wait_list : raw_spin_unlock_irq() (I/O interruption occured) __rwsem_do_wake(mmap_sem) list_del(&waiter->list); waiter->task = NULL wake_up_process(task A) try_to_wake_up() (task is still TASK_UNINTERRUPTIBLE) p->on_rq is still 1.) ttwu_do_wakeup() (*A) : (I/O interruption handler finished) if (!waiter.task) schedule() is not called due to waiter.task is NULL. tsk->state = TASK_RUNNING : check_preempt_curr(); : task->state = TASK_DEAD (*B) <--- set TASK_RUNNING (*C) schedule() (exit task is running again) BUG_ON() is called! -------------------------------------------------------- The execution time between (*A) and (*B) is usually very short, because the interruption is disabled, and setting TASK_RUNNING at (*C) must be executed before setting TASK_DEAD. HOWEVER, if SMI is interrupted between (*A) and (*B), (*C) is able to execute AFTER setting TASK_DEAD! Then, exited task is scheduled again, and BUG_ON() is called.... If the system works on guest system of virtual machine, the time between (*A) and (*B) may be also long due to scheduling of hypervisor, and same phenomenon can occur. By this patch, do_exit() waits for releasing task->pi_lock which is used in try_to_wake_up(). It guarantees the task becomes TASK_DEAD after waking up. Signed-off-by: Yasunori Goto Acked-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/r/20120117174031.3118.E1E9C6FF@jp.fujitsu.com Signed-off-by: Ingo Molnar diff --git a/kernel/exit.c b/kernel/exit.c index 294b170..4b4042f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1038,6 +1038,22 @@ void do_exit(long code) if (tsk->nr_dirtied) __this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied); exit_rcu(); + + /* + * The setting of TASK_RUNNING by try_to_wake_up() may be delayed + * when the following two conditions become true. + * - There is race condition of mmap_sem (It is acquired by + * exit_mm()), and + * - SMI occurs before setting TASK_RUNINNG. + * (or hypervisor of virtual machine switches to other guest) + * As a result, we may become TASK_RUNNING after becoming TASK_DEAD + * + * To avoid it, we have to wait for releasing tsk->pi_lock which + * is held by try_to_wake_up() + */ + smp_mb(); + raw_spin_unlock_wait(&tsk->pi_lock); + /* causes final put_task_struct in finish_task_switch(). */ tsk->state = TASK_DEAD; tsk->flags |= PF_NOFREEZE; /* tell freezer to ignore us */ -- cgit v0.10.2 From e050e3f0a71bf7dc2c148b35caff0234decc8198 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 26 Jan 2012 17:03:19 +0100 Subject: perf: Fix broken interrupt rate throttling This patch fixes the sampling interrupt throttling mechanism. It was broken in v3.2. Events were not being unthrottled. The unthrottling mechanism required that events be checked at each timer tick. This patch solves this problem and also separates: - unthrottling - multiplexing - frequency-mode period adjustments Not all of them need to be executed at each timer tick. This third version of the patch is based on my original patch + PeterZ proposal (https://lkml.org/lkml/2012/1/7/87). At each timer tick, for each context: - if the current CPU has throttled events, we unthrottle events - if context has frequency-based events, we adjust sampling periods - if we have reached the jiffies interval, we multiplex (rotate) We decoupled rotation (multiplexing) from frequency-mode sampling period adjustments. They should not necessarily happen at the same rate. Multiplexing is subject to jiffies_interval (currently at 1 but could be higher once the tunable is exposed via sysfs). We have grouped frequency-mode adjustment and unthrottling into the same routine to minimize code duplication. When throttled while in frequency mode, we scan the events only once. We have fixed the threshold enforcement code in __perf_event_overflow(). There was a bug whereby it would allow more than the authorized rate because an increment of hwc->interrupts was not executed at the right place. The patch was tested with low sampling limit (2000) and fixed periods, frequency mode, overcommitted PMU. On a 2.1GHz AMD CPU: $ cat /proc/sys/kernel/perf_event_max_sample_rate 2000 We set a rate of 3000 samples/sec (2.1GHz/3000 = 700000): $ perf record -e cycles,cycles -c 700000 noploop 10 $ perf report -D | tail -21 Aggregated stats: TOTAL events: 80086 MMAP events: 88 COMM events: 2 EXIT events: 4 THROTTLE events: 19996 UNTHROTTLE events: 19996 SAMPLE events: 40000 cycles stats: TOTAL events: 40006 MMAP events: 5 COMM events: 1 EXIT events: 4 THROTTLE events: 9998 UNTHROTTLE events: 9998 SAMPLE events: 20000 cycles stats: TOTAL events: 39996 THROTTLE events: 9998 UNTHROTTLE events: 9998 SAMPLE events: 20000 For 10s, the cap is 2x2000x10 = 40000 samples. We get exactly that: 20000 samples/event. Signed-off-by: Stephane Eranian Cc: # v3.2+ Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20120126160319.GA5655@quad Signed-off-by: Ingo Molnar diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 0885561..abb2776 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -587,6 +587,7 @@ struct hw_perf_event { u64 sample_period; u64 last_period; local64_t period_left; + u64 interrupts_seq; u64 interrupts; u64 freq_time_stamp; diff --git a/kernel/events/core.c b/kernel/events/core.c index 32b48c8..ba36013 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2300,6 +2300,9 @@ do { \ return div64_u64(dividend, divisor); } +static DEFINE_PER_CPU(int, perf_throttled_count); +static DEFINE_PER_CPU(u64, perf_throttled_seq); + static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) { struct hw_perf_event *hwc = &event->hw; @@ -2325,16 +2328,29 @@ static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) } } -static void perf_ctx_adjust_freq(struct perf_event_context *ctx, u64 period) +/* + * combine freq adjustment with unthrottling to avoid two passes over the + * events. At the same time, make sure, having freq events does not change + * the rate of unthrottling as that would introduce bias. + */ +static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, + int needs_unthr) { struct perf_event *event; struct hw_perf_event *hwc; - u64 interrupts, now; + u64 now, period = TICK_NSEC; s64 delta; - if (!ctx->nr_freq) + /* + * only need to iterate over all events iff: + * - context have events in frequency mode (needs freq adjust) + * - there are events to unthrottle on this cpu + */ + if (!(ctx->nr_freq || needs_unthr)) return; + raw_spin_lock(&ctx->lock); + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (event->state != PERF_EVENT_STATE_ACTIVE) continue; @@ -2344,13 +2360,8 @@ static void perf_ctx_adjust_freq(struct perf_event_context *ctx, u64 period) hwc = &event->hw; - interrupts = hwc->interrupts; - hwc->interrupts = 0; - - /* - * unthrottle events on the tick - */ - if (interrupts == MAX_INTERRUPTS) { + if (needs_unthr && hwc->interrupts == MAX_INTERRUPTS) { + hwc->interrupts = 0; perf_log_throttle(event, 1); event->pmu->start(event, 0); } @@ -2358,14 +2369,26 @@ static void perf_ctx_adjust_freq(struct perf_event_context *ctx, u64 period) if (!event->attr.freq || !event->attr.sample_freq) continue; - event->pmu->read(event); + /* + * stop the event and update event->count + */ + event->pmu->stop(event, PERF_EF_UPDATE); + now = local64_read(&event->count); delta = now - hwc->freq_count_stamp; hwc->freq_count_stamp = now; + /* + * restart the event + * reload only if value has changed + */ if (delta > 0) perf_adjust_period(event, period, delta); + + event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); } + + raw_spin_unlock(&ctx->lock); } /* @@ -2388,16 +2411,13 @@ static void rotate_ctx(struct perf_event_context *ctx) */ static void perf_rotate_context(struct perf_cpu_context *cpuctx) { - u64 interval = (u64)cpuctx->jiffies_interval * TICK_NSEC; struct perf_event_context *ctx = NULL; - int rotate = 0, remove = 1, freq = 0; + int rotate = 0, remove = 1; if (cpuctx->ctx.nr_events) { remove = 0; if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) rotate = 1; - if (cpuctx->ctx.nr_freq) - freq = 1; } ctx = cpuctx->task_ctx; @@ -2405,37 +2425,26 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx) remove = 0; if (ctx->nr_events != ctx->nr_active) rotate = 1; - if (ctx->nr_freq) - freq = 1; } - if (!rotate && !freq) + if (!rotate) goto done; perf_ctx_lock(cpuctx, cpuctx->task_ctx); perf_pmu_disable(cpuctx->ctx.pmu); - if (freq) { - perf_ctx_adjust_freq(&cpuctx->ctx, interval); - if (ctx) - perf_ctx_adjust_freq(ctx, interval); - } - - if (rotate) { - cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); - if (ctx) - ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE); + cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); + if (ctx) + ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE); - rotate_ctx(&cpuctx->ctx); - if (ctx) - rotate_ctx(ctx); + rotate_ctx(&cpuctx->ctx); + if (ctx) + rotate_ctx(ctx); - perf_event_sched_in(cpuctx, ctx, current); - } + perf_event_sched_in(cpuctx, ctx, current); perf_pmu_enable(cpuctx->ctx.pmu); perf_ctx_unlock(cpuctx, cpuctx->task_ctx); - done: if (remove) list_del_init(&cpuctx->rotation_list); @@ -2445,10 +2454,22 @@ void perf_event_task_tick(void) { struct list_head *head = &__get_cpu_var(rotation_list); struct perf_cpu_context *cpuctx, *tmp; + struct perf_event_context *ctx; + int throttled; WARN_ON(!irqs_disabled()); + __this_cpu_inc(perf_throttled_seq); + throttled = __this_cpu_xchg(perf_throttled_count, 0); + list_for_each_entry_safe(cpuctx, tmp, head, rotation_list) { + ctx = &cpuctx->ctx; + perf_adjust_freq_unthr_context(ctx, throttled); + + ctx = cpuctx->task_ctx; + if (ctx) + perf_adjust_freq_unthr_context(ctx, throttled); + if (cpuctx->jiffies_interval == 1 || !(jiffies % cpuctx->jiffies_interval)) perf_rotate_context(cpuctx); @@ -4509,6 +4530,7 @@ static int __perf_event_overflow(struct perf_event *event, { int events = atomic_read(&event->event_limit); struct hw_perf_event *hwc = &event->hw; + u64 seq; int ret = 0; /* @@ -4518,14 +4540,20 @@ static int __perf_event_overflow(struct perf_event *event, if (unlikely(!is_sampling_event(event))) return 0; - if (unlikely(hwc->interrupts >= max_samples_per_tick)) { - if (throttle) { + seq = __this_cpu_read(perf_throttled_seq); + if (seq != hwc->interrupts_seq) { + hwc->interrupts_seq = seq; + hwc->interrupts = 1; + } else { + hwc->interrupts++; + if (unlikely(throttle + && hwc->interrupts >= max_samples_per_tick)) { + __this_cpu_inc(perf_throttled_count); hwc->interrupts = MAX_INTERRUPTS; perf_log_throttle(event, 0); ret = 1; } - } else - hwc->interrupts++; + } if (event->attr.freq) { u64 now = perf_clock(); -- cgit v0.10.2 From cb297a3e433dbdcf7ad81e0564e7b804c941ff0d Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Thu, 5 Jan 2012 20:00:19 +0900 Subject: sched/rt: Fix task stack corruption under __ARCH_WANT_INTERRUPTS_ON_CTXSW This issue happens under the following conditions: 1. preemption is off 2. __ARCH_WANT_INTERRUPTS_ON_CTXSW is defined 3. RT scheduling class 4. SMP system Sequence is as follows: 1.suppose current task is A. start schedule() 2.task A is enqueued pushable task at the entry of schedule() __schedule prev = rq->curr; ... put_prev_task put_prev_task_rt enqueue_pushable_task 4.pick the task B as next task. next = pick_next_task(rq); 3.rq->curr set to task B and context_switch is started. rq->curr = next; 4.At the entry of context_swtich, release this cpu's rq->lock. context_switch prepare_task_switch prepare_lock_switch raw_spin_unlock_irq(&rq->lock); 5.Shortly after rq->lock is released, interrupt is occurred and start IRQ context 6.try_to_wake_up() which called by ISR acquires rq->lock try_to_wake_up ttwu_remote rq = __task_rq_lock(p) ttwu_do_wakeup(rq, p, wake_flags); task_woken_rt 7.push_rt_task picks the task A which is enqueued before. task_woken_rt push_rt_tasks(rq) next_task = pick_next_pushable_task(rq) 8.At find_lock_lowest_rq(), If double_lock_balance() returns 0, lowest_rq can be the remote rq. (But,If preemption is on, double_lock_balance always return 1 and it does't happen.) push_rt_task find_lock_lowest_rq if (double_lock_balance(rq, lowest_rq)).. 9.find_lock_lowest_rq return the available rq. task A is migrated to the remote cpu/rq. push_rt_task ... deactivate_task(rq, next_task, 0); set_task_cpu(next_task, lowest_rq->cpu); activate_task(lowest_rq, next_task, 0); 10. But, task A is on irq context at this cpu. So, task A is scheduled by two cpus at the same time until restore from IRQ. Task A's stack is corrupted. To fix it, don't migrate an RT task if it's still running. Signed-off-by: Chanho Min Signed-off-by: Peter Zijlstra Acked-by: Steven Rostedt Cc: Link: http://lkml.kernel.org/r/CAOAMb1BHA=5fm7KTewYyke6u-8DP0iUuJMpgQw54vNeXFsGpoQ@mail.gmail.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 3640ebb..f42ae7f 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1587,6 +1587,11 @@ static int push_rt_task(struct rq *rq) if (!next_task) return 0; +#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW + if (unlikely(task_running(rq, next_task))) + return 0; +#endif + retry: if (unlikely(next_task == rq->curr)) { WARN_ON(1); -- cgit v0.10.2 From 2d8357e66df9f7593cbe23b224b4ed06aff90e73 Mon Sep 17 00:00:00 2001 From: Ryan Mallon Date: Fri, 27 Jan 2012 17:28:24 +1100 Subject: gma500: Fix suspend/resume functions Both the suspend and resume functions incorrectly set psbfb = to_psb_fb(NULL) outside of the loop over all of the framebuffers. Fix this by moving the assignment of psbfb inside the loop and removing the initialisation of fb. Signed-off-by: Ryan Mallon Acked-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 791c0ef..830dfdd6b 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -113,12 +113,12 @@ static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) void psbfb_suspend(struct drm_device *dev) { - struct drm_framebuffer *fb = 0; - struct psb_framebuffer *psbfb = to_psb_fb(fb); + struct drm_framebuffer *fb; console_lock(); mutex_lock(&dev->mode_config.mutex); list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + struct psb_framebuffer *psbfb = to_psb_fb(fb); struct fb_info *info = psbfb->fbdev; fb_set_suspend(info, 1); drm_fb_helper_blank(FB_BLANK_POWERDOWN, info); @@ -129,12 +129,12 @@ void psbfb_suspend(struct drm_device *dev) void psbfb_resume(struct drm_device *dev) { - struct drm_framebuffer *fb = 0; - struct psb_framebuffer *psbfb = to_psb_fb(fb); + struct drm_framebuffer *fb; console_lock(); mutex_lock(&dev->mode_config.mutex); list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + struct psb_framebuffer *psbfb = to_psb_fb(fb); struct fb_info *info = psbfb->fbdev; fb_set_suspend(info, 0); drm_fb_helper_blank(FB_BLANK_UNBLANK, info); -- cgit v0.10.2 From a389d67cf9849aff1722ed73186a584e2196a873 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 27 Jan 2012 14:31:19 +0100 Subject: ALSA: HDA: Remove quirk for Asus N53Jq The user reports that he needs to add model=auto for audio to work properly. In fact, since node 0x15 is not even a pin node, the existing fixup is definitely wrong. Relevant information can be found in the buglink below. Cc: stable@kernel.org (3.2+) BugLink: https://bugs.launchpad.net/bugs/918254 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0db1dc4..a7f17be 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5377,7 +5377,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC), SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC), -- cgit v0.10.2 From 9998eb703490589c3e8f1bf09b15203156776edb Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 25 Jan 2012 13:47:40 -0500 Subject: Btrfs: fix reservations in btrfs_page_mkwrite Josef fixed btrfs_page_mkwrite to properly release reserved extents if there was an error. But if we fail to get a reservation and we fail to dirty the inode (for ENOSPC reasons), we'll end up trying to release a reservation we never had. This makes sure we only release if we were able to reserve. Signed-off-by: Chris Mason diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5977987..7405753 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6401,18 +6401,23 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long zero_start; loff_t size; int ret; + int reserved = 0; u64 page_start; u64 page_end; ret = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE); - if (!ret) + if (!ret) { ret = btrfs_update_time(vma->vm_file); + reserved = 1; + } if (ret) { if (ret == -ENOMEM) ret = VM_FAULT_OOM; else /* -ENOSPC, -EIO, etc */ ret = VM_FAULT_SIGBUS; - goto out; + if (reserved) + goto out; + goto out_noreserve; } ret = VM_FAULT_NOPAGE; /* make the VM retry the fault */ @@ -6495,6 +6500,7 @@ out_unlock: unlock_page(page); out: btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE); +out_noreserve: return ret; } -- cgit v0.10.2 From 69e8f430e243d657c2053f097efebc2e2cd559f0 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 25 Jan 2012 00:13:20 -0500 Subject: xen/granttable: Disable grant v2 for HVM domains. As proper scaffolding for supporting error status is not yet implemented. BUG: unable to handle kernel NULL pointer dereference at 0000000000000400 IP: [] gnttab_end_foreign_access_ref_v2+0x29/0x40 PGD 32aa3067 PUD 32a87067 PMD 0 Oops: 0000 [#1] PREEMPT SMP CPU 0 Modules linked in: sg sr_mod cdrom ata_generic ata_piix libata scsi_mod xen_blkfront xen_netfront fb_sys_fops sysimgblt sysfillrect syscopyarea xen_kbdfront cmd Pid: 2307, comm: ip Not tainted 3.3.0-rc1 #1 Xen HVM domU RIP: 0010:[] [] gnttab_end_foreign_access_ref_v2+0x29/0x40 RSP: 0018:ffff88003be03d38 EFLAGS: 00010206 RAX: 0000000000000000 RBX: ffff880033210640 RCX: 0000000000000040 RDX: 0000000000002000 RSI: 0000000000000000 RDI: 0000000000000200 RBP: ffff88003be03d38 R08: 0000000000000101 R09: 0000000000000000 R10: dead000000100100 R11: 0000000000000000 R12: ffff88003be03e48 R13: 0000000000000001 R14: ffff880039461c00 R15: 0000000000000200 FS: 00007fb1f84ec700(0000) GS:ffff88003be00000(0000) knlGS:0000000000000000 ... Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 1cd94da..b4d4eac 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -948,9 +948,12 @@ static void gnttab_request_version(void) int rc; struct gnttab_set_version gsv; - gsv.version = 2; + if (xen_hvm_domain()) + gsv.version = 1; + else + gsv.version = 2; rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); - if (rc == 0) { + if (rc == 0 && gsv.version == 2) { grant_table_version = 2; gnttab_interface = &gnttab_v2_ops; } else if (grant_table_version == 2) { -- cgit v0.10.2 From f6f97588a42373a0181215a5f70958756f2492c2 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Sat, 21 Jan 2012 02:48:17 +0530 Subject: ARM: davinci: update mdio bus name Commit 5a05a8200a4359ef2bfe9094c137dee35cfdd516 ("davinci_emac: use an unique MDIO bus name") introduced during the v3.3 merge window updated the davinci mdio bus name to make it unique. Update the bus name in board files which use DaVinci MDIO bus to match the new name. Without this PHY is not detected with error like: PHY 0:01 not found net eth0: could not connect to phy 0:01 Tested on DM365 and DA850 EVMs. Cc: Florian Fainelli Cc: David S. Miller Signed-off-by: Sekhar Nori diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 6b22b54..d508890 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -44,7 +44,7 @@ #include #include -#define DA850_EVM_PHY_ID "0:00" +#define DA850_EVM_PHY_ID "davinci_mdio-0:00" #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) #define DA850_LCD_BL_PIN GPIO_TO_PIN(2, 15) diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 346e1de..849311d 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -54,7 +54,7 @@ static inline int have_tvp7002(void) return 0; } -#define DM365_EVM_PHY_ID "0:01" +#define DM365_EVM_PHY_ID "davinci_mdio-0:01" /* * A MAX-II CPLD is used for various board control functions. */ diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index a64b49c..1247ecd 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -40,7 +40,7 @@ #include #include -#define DM644X_EVM_PHY_ID "0:01" +#define DM644X_EVM_PHY_ID "davinci_mdio-0:01" #define LXT971_PHY_ID (0x001378e2) #define LXT971_PHY_MASK (0xfffffff0) diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 6401755..872ac69 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -736,7 +736,7 @@ static struct davinci_uart_config uart_config __initdata = { .enabled_uarts = (1 << 0), }; -#define DM646X_EVM_PHY_ID "0:01" +#define DM646X_EVM_PHY_ID "davinci_mdio-0:01" /* * The following EDMA channels/slots are not being used by drivers (for * example: Timer, GPIO, UART events etc) on dm646x, hence they are being diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index 6c4a164..8d34f51 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -39,7 +39,7 @@ #include #include -#define NEUROS_OSD2_PHY_ID "0:01" +#define NEUROS_OSD2_PHY_ID "davinci_mdio-0:01" #define LXT971_PHY_ID 0x001378e2 #define LXT971_PHY_MASK 0xfffffff0 diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index e7c0c7c..45e8157 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -21,7 +21,7 @@ #include #include -#define HAWKBOARD_PHY_ID "0:07" +#define HAWKBOARD_PHY_ID "davinci_mdio-0:07" #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) #define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index 0b136a8..31da3c5 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -42,7 +42,7 @@ #include #include -#define SFFSDR_PHY_ID "0:01" +#define SFFSDR_PHY_ID "davinci_mdio-0:01" static struct mtd_partition davinci_sffsdr_nandflash_partition[] = { /* U-Boot Environment: Block 0 * UBL: Block 1 -- cgit v0.10.2 From e47e321a35c741ee41b67976f8c6a3a7a42bc5c0 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Fri, 20 Jan 2012 18:43:54 +0000 Subject: RDMA/core: Fix kernel panic by always initializing qp->usecnt We have just been investigating kernel panics related to cq->ibcq.event_handler() completion calls. The problem is that ib_destroy_qp() fails with -EBUSY. Further investigation revealed qp->usecnt is not initialized. This counter was introduced in linux-3.2 by commit 0e0ec7e0638e ("RDMA/core: Export ib_open_qp() to share XRC TGT QPs") but it only gets initialized for IB_QPT_XRC_TGT, but it is checked in ib_destroy_qp() for any QP type. Fix this by initializing qp->usecnt for every QP we create. Signed-off-by: Bernd Schubert Signed-off-by: Sven Breuner [ Initialize qp->usecnt in uverbs too. - Sean ] Signed-off-by: Sean Hefty Cc: Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index b930da4..4d27e4c3 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -1485,6 +1485,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file, qp->event_handler = attr.event_handler; qp->qp_context = attr.qp_context; qp->qp_type = attr.qp_type; + atomic_set(&qp->usecnt, 0); atomic_inc(&pd->usecnt); atomic_inc(&attr.send_cq->usecnt); if (attr.recv_cq) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 602b1bd..575b780 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -421,6 +421,7 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, qp->uobject = NULL; qp->qp_type = qp_init_attr->qp_type; + atomic_set(&qp->usecnt, 0); if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) { qp->event_handler = __ib_shared_qp_event_handler; qp->qp_context = qp; @@ -430,7 +431,6 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, qp->xrcd = qp_init_attr->xrcd; atomic_inc(&qp_init_attr->xrcd->usecnt); INIT_LIST_HEAD(&qp->open_list); - atomic_set(&qp->usecnt, 0); real_qp = qp; qp = __ib_open_qp(real_qp, qp_init_attr->event_handler, -- cgit v0.10.2 From ef5352875a12a0a45be8bca29fc218a94786d920 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 26 Jan 2012 09:15:29 +0300 Subject: IB/ipath: Calling PTR_ERR() on right variable in create_file() "dentry" is a valid pointer. "*dentry" was intended. Signed-off-by: Dan Carpenter Acked-by: Mike Marciniszyn Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c index b7d4216..a4de9d5 100644 --- a/drivers/infiniband/hw/ipath/ipath_fs.c +++ b/drivers/infiniband/hw/ipath/ipath_fs.c @@ -89,7 +89,7 @@ static int create_file(const char *name, umode_t mode, error = ipathfs_mknod(parent->d_inode, *dentry, mode, fops, data); else - error = PTR_ERR(dentry); + error = PTR_ERR(*dentry); mutex_unlock(&parent->d_inode->i_mutex); return error; -- cgit v0.10.2 From 81f99dcc93226c7accfe5c4edad1749b8aebf7db Mon Sep 17 00:00:00 2001 From: Tatyana Nikolova Date: Tue, 17 Jan 2012 10:17:30 -0600 Subject: RDMA/nes: Fix for sending MPA reject frame Set a reject flag, when sending MPA reject message to inform the peer that the application has rejected the connection. Signed-off-by: Tatyana Nikolova Signed-off-by: Faisal Latif Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 425065b..183f7ab 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -233,6 +233,7 @@ static int send_mpa_reject(struct nes_cm_node *cm_node) u8 *start_ptr = &start_addr; u8 **start_buff = &start_ptr; u16 buff_len = 0; + struct ietf_mpa_v1 *mpa_frame; skb = dev_alloc_skb(MAX_CM_BUFFER); if (!skb) { @@ -242,6 +243,8 @@ static int send_mpa_reject(struct nes_cm_node *cm_node) /* send an MPA reject frame */ cm_build_mpa_frame(cm_node, start_buff, &buff_len, NULL, MPA_KEY_REPLY); + mpa_frame = (struct ietf_mpa_v1 *)*start_buff; + mpa_frame->flags |= IETF_MPA_FLAGS_REJECT; form_cm_frame(skb, cm_node, NULL, 0, *start_buff, buff_len, SET_ACK | SET_FIN); cm_node->state = NES_CM_STATE_FIN_WAIT1; -- cgit v0.10.2 From 7525c85be0e6d18596390e7e2b17a206cd9777f6 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 27 Jan 2012 09:54:30 -0800 Subject: RDMA/nes: Add missing rcu_read_unlock() in nes_addr_resolve_neigh() Make sure all exit paths from this function unlock everything. Reported-by: Bart Van Assche Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 183f7ab..271279c 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1363,8 +1363,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi if (!memcmp(nesadapter->arp_table[arpindex].mac_addr, neigh->ha, ETH_ALEN)) { /* Mac address same as in nes_arp_table */ - ip_rt_put(rt); - return rc; + goto out; } nes_manage_arp_cache(nesvnic->netdev, @@ -1380,6 +1379,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi neigh_event_send(neigh, NULL); } } + +out: rcu_read_unlock(); ip_rt_put(rt); return rc; -- cgit v0.10.2 From 0f3696eb21ef39b7bb7ea7d0160e925904309281 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 9 Jan 2012 10:40:47 +0100 Subject: IB/qib: Use GFP_ATOMIC when locks are held alloc_dummy_hdrq() is called with locks held and thus should not use GFP_KERNEL. The semantic patch that makes this report is available in scripts/coccinelle/locks/call_kern.cocci. Signed-off-by: Julia Lawall Acked-by: Mike Marciniszyn Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c index 4f18e2d..d0c64d5 100644 --- a/drivers/infiniband/hw/qib/qib_iba6120.c +++ b/drivers/infiniband/hw/qib/qib_iba6120.c @@ -2105,7 +2105,7 @@ static void alloc_dummy_hdrq(struct qib_devdata *dd) dd->cspec->dummy_hdrq = dma_alloc_coherent(&dd->pcidev->dev, dd->rcd[0]->rcvhdrq_size, &dd->cspec->dummy_hdrq_phys, - GFP_KERNEL | __GFP_COMP); + GFP_ATOMIC | __GFP_COMP); if (!dd->cspec->dummy_hdrq) { qib_devinfo(dd->pcidev, "Couldn't allocate dummy hdrq\n"); /* fallback to just 0'ing */ -- cgit v0.10.2 From b6bfefb0410dc49853bccd9673ead896d317c082 Mon Sep 17 00:00:00 2001 From: Mike Marciniszyn Date: Thu, 12 Jan 2012 21:29:59 -0500 Subject: IB/qib: Roll back PCIe tuning change Commit 8d4548f2b ("IB/qib: Default some module parameters optimally") introduced an issue with older root complexes. They cannot handle the pcie_caps of 0x51 (MaxReadReq 4096, MaxPayload=256). A typical diagnostic in this situation reported by syslog contains the text: [PCIe Poisoned TLP][Send DMA memory read] Restore the module paramter default to zero with will avoid any changes in the root complex. Reviewed-by: Mark Debbage Signed-off-by: Mike Marciniszyn Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index f695061..0fde788 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -560,7 +560,7 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd) * BIOS may not set PCIe bus-utilization parameters for best performance. * Check and optionally adjust them to maximize our throughput. */ -static int qib_pcie_caps = 0x51; +static int qib_pcie_caps; module_param_named(pcie_caps, qib_pcie_caps, int, S_IRUGO); MODULE_PARM_DESC(pcie_caps, "Max PCIe tuning: Payload (0..3), ReadReq (4..7)"); -- cgit v0.10.2 From 9ced69ca5296567033804950d8d2161f454c5012 Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Tue, 10 Jan 2012 23:53:41 +0000 Subject: RDMA/ucma: Discard all events for new connections until accepted After reporting a new connection request to user space, the rdma_ucm will discard subsequent events until the user has associated a user space idenfier with the kernel cm_id. This is needed to avoid reporting a reject/disconnect event to the user for a request that they may not have processed. The user space identifier is set once the user tries to accept the connection request. However, the following race exists in ucma_accept(): ctx->uid = cmd.uid; ret = rdma_accept(ctx->cm_id, ...); Once ctx->uid has been set, new events may be reported to the user. While the above mentioned race is avoided, there is an issue that the user _may_ receive a reject/disconnect event if rdma_accept() fails, depending on when the event is processed. To simplify the use of rdma_accept(), discard all events unless rdma_accept() succeeds. This problem was discovered based on questions from Roland Dreier . Signed-off-by: Sean Hefty Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index b37b0c0..5034a87 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -808,9 +808,12 @@ static ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf, return PTR_ERR(ctx); if (cmd.conn_param.valid) { - ctx->uid = cmd.uid; ucma_copy_conn_param(&conn_param, &cmd.conn_param); + mutex_lock(&file->mut); ret = rdma_accept(ctx->cm_id, &conn_param); + if (!ret) + ctx->uid = cmd.uid; + mutex_unlock(&file->mut); } else ret = rdma_accept(ctx->cm_id, NULL); -- cgit v0.10.2 From 94f622bdac82dd0542741382ea8c9359fd9c163b Mon Sep 17 00:00:00 2001 From: Tatyana Nikolova Date: Fri, 6 Jan 2012 18:15:01 -0600 Subject: RDMA/nes: Fix fast memory registration length Zero high order word of fast memory registration (FMR) length field. FMR length field is 32 bits, so high word should always be zero. Signed-off-by: Tatyana Nikolova Signed-off-by: Donald Wood Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 5095bc4..ba2eb00 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -3428,6 +3428,8 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, NES_IWARP_SQ_FMR_WQE_LENGTH_LOW_IDX, ib_wr->wr.fast_reg.length); set_wqe_32bit_value(wqe->wqe_words, + NES_IWARP_SQ_FMR_WQE_LENGTH_HIGH_IDX, 0); + set_wqe_32bit_value(wqe->wqe_words, NES_IWARP_SQ_FMR_WQE_MR_STAG_IDX, ib_wr->wr.fast_reg.rkey); /* Set page size: */ -- cgit v0.10.2 From 4a4b03f4efdcf9f4dd8ce68f305c5fb8ff8e56c4 Mon Sep 17 00:00:00 2001 From: Tatyana Nikolova Date: Fri, 6 Jan 2012 18:17:19 -0600 Subject: RDMA/nes: Fix fast memory registration opcode Fix fast memory registration opcode in local invalidate completion. Signed-off-by: Tatyana Nikolova Signed-off-by: Donald Wood Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index ba2eb00..3875365 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -3726,7 +3726,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) entry->opcode = IB_WC_SEND; break; case NES_IWARP_SQ_OP_LOCINV: - entry->opcode = IB_WR_LOCAL_INV; + entry->opcode = IB_WC_LOCAL_INV; break; case NES_IWARP_SQ_OP_FAST_REG: entry->opcode = IB_WC_FAST_REG_MR; -- cgit v0.10.2 From 114395c61ad2eb5a7a5cd163fcadb2414e48245a Mon Sep 17 00:00:00 2001 From: UK KIM Date: Sat, 28 Jan 2012 01:52:22 +0900 Subject: ASoC: wm_hubs: fix wrong bits for LINEOUT2 N/P mixer Signed-off-by: UK KIM Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 9ccc416..ea26724 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -592,8 +592,8 @@ SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), }; static const struct snd_kcontrol_new line2n_mix[] = { -SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), -SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), }; static const struct snd_kcontrol_new line2p_mix[] = { -- cgit v0.10.2 From 3c424f359898aff48c3d5bed608ac706f8a528c3 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 26 Jan 2012 11:47:11 +0100 Subject: ARM: 7304/1: ioremap: fix boundary check when reusing static mapping Since commit 576d2f2525612ecb5af029a76f21f22a3b82563d "ARM: add generic ioremap optimization by reusing static mappings" ioremap() is trying to reuse existing static mapping when possible. The condition checking boundaries of the requested and existing mappings didn't take in-page offset into consideration though, which lead to obscure and hard to debug problems when requested mapping crossed end of the static one. Signed-off-by: Pawel Moll Acked-by: Nicolas Pitre Signed-off-by: Russell King diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 80632e8..ba15937 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -225,7 +225,8 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, if ((area->flags & VM_ARM_MTYPE_MASK) != VM_ARM_MTYPE(mtype)) continue; if (__phys_to_pfn(area->phys_addr) > pfn || - __pfn_to_phys(pfn) + size-1 > area->phys_addr + area->size-1) + __pfn_to_phys(pfn) + offset + size-1 > + area->phys_addr + area->size-1) continue; /* we can drop the lock here as we know *area is static */ read_unlock(&vmlist_lock); -- cgit v0.10.2 From 4dbc5d9f4f791df8a5879f4a655f517adc7f56d1 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 20 Jan 2012 16:16:59 +0800 Subject: virtio: fix typos of memory barriers Note: this fixes a bug introduced recently in 7b21e34fd1c272e3a8c3846168f2f6287a4cd72b. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: Rusty Russell diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 79e1b29..78428a8 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -35,7 +35,7 @@ #define virtio_rmb(vq) \ do { if ((vq)->weak_barriers) smp_rmb(); else rmb(); } while(0) #define virtio_wmb(vq) \ - do { if ((vq)->weak_barriers) smp_rmb(); else rmb(); } while(0) + do { if ((vq)->weak_barriers) smp_wmb(); else wmb(); } while(0) #else /* We must force memory ordering even if guest is UP since host could be * running on another CPU, but SMP barriers are defined to barrier() in that -- cgit v0.10.2 From a72caae21803b74e04e2afda5e035f149d4ea118 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 20 Jan 2012 16:17:08 +0800 Subject: virtio: correct the memory barrier in virtqueue_kick_prepare() Use virtio_mb() to make sure the available index to be exposed before checking the the avail event. Otherwise we may get stale value of avail event in guest and never kick the host after. Note: this fixes a bug introduced by ee7cd8981e15bcb365fc762afe3fc47b8242f630. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: Rusty Russell Cc: stable@kernel.org diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 78428a8..5aa43c3 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -308,9 +308,9 @@ bool virtqueue_kick_prepare(struct virtqueue *_vq) bool needs_kick; START_USE(vq); - /* Descriptors and available array need to be set before we expose the - * new available array entries. */ - virtio_wmb(vq); + /* We need to expose available array entries before checking avail + * event. */ + virtio_mb(vq); old = vq->vring.avail->idx - vq->num_added; new = vq->vring.avail->idx; -- cgit v0.10.2 From 13289d5f2b2ee73583e6c65c46a1e0cd48c3ddc0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 28 Jan 2012 08:10:23 +1030 Subject: lguest: remove reference from Documentation/virtual/00-INDEX We're in tools/lguest now. Reported-by: Stephen Hemminger Signed-off-by: Rusty Russell diff --git a/Documentation/virtual/00-INDEX b/Documentation/virtual/00-INDEX index 8e60199..924bd46 100644 --- a/Documentation/virtual/00-INDEX +++ b/Documentation/virtual/00-INDEX @@ -4,8 +4,6 @@ Virtualization support in the Linux kernel. - this file. kvm/ - Kernel Virtual Machine. See also http://linux-kvm.org -lguest/ - - Extremely simple hypervisor for experimental/educational use. uml/ - User Mode Linux, builds/runs Linux kernel as a userspace program. virtio.txt -- cgit v0.10.2 From 5ee4433efe99b9f39f6eff5052a177bbcfe72cea Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 26 Jan 2012 14:02:55 +0000 Subject: netns: Fail conspicously if someone uses net_generic at an inappropriate time. By definition net_generic should never be called when it can return NULL. Fail conspicously with a BUG_ON to make it clear when people mess up that a NULL return should never happen. Recently there was a bug in the CAIF subsystem where it was registered with register_pernet_device instead of register_pernet_subsys. It was erroneously concluded that net_generic could validly return NULL and that net_assign_generic was buggy (when it was just inefficient). Hopefully this BUG_ON will prevent people to coming to similar erroneous conclusions in the futrue. Signed-off-by: Eric W. Biederman Tested-by: Sasha Levin Signed-off-by: David S. Miller diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h index 3419bf5..d55f434 100644 --- a/include/net/netns/generic.h +++ b/include/net/netns/generic.h @@ -41,6 +41,7 @@ static inline void *net_generic(const struct net *net, int id) ptr = ng->ptr[id - 1]; rcu_read_unlock(); + BUG_ON(!ptr); return ptr; } #endif -- cgit v0.10.2 From 8a8ee9aff6c3077dd9c2c7a77478e8ed362b96c6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 26 Jan 2012 14:04:53 +0000 Subject: net caif: Register properly as a pernet subsystem. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit caif is a subsystem and as such it needs to register with register_pernet_subsys instead of register_pernet_device. Among other problems using register_pernet_device was resulting in net_generic being called before the caif_net structure was allocated. Which has been causing net_generic to fail with either BUG_ON's or by return NULL pointers. A more ugly problem that could be caused is packets in flight why the subsystem is shutting down. To remove confusion also remove the cruft cause by inappropriately trying to fix this bug. With the aid of the previous patch I have tested this patch and confirmed that using register_pernet_subsys makes the failure go away as it should. Signed-off-by: Eric W. Biederman Acked-by: Sjur Brændeland Tested-by: Sasha Levin Signed-off-by: David S. Miller diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 673728a..82c5706 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -59,8 +59,6 @@ struct cfcnfg *get_cfcnfg(struct net *net) { struct caif_net *caifn; caifn = net_generic(net, caif_net_id); - if (!caifn) - return NULL; return caifn->cfg; } EXPORT_SYMBOL(get_cfcnfg); @@ -69,8 +67,6 @@ static struct caif_device_entry_list *caif_device_list(struct net *net) { struct caif_net *caifn; caifn = net_generic(net, caif_net_id); - if (!caifn) - return NULL; return &caifn->caifdevs; } @@ -99,8 +95,6 @@ static struct caif_device_entry *caif_device_alloc(struct net_device *dev) struct caif_device_entry *caifd; caifdevs = caif_device_list(dev_net(dev)); - if (!caifdevs) - return NULL; caifd = kzalloc(sizeof(*caifd), GFP_KERNEL); if (!caifd) @@ -120,8 +114,6 @@ static struct caif_device_entry *caif_get(struct net_device *dev) struct caif_device_entry_list *caifdevs = caif_device_list(dev_net(dev)); struct caif_device_entry *caifd; - if (!caifdevs) - return NULL; list_for_each_entry_rcu(caifd, &caifdevs->list, list) { if (caifd->netdev == dev) @@ -321,8 +313,6 @@ void caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev, struct caif_device_entry_list *caifdevs; caifdevs = caif_device_list(dev_net(dev)); - if (!cfg || !caifdevs) - return; caifd = caif_device_alloc(dev); if (!caifd) return; @@ -374,8 +364,6 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, cfg = get_cfcnfg(dev_net(dev)); caifdevs = caif_device_list(dev_net(dev)); - if (!cfg || !caifdevs) - return 0; caifd = caif_get(dev); if (caifd == NULL && dev->type != ARPHRD_CAIF) @@ -507,9 +495,6 @@ static struct notifier_block caif_device_notifier = { static int caif_init_net(struct net *net) { struct caif_net *caifn = net_generic(net, caif_net_id); - if (WARN_ON(!caifn)) - return -EINVAL; - INIT_LIST_HEAD(&caifn->caifdevs.list); mutex_init(&caifn->caifdevs.lock); @@ -527,9 +512,6 @@ static void caif_exit_net(struct net *net) caif_device_list(net); struct cfcnfg *cfg = get_cfcnfg(net); - if (!cfg || !caifdevs) - return; - rtnl_lock(); mutex_lock(&caifdevs->lock); @@ -569,7 +551,7 @@ static int __init caif_device_init(void) { int result; - result = register_pernet_device(&caif_net_ops); + result = register_pernet_subsys(&caif_net_ops); if (result) return result; @@ -582,7 +564,7 @@ static int __init caif_device_init(void) static void __exit caif_device_exit(void) { - unregister_pernet_device(&caif_net_ops); + unregister_pernet_subsys(&caif_net_ops); unregister_netdevice_notifier(&caif_device_notifier); dev_remove_pack(&caif_packet_type); } diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index 598aafb..ba9cfd4 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -309,7 +309,6 @@ int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, int err; struct cfctrl_link_param param; struct cfcnfg *cfg = get_cfcnfg(net); - caif_assert(cfg != NULL); rcu_read_lock(); err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); -- cgit v0.10.2 From 96150606e2fb82d242c9e4a414e4e922849f7bf7 Mon Sep 17 00:00:00 2001 From: Prasad Joshi Date: Sat, 26 Nov 2011 11:00:47 +0530 Subject: logfs: update page reference count for pined pages LogFS sets PG_private flag to indicate a pined page. We assumed that marking a page as private is enough to ensure its existence. But instead it is necessary to hold a reference count to the page. The change resolves the following BUG BUG: Bad page state in process flush-253:16 pfn:6a6d0 page flags: 0x100000000000808(uptodate|private) Suggested-and-Acked-by: Joern Engel Signed-off-by: Prasad Joshi diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 2ac4217..6d663e8 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -560,8 +560,13 @@ static void inode_free_block(struct super_block *sb, struct logfs_block *block) static void indirect_free_block(struct super_block *sb, struct logfs_block *block) { - ClearPagePrivate(block->page); - block->page->private = 0; + struct page *page = block->page; + + if (PagePrivate(page)) { + ClearPagePrivate(page); + page_cache_release(page); + set_page_private(page, 0); + } __free_block(sb, block); } @@ -650,8 +655,11 @@ static void alloc_data_block(struct inode *inode, struct page *page) logfs_unpack_index(page->index, &bix, &level); block = __alloc_block(inode->i_sb, inode->i_ino, bix, level); block->page = page; + SetPagePrivate(page); - page->private = (unsigned long)block; + page_cache_get(page); + set_page_private(page, (unsigned long) block); + block->ops = &indirect_block_ops; } @@ -1901,8 +1909,11 @@ static void move_page_to_inode(struct inode *inode, struct page *page) li->li_block = block; block->page = NULL; - page->private = 0; - ClearPagePrivate(page); + if (PagePrivate(page)) { + ClearPagePrivate(page); + page_cache_release(page); + set_page_private(page, 0); + } } static void move_inode_to_page(struct page *page, struct inode *inode) @@ -1918,8 +1929,12 @@ static void move_inode_to_page(struct page *page, struct inode *inode) BUG_ON(PagePrivate(page)); block->ops = &indirect_block_ops; block->page = page; - page->private = (unsigned long)block; - SetPagePrivate(page); + + if (!PagePrivate(page)) { + SetPagePrivate(page); + page_cache_get(page); + set_page_private(page, (unsigned long) block); + } block->inode = NULL; li->li_block = NULL; diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index 9d51873..6aee609 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c @@ -86,7 +86,11 @@ int __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len, BUG_ON(!page); /* FIXME: reserve a pool */ SetPageUptodate(page); memcpy(page_address(page) + offset, buf, copylen); - SetPagePrivate(page); + + if (!PagePrivate(page)) { + SetPagePrivate(page); + page_cache_get(page); + } page_cache_release(page); buf += copylen; @@ -110,7 +114,10 @@ static void pad_partial_page(struct logfs_area *area) page = get_mapping_page(sb, index, 0); BUG_ON(!page); /* FIXME: reserve a pool */ memset(page_address(page) + offset, 0xff, len); - SetPagePrivate(page); + if (!PagePrivate(page)) { + SetPagePrivate(page); + page_cache_get(page); + } page_cache_release(page); } } @@ -130,7 +137,10 @@ static void pad_full_pages(struct logfs_area *area) BUG_ON(!page); /* FIXME: reserve a pool */ SetPageUptodate(page); memset(page_address(page), 0xff, PAGE_CACHE_SIZE); - SetPagePrivate(page); + if (!PagePrivate(page)) { + SetPagePrivate(page); + page_cache_get(page); + } page_cache_release(page); index++; no_indizes--; @@ -485,8 +495,12 @@ static void move_btree_to_page(struct inode *inode, struct page *page, mempool_free(item, super->s_alias_pool); } block->page = page; - SetPagePrivate(page); - page->private = (unsigned long)block; + + if (!PagePrivate(page)) { + SetPagePrivate(page); + page_cache_get(page); + set_page_private(page, (unsigned long) block); + } block->ops = &indirect_block_ops; initialize_block_counters(page, block, data, 0); } @@ -536,8 +550,12 @@ void move_page_to_btree(struct page *page) list_add(&item->list, &block->item_list); } block->page = NULL; - ClearPagePrivate(page); - page->private = 0; + + if (PagePrivate(page)) { + ClearPagePrivate(page); + page_cache_release(page); + set_page_private(page, 0); + } block->ops = &btree_block_ops; err = alias_tree_insert(block->sb, block->ino, block->bix, block->level, block); @@ -702,7 +720,10 @@ void freeseg(struct super_block *sb, u32 segno) page = find_get_page(mapping, ofs >> PAGE_SHIFT); if (!page) continue; - ClearPagePrivate(page); + if (PagePrivate(page)) { + ClearPagePrivate(page); + page_cache_release(page); + } page_cache_release(page); } } -- cgit v0.10.2 From 934eed395d201bf0901ca0c0cc3703b18729d0ce Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Sun, 20 Nov 2011 22:29:01 +0530 Subject: logfs: Prevent memory corruption This is a bad one. I wonder whether we were so far protected by no_free_segments(sb) usually being smaller than LOGFS_NO_AREAS. Found by Dan Carpenter using smatch. Signed-off-by: Joern Engel Signed-off-by: Prasad Joshi diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c index caa4419..d4efb06 100644 --- a/fs/logfs/gc.c +++ b/fs/logfs/gc.c @@ -367,7 +367,7 @@ static struct gc_candidate *get_candidate(struct super_block *sb) int i, max_dist; struct gc_candidate *cand = NULL, *this; - max_dist = min(no_free_segments(sb), LOGFS_NO_AREAS); + max_dist = min(no_free_segments(sb), LOGFS_NO_AREAS - 1); for (i = max_dist; i >= 0; i--) { this = first_in_list(&super->s_low_list[i]); -- cgit v0.10.2 From 13ced29cb28996a9bc4f68e43ff0c57eafdb1e21 Mon Sep 17 00:00:00 2001 From: Prasad Joshi Date: Sat, 28 Jan 2012 11:36:06 +0530 Subject: logfs: take write mutex lock during fsync and sync LogFS uses super->s_write_mutex while writing data to disk. Taking the same mutex lock in sync and fsync code path solves the following BUG: ------------[ cut here ]------------ kernel BUG at /home/prasad/logfs/dev_bdev.c:134! Pid: 2387, comm: flush-253:16 Not tainted 3.0.0+ #4 Bochs Bochs RIP: 0010:[] [] bdev_writeseg+0x25d/0x270 [logfs] Call Trace: [] logfs_open_area+0x91/0x150 [logfs] [] ? find_level.clone.9+0x62/0x100 [] __logfs_segment_write.clone.20+0x5c/0x190 [logfs] [] ? mempool_kmalloc+0x15/0x20 [] ? mempool_alloc+0x53/0x130 [] logfs_segment_write+0x1d4/0x230 [logfs] [] logfs_write_i0+0x12e/0x190 [logfs] [] __logfs_write_rec+0x140/0x220 [logfs] [] logfs_write_rec+0x64/0xd0 [logfs] [] __logfs_write_buf+0x106/0x110 [logfs] [] logfs_write_buf+0x4e/0x80 [logfs] [] __logfs_writepage+0x23/0x80 [logfs] [] logfs_writepage+0xdc/0x110 [logfs] [] __writepage+0x17/0x40 [] write_cache_pages+0x208/0x4f0 [] ? set_page_dirty+0x70/0x70 [] generic_writepages+0x4a/0x70 [] do_writepages+0x21/0x40 [] writeback_single_inode+0x101/0x250 [] writeback_sb_inodes+0xed/0x1c0 [] writeback_inodes_wb+0x7b/0x1e0 [] wb_writeback+0x4c3/0x530 [] ? sub_preempt_count+0x9d/0xd0 [] wb_do_writeback+0xdb/0x290 [] ? sub_preempt_count+0x9d/0xd0 [] ? _raw_spin_unlock_irqrestore+0x18/0x40 [] ? del_timer+0x8a/0x120 [] bdi_writeback_thread+0x8c/0x2e0 [] ? wb_do_writeback+0x290/0x290 [] kthread+0x96/0xa0 [] kernel_thread_helper+0x4/0x10 [] ? kthread_worker_fn+0x190/0x190 [] ? gs_change+0xb/0xb RIP [] bdev_writeseg+0x25d/0x270 [logfs] ---[ end trace 0211ad60a57657c4 ]--- Reviewed-by: Joern Engel Signed-off-by: Prasad Joshi diff --git a/fs/logfs/file.c b/fs/logfs/file.c index b548c87..3886cde 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -230,7 +230,9 @@ int logfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) return ret; mutex_lock(&inode->i_mutex); + logfs_get_wblocks(sb, NULL, WF_LOCK); logfs_write_anchor(sb); + logfs_put_wblocks(sb, NULL, WF_LOCK); mutex_unlock(&inode->i_mutex); return 0; diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index 7e441ad..388d7c5 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -364,7 +364,9 @@ static void logfs_init_once(void *_li) static int logfs_sync_fs(struct super_block *sb, int wait) { + logfs_get_wblocks(sb, NULL, WF_LOCK); logfs_write_anchor(sb); + logfs_put_wblocks(sb, NULL, WF_LOCK); return 0; } diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 398ecff..bb43408 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -577,6 +577,8 @@ void initialize_block_counters(struct page *page, struct logfs_block *block, __be64 *array, int page_is_empty); int logfs_exist_block(struct inode *inode, u64 bix); int get_page_reserve(struct inode *inode, struct page *page); +void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock); +void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock); extern struct logfs_block_ops indirect_block_ops; /* segment.c */ diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 6d663e8..7b10e8a 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -244,8 +244,7 @@ static void preunlock_page(struct super_block *sb, struct page *page, int lock) * is waiting for s_write_mutex. We annotate this fact by setting PG_pre_locked * in addition to PG_locked. */ -static void logfs_get_wblocks(struct super_block *sb, struct page *page, - int lock) +void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock) { struct logfs_super *super = logfs_super(sb); @@ -260,8 +259,7 @@ static void logfs_get_wblocks(struct super_block *sb, struct page *page, } } -static void logfs_put_wblocks(struct super_block *sb, struct page *page, - int lock) +void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock) { struct logfs_super *super = logfs_super(sb); -- cgit v0.10.2 From ecfd890991a26e70547e025673580923a004c5e4 Mon Sep 17 00:00:00 2001 From: Prasad Joshi Date: Sun, 30 Oct 2011 22:15:32 +0530 Subject: logfs: set superblock shutdown flag after generic sb shutdown While unmounting the file system LogFS calls generic_shutdown_super. The function does file system independent superblock shutdown. However, it might result in call file system specific inode eviction. LogFS marks FS shutting down by setting bit LOGFS_SB_FLAG_SHUTDOWN in super->s_flags. Since, inode eviction might call truncate on inode, following BUG is observed when file system is unmounted: ------------[ cut here ]------------ kernel BUG at /home/prasad/logfs/segment.c:362! invalid opcode: 0000 [#1] PREEMPT SMP CPU 3 Modules linked in: logfs binfmt_misc ppdev virtio_blk parport_pc lp parport psmouse floppy virtio_pci serio_raw virtio_ring virtio Pid: 1933, comm: umount Not tainted 3.0.0+ #4 Bochs Bochs RIP: 0010:[] [] logfs_segment_write+0x211/0x230 [logfs] RSP: 0018:ffff880062d7b9e8 EFLAGS: 00010202 RAX: 000000000000000e RBX: ffff88006eca9000 RCX: 0000000000000000 RDX: ffff88006fd87c40 RSI: ffffea00014ff468 RDI: ffff88007b68e000 RBP: ffff880062d7ba48 R08: 8000000020451430 R09: 0000000000000000 R10: dead000000100100 R11: 0000000000000000 R12: ffff88006fd87c40 R13: ffffea00014ff468 R14: ffff88005ad0a460 R15: 0000000000000000 FS: 00007f25d50ea760(0000) GS:ffff88007fd80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000d05e48 CR3: 0000000062c72000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process umount (pid: 1933, threadinfo ffff880062d7a000, task ffff880070b44500) Stack: ffff880062d7ba38 ffff88005ad0a508 0000000000001000 0000000000000000 8000000020451430 ffffea00014ff468 ffff880062d7ba48 ffff88005ad0a460 ffff880062d7bad8 ffffea00014ff468 ffff88006fd87c40 0000000000000000 Call Trace: [] logfs_write_i0+0x12e/0x190 [logfs] [] __logfs_write_rec+0x140/0x220 [logfs] [] __logfs_write_rec+0xf2/0x220 [logfs] [] logfs_write_rec+0x64/0xd0 [logfs] [] __logfs_write_buf+0x106/0x110 [logfs] [] logfs_write_buf+0x4e/0x80 [logfs] [] __logfs_write_inode+0x98/0x110 [logfs] [] logfs_truncate+0x54/0x290 [logfs] [] logfs_evict_inode+0xdc/0x190 [logfs] [] evict+0x85/0x170 [] iput+0xe6/0x1b0 [] shrink_dcache_for_umount_subtree+0x218/0x280 [] shrink_dcache_for_umount+0x51/0x90 [] generic_shutdown_super+0x2c/0x100 [] logfs_kill_sb+0x57/0xf0 [logfs] [] deactivate_locked_super+0x45/0x70 [] deactivate_super+0x4a/0x70 [] mntput_no_expire+0xa4/0xf0 [] sys_umount+0x6f/0x380 [] system_call_fastpath+0x16/0x1b Code: 55 c8 49 8d b6 a8 00 00 00 45 89 f9 45 89 e8 4c 89 e1 4c 89 55 b8 c7 04 24 00 00 00 00 e8 68 fc ff ff 4c 8b 55 b8 e9 3c ff ff ff <0f> 0b 0f 0b c7 45 c0 00 00 00 00 e9 44 fe ff ff 66 66 66 66 66 RIP [] logfs_segment_write+0x211/0x230 [logfs] RSP ---[ end trace fe6b040cea952290 ]--- Therefore, move super->s_flags setting after the fs-indenpendent work has been finished. Reviewed-by: Joern Engel Signed-off-by: Prasad Joshi diff --git a/fs/logfs/super.c b/fs/logfs/super.c index e795c234..f9b7a30 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -491,9 +491,9 @@ static void logfs_kill_sb(struct super_block *sb) * From this point on alias entries are simply dropped - and any * writes to the object store are considered bugs. */ - super->s_flags |= LOGFS_SB_FLAG_SHUTDOWN; log_super("LogFS: Now in shutdown\n"); generic_shutdown_super(sb); + super->s_flags |= LOGFS_SB_FLAG_SHUTDOWN; BUG_ON(super->s_dirty_used_bytes || super->s_dirty_free_bytes); -- cgit v0.10.2 From 0bd90387ed5a8abbcf43391b480efdc211721cfe Mon Sep 17 00:00:00 2001 From: Prasad Joshi Date: Sun, 2 Oct 2011 23:46:51 +0530 Subject: logfs: Propagate page parameter to __logfs_write_inode During GC LogFS has to rewrite each valid block to a separate segment. Rewrite operation reads data from an old segment and writes it to a newly allocated segment. Since every write operation changes data block pointers maintained in inode, inode should also be rewritten. In GC path to avoid AB-BA deadlock LogFS marks a page with PG_pre_locked in addition to locking the page (PG_locked). The page lock is ignored iff the page is pre-locked. LogFS uses a special file called segment file. The segment file maintains an 8 bytes entry for every segment. It keeps track of erase count, level etc. for every segment. Bad things happen with a segment belonging to the segment file is GCed ------------[ cut here ]------------ kernel BUG at /home/prasad/logfs/readwrite.c:297! invalid opcode: 0000 [#1] SMP Modules linked in: logfs joydev usbhid hid psmouse e1000 i2c_piix4 serio_raw [last unloaded: logfs] Pid: 20161, comm: mount Not tainted 3.1.0-rc3+ #3 innotek GmbH VirtualBox EIP: 0060:[] EFLAGS: 00010292 CPU: 0 EIP is at logfs_lock_write_page+0x6a/0x70 [logfs] EAX: 00000027 EBX: f73f5b20 ECX: c16007c8 EDX: 00000094 ESI: 00000000 EDI: e59be6e4 EBP: c7337b28 ESP: c7337b18 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 Process mount (pid: 20161, ti=c7336000 task=eb323f70 task.ti=c7336000) Stack: f8099a3d c7337b24 f73f5b20 00001002 c7337b50 f8091f6d f8099a4d f80994e4 00000003 00000000 c7337b68 00000000 c67e4400 00001000 c7337b80 f80935e5 00000000 00000000 00000000 00000000 e1fcf000 0000000f e59be618 c70bf900 Call Trace: [] logfs_get_write_page.clone.16+0xdd/0x100 [logfs] [] logfs_mod_segment_entry+0x55/0x110 [logfs] [] logfs_get_segment_entry+0x1d/0x20 [logfs] [] ? logfs_cleanup_journal+0x50/0x50 [logfs] [] ostore_get_erase_count+0x1b/0x40 [logfs] [] logfs_open_area+0xc8/0x150 [logfs] [] ? kmemleak_alloc+0x2c/0x60 [] __logfs_segment_write.clone.16+0x4e/0x1b0 [logfs] [] ? mempool_kmalloc+0x13/0x20 [] ? mempool_kmalloc+0x13/0x20 [] logfs_segment_write+0x17f/0x1d0 [logfs] [] logfs_write_i0+0x11c/0x180 [logfs] [] logfs_write_direct+0x45/0x90 [logfs] [] __logfs_write_buf+0xbd/0xf0 [logfs] [] ? kmap_atomic_prot+0x4e/0xe0 [] logfs_write_buf+0x3b/0x60 [logfs] [] __logfs_write_inode+0xa9/0x110 [logfs] [] logfs_rewrite_block+0xc0/0x110 [logfs] [] ? get_mapping_page+0x10/0x60 [logfs] [] ? logfs_load_object_aliases+0x2e0/0x2f0 [logfs] [] logfs_gc_segment+0x2ad/0x310 [logfs] [] __logfs_gc_once+0x4a/0x80 [logfs] [] logfs_gc_pass+0x683/0x6a0 [logfs] [] logfs_mount+0x5a9/0x680 [logfs] [] mount_fs+0x21/0xd0 [] ? __alloc_percpu+0xf/0x20 [] ? alloc_vfsmnt+0xb1/0x130 [] vfs_kern_mount+0x4b/0xa0 [] do_kern_mount+0x3e/0xe0 [] do_mount+0x34d/0x670 [] ? strndup_user+0x49/0x70 [] sys_mount+0x6b/0xa0 [] syscall_call+0x7/0xb Code: f8 e8 8b 93 39 c9 8b 45 f8 3e 0f ba 28 00 19 d2 85 d2 74 ca eb d0 0f 0b 8d 45 fc 89 44 24 04 c7 04 24 3d 9a 09 f8 e8 09 92 39 c9 <0f> 0b 8d 74 26 00 55 89 e5 3e 8d 74 26 00 8b 10 80 e6 01 74 09 EIP: [] logfs_lock_write_page+0x6a/0x70 [logfs] SS:ESP 0068:c7337b18 ---[ end trace 96e67d5b3aa3d6ca ]--- The patch passes locked page to __logfs_write_inode. It calls function logfs_get_wblocks() to pre-lock the page. This ensures any further attempts to lock the page are ignored (esp from get_erase_count). Acked-by: Joern Engel Signed-off-by: Prasad Joshi diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index b7d7f67..b640489 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -71,7 +71,7 @@ static int write_dir(struct inode *dir, struct logfs_disk_dentry *dd, static int write_inode(struct inode *inode) { - return __logfs_write_inode(inode, WF_LOCK); + return __logfs_write_inode(inode, NULL, WF_LOCK); } static s64 dir_seek_data(struct inode *inode, s64 pos) diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index 388d7c5..7c42c13 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -287,7 +287,7 @@ static int logfs_write_inode(struct inode *inode, struct writeback_control *wbc) if (logfs_inode(inode)->li_flags & LOGFS_IF_STILLBORN) return 0; - ret = __logfs_write_inode(inode, flags); + ret = __logfs_write_inode(inode, NULL, flags); LOGFS_BUG_ON(ret, inode->i_sb); return ret; } diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index bb43408..0dec298 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -528,7 +528,7 @@ void logfs_destroy_inode_cache(void); void logfs_set_blocks(struct inode *inode, u64 no); /* these logically belong into inode.c but actually reside in readwrite.c */ int logfs_read_inode(struct inode *inode); -int __logfs_write_inode(struct inode *inode, long flags); +int __logfs_write_inode(struct inode *inode, struct page *, long flags); void logfs_evict_inode(struct inode *inode); /* journal.c */ diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 7b10e8a..88284c6 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -422,7 +422,7 @@ static void inode_write_block(struct logfs_block *block) if (inode->i_ino == LOGFS_INO_MASTER) logfs_write_anchor(inode->i_sb); else { - ret = __logfs_write_inode(inode, 0); + ret = __logfs_write_inode(inode, NULL, 0); /* see indirect_write_block comment */ BUG_ON(ret); } @@ -1629,7 +1629,7 @@ int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs, if (inode->i_ino == LOGFS_INO_MASTER) logfs_write_anchor(inode->i_sb); else { - err = __logfs_write_inode(inode, flags); + err = __logfs_write_inode(inode, page, flags); } } } @@ -1879,7 +1879,7 @@ int logfs_truncate(struct inode *inode, u64 target) logfs_get_wblocks(sb, NULL, 1); err = __logfs_truncate(inode, size); if (!err) - err = __logfs_write_inode(inode, 0); + err = __logfs_write_inode(inode, NULL, 0); logfs_put_wblocks(sb, NULL, 1); } @@ -2119,14 +2119,14 @@ void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec) ec_level); } -int __logfs_write_inode(struct inode *inode, long flags) +int __logfs_write_inode(struct inode *inode, struct page *page, long flags) { struct super_block *sb = inode->i_sb; int ret; - logfs_get_wblocks(sb, NULL, flags & WF_LOCK); + logfs_get_wblocks(sb, page, flags & WF_LOCK); ret = do_write_inode(inode); - logfs_put_wblocks(sb, NULL, flags & WF_LOCK); + logfs_put_wblocks(sb, page, flags & WF_LOCK); return ret; } -- cgit v0.10.2 From 756ccb3c351e425ad5544ff1a92cfe6bec83b904 Mon Sep 17 00:00:00 2001 From: Prasad Joshi Date: Tue, 13 Sep 2011 23:04:11 +0530 Subject: MAINTAINERS: Add Prasad Joshi in LogFS maintiners Acked-by: Joern Engel Signed-off-by: Prasad Joshi diff --git a/MAINTAINERS b/MAINTAINERS index 0e7a80a..ce7029b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4116,6 +4116,7 @@ F: fs/partitions/ldm.* LogFS M: Joern Engel +M: Prasad Joshi L: logfs@logfs.org W: logfs.org S: Maintained -- cgit v0.10.2 From 6c69494f6b442834f26377e02d43fc8e1272221d Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Mon, 12 Sep 2011 21:09:16 +0530 Subject: logfs: remove useless BUG_ON It prevents write sizes >4k. Signed-off-by: Joern Engel diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index 9da2970..1e1c369 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c @@ -612,7 +612,6 @@ static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type, if (len == 0) return logfs_write_header(super, header, 0, type); - BUG_ON(len > sb->s_blocksize); compr_len = logfs_compress(buf, data, len, sb->s_blocksize); if (compr_len < 0 || type == JE_ANCHOR) { memcpy(data, buf, len); -- cgit v0.10.2 From 1bcceaff8cbe5e5698ccf1015c9a938aa72718c4 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Fri, 5 Aug 2011 11:18:19 +0200 Subject: logfs: Free areas before calling generic_shutdown_super() Or hit an assertion in map_invalidatepage() instead. Signed-off-by: Joern Engel diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 0dec298..59ed32c 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -596,6 +596,7 @@ int logfs_init_mapping(struct super_block *sb); void logfs_sync_area(struct logfs_area *area); void logfs_sync_segments(struct super_block *sb); void freeseg(struct super_block *sb, u32 segno); +void free_areas(struct super_block *sb); /* area handling */ int logfs_init_areas(struct super_block *sb); diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index 6aee609..ab798ed 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c @@ -862,6 +862,16 @@ static void free_area(struct logfs_area *area) kfree(area); } +void free_areas(struct super_block *sb) +{ + struct logfs_super *super = logfs_super(sb); + int i; + + for_each_area(i) + free_area(super->s_area[i]); + free_area(super->s_journal_area); +} + static struct logfs_area *alloc_area(struct super_block *sb) { struct logfs_area *area; @@ -944,10 +954,6 @@ err: void logfs_cleanup_areas(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); - int i; btree_grim_visitor128(&super->s_object_alias_tree, 0, kill_alias); - for_each_area(i) - free_area(super->s_area[i]); - free_area(super->s_journal_area); } diff --git a/fs/logfs/super.c b/fs/logfs/super.c index f9b7a30..c9ee7f5 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -486,6 +486,7 @@ static void logfs_kill_sb(struct super_block *sb) /* Alias entries slow down mount, so evict as many as possible */ sync_filesystem(sb); logfs_write_anchor(sb); + free_areas(sb); /* * From this point on alias entries are simply dropped - and any -- cgit v0.10.2 From bbe01387129f76fa4bec17904eb14c4bdc3c179f Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Fri, 5 Aug 2011 11:13:30 +0200 Subject: logfs: Grow inode in delete path Can be necessary if an inode gets deleted (through -ENOSPC) before being written. Might be better to move this into logfs_write_rec(), but for now go with the stupid&safe patch. Signed-off-by: Joern Engel diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 88284c6..4153e65 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -1576,11 +1576,15 @@ int logfs_write_buf(struct inode *inode, struct page *page, long flags) static int __logfs_delete(struct inode *inode, struct page *page) { long flags = WF_DELETE; + int err; inode->i_ctime = inode->i_mtime = CURRENT_TIME; if (page->index < I0_BLOCKS) return logfs_write_direct(inode, page, flags); + err = grow_inode(inode, page->index, 0); + if (err) + return err; return logfs_write_rec(inode, page, page->index, 0, flags); } -- cgit v0.10.2 From f2933e86ad93a8d1287079d59e67afd6f4166a9d Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Fri, 5 Aug 2011 11:09:55 +0200 Subject: Logfs: Allow NULL block_isbad() methods Not all mtd drivers define block_isbad(). Let's assume no bad blocks instead of refusing to mount. Signed-off-by: Joern Engel diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c index 339e17e..d054d7e 100644 --- a/fs/logfs/dev_mtd.c +++ b/fs/logfs/dev_mtd.c @@ -150,14 +150,13 @@ static struct page *mtd_find_first_sb(struct super_block *sb, u64 *ofs) filler_t *filler = mtd_readpage; struct mtd_info *mtd = super->s_mtd; - if (!mtd->block_isbad) - return NULL; - *ofs = 0; - while (mtd->block_isbad(mtd, *ofs)) { - *ofs += mtd->erasesize; - if (*ofs >= mtd->size) - return NULL; + if (mtd->block_isbad) { + while (mtd->block_isbad(mtd, *ofs)) { + *ofs += mtd->erasesize; + if (*ofs >= mtd->size) + return NULL; + } } BUG_ON(*ofs & ~PAGE_MASK); return read_cache_page(mapping, *ofs >> PAGE_SHIFT, filler, sb); @@ -170,14 +169,13 @@ static struct page *mtd_find_last_sb(struct super_block *sb, u64 *ofs) filler_t *filler = mtd_readpage; struct mtd_info *mtd = super->s_mtd; - if (!mtd->block_isbad) - return NULL; - *ofs = mtd->size - mtd->erasesize; - while (mtd->block_isbad(mtd, *ofs)) { - *ofs -= mtd->erasesize; - if (*ofs <= 0) - return NULL; + if (mtd->block_isbad) { + while (mtd->block_isbad(mtd, *ofs)) { + *ofs -= mtd->erasesize; + if (*ofs <= 0) + return NULL; + } } *ofs = *ofs + mtd->erasesize - 0x1000; BUG_ON(*ofs & ~PAGE_MASK); -- cgit v0.10.2 From d56d8b28e9247e7e35e02fbb12b12239a2c33ad1 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Nov 2011 23:17:34 +0000 Subject: drm/i915:: Disable FBC on SandyBridge Enabling FBC is causing the BLT ring to run between 10-100x slower than normal and frequently lockup. The interim solution is disable FBC once more until we know why. Signed-off-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b3b51c4..19f35ec 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1872,7 +1872,7 @@ static void intel_update_fbc(struct drm_device *dev) if (enable_fbc < 0) { DRM_DEBUG_KMS("fbc set to per-chip default\n"); enable_fbc = 1; - if (INTEL_INFO(dev)->gen <= 5) + if (INTEL_INFO(dev)->gen <= 6) enable_fbc = 0; } if (!enable_fbc) { -- cgit v0.10.2 From d0caf292505d051b1026e85faf3a85e907566f31 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 28 Jan 2012 13:52:46 +0300 Subject: x86/dumpstack: Remove unneeded check in dump_trace() Smatch complains that we have some inconsistent NULL checking. If "task" were NULL then it would lead to a NULL dereference later. We can remove this test because earlier on in the function we have: if (!task) task = current; Signed-off-by: Dan Carpenter Acked-by: Frederic Weisbecker Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Clemens Ladisch Link: http://lkml.kernel.org/r/20120128105246.GA25092@elgon.mountain Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index 6d728d9..af7785f 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c @@ -129,7 +129,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, if (!stack) { if (regs) stack = (unsigned long *)regs->sp; - else if (task && task != current) + else if (task != current) stack = (unsigned long *)task->thread.sp; else stack = &dummy; -- cgit v0.10.2 From f7f5ef0afdcca69fb02ded1763c28eca74e6b54d Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Sat, 28 Jan 2012 19:35:24 +0000 Subject: drivers/video: compile fixes for fsl-diu-fb.c Fix a compiler errors introduced in: commit ddd3d905436b572ebadc09dcf2d12ca5b37020a0 Author: Timur Tabi drivers/video: fsl-diu-fb: merge all allocated data into one block Signed-off-by: Michael Neuling Acked-by: Timur Tabi Signed-off-by: Florian Tobias Schandinat diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index acf292b..6af3f16 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -1432,7 +1432,7 @@ static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) struct fsl_diu_data *data; data = dev_get_drvdata(&ofdev->dev); - disable_lcdc(data->fsl_diu_info[0]); + disable_lcdc(data->fsl_diu_info); return 0; } @@ -1442,7 +1442,7 @@ static int fsl_diu_resume(struct platform_device *ofdev) struct fsl_diu_data *data; data = dev_get_drvdata(&ofdev->dev); - enable_lcdc(data->fsl_diu_info[0]); + enable_lcdc(data->fsl_diu_info); return 0; } -- cgit v0.10.2 From 7bc1dc7174d06104fde069a3b5aef1c5b902a5c9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 27 Jan 2012 15:58:33 +0300 Subject: intelfb: remove some dead code The goto on the line before means the return is unreachable. The goto also returns -ENODEV so no changes are needed there. Signed-off-by: Dan Carpenter Signed-off-by: Florian Tobias Schandinat diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 5ba3999..a5947c9 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -529,7 +529,6 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { ERR_MSG("Could not allocate cmap for intelfb_info.\n"); goto err_out_cmap; - return -ENODEV; } dinfo = info->par; -- cgit v0.10.2 From 9f1065032ceb7e86c7c9f16bb86518857e88a172 Mon Sep 17 00:00:00 2001 From: Hubert Feurstein Date: Mon, 9 Jan 2012 17:23:57 +0100 Subject: atmel_lcdfb: fix usage of CONTRAST_CTR in suspend/resume An error was existing in the saving of CONTRAST_CTR register across suspend/resume. Signed-off-by: Hubert Feurstein Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Cc: stable Signed-off-by: Florian Tobias Schandinat diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 0d7b20d..e40c00f 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -1108,7 +1108,7 @@ static int atmel_lcdfb_suspend(struct platform_device *pdev, pm_message_t mesg) */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); - sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL); + sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_CTR); lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, 0); if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(0); -- cgit v0.10.2 From a4ea430853b71753103ec693acfc8624bd3e748e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Jan 2012 17:18:47 +0100 Subject: drm/i915: fixup interlaced bits clearing in PIPECONF on PCH_SPLIT An identical patch has been merged for i9xx_crtc_mode_set: Commit 59df7b1771c150163e522f33c638096ab0efbf42 Author: Christian Schmidt Date: Mon Dec 19 20:03:33 2011 +0100 drm/intel: Fix initialization if startup happens in interlaced mode [v2] But that one neglected to fix up the ironlake+ path. This should fix the issue reported by Alfonso Fiore where booting with only a HDMI cable connected to his TV failed to display anything. The issue is that the bios set up things for 1080i and used the pannel fitter to scale up the lower progressive resolutions. We failed to clear the interlace bit in the PIPEACONF register, resulting in havoc. Cc: Peter Ross Tested-by: Alfonso Fiore Signed-Off-by: Daniel Vetter Reviewed-by: Keith Packard Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 19f35ec..f1744aa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5912,7 +5912,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, adjusted_mode->crtc_vsync_end -= 1; adjusted_mode->crtc_vsync_start -= 1; } else - pipeconf &= ~PIPECONF_INTERLACE_W_FIELD_INDICATION; /* progressive */ + pipeconf &= ~PIPECONF_INTERLACE_MASK; /* progressive */ I915_WRITE(HTOTAL(pipe), (adjusted_mode->crtc_hdisplay - 1) | -- cgit v0.10.2 From 6edf3c30af01854c416f8654d3d5d2652470afd4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Jan 2012 15:59:00 -0500 Subject: hwmon: (sht15) fix bad error code When no platform data was supplied, returned error code was 0. Signed-off-by: Vivien Didelot Cc: stable@vger.kernel.org # 2.6.32+ Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 6ddeae0..91fdd1f 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -883,7 +883,7 @@ static int sht15_invalidate_voltage(struct notifier_block *nb, static int __devinit sht15_probe(struct platform_device *pdev) { - int ret = 0; + int ret; struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL); u8 status = 0; @@ -901,6 +901,7 @@ static int __devinit sht15_probe(struct platform_device *pdev) init_waitqueue_head(&data->wait_queue); if (pdev->dev.platform_data == NULL) { + ret = -EINVAL; dev_err(&pdev->dev, "no platform data supplied\n"); goto err_free_data; } -- cgit v0.10.2 From 181e9bdef37bfcaa41f3ab6c948a2a0d60a268b5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 29 Jan 2012 20:35:52 +0100 Subject: PM / Hibernate: Fix s2disk regression related to freezing workqueues Commit 2aede851ddf08666f68ffc17be446420e9d2a056 PM / Hibernate: Freeze kernel threads after preallocating memory introduced a mechanism by which kernel threads were frozen after the preallocation of hibernate image memory to avoid problems with frozen kernel threads not responding to memory freeing requests. However, it overlooked the s2disk code path in which the SNAPSHOT_CREATE_IMAGE ioctl was run directly after SNAPSHOT_FREE, which caused freeze_workqueues_begin() to BUG(), because it saw that worqueues had been already frozen. Although in principle this issue might be addressed by removing the relevant BUG_ON() from freeze_workqueues_begin(), that would reintroduce the very problem that commit 2aede851ddf08666f68ffc17be4 attempted to avoid into that particular code path. For this reason, to fix the issue at hand, introduce thaw_kernel_threads() and make the SNAPSHOT_FREE ioctl execute it. Special thanks to Srivatsa S. Bhat for detailed analysis of the problem. Reported-and-tested-by: Jiri Slaby Signed-off-by: Rafael J. Wysocki Acked-by: Srivatsa S. Bhat Cc: stable@kernel.org diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 0ab54e1..d09af4b 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -39,6 +39,7 @@ extern bool __refrigerator(bool check_kthr_stop); extern int freeze_processes(void); extern int freeze_kernel_threads(void); extern void thaw_processes(void); +extern void thaw_kernel_threads(void); static inline bool try_to_freeze(void) { @@ -174,6 +175,7 @@ static inline bool __refrigerator(bool check_kthr_stop) { return false; } static inline int freeze_processes(void) { return -ENOSYS; } static inline int freeze_kernel_threads(void) { return -ENOSYS; } static inline void thaw_processes(void) {} +static inline void thaw_kernel_threads(void) {} static inline bool try_to_freeze(void) { return false; } diff --git a/kernel/power/process.c b/kernel/power/process.c index 77274c9..eeca003 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -188,3 +188,22 @@ void thaw_processes(void) printk("done.\n"); } +void thaw_kernel_threads(void) +{ + struct task_struct *g, *p; + + pm_nosig_freezing = false; + printk("Restarting kernel threads ... "); + + thaw_workqueues(); + + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (p->flags & (PF_KTHREAD | PF_WQ_WORKER)) + __thaw_task(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); + + schedule(); + printk("done.\n"); +} diff --git a/kernel/power/user.c b/kernel/power/user.c index 6b1ab7a..e5a21a8 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -274,6 +274,15 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, swsusp_free(); memset(&data->handle, 0, sizeof(struct snapshot_handle)); data->ready = 0; + /* + * It is necessary to thaw kernel threads here, because + * SNAPSHOT_CREATE_IMAGE may be invoked directly after + * SNAPSHOT_FREE. In that case, if kernel threads were not + * thawed, the preallocation of memory carried out by + * hibernation_snapshot() might run into problems (i.e. it + * might fail or even deadlock). + */ + thaw_kernel_threads(); break; case SNAPSHOT_PREF_IMAGE_SIZE: -- cgit v0.10.2 From ad77c3e1808f07fa70f707b1c92a683b7c7d3f85 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 27 Jan 2012 17:56:06 -0800 Subject: hwmon: (w83627ehf) Disable setting DC mode for pwm2, pwm3 on NCT6776F NCT6776F only supports pwm mode for pwm2 and pwm3. Return error if an attempt is made to set those pwm channels to DC mode. Signed-off-by: Guenter Roeck Acked-by: Jean Delvare Cc: stable@vger.kernel.org # 3.0+ Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 0e0af04..2dfae7d 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1319,6 +1319,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, { struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83627ehf_sio_data *sio_data = dev->platform_data; int nr = sensor_attr->index; unsigned long val; int err; @@ -1330,6 +1331,11 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, if (val > 1) return -EINVAL; + + /* On NCT67766F, DC mode is only supported for pwm1 */ + if (sio_data->kind == nct6776 && nr && val != 1) + return -EINVAL; + mutex_lock(&data->update_lock); reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); data->pwm_mode[nr] = val; -- cgit v0.10.2 From 683da59d7b8ae04891636d4b59893cd4e9b0b7e5 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 29 Jan 2012 16:12:13 +0200 Subject: usb: gadget: zero: fix bug in loopback autoresume handling ab943a2e125b (USB: gadget: gadget zero uses new suspend/resume hooks) introduced a copy-paste error where f_loopback.c writes to a variable declared in f_sourcesink.c. This prevents one from creating gadgets that only have a loopback function. Cc: stable Signed-off-by: Timo Juhani Lindfors Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index 6d87f28..2c0cd82 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -418,7 +418,7 @@ int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) /* support autoresume for remote wakeup testing */ if (autoresume) - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; /* support OTG systems */ if (gadget_is_otg(cdev->gadget)) { -- cgit v0.10.2 From aa3e8b4e715d0781a4e6cd1bb7570410209f0ba8 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Mon, 30 Jan 2012 11:44:23 +0800 Subject: usb: otg: mv_otg: Add dependence This otg driver depends on marvell EHCI driver, so add the dependence. It can fix the following build error on i386: ERROR: "usb_remove_hcd" [drivers/usb/otg/mv_otg.ko] undefined! ERROR: "usb_add_hcd" [drivers/usb/otg/mv_otg.ko] undefined! Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 76d6293..735ef4c 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -118,7 +118,7 @@ config FSL_USB2_OTG config USB_MV_OTG tristate "Marvell USB OTG support" - depends on USB_MV_UDC && USB_SUSPEND + depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help -- cgit v0.10.2 From 8422fa110334cea79ab16c474902edb21a8b3168 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 30 Jan 2012 17:10:58 +0800 Subject: ALSA: Add #ifdef CONFIG_PCI guard for snd_pci_quirk_* functions This fixes below build warning when CONFIG_PCI is not set. CC sound/sound_core.o In file included from sound/sound_core.c:15: include/sound/core.h:454: warning: 'struct pci_dev' declared inside parameter list include/sound/core.h:454: warning: its scope is only this definition or declaration, which is probably not what you want Signed-off-by: Axel Lin Signed-off-by: Takashi Iwai diff --git a/include/sound/core.h b/include/sound/core.h index 5ab255f..cea1b54 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -417,6 +417,7 @@ static inline int __snd_bug_on(int cond) #define gameport_get_port_data(gp) (gp)->port_data #endif +#ifdef CONFIG_PCI /* PCI quirk list helper */ struct snd_pci_quirk { unsigned short subvendor; /* PCI subvendor ID */ @@ -456,5 +457,6 @@ snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list); const struct snd_pci_quirk * snd_pci_quirk_lookup_id(u16 vendor, u16 device, const struct snd_pci_quirk *list); +#endif #endif /* __SOUND_CORE_H */ -- cgit v0.10.2 From 6e877b576ddf7cde5db2e9a6dcb56fef0ea77e64 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 29 Jan 2012 17:05:52 +0100 Subject: Revert "drm/i810: cleanup reclaim_buffers" This reverts commit 87499ffdcb1c70f66988cd8febc4ead0ba2f9118. Where is that paper bag ... ah here. I've failed to take an odd interaction between my other cleanups and this reclaim_buffers patch into account and also failed to properly test it. Looks like there are more dragons and hidden trapdoors in the drm release path than actual lines of code. Until I get a clue, let's just revert this. Signed-Off-by: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index f7c17b2..7f4b4e1 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -886,7 +886,7 @@ static int i810_flush_queue(struct drm_device *dev) } /* Must be called with the lock held */ -void i810_driver_reclaim_buffers(struct drm_device *dev, +static void i810_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; @@ -1223,17 +1223,12 @@ void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) if (dev_priv->page_flipping) i810_do_cleanup_pageflip(dev); } +} - if (file_priv->master && file_priv->master->lock.hw_lock) { - drm_idlelock_take(&file_priv->master->lock); - i810_driver_reclaim_buffers(dev, file_priv); - drm_idlelock_release(&file_priv->master->lock); - } else { - /* master disappeared, clean up stuff anyway and hope nothing - * goes wrong */ - i810_driver_reclaim_buffers(dev, file_priv); - } - +void i810_driver_reclaim_buffers_locked(struct drm_device *dev, + struct drm_file *file_priv) +{ + i810_reclaim_buffers(dev, file_priv); } int i810_driver_dma_quiescent(struct drm_device *dev) diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 053f1ee..ec12f7d 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -63,6 +63,7 @@ static struct drm_driver driver = { .lastclose = i810_driver_lastclose, .preclose = i810_driver_preclose, .device_is_agp = i810_driver_device_is_agp, + .reclaim_buffers_locked = i810_driver_reclaim_buffers_locked, .dma_quiescent = i810_driver_dma_quiescent, .ioctls = i810_ioctls, .fops = &i810_driver_fops, diff --git a/drivers/gpu/drm/i810/i810_drv.h b/drivers/gpu/drm/i810/i810_drv.h index 6e0acad..c9339f4 100644 --- a/drivers/gpu/drm/i810/i810_drv.h +++ b/drivers/gpu/drm/i810/i810_drv.h @@ -116,12 +116,14 @@ typedef struct drm_i810_private { /* i810_dma.c */ extern int i810_driver_dma_quiescent(struct drm_device *dev); -void i810_driver_reclaim_buffers(struct drm_device *dev, - struct drm_file *file_priv); +extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev, + struct drm_file *file_priv); extern int i810_driver_load(struct drm_device *, unsigned long flags); extern void i810_driver_lastclose(struct drm_device *dev); extern void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv); +extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev, + struct drm_file *file_priv); extern int i810_driver_device_is_agp(struct drm_device *dev); extern long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -- cgit v0.10.2 From dd8bc93d45c0ac4f64bf074d4be72418aac1609b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 29 Jan 2012 16:45:32 +0000 Subject: drm: Pass the real error code back during GEM bo initialisation In particular, I found I was hitting the max-file limit in the VFS, and the EFILE was being magically transformed into ENOMEM. Confusion reigns. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 396e60c..f8625e2 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -140,7 +140,7 @@ int drm_gem_object_init(struct drm_device *dev, obj->dev = dev; obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); if (IS_ERR(obj->filp)) - return -ENOMEM; + return PTR_ERR(obj->filp); kref_init(&obj->refcount); atomic_set(&obj->handle_count, 0); -- cgit v0.10.2 From 1ffd57c1da2a73b0a0e5cd7a6dd52cc49e36bef9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 28 Jan 2012 11:10:38 +0100 Subject: drm/radeon/kms: Fix device tree linkage of i2c buses Properly set the parent device of i2c buses before registering them so that they will show at the right place in the device tree (rather than in /sys/devices directly.) Signed-off-by: Jean Delvare Cc: Dave Airlie Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index 7bb1b07..e2a393f 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -897,6 +897,7 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, i2c->rec = *rec; i2c->adapter.owner = THIS_MODULE; i2c->adapter.class = I2C_CLASS_DDC; + i2c->adapter.dev.parent = &dev->pdev->dev; i2c->dev = dev; i2c_set_adapdata(&i2c->adapter, i2c); if (rec->mm_i2c || -- cgit v0.10.2 From bf9c05d5b6d19b3e4c9fe21047694e94f48db89b Mon Sep 17 00:00:00 2001 From: Ryan Mallon Date: Sat, 28 Jan 2012 08:51:40 +1100 Subject: vmwgfx: Fix assignment in vmw_framebuffer_create_handle The assignment of handle in vmw_framebuffer_create_handle doesn't actually do anything useful and is incorrectly assigning an integer value to a pointer argument. It appears that this is a typo and should be dereferencing handle rather than assigning to it directly. This fixes a bug where an undefined handle value is potentially returned to user-space. Signed-off-by: Ryan Mallon Reviewed-by: Jakob Bornecrantz Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 0af6ebd..b66ef0e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -378,7 +378,7 @@ int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, unsigned int *handle) { if (handle) - handle = 0; + *handle = 0; return 0; } -- cgit v0.10.2 From 5955633e91bfc5cd0a41d8d82259e1d8b32980ef Mon Sep 17 00:00:00 2001 From: Michael D Labriola Date: Sun, 29 Jan 2012 14:17:22 -0500 Subject: x86/reboot: Skip DMI checks if reboot set by user Skip DMI checks for vendor specific reboot quirks if the user passed in a reboot= arg on the command line - we should never override user choices. Signed-off-by: Michael D Labriola Cc: Alan Cox Cc: Michael D Labriola Cc: Matthew Garrett Cc: Linus Torvalds Link: http://lkml.kernel.org/r/87wr8ab9od.fsf@gmail.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 37a458b..b257f0e 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -39,6 +39,14 @@ static int reboot_mode; enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; +/* This variable is used privately to keep track of whether or not + * reboot_type is still set to its default value (i.e., reboot= hasn't + * been set on the command line). This is needed so that we can + * suppress DMI scanning for reboot quirks. Without it, it's + * impossible to override a faulty reboot quirk without recompiling. + */ +static int reboot_default = 1; + #if defined(CONFIG_X86_32) && defined(CONFIG_SMP) static int reboot_cpu = -1; #endif @@ -67,6 +75,12 @@ bool port_cf9_safe = false; static int __init reboot_setup(char *str) { for (;;) { + /* Having anything passed on the command line via + * reboot= will cause us to disable DMI checking + * below. + */ + reboot_default = 0; + switch (*str) { case 'w': reboot_mode = 0x1234; @@ -316,7 +330,12 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { static int __init reboot_init(void) { - dmi_check_system(reboot_dmi_table); + /* Only do the DMI check if reboot_type hasn't been overridden + * on the command line + */ + if (reboot_default) { + dmi_check_system(reboot_dmi_table); + } return 0; } core_initcall(reboot_init); @@ -465,7 +484,12 @@ static struct dmi_system_id __initdata pci_reboot_dmi_table[] = { static int __init pci_reboot_init(void) { - dmi_check_system(pci_reboot_dmi_table); + /* Only do the DMI check if reboot_type hasn't been overridden + * on the command line + */ + if (reboot_default) { + dmi_check_system(pci_reboot_dmi_table); + } return 0; } core_initcall(pci_reboot_init); -- cgit v0.10.2 From e6d36a653becc7bbc643c399a77882e02bf552cb Mon Sep 17 00:00:00 2001 From: Michael D Labriola Date: Sun, 29 Jan 2012 14:21:17 -0500 Subject: x86/reboot: Remove VersaLogic Menlow reboot quirk This commit removes the reboot quirk originally added by commit e19e074 ("x86: Fix reboot problem on VersaLogic Menlow boards"). Testing with a VersaLogic Ocelot (VL-EPMs-21a rev 1.00 w/ BIOS 6.5.102) revealed the following regarding the reboot hang problem: - v2.6.37 reboot=bios was needed. - v2.6.38-rc1: behavior changed, reboot=acpi is needed, reboot=kbd and reboot=bios results in system hang. - v2.6.38: VersaLogic patch (e19e074 "x86: Fix reboot problem on VersaLogic Menlow boards") was applied prior to v2.6.38-rc7. This patch sets a quirk for VersaLogic Menlow boards that forces the use of reboot=bios, which doesn't work anymore. - v3.2: It seems that commit 660e34c ("x86: Reorder reboot method preferences") changed the default reboot method to acpi prior to v3.0-rc1, which means the default behavior is appropriate for the Ocelot. No VersaLogic quirk is required. The Ocelot board used for testing can successfully reboot w/out having to pass any reboot= arguments for all 3 current versions of the BIOS. Signed-off-by: Michael D Labriola Cc: Matthew Garrett Cc: Michael D Labriola Cc: Kushal Koolwal Cc: Linus Torvalds Link: http://lkml.kernel.org/r/87vcnub9hu.fsf@gmail.com Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index b257f0e..d840e69 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -309,14 +309,6 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P4S800"), }, }, - { /* Handle problems with rebooting on VersaLogic Menlow boards */ - .callback = set_bios_reboot, - .ident = "VersaLogic Menlow based board", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "VersaLogic Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "VersaLogic Menlow board"), - }, - }, { /* Handle reboot issue on Acer Aspire one */ .callback = set_kbd_reboot, .ident = "Acer Aspire One A110", -- cgit v0.10.2 From 05df1f3c2afaef5672627f2b7095f0d4c4dbc3a0 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Jan 2012 18:25:37 +0100 Subject: iommu/msm: Fix error handling in msm_iommu_unmap() Error handling in msm_iommu_unmap() is broken. On some error conditions retval is set to a non-zero value which causes the function to return 'len' at the end. This hides the error from the user. Zero should be returned in those error cases. Cc: David Brown Cc: Stepan Moskovchenko Cc: stable@vger.kernel.org # >= 3.1 Signed-off-by: Joerg Roedel Acked-by: David Brown diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 08a90b8..cee307e 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -482,23 +482,19 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va, priv = domain->priv; - if (!priv) { - ret = -ENODEV; + if (!priv) goto fail; - } fl_table = priv->pgtable; if (len != SZ_16M && len != SZ_1M && len != SZ_64K && len != SZ_4K) { pr_debug("Bad length: %d\n", len); - ret = -EINVAL; goto fail; } if (!fl_table) { pr_debug("Null page table\n"); - ret = -EINVAL; goto fail; } @@ -507,7 +503,6 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va, if (*fl_pte == 0) { pr_debug("First level PTE is 0\n"); - ret = -ENODEV; goto fail; } -- cgit v0.10.2 From 1ae5cbc52e7c6619a3f44b87809fd25370df31bb Mon Sep 17 00:00:00 2001 From: Denis 'GNUtoo' Carikli Date: Mon, 30 Jan 2012 00:31:47 +0100 Subject: ASoC: neo1973_wm8753: remove references to the neo1973-gta01 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Openmoko GTA01 machine has been removed from the machine ID database, so we need to remove references to it as well. Without that fix we have: sound/soc/samsung/neo1973_wm8753.c: In function ‘neo1973_wm8753_init’: sound/soc/samsung/neo1973_wm8753.c:325:2: error: implicit declaration of function ‘machine_is_neo1973_gta01’ Signed-off-by: Denis 'GNUtoo' Carikli Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 7ac0ba2..c6012ff 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -230,8 +230,6 @@ static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { /* GTA02 specific routes and controls */ -#ifdef CONFIG_MACH_NEO1973_GTA02 - static int gta02_speaker_enabled; static int lm4853_set_spk(struct snd_kcontrol *kcontrol, @@ -311,10 +309,6 @@ static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) return 0; } -#else -static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; } -#endif - static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; @@ -322,10 +316,6 @@ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) int ret; /* set up NC codec pins */ - if (machine_is_neo1973_gta01()) { - snd_soc_dapm_nc_pin(dapm, "LOUT2"); - snd_soc_dapm_nc_pin(dapm, "ROUT2"); - } snd_soc_dapm_nc_pin(dapm, "OUT3"); snd_soc_dapm_nc_pin(dapm, "OUT4"); snd_soc_dapm_nc_pin(dapm, "LINE1"); @@ -370,50 +360,6 @@ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) return 0; } -/* GTA01 specific controls */ - -#ifdef CONFIG_MACH_NEO1973_GTA01 - -static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = { - {"Amp IN", NULL, "ROUT1"}, - {"Amp IN", NULL, "LOUT1"}, - - {"Handset Spk", NULL, "Amp EP"}, - {"Stereo Out", NULL, "Amp LS"}, - {"Headphone", NULL, "Amp HP"}, -}; - -static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Handset Spk", NULL), - SND_SOC_DAPM_SPK("Stereo Out", NULL), - SND_SOC_DAPM_HP("Headphone", NULL), -}; - -static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) -{ - int ret; - - ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets, - ARRAY_SIZE(neo1973_lm4857_dapm_widgets)); - if (ret) - return ret; - - ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes, - ARRAY_SIZE(neo1973_lm4857_routes)); - if (ret) - return ret; - - snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); - snd_soc_dapm_ignore_suspend(dapm, "Headphone"); - - return 0; -} - -#else -static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; }; -#endif - static struct snd_soc_dai_link neo1973_dai[] = { { /* Hifi Playback - for similatious use with voice below */ .name = "WM8753", @@ -440,11 +386,6 @@ static struct snd_soc_aux_dev neo1973_aux_devs[] = { .name = "dfbmcs320", .codec_name = "dfbmcs320.0", }, - { - .name = "lm4857", - .codec_name = "lm4857.0-007c", - .init = neo1973_lm4857_init, - }, }; static struct snd_soc_codec_conf neo1973_codec_conf[] = { @@ -454,14 +395,10 @@ static struct snd_soc_codec_conf neo1973_codec_conf[] = { }, }; -#ifdef CONFIG_MACH_NEO1973_GTA02 static const struct gpio neo1973_gta02_gpios[] = { { GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" }, { GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" }, }; -#else -static const struct gpio neo1973_gta02_gpios[] = {}; -#endif static struct snd_soc_card neo1973 = { .name = "neo1973", @@ -480,7 +417,7 @@ static int __init neo1973_init(void) { int ret; - if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02()) + if (!machine_is_neo1973_gta02()) return -ENODEV; if (machine_is_neo1973_gta02()) { -- cgit v0.10.2 From 1a30871fe635d3e92972e6b93e39ff65bb57e52d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 16 Jan 2012 11:07:16 +0200 Subject: mtd: fix MTD suspend Commits 3fe4bae88460869a8e553397cd9057a4ee7ca341 and 079c985e7a6f4ce60f931cebfdd5ee3c3 broke MTD suspend in 2 ways: 1. When the '->suspend' method is not present, we return -EOPNOTSUPP, but the callers of 'mtd_suspend()' expects 0 instead. 2. Checking of the 'mtd' parameter against NULL has been incorrectly removed in 'mtd_cls_suspend()'. This patch fixes the breakages. This has been found, analyzed, reported and tested by Rafael J. Wysocki . Note, this patch is not needed in the stable tree because it causes a regression introduced during the v3.3 merge window. Reported-by: Rafael J. Wysocki Tested-by: Rafael J. Wysocki Tested-by: Russell King Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6ae9ca0..9a9ce71 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -119,7 +119,7 @@ static int mtd_cls_suspend(struct device *dev, pm_message_t state) { struct mtd_info *mtd = dev_get_drvdata(dev); - return mtd_suspend(mtd); + return mtd ? mtd_suspend(mtd) : 0; } static int mtd_cls_resume(struct device *dev) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 1a81fde..d8c7aad 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -427,9 +427,7 @@ static inline int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) static inline int mtd_suspend(struct mtd_info *mtd) { - if (!mtd->suspend) - return -EOPNOTSUPP; - return mtd->suspend(mtd); + return mtd->suspend ? mtd->suspend(mtd) : 0; } static inline void mtd_resume(struct mtd_info *mtd) -- cgit v0.10.2 From 31150f2327cbb66363f38e13ca1be973d2f9203a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Jan 2012 10:54:08 +0100 Subject: ALSA: hda - Apply 0x0f-VREF fix to all ASUS laptops with ALC861/660 It turned out that other ASUS laptops require the similar fix to enable the VREF on the pin 0x0f for the secret output amp, not only ASUS A6Rp. Moreover, it's required even when the pin is being used as the output. Thus, writing a fixed value doesn't work always. This patch applies the VREF-fix for all ASUS laptops with ALC861/660 in a fixup function that checks the current value and turns on only the VREF value no matter whether input or output direction is set. The automute function is modified as well to keep the pin VREF upon muting/unmuting via pin-control; otherwise the pin VREF is reset at plugging/unplugging a jack. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42588 Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a7f17be..42b6a01 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -177,6 +177,7 @@ struct alc_spec { unsigned int detect_lo:1; /* Line-out detection enabled */ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ + unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ /* other flags */ unsigned int no_analog :1; /* digital I/O only */ @@ -495,13 +496,24 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, for (i = 0; i < num_pins; i++) { hda_nid_t nid = pins[i]; + unsigned int val; if (!nid) break; switch (spec->automute_mode) { case ALC_AUTOMUTE_PIN: + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) { + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val &= ~PIN_HP; + } else + val = 0; + val |= pin_bits; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_bits); + val); break; case ALC_AUTOMUTE_AMP: snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, @@ -5588,6 +5600,25 @@ enum { PINFIX_ASUS_A6RP, }; +/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ +static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + unsigned int val; + + if (action != ALC_FIXUP_ACT_INIT) + return; + val = snd_hda_codec_read(codec, 0x0f, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) + val |= AC_PINCTL_IN_EN; + val |= AC_PINCTL_VREF_50; + snd_hda_codec_write(codec, 0x0f, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + spec->keep_vref_in_automute = 1; +} + static const struct alc_fixup alc861_fixups[] = { [PINFIX_FSC_AMILO_PI1505] = { .type = ALC_FIXUP_PINS, @@ -5598,17 +5629,13 @@ static const struct alc_fixup alc861_fixups[] = { } }, [PINFIX_ASUS_A6RP] = { - .type = ALC_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* node 0x0f VREF seems controlling the master output */ - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, - { } - }, + .type = ALC_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, }, }; static const struct snd_pci_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", PINFIX_ASUS_A6RP), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), {} -- cgit v0.10.2 From 4acb41903b2f99f3dffd4c3df9acc84ca5942cb2 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Mon, 30 Jan 2012 01:20:17 +0000 Subject: net/tcp: Fix tcp memory limits initialization when !CONFIG_SYSCTL sysctl_tcp_mem() initialization was moved to sysctl_tcp_ipv4.c in commit 3dc43e3e4d0b52197d3205214fe8f162f9e0c334, since it became a per-ns value. That code, however, will never run when CONFIG_SYSCTL is disabled, leading to bogus values on those fields - causing hung TCP sockets. This patch fixes it by keeping an initialization code in tcp_init(). It will be overwritten by the first net namespace init if CONFIG_SYSCTL is compiled in, and do the right thing if it is compiled out. It is also named properly as tcp_init_mem(), to properly signal its non-sysctl side effect on TCP limits. Reported-by: Ingo Molnar Signed-off-by: Glauber Costa Cc: David S. Miller Link: http://lkml.kernel.org/r/4F22D05A.8030604@parallels.com [ renamed the function, tidied up the changelog a bit ] Signed-off-by: Ingo Molnar Signed-off-by: David S. Miller diff --git a/include/net/tcp.h b/include/net/tcp.h index 0118ea9..d49db01 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -311,6 +311,8 @@ extern struct proto tcp_prot; #define TCP_ADD_STATS_USER(net, field, val) SNMP_ADD_STATS_USER((net)->mib.tcp_statistics, field, val) #define TCP_ADD_STATS(net, field, val) SNMP_ADD_STATS((net)->mib.tcp_statistics, field, val) +extern void tcp_init_mem(struct net *net); + extern void tcp_v4_err(struct sk_buff *skb, u32); extern void tcp_shutdown (struct sock *sk, int how); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 4aa7e9d..4cb9cd2 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -814,6 +814,7 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) net->ipv4.sysctl_rt_cache_rebuild_count = 4; + tcp_init_mem(net); limit = nr_free_buffer_pages() / 8; limit = max(limit, 128UL); net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 9bcdec3..06373b4 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3216,6 +3216,16 @@ static int __init set_thash_entries(char *str) } __setup("thash_entries=", set_thash_entries); +void tcp_init_mem(struct net *net) +{ + /* Set per-socket limits to no more than 1/128 the pressure threshold */ + unsigned long limit = nr_free_buffer_pages() / 8; + limit = max(limit, 128UL); + net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; + net->ipv4.sysctl_tcp_mem[1] = limit; + net->ipv4.sysctl_tcp_mem[2] = net->ipv4.sysctl_tcp_mem[0] * 2; +} + void __init tcp_init(void) { struct sk_buff *skb = NULL; @@ -3276,9 +3286,9 @@ void __init tcp_init(void) sysctl_tcp_max_orphans = cnt / 2; sysctl_max_syn_backlog = max(128, cnt / 256); - /* Set per-socket limits to no more than 1/128 the pressure threshold */ - limit = ((unsigned long)init_net.ipv4.sysctl_tcp_mem[1]) - << (PAGE_SHIFT - 7); + tcp_init_mem(&init_net); + limit = nr_free_buffer_pages() / 8; + limit = max(limit, 128UL); max_share = min(4UL*1024*1024, limit); sysctl_tcp_wmem[0] = SK_MEM_QUANTUM; -- cgit v0.10.2 From 5b35e1e6e9ca651e6b291c96d1106043c9af314a Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Sat, 28 Jan 2012 17:29:46 +0000 Subject: tcp: fix tcp_trim_head() to adjust segment count with skb MSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes tcp_trim_head() to recalculate the number of segments in the skb with the skb's existing MSS, so trimming the head causes the skb segment count to be monotonically non-increasing - it should stay the same or go down, but not increase. Previously tcp_trim_head() used the current MSS of the connection. But if there was a decrease in MSS between original transmission and ACK (e.g. due to PMTUD), this could cause tcp_trim_head() to counter-intuitively increase the segment count when trimming bytes off the head of an skb. This violated assumptions in tcp_tso_acked() that tcp_trim_head() only decreases the packet count, so that packets_acked in tcp_tso_acked() could underflow, leading tcp_clean_rtx_queue() to pass u32 pkts_acked values as large as 0xffffffff to ca_ops->pkts_acked(). As an aside, if tcp_trim_head() had really wanted the skb to reflect the current MSS, it should have called tcp_set_skb_tso_segs() unconditionally, since a decrease in MSS would mean that a single-packet skb should now be sliced into multiple segments. Signed-off-by: Neal Cardwell Acked-by: Nandita Dukkipati Acked-by: Ilpo Järvinen Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8c8de27..4ff3b6d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1141,11 +1141,9 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) sk_mem_uncharge(sk, len); sock_set_flag(sk, SOCK_QUEUE_SHRUNK); - /* Any change of skb->len requires recalculation of tso - * factor and mss. - */ + /* Any change of skb->len requires recalculation of tso factor. */ if (tcp_skb_pcount(skb) > 1) - tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk)); + tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); return 0; } -- cgit v0.10.2 From 6f01fd6e6f6809061b56e78f1e8d143099716d70 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 28 Jan 2012 16:11:03 +0000 Subject: af_unix: fix EPOLLET regression for stream sockets Commit 0884d7aa24 (AF_UNIX: Fix poll blocking problem when reading from a stream socket) added a regression for epoll() in Edge Triggered mode (EPOLLET) Appropriate fix is to use skb_peek()/skb_unlink() instead of skb_dequeue(), and only call skb_unlink() when skb is fully consumed. This remove the need to requeue a partial skb into sk_receive_queue head and the extra sk->sk_data_ready() calls that added the regression. This is safe because once skb is given to sk_receive_queue, it is not modified by a writer, and readers are serialized by u->readlock mutex. This also reduce number of spinlock acquisition for small reads or MSG_PEEK users so should improve overall performance. Reported-by: Nick Mathewson Signed-off-by: Eric Dumazet Cc: Alexey Moiseytsev Signed-off-by: David S. Miller diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index aad8fb6..85d3bb7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1918,7 +1918,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; unix_state_lock(sk); - skb = skb_dequeue(&sk->sk_receive_queue); + skb = skb_peek(&sk->sk_receive_queue); if (skb == NULL) { unix_sk(sk)->recursion_level = 0; if (copied >= target) @@ -1958,11 +1958,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (check_creds) { /* Never glue messages from different writers */ if ((UNIXCB(skb).pid != siocb->scm->pid) || - (UNIXCB(skb).cred != siocb->scm->cred)) { - skb_queue_head(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); + (UNIXCB(skb).cred != siocb->scm->cred)) break; - } } else { /* Copy credentials */ scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred); @@ -1977,8 +1974,6 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, chunk = min_t(unsigned int, skb->len, size); if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { - skb_queue_head(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); if (copied == 0) copied = -EFAULT; break; @@ -1993,13 +1988,10 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (UNIXCB(skb).fp) unix_detach_fds(siocb->scm, skb); - /* put the skb back if we didn't use it up.. */ - if (skb->len) { - skb_queue_head(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); + if (skb->len) break; - } + skb_unlink(skb, &sk->sk_receive_queue); consume_skb(skb); if (siocb->scm->fp) @@ -2010,9 +2002,6 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (UNIXCB(skb).fp) siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); break; } } while (size); -- cgit v0.10.2 From 320cfa6ce0b3dc794fedfa4bae54c0f65077234d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 29 Jan 2012 12:41:15 +0100 Subject: firewire: ohci: disable MSI on Ricoh controllers The PCIe device FireWire (IEEE 1394) [0c00]: Ricoh Co Ltd FireWire Host Controller [1180:e832] (prog-if 10 [OHCI]) is unable to access attached FireWire devices when MSI is enabled but works if MSI is disabled. http://www.mail-archive.com/alsa-user@lists.sourceforge.net/msg28251.html Hence add the "disable MSI" quirks flag for this device, or in fact for safety and simplicity for all current (R5U230, R5U231, R5U240) and future Ricoh PCIe 1394 controllers. Reported-by: Stefan Thomas Cc: 2.6.36+ Signed-off-by: Stefan Richter diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 21250ec..7f5f0da 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -303,7 +303,7 @@ static const struct { QUIRK_NO_MSI}, {PCI_VENDOR_ID_RICOH, PCI_ANY_ID, PCI_ANY_ID, - QUIRK_CYCLE_TIMER}, + QUIRK_CYCLE_TIMER | QUIRK_NO_MSI}, {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID, QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A}, -- cgit v0.10.2 From 34f8ac6d79e5446c6242e4bcb474f152c857c5c6 Mon Sep 17 00:00:00 2001 From: Jonathan Brassow Date: Fri, 27 Jan 2012 14:53:53 -0600 Subject: Prevent DM RAID from loading bitmap twice. The life cycle of a device-mapper target is: 1) create 2) resume 3) suspend *) possibly repeat from 2 4) destroy The dm-raid target is unconditionally calling MD's bitmap_load function upon every resume. If steps 2 & 3 above are repeated, bitmap_load is called multiple times. It is only written to be called once; otherwise, it allocates new memory for the bitmap (without freeing the old) and incrementing the number of pages it thinks it has without zeroing first. This ultimately leads to access beyond allocated memory and lost memory. Simply avoiding the bitmap_load call upon resume is not sufficient. If the target was suspended while the initial recovery was only partially complete, it needs to be restarted when the target is resumed. This is why 'md_wakeup_thread' is called before issuing the 'mddev_resume'. Signed-off-by: Jonathan Brassow Signed-off-by: NeilBrown diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index c2907d8..86cb7e5 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -56,7 +56,8 @@ struct raid_dev { struct raid_set { struct dm_target *ti; - uint64_t print_flags; + uint32_t bitmap_loaded; + uint32_t print_flags; struct mddev md; struct raid_type *raid_type; @@ -1085,7 +1086,7 @@ static int raid_status(struct dm_target *ti, status_type_t type, raid_param_cnt += 2; } - raid_param_cnt += (hweight64(rs->print_flags & ~DMPF_REBUILD) * 2); + raid_param_cnt += (hweight32(rs->print_flags & ~DMPF_REBUILD) * 2); if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)) raid_param_cnt--; @@ -1197,7 +1198,12 @@ static void raid_resume(struct dm_target *ti) { struct raid_set *rs = ti->private; - bitmap_load(&rs->md); + if (!rs->bitmap_loaded) { + bitmap_load(&rs->md); + rs->bitmap_loaded = 1; + } else + md_wakeup_thread(rs->md.thread); + mddev_resume(&rs->md); } -- cgit v0.10.2 From a6f7feae6d19e84253918d88b04153af09d3a243 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Thu, 26 Jan 2012 16:41:33 +0200 Subject: IB/mlx4: pass SMP vendor-specific attribute MADs to firmware In the current code, vendor-specific MADs (e.g with the FDR-10 attribute) are silently dropped by the driver, resulting in timeouts at the sending side and inability to query/configure the relevant feature. However, the ConnectX firmware is able to handle such MADs. For unsupported attributes, the firmware returns a GET_RESPONSE MAD containing an error status. For example, for a FDR-10 node with LID 11: # ibstat mlx4_0 1 CA: 'mlx4_0' Port 1: State: Active Physical state: LinkUp Rate: 40 (FDR10) Base lid: 11 LMC: 0 SM lid: 24 Capability mask: 0x02514868 Port GUID: 0x0002c903002e65d1 Link layer: InfiniBand Extended Port Query (EPI) vendor mad timeouts before the patch: # smpquery MEPI 11 -d ibwarn: [4196] smp_query_via: attr 0xff90 mod 0x0 route Lid 11 ibwarn: [4196] _do_madrpc: retry 1 (timeout 1000 ms) ibwarn: [4196] _do_madrpc: retry 2 (timeout 1000 ms) ibwarn: [4196] _do_madrpc: timeout after 3 retries, 3000 ms ibwarn: [4196] mad_rpc: _do_madrpc failed; dport (Lid 11) smpquery: iberror: [pid 4196] main: failed: operation EPI: ext port info query failed EPI query works OK with the patch: # smpquery MEPI 11 -d ibwarn: [6548] smp_query_via: attr 0xff90 mod 0x0 route Lid 11 ibwarn: [6548] mad_rpc: data offs 64 sz 64 mad data 0000 0000 0000 0001 0000 0001 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 # Ext Port info: Lid 11 port 0 StateChangeEnable:...............0x00 LinkSpeedSupported:..............0x01 LinkSpeedEnabled:................0x01 LinkSpeedActive:.................0x01 Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Acked-by: Ira Weiny Cc: Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 95c94d8..259b067 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -257,12 +257,9 @@ static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, return IB_MAD_RESULT_SUCCESS; /* - * Don't process SMInfo queries or vendor-specific - * MADs -- the SMA can't handle them. + * Don't process SMInfo queries -- the SMA can't handle them. */ - if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO || - ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) == - IB_SMP_ATTR_VENDOR_MASK)) + if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO) return IB_MAD_RESULT_SUCCESS; } else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT || in_mad->mad_hdr.mgmt_class == MLX4_IB_VENDOR_CLASS1 || -- cgit v0.10.2 From c5488c571f08b2e21b060a1b43aa840d0a3c6e7a Mon Sep 17 00:00:00 2001 From: Tatyana Nikolova Date: Mon, 28 Nov 2011 14:22:29 -0600 Subject: RDMA/nes: Copyright update Update copyright information in the source files. Signed-off-by: Tatyana Nikolova Signed-off-by: Faisal Latif Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index 7013da5..7140199 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h index 568b4f1..c438e46 100644 --- a/drivers/infiniband/hw/nes/nes.h +++ b/drivers/infiniband/hw/nes/nes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 271279c..a4972ab 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h index bdfa1fb..4646e66 100644 --- a/drivers/infiniband/hw/nes/nes_cm.h +++ b/drivers/infiniband/hw/nes/nes_cm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_context.h b/drivers/infiniband/hw/nes/nes_context.h index b4393a1..a69eef1 100644 --- a/drivers/infiniband/hw/nes/nes_context.h +++ b/drivers/infiniband/hw/nes/nes_context.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 055f4b5..d42c9f4 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h index 0b590e1..d748e4b 100644 --- a/drivers/infiniband/hw/nes/nes_hw.h +++ b/drivers/infiniband/hw/nes/nes_hw.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. +* Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_mgt.c b/drivers/infiniband/hw/nes/nes_mgt.c index b3b2a24..3ba7be3 100644 --- a/drivers/infiniband/hw/nes/nes_mgt.c +++ b/drivers/infiniband/hw/nes/nes_mgt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel-NE, Inc. All rights reserved. + * Copyright (c) 2006 - 2011 Intel-NE, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_mgt.h b/drivers/infiniband/hw/nes/nes_mgt.h index 8c8af25..4f7f701 100644 --- a/drivers/infiniband/hw/nes/nes_mgt.h +++ b/drivers/infiniband/hw/nes/nes_mgt.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2010 Intel-NE, Inc. All rights reserved. +* Copyright (c) 2006 - 2011 Intel-NE, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 4b3fa71..f3a3ecf 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_user.h b/drivers/infiniband/hw/nes/nes_user.h index 71e133a..4926de7 100644 --- a/drivers/infiniband/hw/nes/nes_user.h +++ b/drivers/infiniband/hw/nes/nes_user.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * Copyright (c) 2005 Topspin Communications. All rights reserved. * Copyright (c) 2005 Cisco Systems. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. diff --git a/drivers/infiniband/hw/nes/nes_utils.c b/drivers/infiniband/hw/nes/nes_utils.c index 8b4c2ff..e98f4fc 100644 --- a/drivers/infiniband/hw/nes/nes_utils.c +++ b/drivers/infiniband/hw/nes/nes_utils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 3875365..0927b5c 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/nes/nes_verbs.h b/drivers/infiniband/hw/nes/nes_verbs.h index fe6b6e9..0eff7c4 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.h +++ b/drivers/infiniband/hw/nes/nes_verbs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 - 2009 Intel Corporation. All rights reserved. + * Copyright (c) 2006 - 2011 Intel Corporation. All rights reserved. * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two -- cgit v0.10.2 From 0a84f007f97ce6fc7c07a481a5b9808b187a0193 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 17 Jan 2012 13:41:01 -0800 Subject: perf tools: Fix broken build by defining _GNU_SOURCE in Makefile When building on my Debian/mips system, util/util.c fails to build because commit 1aed2671738785e8f5aea663a6fda91aa7ef59b5 (perf kvm: Do guest-only counting by default) indirectly includes stdio.h before the feature selection in util.h is done. This prevents _GNU_SOURCE in util.h from enabling the declaration of getline(), from now second inclusion of stdio.h, and the build is broken. There is another breakage in util/evsel.c caused by include ordering, but I didn't fully track down the commit that caused it. The root cause of all this is an inconsistent definition of _GNU_SOURCE, so I move the definition into the Makefile so that it is passed to all invocations of the compiler and used uniformly for all system header files. All other #define and #undef of _GNU_SOURCE are removed as they cause conflicts with the definition passed to the compiler. All the features.h definitions (_LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 and _GNU_SOURCE) are needed by the python glue code too, so they are moved to BASIC_CFLAGS, and the misleading comments about BASIC_CFLAGS are removed. This gives me a clean build on x86_64 (fc12) and mips (Debian). Cc: David Daney Cc: Ingo Molnar Cc: Joerg Roedel Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1326836461-11952-1-git-send-email-ddaney.cavm@gmail.com Signed-off-by: David Daney Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index ac86d67..7c12650 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -104,7 +104,7 @@ endif CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) EXTLIBS = -lpthread -lrt -lelf -lm -ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE ALL_LDFLAGS = $(LDFLAGS) STRIP ?= strip @@ -168,10 +168,7 @@ endif ### --- END CONFIGURATION SECTION --- -# Those must not be GNU-specific; they are shared with perl/ which may -# be built by a different compiler. (Note that this is an artifact now -# but it still might be nice to keep that distinction.) -BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include +BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE BASIC_LDFLAGS = # Guard against environment variables diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 59d43ab..fb85661 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ -#define _GNU_SOURCE #include #include #include @@ -31,7 +30,6 @@ #include #include -#undef _GNU_SOURCE #include "perf.h" #include "builtin.h" #include "util/util.h" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index eb25900..29cb654 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -19,7 +19,6 @@ * */ -#define _GNU_SOURCE #include #include #include @@ -33,7 +32,6 @@ #include #include -#undef _GNU_SOURCE #include "util.h" #include "event.h" #include "string.h" diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 215d50f..0975438 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,4 +1,3 @@ -#define _GNU_SOURCE #include #include #include diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 6c164dc..1a8d4dc 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -21,14 +21,13 @@ * The parts for function graph printing was taken and modified from the * Linux Kernel that were written by Frederic Weisbecker. */ -#define _GNU_SOURCE + #include #include #include #include #include -#undef _GNU_SOURCE #include "../perf.h" #include "util.h" #include "trace-event.h" diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 1212a38..e81aef1 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -1,6 +1,4 @@ -#define _GNU_SOURCE #include -#undef _GNU_SOURCE #include "../libslang.h" #include #include diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 6ef3c56..4f48f59 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -1,4 +1,3 @@ -#define _GNU_SOURCE #include #include #include diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b9c530c..ecf9898 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -40,7 +40,6 @@ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) #define _ALL_SOURCE 1 -#define _GNU_SOURCE 1 #define _BSD_SOURCE 1 #define HAS_BOOL -- cgit v0.10.2 From d8d9c282a1abbe2baf6d22f959e6adf4c90629bf Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Fri, 20 Jan 2012 10:49:12 +0100 Subject: perf tools: Fix strlen() bug in perf_event__synthesize_event_type() The event_type record has a max length for the event name. It's called MAX_EVENT_NAME. The name may be truncated to fit the max length. But the header.size still reflects the original name length. If that length is > MAX_EVENT_NAME, then the header.size field is bogus. Fix this by using the length of the name after the potential truncation. Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120120094912.GA4882@quad Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 3e7e0b0..ecd7f4d 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2105,7 +2105,7 @@ int perf_event__synthesize_event_type(struct perf_tool *tool, strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; - size = strlen(name); + size = strlen(ev.event_type.event_type.name); size = ALIGN(size, sizeof(u64)); ev.event_type.header.size = sizeof(ev.event_type) - (sizeof(ev.event_type.event_type.name) - size); -- cgit v0.10.2 From 509605dbe9395ce1abbfac1dba375f074ff57a53 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 30 Jan 2012 11:50:37 +0100 Subject: perf top: Fix number of samples displayed In recent versions of perf top, pressing the 'e' key to change the number of displayed samples had no effect. The number of samples was still dictated by the size of the terminal (stdio mode). That was quite annoying because typically only the first dozen samples really matter. This patch fixes this. Cc: David Ahern Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120130105037.GA5160@quad Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 8f80df8..dd162aa 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -89,8 +89,6 @@ void get_term_dimensions(struct winsize *ws) static void perf_top__update_print_entries(struct perf_top *top) { - top->print_entries = top->winsize.ws_row; - if (top->print_entries > 9) top->print_entries -= 9; } @@ -100,6 +98,13 @@ static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *ar struct perf_top *top = arg; get_term_dimensions(&top->winsize); + if (!top->print_entries + || (top->print_entries+4) > top->winsize.ws_row) { + top->print_entries = top->winsize.ws_row; + } else { + top->print_entries += 4; + top->winsize.ws_row = top->print_entries; + } perf_top__update_print_entries(top); } @@ -453,8 +458,10 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) }; perf_top__sig_winch(SIGWINCH, NULL, top); sigaction(SIGWINCH, &act, NULL); - } else + } else { + perf_top__sig_winch(SIGWINCH, NULL, top); signal(SIGWINCH, SIG_DFL); + } break; case 'E': if (top->evlist->nr_entries > 1) { -- cgit v0.10.2 From bda3a47c886664e86ee14eb79e9072b9e341f575 Mon Sep 17 00:00:00 2001 From: Nikolaus Voss Date: Tue, 17 Jan 2012 10:28:33 +0100 Subject: at_hdmac: bugfix for enabling channel irq commit 463894705e4089d0ff69e7d877312d496ac70e5b deleted redundant chan_id and chancnt initialization in dma drivers as this is done in dma_async_device_register(). However, atc_enable_irq() relied on chan_id set before registering the device, what left only channel 0 functional for this driver. This patch introduces atc_enable/disable_chan_irq() as a variant of atc_enable/disable_irq() with the channel as explicit argument. Signed-off-by: Nikolaus Voss Signed-off-by: Nicolas Ferre Cc: stable Signed-off-by: Vinod Koul diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 97f87b2..f4aed5f 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1343,7 +1343,7 @@ static int __init at_dma_probe(struct platform_device *pdev) tasklet_init(&atchan->tasklet, atc_tasklet, (unsigned long)atchan); - atc_enable_irq(atchan); + atc_enable_chan_irq(atdma, i); } /* set base routines */ @@ -1410,7 +1410,7 @@ static int __exit at_dma_remove(struct platform_device *pdev) struct at_dma_chan *atchan = to_at_dma_chan(chan); /* Disable interrupts */ - atc_disable_irq(atchan); + atc_disable_chan_irq(atdma, chan->chan_id); tasklet_disable(&atchan->tasklet); tasklet_kill(&atchan->tasklet); diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index dcaedfc..a8d3277 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -327,28 +327,27 @@ static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) } -static void atc_setup_irq(struct at_dma_chan *atchan, int on) +static void atc_setup_irq(struct at_dma *atdma, int chan_id, int on) { - struct at_dma *atdma = to_at_dma(atchan->chan_common.device); - u32 ebci; + u32 ebci; /* enable interrupts on buffer transfer completion & error */ - ebci = AT_DMA_BTC(atchan->chan_common.chan_id) - | AT_DMA_ERR(atchan->chan_common.chan_id); + ebci = AT_DMA_BTC(chan_id) + | AT_DMA_ERR(chan_id); if (on) dma_writel(atdma, EBCIER, ebci); else dma_writel(atdma, EBCIDR, ebci); } -static inline void atc_enable_irq(struct at_dma_chan *atchan) +static void atc_enable_chan_irq(struct at_dma *atdma, int chan_id) { - atc_setup_irq(atchan, 1); + atc_setup_irq(atdma, chan_id, 1); } -static inline void atc_disable_irq(struct at_dma_chan *atchan) +static void atc_disable_chan_irq(struct at_dma *atdma, int chan_id) { - atc_setup_irq(atchan, 0); + atc_setup_irq(atdma, chan_id, 0); } -- cgit v0.10.2 From 7cc98aaea4ce040f119457f5b0bb9487acfb5205 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 31 Jan 2012 12:58:31 +0900 Subject: ARM: EXYNOS: Correct framebuffer window size on Nuri board The real LCD resolution on Nuri is 1024x600, not 1280x800. This change fixes the color distortion (green shadows) on half of the screen. Also increase framebuffer virtual size for display panning support. Signed-off-by: Sylwester Nawrocki Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index b895ec0..435261f 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -220,14 +220,14 @@ static struct s3c_fb_pd_win nuri_fb_win0 = { .lower_margin = 1, .hsync_len = 48, .vsync_len = 3, - .xres = 1280, - .yres = 800, + .xres = 1024, + .yres = 600, .refresh = 60, }, .max_bpp = 24, .default_bpp = 16, - .virtual_x = 1280, - .virtual_y = 800, + .virtual_x = 1024, + .virtual_y = 2 * 600, }; static struct s3c_fb_platdata nuri_fb_pdata __initdata = { -- cgit v0.10.2 From 3e8ad5610c07f88bdecf94468929c86631be476f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 31 Jan 2012 12:58:31 +0900 Subject: ARM: EXYNOS: Correct M-5MOLS sensor clock frequency on Universal C210 board In order to keep the sensor's master clock frequency in valid range when FIMC parent clock is xusbxti, the specified frequency must be exactly 24MHZ, otherwise it's being set to too low value due to rounding. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 37ac93e..0fc65ff 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -910,7 +910,7 @@ static struct s5p_fimc_isp_info universal_camera_sensors[] = { .bus_type = FIMC_MIPI_CSI2, .board_info = &m5mols_board_info, .i2c_bus_num = 0, - .clk_frequency = 21600000UL, + .clk_frequency = 24000000UL, .csi_data_align = 32, }, }; -- cgit v0.10.2 From 9afc416517f36b3b0b109e6590d0b74468fd80f9 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 30 Jan 2012 10:53:08 +0100 Subject: Revert "microblaze: Add topology init" This reverts commit d761f0c521868e59cd0bc59159cbdb4686fe210d. Patch: "cpu: Register a generic CPU device on architectures that currently do not" (sha1: 9f13a1fd452f11c18004ba2422a6384b424ec8a9) selects GENERIC_CPU_DEVICES for Microblaze which register cpu. My patch was done in the same time that's why cpu was registered twice which caused this warning log: ------------[ cut here ]------------ WARNING: at fs/sysfs/dir.c:481 sysfs_add_one+0xb0/0xdc() sysfs: cannot create duplicate filename '/devices/system/cpu/cpu0' Modules linked in: ... Signed-off-by: Michal Simek diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index d4fc1a9..604cd9d 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -227,23 +226,5 @@ static int __init setup_bus_notifier(void) return 0; } -arch_initcall(setup_bus_notifier); - -static DEFINE_PER_CPU(struct cpu, cpu_devices); - -static int __init topology_init(void) -{ - int i, ret; - - for_each_present_cpu(i) { - struct cpu *c = &per_cpu(cpu_devices, i); - ret = register_cpu(c, i); - if (ret) - printk(KERN_WARNING "topology_init: register_cpu %d " - "failed (%d)\n", i, ret); - } - - return 0; -} -subsys_initcall(topology_init); +arch_initcall(setup_bus_notifier); -- cgit v0.10.2 From ee76744c51ec342df9822b4a85dbbfc3887b6d60 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 31 Jan 2012 11:55:32 +0000 Subject: ASoC: wm_hubs: Fix routing of input PGAs to line output mixer IN1L/R is routed to both line output mixers, we don't route IN1 to LINEOUT1 and IN2 to LINEOUT2. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index ea26724..c1a3f8c 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -586,8 +586,8 @@ SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), }; static const struct snd_kcontrol_new line2_mix[] = { -SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0), -SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 1, 1, 0), SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), }; @@ -848,8 +848,8 @@ static const struct snd_soc_dapm_route lineout1_se_routes[] = { }; static const struct snd_soc_dapm_route lineout2_diff_routes[] = { - { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" }, - { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" }, + { "LINEOUT2 Mixer", "IN1L Switch", "IN1L PGA" }, + { "LINEOUT2 Mixer", "IN1R Switch", "IN1R PGA" }, { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, -- cgit v0.10.2 From 4991a5faab7368daac463181e786608b4eb63675 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 31 Jan 2012 11:52:01 +0300 Subject: cifs: check offset in decode_ntlmssp_challenge() We should check that we're not copying memory from beyond the end of the blob. Signed-off-by: Dan Carpenter Reviewed-by: Jeff Layton diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index d85efad..eb76741 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -395,6 +395,10 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, ses->ntlmssp->server_flags = le32_to_cpu(pblob->NegotiateFlags); tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); tilen = le16_to_cpu(pblob->TargetInfoArray.Length); + if (tioffset > blob_len || tioffset + tilen > blob_len) { + cERROR(1, "tioffset + tilen too high %u + %u", tioffset, tilen); + return -EINVAL; + } if (tilen) { ses->auth_key.response = kmalloc(tilen, GFP_KERNEL); if (!ses->auth_key.response) { -- cgit v0.10.2 From 000f9bb83968ebd6959ff76870f16fc8f766ebd3 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 30 Jan 2012 19:50:01 -0800 Subject: cifs: fix printk format warnings Fix printk format warnings for ssize_t variables: fs/cifs/connect.c:2145:3: warning: format '%ld' expects type 'long int', but argument 3 has type 'ssize_t' fs/cifs/connect.c:2152:3: warning: format '%ld' expects type 'long int', but argument 3 has type 'ssize_t' fs/cifs/connect.c:2160:3: warning: format '%ld' expects type 'long int', but argument 3 has type 'ssize_t' fs/cifs/connect.c:2170:3: warning: format '%ld' expects type 'long int', but argument 3 has type 'ssize_t' Signed-off-by: Randy Dunlap Acked-by: Jeff Layton Cc: linux-cifs@vger.kernel.org diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 026d646..9c28865 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2142,14 +2142,14 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) len = delim - payload; if (len > MAX_USERNAME_SIZE || len <= 0) { - cFYI(1, "Bad value from username search (len=%ld)", len); + cFYI(1, "Bad value from username search (len=%zd)", len); rc = -EINVAL; goto out_key_put; } vol->username = kstrndup(payload, len, GFP_KERNEL); if (!vol->username) { - cFYI(1, "Unable to allocate %ld bytes for username", len); + cFYI(1, "Unable to allocate %zd bytes for username", len); rc = -ENOMEM; goto out_key_put; } @@ -2157,7 +2157,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) len = key->datalen - (len + 1); if (len > MAX_PASSWORD_SIZE || len <= 0) { - cFYI(1, "Bad len for password search (len=%ld)", len); + cFYI(1, "Bad len for password search (len=%zd)", len); rc = -EINVAL; kfree(vol->username); vol->username = NULL; @@ -2167,7 +2167,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) ++delim; vol->password = kstrndup(delim, len, GFP_KERNEL); if (!vol->password) { - cFYI(1, "Unable to allocate %ld bytes for password", len); + cFYI(1, "Unable to allocate %zd bytes for password", len); rc = -ENOMEM; kfree(vol->username); vol->username = NULL; -- cgit v0.10.2 From 05c3b36e539627b7aed67d038381d0d9fa9d61e7 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 31 Jan 2012 09:04:15 +0100 Subject: ALSA: HDA: Fix jack creation for codecs with front and rear Line In If a codec has both a front and a rear Line In, two controls both named "Line Jack" will be created, which causes parsing to fail. While a long term solution might be to name the jacks differently, this extra check is consistent with what is already being done in many auto-parsers, and will also protect against other cases when two inputs have the same label. BugLink: https://bugs.launchpad.net/bugs/923409 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index d8a35da..9d819c4 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -282,7 +282,8 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl); static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg) + const struct auto_pin_cfg *cfg, + char *lastname, int *lastidx) { unsigned int def_conf, conn; char name[44]; @@ -298,6 +299,10 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, return 0; snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); + if (!strcmp(name, lastname) && idx == *lastidx) + idx++; + strncpy(lastname, name, 44); + *lastidx = idx; err = snd_hda_jack_add_kctl(codec, nid, name, idx); if (err < 0) return err; @@ -311,41 +316,42 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { const hda_nid_t *p; - int i, err; + int i, err, lastidx = 0; + char lastname[44] = ""; for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); if (err < 0) return err; } for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); if (err < 0) return err; } for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); if (err < 0) return err; } for (i = 0; i < cfg->num_inputs; i++) { - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg); + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, lastname, &lastidx); if (err < 0) return err; } for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg); + err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx); if (err < 0) return err; } - err = add_jack_kctl(codec, cfg->dig_in_pin, cfg); + err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, lastname, &lastidx); if (err < 0) return err; - err = add_jack_kctl(codec, cfg->mono_out_pin, cfg); + err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, lastname, &lastidx); if (err < 0) return err; return 0; -- cgit v0.10.2 From 3422a47041b8cb8f14ac1e3926bcf711121df6dc Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 31 Jan 2012 10:31:49 +0100 Subject: ALSA: HDA: Remove quirk for Toshiba Qosmio G50 The user reports that model=auto works better than current handling on a 3.2 based kernel (with jack detection patches backported). Since model=auto is what we prefer these days anyway, the quirk should be removed. Alsa-info for the relevant machine: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/923316/+attachment/2702812/+files/alsa-info.txt.Pbfno2x7bp BugLink: https://bugs.launchpad.net/bugs/923316 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 42b6a01..a8e82be 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4747,7 +4747,6 @@ enum { ALC262_FIXUP_FSC_H270, ALC262_FIXUP_HP_Z200, ALC262_FIXUP_TYAN, - ALC262_FIXUP_TOSHIBA_RX1, ALC262_FIXUP_LENOVO_3000, ALC262_FIXUP_BENQ, ALC262_FIXUP_BENQ_T31, @@ -4777,16 +4776,6 @@ static const struct alc_fixup alc262_fixups[] = { { } } }, - [ALC262_FIXUP_TOSHIBA_RX1] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x90170110 }, /* speaker */ - { 0x15, 0x0421101f }, /* HP */ - { 0x1a, 0x40f000f0 }, /* N/A */ - { 0x1b, 0x40f000f0 }, /* N/A */ - { 0x1e, 0x40f000f0 }, /* N/A */ - } - }, [ALC262_FIXUP_LENOVO_3000] = { .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -4819,8 +4808,6 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", - ALC262_FIXUP_TOSHIBA_RX1), SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), -- cgit v0.10.2 From 67f97f5c3edad35c4d37a94f994c76111a177fb6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 31 Jan 2012 14:51:29 +0000 Subject: ASoC: wm8994: Remove ASoC level register cache sync Now we've switched over to regmap the ASoC level cache sync will be ineffectual and potentially harmful as there is no longer an ASoC level cache. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 93d27b6..8623950 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2753,11 +2753,6 @@ static int wm8994_resume(struct snd_soc_codec *codec) codec->cache_only = 0; } - /* Restore the registers */ - ret = snd_soc_cache_sync(codec); - if (ret != 0) - dev_err(codec->dev, "Failed to sync cache: %d\n", ret); - wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY); for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { -- cgit v0.10.2 From 4505360376637832f79f84f352588b0a045ad113 Mon Sep 17 00:00:00 2001 From: Mitsuo Hayasaka Date: Fri, 27 Jan 2012 06:37:26 +0000 Subject: xfs: pass KM_SLEEP flag to kmem_realloc() in xlog_recover_add_to_cnt_trans() The kmem_realloc() in xfs is given KM_* memory allocation flags. And it allocates memory using kmalloc() after they are converted to gfp_mask flags. In xlog_recover_add_to_cont_trans(), 0u is passed to kmem_realloc(), instead of them. I guess it is preferred to use them, and here memory must be allocated but don't have to be done with GFP_ATOMIC. So, this patch changes it to KM_SLEEP. Signed-off-by: Mitsuo Hayasaka Cc: Ben Myers Cc: Alex Elder Cc: Christoph Hellwig Reviewed-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 541a508..15ff539 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1489,7 +1489,7 @@ xlog_recover_add_to_cont_trans( old_ptr = item->ri_buf[item->ri_cnt-1].i_addr; old_len = item->ri_buf[item->ri_cnt-1].i_len; - ptr = kmem_realloc(old_ptr, len+old_len, old_len, 0u); + ptr = kmem_realloc(old_ptr, len+old_len, old_len, KM_SLEEP); memcpy(&ptr[old_len], dp, len); /* d, s, l */ item->ri_buf[item->ri_cnt-1].i_len += len; item->ri_buf[item->ri_cnt-1].i_addr = ptr; -- cgit v0.10.2 From 2a73ca8208197d03f78d680b3c7953b897e91eb6 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 31 Jan 2012 12:51:24 -0600 Subject: [CIFS] Update cifs Kconfig title to match removal of experimental dependency Removed the dependency on CONFIG_EXPERIMENTAL but forgot to update the text description to be consistent. Signed-off-by: Steve French diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 0554b00..2b243af 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -139,7 +139,7 @@ config CIFS_DFS_UPCALL points. If unsure, say N. config CIFS_FSCACHE - bool "Provide CIFS client caching support (EXPERIMENTAL)" + bool "Provide CIFS client caching support" depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data @@ -147,7 +147,7 @@ config CIFS_FSCACHE manager. If unsure, say N. config CIFS_ACL - bool "Provide CIFS ACL support (EXPERIMENTAL)" + bool "Provide CIFS ACL support" depends on CIFS_XATTR && KEYS help Allows to fetch CIFS/NTFS ACL from the server. The DACL blob -- cgit v0.10.2 From 125a25da5729740b7d1dc417a3d5549321baae17 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 31 Jan 2012 15:49:10 +0000 Subject: ASoC: core: Better support for idle_bias_off suspend ignores If an idle_bias_off device is in any state other than off then it is still active for some reason (typically a low power function such as accessory detection). This wasn't an issue when the feature was implemented as we always went to _ON for any active function, subsequent power improvements have changed things. With the modern way of doing things we should overhaul the infrastructure to allow devices to explicitly take references for these functions but that's a much more invasive change and will require driver updates to deploy, this will bring the framework into line with the existing driver set before we do that work. Signed-off-by: Mark Brown Acked-by: Liam Girdwood diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b5ecf6d..92cee24 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -567,6 +567,17 @@ int snd_soc_suspend(struct device *dev) if (!codec->suspended && codec->driver->suspend) { switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: + /* + * If the CODEC is capable of idle + * bias off then being in STANDBY + * means it's doing something, + * otherwise fall through. + */ + if (codec->dapm.idle_bias_off) { + dev_dbg(codec->dev, + "idle_bias_off CODEC on over suspend\n"); + break; + } case SND_SOC_BIAS_OFF: codec->driver->suspend(codec); codec->suspended = 1; -- cgit v0.10.2 From a7ed07d51c8abdb407be454c6cb6cfad613759d9 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sun, 29 Jan 2012 22:08:12 +0000 Subject: net: fec: correct phy_name buffer length when init phy_name Fix the bug that we got wrong phy_name on imx6q sabrelite board. snprintf used wrong length of phy_name. phy_name length is MII_BUS_ID_SIZE + 3 rather not MII_BUS_ID_SIZE. I change it to sizeof(phy_name). Signed-off-by: Richard Zhao Acked-by: Shawn Guo Acked-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 7b25e9c..1c7aad8 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -990,7 +990,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) phy_id = 0; } - snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); + snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id); phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, fep->phy_interface); if (IS_ERR(phy_dev)) { -- cgit v0.10.2 From b923650b84068b74b6df838aa8f9b2a350171de6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 30 Jan 2012 00:20:48 +0200 Subject: lib: add NO_GENERIC_PCI_IOPORT_MAP Some architectures need to override the way IO port mapping is done on PCI devices. Supply a generic macro that calls ioport_map, and make it possible for architectures to override. Signed-off-by: Michael S. Tsirkin Acked-by: Arnd Bergmann diff --git a/include/asm-generic/pci_iomap.h b/include/asm-generic/pci_iomap.h index 8de4b73..e58fcf8 100644 --- a/include/asm-generic/pci_iomap.h +++ b/include/asm-generic/pci_iomap.h @@ -15,6 +15,16 @@ struct pci_dev; #ifdef CONFIG_PCI /* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); +/* Create a virtual mapping cookie for a port on a given PCI device. + * Do not call this directly, it exists to make it easier for architectures + * to override */ +#ifdef CONFIG_NO_GENERIC_PCI_IOPORT_MAP +extern void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port, + unsigned int nr); +#else +#define __pci_ioport_map(dev, port, nr) ioport_map((port), (nr)) +#endif + #else static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) { diff --git a/lib/Kconfig b/lib/Kconfig index 169eb7c..1df1388 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -19,6 +19,9 @@ config RATIONAL config GENERIC_FIND_FIRST_BIT bool +config NO_GENERIC_PCI_IOPORT_MAP + bool + config GENERIC_PCI_IOMAP bool diff --git a/lib/pci_iomap.c b/lib/pci_iomap.c index 4b0fdc2..0d83ea8 100644 --- a/lib/pci_iomap.c +++ b/lib/pci_iomap.c @@ -34,7 +34,7 @@ void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) if (maxlen && len > maxlen) len = maxlen; if (flags & IORESOURCE_IO) - return ioport_map(start, len); + return __pci_ioport_map(dev, start, len); if (flags & IORESOURCE_MEM) { if (flags & IORESOURCE_CACHEABLE) return ioremap(start, len); -- cgit v0.10.2 From 0f3b3956c4946a6a991974943e3de33ae3d2523f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 30 Jan 2012 00:23:38 +0200 Subject: mips: use the the PCI controller's io_map_base commit eab90291d35438bcebf7c3dc85be66d0f24e3002 (mips: switch to GENERIC_PCI_IOMAP) failed to take into account the PCI controller's io_map_base for mapping IO BARs. This also caused a new warning on mips. Fix this, without re-introducing code duplication, by setting NO_GENERIC_PCI_IOPORT_MAP and supplying a mips-specific __pci_ioport_map. Reported-by: Kevin Cernekee Signed-off-by: Michael S. Tsirkin diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index c4c1312..5ab6e89 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2356,6 +2356,7 @@ config PCI depends on HW_HAS_PCI select PCI_DOMAINS select GENERIC_PCI_IOMAP + select NO_GENERIC_PCI_IOPORT_MAP help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside diff --git a/arch/mips/lib/iomap-pci.c b/arch/mips/lib/iomap-pci.c index 2635b1a..fd35daa 100644 --- a/arch/mips/lib/iomap-pci.c +++ b/arch/mips/lib/iomap-pci.c @@ -10,8 +10,8 @@ #include #include -static void __iomem *ioport_map_pci(struct pci_dev *dev, - unsigned long port, unsigned int nr) +void __iomem *__pci_ioport_map(struct pci_dev *dev, + unsigned long port, unsigned int nr) { struct pci_controller *ctrl = dev->bus->sysdata; unsigned long base = ctrl->io_map_base; -- cgit v0.10.2 From 1e05b62ae4bd4c1209229de367b0989b39644f88 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 30 Jan 2012 00:29:10 +0200 Subject: sh: use the the PCI channels's io_map_base commit 43db595e8b5d78ce5ad2feab719814a76e3ad2e5 (sh: switch to GENERIC_PCI_IOMAP) failed to take into account the PCI channels's io_map_base for mapping IO BARs. This also caused a new warning on sh. Fix this, without re-introducing code duplication, by setting NO_GENERIC_PCI_IOPORT_MAP and supplying a sh-specific __pci_ioport_map. Reported-by: Kevin Cernekee Signed-off-by: Michael S. Tsirkin diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 3c8db65..713fb58 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -859,6 +859,7 @@ config PCI depends on SYS_SUPPORTS_PCI select PCI_DOMAINS select GENERIC_PCI_IOMAP + select NO_GENERIC_PCI_IOPORT_MAP help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 8f18dd0..1e7b0e2 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -356,8 +356,8 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, #ifndef CONFIG_GENERIC_IOMAP -static void __iomem *ioport_map_pci(struct pci_dev *dev, - unsigned long port, unsigned int nr) +void __iomem *__pci_ioport_map(struct pci_dev *dev, + unsigned long port, unsigned int nr) { struct pci_channel *chan = dev->sysdata; -- cgit v0.10.2 From 62aa2b537c6f5957afd98e29f96897419ed5ebab Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 31 Jan 2012 13:31:54 -0800 Subject: Linux 3.3-rc2 diff --git a/Makefile b/Makefile index 71e6ed2..e3b23e8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Saber-toothed Squirrel # *DOCUMENTATION* -- cgit v0.10.2 From 7df898b1a70b13c3a8892625f4ead929d9554293 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 31 Jan 2012 09:16:59 +1000 Subject: drm/nouveau/disp: check that panel power gpio is enabled at init time Reported-by: Yuriy Khomchik Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 1e382ad..a37c31e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -54,9 +54,10 @@ struct bit_entry { int bit_table(struct drm_device *, u8 id, struct bit_entry *); enum dcb_gpio_tag { - DCB_GPIO_TVDAC0 = 0xc, + DCB_GPIO_PANEL_POWER = 0x01, + DCB_GPIO_TVDAC0 = 0x0c, DCB_GPIO_TVDAC1 = 0x2d, - DCB_GPIO_PWM_FAN = 0x9, + DCB_GPIO_PWM_FAN = 0x09, DCB_GPIO_FAN_SENSE = 0x3d, DCB_GPIO_UNUSED = 0xff }; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 3cb52bc5..795a9e3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -219,6 +219,16 @@ nouveau_display_init(struct drm_device *dev) if (ret) return ret; + /* power on internal panel if it's not already. the init tables of + * some vbios default this to off for some reason, causing the + * panel to not work after resume + */ + if (nouveau_gpio_func_get(dev, DCB_GPIO_PANEL_POWER) == 0) { + nouveau_gpio_func_set(dev, DCB_GPIO_PANEL_POWER, true); + msleep(300); + } + + /* enable polling for external displays */ drm_kms_helper_poll_enable(dev); /* enable hotplug interrupts */ -- cgit v0.10.2 From ce2e7895faba8fabaa917f52293126e5f4174fa9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 1 Feb 2012 15:08:59 +1000 Subject: drm/nouveau/mxm: pretend to succeed, even if we can't shadow the MXM-SIS There's at least one known case where our shadowing code is buggy, and we fail init. Until we can be confident we're doing all this correctly, lets succeed and risk crazy bios tables rather than failing for perfectly valid configs too. Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c index 8bccddf..e5a64f0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -656,7 +656,16 @@ nouveau_mxm_init(struct drm_device *dev) if (mxm_shadow(dev, mxm[0])) { MXM_MSG(dev, "failed to locate valid SIS\n"); +#if 0 + /* we should, perhaps, fall back to some kind of limited + * mode here if the x86 vbios hasn't already done the + * work for us (so we prevent loading with completely + * whacked vbios tables). + */ return -EINVAL; +#else + return 0; +#endif } MXM_MSG(dev, "MXMS Version %d.%d\n", -- cgit v0.10.2 From 1eb8a619b43c1e99179ebadbc9c614ed37358f2d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sat, 7 Jan 2012 16:48:52 +1000 Subject: drm/nouveau: fix typo on mxmdcb option Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index e4a7cfe..81d7962 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -124,7 +124,7 @@ MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n"); int nouveau_ctxfw; module_param_named(ctxfw, nouveau_ctxfw, int, 0400); -MODULE_PARM_DESC(ctxfw, "Santise DCB table according to MXM-SIS\n"); +MODULE_PARM_DESC(mxmdcb, "Santise DCB table according to MXM-SIS\n"); int nouveau_mxmdcb = 1; module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400); -- cgit v0.10.2 From 525895ba388c949aa906f26e3ec5cb1ab041f56b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 10 Jan 2012 10:18:28 +1000 Subject: drm/nouveau/gem: fix fence_sync race / oops Due to a race it was possible for a fence to be destroyed while another thread was trying to synchronise with it. If this happened in the fallback non-semaphore path, it lead to the following oops due to fence->channel being NULL. BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] nouveau_fence_update+0xe/0xe0 [nouveau] *pde = a649c067 SMP Modules linked in: fuse nouveau(O) ttm(O) drm_kms_helper(O) drm(O) mxm_wmi video wmi netconsole configfs lockd bnep bluetooth rfkill ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ip6table_filter ip6_tables snd_hda_codec_realtek snd_hda_intel snd_hda_cobinfmt_misc uinput ata_generic pata_acpi pata_aet2c_algo_bit i2c_core [last unloaded: wmi] Pid: 2255, comm: gnome-shell Tainted: G O 3.2.0-0.rc5.git0.1.fc17.i686 #1 System manufacturer System Product Name/M2A-VM EIP: 0060:[] EFLAGS: 00010296 CPU: 1 EIP is at nouveau_fence_update+0xe/0xe0 [nouveau] EAX: 00000000 EBX: ddfc6dd0 ECX: dd111580 EDX: 00000000 ESI: 00003e80 EDI: dd111580 EBP: dd121d00 ESP: dd121ce8 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 Process gnome-shell (pid: 2255, ti=dd120000 task=dd111580 task.ti=dd120000) Stack: 7dc86c76 00000000 00003e80 ddfc6dd0 00003e80 dd111580 dd121d0c fa96371f 00000000 dd121d3c fa963773 dd111580 01000246 000ec53d 00000000 ddfc6dd0 00001f40 00000000 ddfc6dd0 00000010 dc7df840 dd121d6c fa9639a0 00000000 Call Trace: [] __nouveau_fence_signalled+0x1f/0x30 [nouveau] [] __nouveau_fence_wait+0x43/0xd0 [nouveau] [] nouveau_fence_sync+0x1a0/0x1c0 [nouveau] [] validate_list+0x176/0x300 [nouveau] [] ? ttm_bo_mem_put+0x30/0x30 [ttm] [] nouveau_gem_ioctl_pushbuf+0x48a/0xfd0 [nouveau] [] ? die+0x31/0x80 [] drm_ioctl+0x388/0x490 [drm] [] ? die+0x31/0x80 [] ? nouveau_gem_ioctl_new+0x150/0x150 [nouveau] [] ? file_has_perm+0xcb/0xe0 [] ? drm_copy_field+0x80/0x80 [drm] [] do_vfs_ioctl+0x86/0x5b0 [] ? die+0x31/0x80 [] ? selinux_file_ioctl+0x62/0x130 [] ? fget_light+0x30/0x340 [] sys_ioctl+0x6f/0x80 [] syscall_call+0x7/0xb [] ? die+0x31/0x80 [] ? die+0x31/0x80 Signed-off-by: Ben Skeggs Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 5f0bc57..7ce3fde 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -380,6 +380,25 @@ retry: } static int +validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo) +{ + struct nouveau_fence *fence = NULL; + int ret = 0; + + spin_lock(&nvbo->bo.bdev->fence_lock); + if (nvbo->bo.sync_obj) + fence = nouveau_fence_ref(nvbo->bo.sync_obj); + spin_unlock(&nvbo->bo.bdev->fence_lock); + + if (fence) { + ret = nouveau_fence_sync(fence, chan); + nouveau_fence_unref(&fence); + } + + return ret; +} + +static int validate_list(struct nouveau_channel *chan, struct list_head *list, struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr) { @@ -393,7 +412,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, list_for_each_entry(nvbo, list, entry) { struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; - ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan); + ret = validate_sync(chan, nvbo); if (unlikely(ret)) { NV_ERROR(dev, "fail pre-validate sync\n"); return ret; @@ -416,7 +435,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, return ret; } - ret = nouveau_fence_sync(nvbo->bo.sync_obj, chan); + ret = validate_sync(chan, nvbo); if (unlikely(ret)) { NV_ERROR(dev, "fail post-validate sync\n"); return ret; -- cgit v0.10.2 From a9d993882008a1ae2c953064f0c2ca7e604b1333 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 Jan 2012 10:20:47 +0300 Subject: drm/nv50/pm: signedness bug in nv50_pm_clocks_pre() calc_mclk() returns zero on success and negative on failure but clk is a u32. v2: Martin Peres: - clk should be an int, not a u32 Signed-off-by: Martin Peres Signed-off-by: Dan Carpenter Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 0393721..ec5481d 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -495,9 +495,9 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_pm_state *info; struct pll_lims pll; - int ret = -EINVAL; + int clk, ret = -EINVAL; int N, M, P1, P2; - u32 clk, out; + u32 out; if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) -- cgit v0.10.2 From f70eecde3bca92630d3886496e73316ff353f185 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Tue, 31 Jan 2012 13:04:41 -0800 Subject: ALSA: hda - Fix calling cs_automic twice for Cirrus codecs. If cs_automic is called twice (like it is during init) while the mic is present, it will over-write the last_input with the new one, causing it to switch back to the automic input when the mic is unplugged. This leaves the driver in a state (cur_input, last_input, and automix_idx the same) where the internal mic can not be selected until it is rebooted without the mic attached. Check that the mic hasn't already been switched to before setting last_input. Signed-off-by: Dylan Reid Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 0e99357..bc5a993 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -988,8 +988,10 @@ static void cs_automic(struct hda_codec *codec) change_cur_input(codec, !spec->automic_idx, 0); } else { if (present) { - spec->last_input = spec->cur_input; - spec->cur_input = spec->automic_idx; + if (spec->cur_input != spec->automic_idx) { + spec->last_input = spec->cur_input; + spec->cur_input = spec->automic_idx; + } } else { spec->cur_input = spec->last_input; } -- cgit v0.10.2 From 2673b4cf5d59c3ee5e0c12f6d734d38770324dc4 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Sun, 29 Jan 2012 12:17:33 -0600 Subject: backing-dev: fix wakeup timer races with bdi_unregister() While 7a401a972df8e18 ("backing-dev: ensure wakeup_timer is deleted") addressed the problem of the bdi being freed with a queued wakeup timer, there are other races that could happen if the wakeup timer expires after/during bdi_unregister(), before bdi_destroy() is called. wakeup_timer_fn() could attempt to wakeup a task which has already has been freed, or could access a NULL bdi->dev via the wake_forker_thread tracepoint. Cc: Cc: Jens Axboe Reported-by: Chanho Min Reviewed-by: Namjae Jeon Signed-off-by: Rabin Vincent Signed-off-by: Wu Fengguang diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 7ba8fea..dd8e2aa 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -318,7 +318,7 @@ static void wakeup_timer_fn(unsigned long data) if (bdi->wb.task) { trace_writeback_wake_thread(bdi); wake_up_process(bdi->wb.task); - } else { + } else if (bdi->dev) { /* * When bdi tasks are inactive for long time, they are killed. * In this case we have to wake-up the forker thread which @@ -584,6 +584,8 @@ EXPORT_SYMBOL(bdi_register_dev); */ static void bdi_wb_shutdown(struct backing_dev_info *bdi) { + struct task_struct *task; + if (!bdi_cap_writeback_dirty(bdi)) return; @@ -602,8 +604,13 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) * Finally, kill the kernel thread. We don't need to be RCU * safe anymore, since the bdi is gone from visibility. */ - if (bdi->wb.task) - kthread_stop(bdi->wb.task); + spin_lock_bh(&bdi->wb_lock); + task = bdi->wb.task; + bdi->wb.task = NULL; + spin_unlock_bh(&bdi->wb_lock); + + if (task) + kthread_stop(task); } /* @@ -623,7 +630,9 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) void bdi_unregister(struct backing_dev_info *bdi) { - if (bdi->dev) { + struct device *dev = bdi->dev; + + if (dev) { bdi_set_min_ratio(bdi, 0); trace_writeback_bdi_unregister(bdi); bdi_prune_sb(bdi); @@ -632,8 +641,12 @@ void bdi_unregister(struct backing_dev_info *bdi) if (!bdi_cap_flush_forker(bdi)) bdi_wb_shutdown(bdi); bdi_debug_unregister(bdi); - device_unregister(bdi->dev); + + spin_lock_bh(&bdi->wb_lock); bdi->dev = NULL; + spin_unlock_bh(&bdi->wb_lock); + + device_unregister(dev); } } EXPORT_SYMBOL(bdi_unregister); -- cgit v0.10.2 From 15eb77a07c714ac80201abd0a9568888bcee6276 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Tue, 17 Jan 2012 11:18:56 -0600 Subject: writeback: fix NULL bdi->dev in trace writeback_single_inode bdi_prune_sb() resets sb->s_bdi to default_backing_dev_info when the tearing down the original bdi. Fix trace_writeback_single_inode to use sb->s_bdi=default_backing_dev_info rather than bdi->dev=NULL for a teared down bdi. Cc: Reported-by: Rabin Vincent Tested-by: Rabin Vincent Signed-off-by: Wu Fengguang diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index f855916..5b4a936 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -53,14 +53,6 @@ struct wb_writeback_work { }; /* - * Include the creation of the trace points after defining the - * wb_writeback_work structure so that the definition remains local to this - * file. - */ -#define CREATE_TRACE_POINTS -#include - -/* * We don't actually have pdflush, but this one is exported though /proc... */ int nr_pdflush_threads; @@ -92,6 +84,14 @@ static inline struct inode *wb_inode(struct list_head *head) return list_entry(head, struct inode, i_wb_list); } +/* + * Include the creation of the trace points after defining the + * wb_writeback_work structure and inline functions so that the definition + * remains local to this file. + */ +#define CREATE_TRACE_POINTS +#include + /* Wakeup flusher thread or forker thread to fork it. Requires bdi->wb_lock. */ static void bdi_wakeup_flusher(struct backing_dev_info *bdi) { diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 8588a89..06d302e 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -426,7 +426,7 @@ DECLARE_EVENT_CLASS(writeback_single_inode_template, TP_fast_assign( strncpy(__entry->name, - dev_name(inode->i_mapping->backing_dev_info->dev), 32); + dev_name(inode_to_bdi(inode)->dev), 32); __entry->ino = inode->i_ino; __entry->state = inode->i_state; __entry->dirtied_when = inode->dirtied_when; -- cgit v0.10.2 From 3310225dfc71a35a2cc9340c15c0e08b14b3c754 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Mon, 9 Jan 2012 11:53:50 -0600 Subject: lib: proportion: lower PROP_MAX_SHIFT to 32 on 64-bit kernel PROP_MAX_SHIFT should be set to <=32 on 64-bit box. This fixes two bugs in the below lines of bdi_dirty_limit(): bdi_dirty *= numerator; do_div(bdi_dirty, denominator); 1) divide error: do_div() only uses the lower 32 bit of the denominator, which may trimmed to be 0 when PROP_MAX_SHIFT > 32. 2) overflow: (bdi_dirty * numerator) could easily overflow if numerator used up to 48 bits, leaving only 16 bits to bdi_dirty Cc: Cc: Peter Zijlstra Reported-by: Ilya Tumaykin Tested-by: Ilya Tumaykin Signed-off-by: Wu Fengguang diff --git a/include/linux/proportions.h b/include/linux/proportions.h index ef35bb7..26a8a4e 100644 --- a/include/linux/proportions.h +++ b/include/linux/proportions.h @@ -81,7 +81,11 @@ void prop_inc_percpu(struct prop_descriptor *pd, struct prop_local_percpu *pl) * Limit the time part in order to ensure there are some bits left for the * cycle counter and fraction multiply. */ +#if BITS_PER_LONG == 32 #define PROP_MAX_SHIFT (3*BITS_PER_LONG/4) +#else +#define PROP_MAX_SHIFT (BITS_PER_LONG/2) +#endif #define PROP_FRAC_SHIFT (BITS_PER_LONG - PROP_MAX_SHIFT - 1) #define PROP_FRAC_BASE (1UL << PROP_FRAC_SHIFT) -- cgit v0.10.2 From 50e92b3c971129c96a5fffb51dd42691e2ee4004 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 4 Jan 2012 15:06:43 +0900 Subject: KVM: Fix __set_bit() race in mark_page_dirty() during dirty logging It is possible that the __set_bit() in mark_page_dirty() is called simultaneously on the same region of memory, which may result in only one bit being set, because some callers do not take mmu_lock before mark_page_dirty(). This problem is hard to produce because when we reach mark_page_dirty() beginning from, e.g., tdp_page_fault(), mmu_lock is being held during __direct_map(): making kvm-unit-tests' dirty log api test write to two pages concurrently was not useful for this reason. So we have confirmed that there can actually be race condition by checking if some callers really reach there without holding mmu_lock using spin_is_locked(): probably they were from kvm_write_guest_page(). To fix this race, this patch changes the bit operation to the atomic version: note that nr_dirty_pages also suffers from the race but we do not need exactly correct numbers for now. Signed-off-by: Takuya Yoshikawa Signed-off-by: Marcelo Tosatti diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 7287bf5..a91f980 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1543,7 +1543,7 @@ void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot, if (memslot && memslot->dirty_bitmap) { unsigned long rel_gfn = gfn - memslot->base_gfn; - if (!__test_and_set_bit_le(rel_gfn, memslot->dirty_bitmap)) + if (!test_and_set_bit_le(rel_gfn, memslot->dirty_bitmap)) memslot->nr_dirty_pages++; } } -- cgit v0.10.2 From bdb42f5afebe208eae90406959383856ae2caf2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20B=C3=A4rwolf?= Date: Thu, 12 Jan 2012 16:43:03 +0100 Subject: KVM: x86: extend "struct x86_emulate_ops" with "get_cpuid" In order to be able to proceed checks on CPU-specific properties within the emulator, function "get_cpuid" is introduced. With "get_cpuid" it is possible to virtually call the guests "cpuid"-opcode without changing the VM's context. [mtosatti: cleanup/beautify code] Signed-off-by: Stephan Baerwolf Signed-off-by: Marcelo Tosatti diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index ab4092e..c8b2868 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h @@ -190,6 +190,9 @@ struct x86_emulate_ops { int (*intercept)(struct x86_emulate_ctxt *ctxt, struct x86_instruction_info *info, enum x86_intercept_stage stage); + + bool (*get_cpuid)(struct x86_emulate_ctxt *ctxt, + u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); }; typedef u32 __attribute__((vector_size(16))) sse128_t; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 14d6cad..8c890e2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4180,6 +4180,28 @@ static int emulator_intercept(struct x86_emulate_ctxt *ctxt, return kvm_x86_ops->check_intercept(emul_to_vcpu(ctxt), info, stage); } +static bool emulator_get_cpuid(struct x86_emulate_ctxt *ctxt, + u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) +{ + struct kvm_cpuid_entry2 *cpuid = NULL; + + if (eax && ecx) + cpuid = kvm_find_cpuid_entry(emul_to_vcpu(ctxt), + *eax, *ecx); + + if (cpuid) { + *eax = cpuid->eax; + *ecx = cpuid->ecx; + if (ebx) + *ebx = cpuid->ebx; + if (edx) + *edx = cpuid->edx; + return true; + } + + return false; +} + static struct x86_emulate_ops emulate_ops = { .read_std = kvm_read_guest_virt_system, .write_std = kvm_write_guest_virt_system, @@ -4211,6 +4233,7 @@ static struct x86_emulate_ops emulate_ops = { .get_fpu = emulator_get_fpu, .put_fpu = emulator_put_fpu, .intercept = emulator_intercept, + .get_cpuid = emulator_get_cpuid, }; static void cache_all_regs(struct kvm_vcpu *vcpu) -- cgit v0.10.2 From c2226fc9e87ba3da060e47333657cd6616652b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20B=C3=A4rwolf?= Date: Thu, 12 Jan 2012 16:43:04 +0100 Subject: KVM: x86: fix missing checks in syscall emulation On hosts without this patch, 32bit guests will crash (and 64bit guests may behave in a wrong way) for example by simply executing following nasm-demo-application: [bits 32] global _start SECTION .text _start: syscall (I tested it with winxp and linux - both always crashed) Disassembly of section .text: 00000000 <_start>: 0: 0f 05 syscall The reason seems a missing "invalid opcode"-trap (int6) for the syscall opcode "0f05", which is not available on Intel CPUs within non-longmodes, as also on some AMD CPUs within legacy-mode. (depending on CPU vendor, MSR_EFER and cpuid) Because previous mentioned OSs may not engage corresponding syscall target-registers (STAR, LSTAR, CSTAR), they remain NULL and (non trapping) syscalls are leading to multiple faults and finally crashs. Depending on the architecture (AMD or Intel) pretended by guests, various checks according to vendor's documentation are implemented to overcome the current issue and behave like the CPUs physical counterparts. [mtosatti: cleanup/beautify code] Signed-off-by: Stephan Baerwolf Signed-off-by: Marcelo Tosatti diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index c8b2868..7b9cfc4 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h @@ -301,6 +301,19 @@ struct x86_emulate_ctxt { #define X86EMUL_MODE_PROT (X86EMUL_MODE_PROT16|X86EMUL_MODE_PROT32| \ X86EMUL_MODE_PROT64) +/* CPUID vendors */ +#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx 0x68747541 +#define X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx 0x444d4163 +#define X86EMUL_CPUID_VENDOR_AuthenticAMD_edx 0x69746e65 + +#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx 0x69444d41 +#define X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx 0x21726574 +#define X86EMUL_CPUID_VENDOR_AMDisbetterI_edx 0x74656273 + +#define X86EMUL_CPUID_VENDOR_GenuineIntel_ebx 0x756e6547 +#define X86EMUL_CPUID_VENDOR_GenuineIntel_ecx 0x6c65746e +#define X86EMUL_CPUID_VENDOR_GenuineIntel_edx 0x49656e69 + enum x86_intercept_stage { X86_ICTP_NONE = 0, /* Allow zero-init to not match anything */ X86_ICPT_PRE_EXCEPT, diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 05a562b..0982507 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -1891,6 +1891,51 @@ setup_syscalls_segments(struct x86_emulate_ctxt *ctxt, ss->p = 1; } +static bool em_syscall_is_enabled(struct x86_emulate_ctxt *ctxt) +{ + struct x86_emulate_ops *ops = ctxt->ops; + u32 eax, ebx, ecx, edx; + + /* + * syscall should always be enabled in longmode - so only become + * vendor specific (cpuid) if other modes are active... + */ + if (ctxt->mode == X86EMUL_MODE_PROT64) + return true; + + eax = 0x00000000; + ecx = 0x00000000; + if (ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx)) { + /* + * Intel ("GenuineIntel") + * remark: Intel CPUs only support "syscall" in 64bit + * longmode. Also an 64bit guest with a + * 32bit compat-app running will #UD !! While this + * behaviour can be fixed (by emulating) into AMD + * response - CPUs of AMD can't behave like Intel. + */ + if (ebx == X86EMUL_CPUID_VENDOR_GenuineIntel_ebx && + ecx == X86EMUL_CPUID_VENDOR_GenuineIntel_ecx && + edx == X86EMUL_CPUID_VENDOR_GenuineIntel_edx) + return false; + + /* AMD ("AuthenticAMD") */ + if (ebx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ebx && + ecx == X86EMUL_CPUID_VENDOR_AuthenticAMD_ecx && + edx == X86EMUL_CPUID_VENDOR_AuthenticAMD_edx) + return true; + + /* AMD ("AMDisbetter!") */ + if (ebx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ebx && + ecx == X86EMUL_CPUID_VENDOR_AMDisbetterI_ecx && + edx == X86EMUL_CPUID_VENDOR_AMDisbetterI_edx) + return true; + } + + /* default: (not Intel, not AMD), apply Intel's stricter rules... */ + return false; +} + static int em_syscall(struct x86_emulate_ctxt *ctxt) { struct x86_emulate_ops *ops = ctxt->ops; @@ -1904,9 +1949,15 @@ static int em_syscall(struct x86_emulate_ctxt *ctxt) ctxt->mode == X86EMUL_MODE_VM86) return emulate_ud(ctxt); + if (!(em_syscall_is_enabled(ctxt))) + return emulate_ud(ctxt); + ops->get_msr(ctxt, MSR_EFER, &efer); setup_syscalls_segments(ctxt, &cs, &ss); + if (!(efer & EFER_SCE)) + return emulate_ud(ctxt); + ops->get_msr(ctxt, MSR_STAR, &msr_data); msr_data >>= 32; cs_sel = (u16)(msr_data & 0xfffc); -- cgit v0.10.2 From 5753785fa97742d2723ed8ebb29ae59cac912705 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Sun, 15 Jan 2012 14:17:22 +0200 Subject: KVM: do not #GP on perf MSR writes when vPMU is disabled Return to behaviour perf MSR had before introducing vPMU in case vPMU is disabled. Some guests access those registers unconditionally and do not expect it to fail. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8c890e2..9cbfc06 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1495,6 +1495,8 @@ static void record_steal_time(struct kvm_vcpu *vcpu) int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) { + bool pr = false; + switch (msr) { case MSR_EFER: return set_efer(vcpu, data); @@ -1635,6 +1637,18 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " "0x%x data 0x%llx\n", msr, data); break; + case MSR_P6_PERFCTR0: + case MSR_P6_PERFCTR1: + pr = true; + case MSR_P6_EVNTSEL0: + case MSR_P6_EVNTSEL1: + if (kvm_pmu_msr(vcpu, msr)) + return kvm_pmu_set_msr(vcpu, msr, data); + + if (pr || data != 0) + pr_unimpl(vcpu, "disabled perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); + break; case MSR_K7_CLK_CTL: /* * Ignore all writes to this no longer documented MSR. @@ -1835,6 +1849,14 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) case MSR_FAM10H_MMIO_CONF_BASE: data = 0; break; + case MSR_P6_PERFCTR0: + case MSR_P6_PERFCTR1: + case MSR_P6_EVNTSEL0: + case MSR_P6_EVNTSEL1: + if (kvm_pmu_msr(vcpu, msr)) + return kvm_pmu_get_msr(vcpu, msr, pdata); + data = 0; + break; case MSR_IA32_UCODE_REV: data = 0x100000000ULL; break; -- cgit v0.10.2 From 2b6712b19531e22455e7fa18371c5ba9eec76699 Mon Sep 17 00:00:00 2001 From: Susan Gao Date: Mon, 30 Jan 2012 13:57:04 -0800 Subject: ASoC: wm8962: Fix word length configuration Signed-off-by: Susan Gao Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index bda3da8..29c4b02 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3159,13 +3159,13 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: - aif0 |= 0x40; + aif0 |= 0x4; break; case SNDRV_PCM_FORMAT_S24_LE: - aif0 |= 0x80; + aif0 |= 0x8; break; case SNDRV_PCM_FORMAT_S32_LE: - aif0 |= 0xc0; + aif0 |= 0xc; break; default: return -EINVAL; -- cgit v0.10.2 From 44bed4838dc191988fd1d03deccc3a845705d2de Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 31 Jan 2012 09:49:04 +0800 Subject: ASoC: cs42l73: Fix Output [X|A|V]SP_SCLK Sourcing Mode setting for master mode For master mode, set Output [X|A|V]SP_SCLK Sourcing Mode to MCLK Mode. Signed-off-by: Axel Lin Acked-by: Brian Austin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 9d38db8..78979b3 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1113,7 +1113,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, priv->config[id].mmcc &= 0xC0; priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc; priv->config[id].spc &= 0xFC; - priv->config[id].spc &= MCK_SCLK_64FS; + priv->config[id].spc |= MCK_SCLK_MCLK; } else { /* CS42L73 Slave */ priv->config[id].spc &= 0xFC; -- cgit v0.10.2 From 54c2a89f60fd71b924d0f848ac892442951401a6 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 1 Feb 2012 12:05:41 +0100 Subject: ALSA: HDA: Fix duplicated output to more than one codec This typo caused the wrong codec's nid to be checked for wcaps type. As a result, sometimes speakers would duplicate the output sent to HDMI output. Cc: stable@kernel.org BugLink: https://bugs.launchpad.net/bugs/924320 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 4df72c0..c2c65f6 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1447,7 +1447,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && - get_wcaps_type(get_wcaps(codec, p->nid)) == type) + get_wcaps_type(get_wcaps(c, p->nid)) == type) p->dirty = 1; } } -- cgit v0.10.2 From c70c471c585a3fc1a10c792d5121b3803c83dde0 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:17 +0200 Subject: lib/mpi: added missing NULL check Added missing NULL check after mpi_alloc(). Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index 716802b..6116fc4 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -79,7 +79,8 @@ MPI do_encode_md(const void *sha_buffer, unsigned nbits) } a = mpi_alloc((nframe + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB); - mpi_set_buffer(a, frame, nframe, 0); + if (a) + mpi_set_buffer(a, frame, nframe, 0); kfree(frame); return a; -- cgit v0.10.2 From 3cccd1543ab623a5065335bf08350e06ffc788ab Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:16 +0200 Subject: lib/mpi: replaced MPI_NULL with normal NULL MPI_NULL is replaced with normal NULL. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/include/linux/mpi.h b/include/linux/mpi.h index 06f8899..d02cca6 100644 --- a/include/linux/mpi.h +++ b/include/linux/mpi.h @@ -57,8 +57,6 @@ struct gcry_mpi { typedef struct gcry_mpi *MPI; -#define MPI_NULL NULL - #define mpi_get_nlimbs(a) ((a)->nlimbs) #define mpi_is_neg(a) ((a)->sign) diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index 6116fc4..d7684aa 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -34,7 +34,7 @@ MPI do_encode_md(const void *sha_buffer, unsigned nbits) uint8_t *frame, *fr_pt; int i = 0, n; size_t asnlen = DIM(asn); - MPI a = MPI_NULL; + MPI a = NULL; if (SHA1_DIGEST_LENGTH + asnlen + 4 > nframe) pr_info("MPI: can't encode a %d bit MD into a %d bits frame\n", @@ -48,7 +48,7 @@ MPI do_encode_md(const void *sha_buffer, unsigned nbits) */ frame = kmalloc(nframe, GFP_KERNEL); if (!frame) - return MPI_NULL; + return NULL; n = 0; frame[n++] = 0; frame[n++] = 1; /* block type */ @@ -92,7 +92,7 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) int i, j; unsigned nbits, nbytes, nlimbs, nread = 0; mpi_limb_t a; - MPI val = MPI_NULL; + MPI val = NULL; if (*ret_nread < 2) goto leave; @@ -109,7 +109,7 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB; val = mpi_alloc(nlimbs); if (!val) - return MPI_NULL; + return NULL; i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB; i %= BYTES_PER_MPI_LIMB; val->nbits = nbits; diff --git a/lib/mpi/mpiutil.c b/lib/mpi/mpiutil.c index eefc55d..6bfc41f 100644 --- a/lib/mpi/mpiutil.c +++ b/lib/mpi/mpiutil.c @@ -135,7 +135,7 @@ int mpi_copy(MPI *copied, const MPI a) size_t i; MPI b; - *copied = MPI_NULL; + *copied = NULL; if (a) { b = mpi_alloc(a->nlimbs); -- cgit v0.10.2 From e87c5e35a92e045de75fb6ae9846a38bdd0f92bd Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:21 +0200 Subject: lib/mpi: return error code on dividing by zero Definitely better to return error code than to divide by zero. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c index b04a3cf..67f3e79 100644 --- a/lib/mpi/mpi-pow.c +++ b/lib/mpi/mpi-pow.c @@ -59,7 +59,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) ep = exp->d; if (!msize) - msize = 1 / msize; /* provoke a signal */ + return -EINVAL; if (!esize) { /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 -- cgit v0.10.2 From e2fe85c236736c866481de288f636ab06ef49787 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:22 +0200 Subject: lib/mpi: checks for zero divisor length Divisor length should not be 0. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpi-div.c b/lib/mpi/mpi-div.c index c3087d1..8a45717 100644 --- a/lib/mpi/mpi-div.c +++ b/lib/mpi/mpi-div.c @@ -149,6 +149,9 @@ int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den) mpi_ptr_t marker[5]; int markidx = 0; + if (!dsize) + return -EINVAL; + memset(marker, 0, sizeof(marker)); /* Ensure space is enough for quotient and remainder. -- cgit v0.10.2 From bc95eeadf5c6fd9e9840898a83a93718a0114b6d Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:23 +0200 Subject: lib/mpi: removed unused functions do_encode_md() and mpi_get_keyid() are not parts of mpi library. They were used early versions of gnupg and in digsig project, but they are not used neither here nor there anymore. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index d7684aa..f26b41f 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -20,72 +20,8 @@ #include "mpi-internal.h" -#define DIM(v) (sizeof(v)/sizeof((v)[0])) #define MAX_EXTERN_MPI_BITS 16384 -static uint8_t asn[15] = /* Object ID is 1.3.14.3.2.26 */ -{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, - 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 -}; - -MPI do_encode_md(const void *sha_buffer, unsigned nbits) -{ - int nframe = (nbits + 7) / 8; - uint8_t *frame, *fr_pt; - int i = 0, n; - size_t asnlen = DIM(asn); - MPI a = NULL; - - if (SHA1_DIGEST_LENGTH + asnlen + 4 > nframe) - pr_info("MPI: can't encode a %d bit MD into a %d bits frame\n", - (int)(SHA1_DIGEST_LENGTH * 8), (int)nbits); - - /* We encode the MD in this way: - * - * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) - * - * PAD consists of FF bytes. - */ - frame = kmalloc(nframe, GFP_KERNEL); - if (!frame) - return NULL; - n = 0; - frame[n++] = 0; - frame[n++] = 1; /* block type */ - i = nframe - SHA1_DIGEST_LENGTH - asnlen - 3; - - if (i <= 1) { - pr_info("MPI: message digest encoding failed\n"); - kfree(frame); - return a; - } - - memset(frame + n, 0xff, i); - n += i; - frame[n++] = 0; - memcpy(frame + n, &asn, asnlen); - n += asnlen; - memcpy(frame + n, sha_buffer, SHA1_DIGEST_LENGTH); - n += SHA1_DIGEST_LENGTH; - - i = nframe; - fr_pt = frame; - - if (n != nframe) { - printk - ("MPI: message digest encoding failed, frame length is wrong\n"); - kfree(frame); - return a; - } - - a = mpi_alloc((nframe + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB); - if (a) - mpi_set_buffer(a, frame, nframe, 0); - kfree(frame); - - return a; -} - MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) { const uint8_t *buffer = xbuffer; @@ -213,30 +149,6 @@ int mpi_fromstr(MPI val, const char *str) EXPORT_SYMBOL_GPL(mpi_fromstr); /**************** - * Special function to get the low 8 bytes from an mpi. - * This can be used as a keyid; KEYID is an 2 element array. - * Return the low 4 bytes. - */ -u32 mpi_get_keyid(const MPI a, u32 *keyid) -{ -#if BYTES_PER_MPI_LIMB == 4 - if (keyid) { - keyid[0] = a->nlimbs >= 2 ? a->d[1] : 0; - keyid[1] = a->nlimbs >= 1 ? a->d[0] : 0; - } - return a->nlimbs >= 1 ? a->d[0] : 0; -#elif BYTES_PER_MPI_LIMB == 8 - if (keyid) { - keyid[0] = a->nlimbs ? (u32) (a->d[0] >> 32) : 0; - keyid[1] = a->nlimbs ? (u32) (a->d[0] & 0xffffffff) : 0; - } - return a->nlimbs ? (u32) (a->d[0] & 0xffffffff) : 0; -#else -#error Make this function work with other LIMB sizes -#endif -} - -/**************** * Return an allocated buffer with the MPI (msb first). * NBYTES receives the length of this buffer. Caller must free the * return string (This function does return a 0 byte buffer with NBYTES -- cgit v0.10.2 From f58a08152ce4198a2a1da162b97ecf8264c24866 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:25 +0200 Subject: lib/digsig: additional sanity checks against badly formated key payload Added sanity checks for possible wrongly formatted key payload data: - minimum key payload size - zero modulus length - corrected upper key payload boundary. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/digsig.c b/lib/digsig.c index fd2402f..5d840ac 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -105,6 +105,10 @@ static int digsig_verify_rsa(struct key *key, down_read(&key->sem); ukp = key->payload.data; + + if (ukp->datalen < sizeof(*pkh)) + goto err1; + pkh = (struct pubkey_hdr *)ukp->data; if (pkh->version != 1) @@ -117,7 +121,7 @@ static int digsig_verify_rsa(struct key *key, goto err1; datap = pkh->mpi; - endp = datap + ukp->datalen; + endp = ukp->data + ukp->datalen; for (i = 0; i < pkh->nmpi; i++) { unsigned int remaining = endp - datap; @@ -128,7 +132,8 @@ static int digsig_verify_rsa(struct key *key, mblen = mpi_get_nbits(pkey[0]); mlen = (mblen + 7)/8; - err = -ENOMEM; + if (mlen == 0) + goto err; out1 = kzalloc(mlen, GFP_KERNEL); if (!out1) -- cgit v0.10.2 From b35e286a640f31d619a637332972498b51f3fd90 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:26 +0200 Subject: lib/digsig: pkcs_1_v1_5_decode_emsa cleanup Removed useless 'is_valid' variable in pkcs_1_v1_5_decode_emsa(), which was inhereted from original code. Client now uses return value to check for an error. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/digsig.c b/lib/digsig.c index 5d840ac..b67e82c 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -34,14 +34,9 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, unsigned long msglen, unsigned long modulus_bitlen, unsigned char *out, - unsigned long *outlen, - int *is_valid) + unsigned long *outlen) { unsigned long modulus_len, ps_len, i; - int result; - - /* default to invalid packet */ - *is_valid = 0; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); @@ -50,39 +45,30 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, return -EINVAL; /* separate encoded message */ - if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1)) { - result = -EINVAL; - goto bail; - } + if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1)) + return -EINVAL; for (i = 2; i < modulus_len - 1; i++) if (msg[i] != 0xFF) break; /* separator check */ - if (msg[i] != 0) { + if (msg[i] != 0) /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ - result = -EINVAL; - goto bail; - } + return -EINVAL; ps_len = i - 2; if (*outlen < (msglen - (2 + ps_len + 1))) { *outlen = msglen - (2 + ps_len + 1); - result = -EOVERFLOW; - goto bail; + return -EOVERFLOW; } *outlen = (msglen - (2 + ps_len + 1)); memcpy(out, &msg[2 + ps_len + 1], *outlen); - /* valid packet */ - *is_valid = 1; - result = 0; -bail: - return result; + return 0; } /* @@ -96,7 +82,7 @@ static int digsig_verify_rsa(struct key *key, unsigned long len; unsigned long mlen, mblen; unsigned nret, l; - int valid, head, i; + int head, i; unsigned char *out1 = NULL, *out2 = NULL; MPI in = NULL, res = NULL, pkey[2]; uint8_t *p, *datap, *endp; @@ -172,10 +158,9 @@ static int digsig_verify_rsa(struct key *key, memset(out1, 0, head); memcpy(out1 + head, p, l); - err = -EINVAL; - pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len, &valid); + err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len); - if (valid && len == hlen) + if (!err && len == hlen) err = memcmp(out2, h, hlen); err: -- cgit v0.10.2 From 4877e056192245b387aae004ab38f7f3899dd57c Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:19 +0200 Subject: lib/mpi: check for possible zero length Buggy client might pass zero nlimbs which is meaningless. Added check for zero length. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpiutil.c b/lib/mpi/mpiutil.c index 6bfc41f..26e4ed3 100644 --- a/lib/mpi/mpiutil.c +++ b/lib/mpi/mpiutil.c @@ -58,6 +58,9 @@ mpi_ptr_t mpi_alloc_limb_space(unsigned nlimbs) { size_t len = nlimbs * sizeof(mpi_limb_t); + if (!len) + return NULL; + return kmalloc(len, GFP_KERNEL); } -- cgit v0.10.2 From a6d68ecc56ed182644c81ef6917ce1f5404db7dd Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:20 +0200 Subject: lib/mpi: added comment on divide by 0 case Comment explains that existing clients do not call this function with dsize == 0, which means that 1/0 should not happen. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpih-div.c b/lib/mpi/mpih-div.c index 87ede16..cde1aae 100644 --- a/lib/mpi/mpih-div.c +++ b/lib/mpi/mpih-div.c @@ -217,6 +217,10 @@ mpihelp_divrem(mpi_ptr_t qp, mpi_size_t qextra_limbs, case 0: /* We are asked to divide by zero, so go ahead and do it! (To make the compiler not remove this statement, return the value.) */ + /* + * existing clients of this function have been modified + * not to call it with dsize == 0, so this should not happen + */ return 1 / dsize; case 1: -- cgit v0.10.2 From 43b2c0aeaab2237996a72f9b9d7952ba82d56913 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:18 +0200 Subject: lib/mpi: added missing NULL check Added missing NULL check after mpi_alloc_limb_space(). Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/mpi/mpi-div.c b/lib/mpi/mpi-div.c index 8a45717..f68cbbb 100644 --- a/lib/mpi/mpi-div.c +++ b/lib/mpi/mpi-div.c @@ -210,6 +210,8 @@ int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den) * numerator would be gradually overwritten by the quotient limbs. */ if (qp == np) { /* Copy NP object to temporary space. */ np = marker[markidx++] = mpi_alloc_limb_space(nsize); + if (!np) + goto nomem; MPN_COPY(np, qp, nsize); } } else /* Put quotient at top of remainder. */ -- cgit v0.10.2 From 86f8bedc9e1a8ddb4f1d9ff1f0c1229cc0797d6d Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 26 Jan 2012 19:13:24 +0200 Subject: lib/digsig: checks for NULL return value mpi_read_from_buffer() return value must not be NULL. Signed-off-by: Dmitry Kasatkin Reviewed-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/lib/digsig.c b/lib/digsig.c index b67e82c..286d558 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -109,9 +109,13 @@ static int digsig_verify_rsa(struct key *key, datap = pkh->mpi; endp = ukp->data + ukp->datalen; + err = -ENOMEM; + for (i = 0; i < pkh->nmpi; i++) { unsigned int remaining = endp - datap; pkey[i] = mpi_read_from_buffer(datap, &remaining); + if (!pkey[i]) + goto err; datap += remaining; } @@ -168,8 +172,8 @@ err: mpi_free(res); kfree(out1); kfree(out2); - mpi_free(pkey[0]); - mpi_free(pkey[1]); + while (--i >= 0) + mpi_free(pkey[i]); err1: up_read(&key->sem); -- cgit v0.10.2 From 86698c20f71d488b32c49ed4687fb3cf8a88a5ca Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 31 Jan 2012 19:06:25 -0600 Subject: drm/radeon/kms: disable output polling when suspended Polling the outputs when the device is suspended can result in erroneous status updates. Disable output polling during suspend to prevent this from happening. Signed-off-by: Seth Forshee Reviewed-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index cec51a5..49f7cb7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -883,6 +883,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; + drm_kms_helper_poll_disable(dev); + /* turn off display hw */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); @@ -972,6 +974,8 @@ int radeon_resume_kms(struct drm_device *dev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); } + + drm_kms_helper_poll_enable(dev); return 0; } -- cgit v0.10.2 From 1b61925061660009f5b8047f93c5297e04541273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Wed, 1 Feb 2012 12:09:55 +0100 Subject: drm/radeon: Set DESKTOP_HEIGHT register to the framebuffer (not mode) height. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value of this register is transferred to the V_COUNTER register at the beginning of vertical blank. V_COUNTER is the reference for VLINE waits and goes from VIEWPORT_Y_START to VIEWPORT_Y_START+VIEWPORT_HEIGHT during scanout, so if VIEWPORT_Y_START is not 0, V_COUNTER actually went backwards at the beginning of vertical blank, and VLINE waits excluding the whole scanout area could never finish (possibly only if VIEWPORT_Y_START is larger than the length of vertical blank in scanlines). Setting DESKTOP_HEIGHT to the framebuffer height should prevent this for any kind of VLINE wait. Fixes https://bugs.freedesktop.org/show_bug.cgi?id=45329 . CC: stable@vger.kernel.org Signed-off-by: Michel Dänzer Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 8919352..742f17f 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1184,7 +1184,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1); WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - crtc->mode.vdisplay); + target_fb->height); x &= ~3; y &= ~1; WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset, @@ -1353,7 +1353,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); WREG32(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - crtc->mode.vdisplay); + target_fb->height); x &= ~3; y &= ~1; WREG32(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, -- cgit v0.10.2 From 3f7e363249ad5f4070025f6c09fd264f93f24eab Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 31 Jan 2012 09:55:21 +0100 Subject: drm/radeon/kms: Fix device tree linkage of DP i2c buses too Properly set the parent device of DP i2c buses before registering them too. Signed-off-by: Jean Delvare Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index e2a393f..98a8ad6 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -958,6 +958,7 @@ struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev, i2c->rec = *rec; i2c->adapter.owner = THIS_MODULE; i2c->adapter.class = I2C_CLASS_DDC; + i2c->adapter.dev.parent = &dev->pdev->dev; i2c->dev = dev; snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), "Radeon aux bus %s", name); -- cgit v0.10.2 From e9c8d7a03e69093e4c33c5056a45c1233a42e8a4 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 18 Jan 2012 10:14:25 +0100 Subject: dma: sh_dma: not all SH DMAC implementations support MEMCPY Add a flag to allow platforms to specify, whether a DMAC instance supports the MEMCPY operation. To avoid regressions, preserve the current default. Signed-off-by: Guennadi Liakhovetski Acked-by: Paul Mundt Signed-off-by: Vinod Koul diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 54043cd..812fd76 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -1262,7 +1262,8 @@ static int __init sh_dmae_probe(struct platform_device *pdev) INIT_LIST_HEAD(&shdev->common.channels); - dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask); + if (!pdata->slave_only) + dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask); if (pdata->slave && pdata->slave_num) dma_cap_set(DMA_SLAVE, shdev->common.cap_mask); diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 8cd7fe5..425450b 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -70,6 +70,7 @@ struct sh_dmae_pdata { unsigned int needs_tend_set:1; unsigned int no_dmars:1; unsigned int chclr_present:1; + unsigned int slave_only:1; }; /* DMA register */ -- cgit v0.10.2 From c8ddf036d99e7fab943b7587c75a905e789ea7e7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 18 Jan 2012 10:14:29 +0100 Subject: ARM: mach-shmobile: both USB DMAC instances on sh7372 are slave-only Signed-off-by: Guennadi Liakhovetski Acked-by: Paul Mundt Signed-off-by: Vinod Koul diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c index 6fcf304..a83cf51 100644 --- a/arch/arm/mach-shmobile/setup-sh7372.c +++ b/arch/arm/mach-shmobile/setup-sh7372.c @@ -662,6 +662,7 @@ static struct sh_dmae_pdata usb_dma0_platform_data = { .dmaor_is_32bit = 1, .needs_tend_set = 1, .no_dmars = 1, + .slave_only = 1, }; static struct resource sh7372_usb_dmae0_resources[] = { @@ -723,6 +724,7 @@ static struct sh_dmae_pdata usb_dma1_platform_data = { .dmaor_is_32bit = 1, .needs_tend_set = 1, .no_dmars = 1, + .slave_only = 1, }; static struct resource sh7372_usb_dmae1_resources[] = { -- cgit v0.10.2 From 42f578741bc59dc871bef03ff0f72dd197388409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 1 Feb 2012 09:12:23 -0800 Subject: Input: evdev - fix variable initialisation Commit 509f87c5f564 (evdev - do not block waiting for an event if fd is nonblock) created a code path were it was possible to use retval uninitialized. This could lead to the xorg evdev input driver getting corrupt data and refusing to work with log messages like AUO-Pixcir touchscreen: Read error: Success sg060_keys: Read error: Success AUO-Pixcir touchscreen: Read error: Success sg060_keys: Read error: Success (for drivers auo-pixcir-ts and gpio-keys). Signed-off-by: Heiko Stuebner Acked-by: Dima Zavin Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 76457d5..afc166f 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -386,7 +386,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; - int retval; + int retval = 0; if (count < input_event_size()) return -EINVAL; -- cgit v0.10.2 From b18db3d91234c03ad080d317878c7c77672ba326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 1 Feb 2012 09:12:24 -0800 Subject: Input: gpio_keys - fix struct device declared inside parameter list A struct device parameter is used in the enable and disable callbacks to distinguish between different gpio_keys devices. Platforms that don't use these callbacks may not include struct device at all, as seen on arch/arm/mach-s3c2410/mach-n30.c Signed-off-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index b5ca4b2..004ff33 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -1,6 +1,8 @@ #ifndef _GPIO_KEYS_H #define _GPIO_KEYS_H +struct device; + struct gpio_keys_button { /* Configuration parameters */ unsigned int code; /* input event code (KEY_*, SW_*) */ -- cgit v0.10.2 From 4936f97cbb99c19680ea6a3cc0148ebe51f0eb68 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Wed, 1 Feb 2012 09:12:23 -0800 Subject: Input: twl4030_keypad - fix comment (trivial) And trivial whitespace fixes. Signed-off-by: Felipe Contreras Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index a588578..67bec14 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -34,7 +34,6 @@ #include #include - /* * The TWL4030 family chips include a keypad controller that supports * up to an 8x8 switch matrix. The controller can issue system wakeup @@ -302,7 +301,7 @@ static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0) return -EIO; - /* Set timeout period to 100 ms */ + /* Set timeout period to 200 ms */ i = KEYP_PERIOD_US(200000, PTV_PRESCALER); if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0) return -EIO; @@ -466,4 +465,3 @@ MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TWL4030 Keypad Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_keypad"); - -- cgit v0.10.2 From d04df0232d6b5172dc1958df1829abd0214c8969 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 1 Feb 2012 09:12:24 -0800 Subject: Input: document device properties Add a section which defines the input device properties and provides guidelines on how to use them. Signed-off-by: Henrik Rydberg Reviewed-by: Chase Douglas Reviewed-by: Jussi Pakkanen Signed-off-by: Dmitry Torokhov diff --git a/Documentation/input/event-codes.txt b/Documentation/input/event-codes.txt index 23fcb05..53305bd 100644 --- a/Documentation/input/event-codes.txt +++ b/Documentation/input/event-codes.txt @@ -17,11 +17,11 @@ reports supported by a device are also provided by sysfs in class/input/event*/device/capabilities/, and the properties of a device are provided in class/input/event*/device/properties. -Types: -========== -Types are groupings of codes under a logical input construct. Each type has a -set of applicable codes to be used in generating events. See the Codes section -for details on valid codes for each type. +Event types: +=========== +Event types are groupings of codes under a logical input construct. Each +type has a set of applicable codes to be used in generating events. See the +Codes section for details on valid codes for each type. * EV_SYN: - Used as markers to separate events. Events may be separated in time or in @@ -63,9 +63,9 @@ for details on valid codes for each type. * EV_FF_STATUS: - Used to receive force feedback device status. -Codes: -========== -Codes define the precise type of event. +Event codes: +=========== +Event codes define the precise type of event. EV_SYN: ---------- @@ -220,6 +220,56 @@ EV_PWR: EV_PWR events are a special type of event used specifically for power mangement. Its usage is not well defined. To be addressed later. +Device properties: +================= +Normally, userspace sets up an input device based on the data it emits, +i.e., the event types. In the case of two devices emitting the same event +types, additional information can be provided in the form of device +properties. + +INPUT_PROP_DIRECT + INPUT_PROP_POINTER: +-------------------------------------- +The INPUT_PROP_DIRECT property indicates that device coordinates should be +directly mapped to screen coordinates (not taking into account trivial +transformations, such as scaling, flipping and rotating). Non-direct input +devices require non-trivial transformation, such as absolute to relative +transformation for touchpads. Typical direct input devices: touchscreens, +drawing tablets; non-direct devices: touchpads, mice. + +The INPUT_PROP_POINTER property indicates that the device is not transposed +on the screen and thus requires use of an on-screen pointer to trace user's +movements. Typical pointer devices: touchpads, tablets, mice; non-pointer +device: touchscreen. + +If neither INPUT_PROP_DIRECT or INPUT_PROP_POINTER are set, the property is +considered undefined and the device type should be deduced in the +traditional way, using emitted event types. + +INPUT_PROP_BUTTONPAD: +-------------------- +For touchpads where the button is placed beneath the surface, such that +pressing down on the pad causes a button click, this property should be +set. Common in clickpad notebooks and macbooks from 2009 and onwards. + +Originally, the buttonpad property was coded into the bcm5974 driver +version field under the name integrated button. For backwards +compatibility, both methods need to be checked in userspace. + +INPUT_PROP_SEMI_MT: +------------------ +Some touchpads, most common between 2008 and 2011, can detect the presence +of multiple contacts without resolving the individual positions; only the +number of contacts and a rectangular shape is known. For such +touchpads, the semi-mt property should be set. + +Depending on the device, the rectangle may enclose all touches, like a +bounding box, or just some of them, for instance the two most recent +touches. The diversity makes the rectangle of limited use, but some +gestures can normally be extracted from it. + +If INPUT_PROP_SEMI_MT is not set, the device is assumed to be a true MT +device. + Guidelines: ========== The guidelines below ensure proper single-touch and multi-finger functionality. @@ -240,6 +290,8 @@ used to report when a touch is active on the screen. BTN_{MOUSE,LEFT,MIDDLE,RIGHT} must not be reported as the result of touch contact. BTN_TOOL_ events should be reported where possible. +For new hardware, INPUT_PROP_DIRECT should be set. + Trackpads: ---------- Legacy trackpads that only provide relative position information must report @@ -250,6 +302,8 @@ location of the touch. BTN_TOUCH should be used to report when a touch is active on the trackpad. Where multi-finger support is available, BTN_TOOL_ should be used to report the number of touches active on the trackpad. +For new hardware, INPUT_PROP_POINTER should be set. + Tablets: ---------- BTN_TOOL_ events must be reported when a stylus or other tool is active on @@ -260,3 +314,5 @@ button may be used for buttons on the tablet except BTN_{MOUSE,LEFT}. BTN_{0,1,2,etc} are good generic codes for unlabeled buttons. Do not use meaningful buttons, like BTN_FORWARD, unless the button is labeled for that purpose on the device. + +For new hardware, both INPUT_PROP_DIRECT and INPUT_PROP_POINTER should be set. -- cgit v0.10.2 From b7aaacf56ac9e0cdb58c3d087fea7084d897c307 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 1 Feb 2012 21:20:15 +0400 Subject: Revert "bq27x00_battery: Fix reporting status value for bq27500 battery" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 270968c0984aeed096da3cfffb0e131f4c416166. Grazvydas Ignotas wrote: After 270968c0984ae "bq27x00_battery: Fix reporting status value for bq27500 battery" status doesn't seem to be reported correctly when the battery is close to fully charged state. It reports "Not charging" while in fact there is >130mA current flowing to the battery according to current_now. This status report seems to be based on CHG bit in status register, but looking at the datasheet the description says "(Fast) charging allowed", which I guess means that the chip recommends charging and not that charging is actually going on? If you check the bit while battery is discharging and it's not full, the bit is also set. Suggested-by: Grazvydas Ignotas Acked-by: Pali Rohár Signed-off-by: Anton Vorontsov diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 98bf567..d4b5281 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -62,11 +62,10 @@ #define BQ27500_REG_SOC 0x2C #define BQ27500_REG_DCAP 0x3C /* Design capacity */ -#define BQ27500_FLAG_DSG BIT(0) /* Discharging */ +#define BQ27500_FLAG_DSC BIT(0) #define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ #define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ -#define BQ27500_FLAG_CHG BIT(8) /* Charging */ -#define BQ27500_FLAG_FC BIT(9) /* Fully charged */ +#define BQ27500_FLAG_FC BIT(9) #define BQ27000_RS 20 /* Resistor sense */ @@ -401,14 +400,10 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, if (di->chip == BQ27500) { if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; - else if (di->cache.flags & BQ27500_FLAG_DSG) + else if (di->cache.flags & BQ27500_FLAG_DSC) status = POWER_SUPPLY_STATUS_DISCHARGING; - else if (di->cache.flags & BQ27500_FLAG_CHG) - status = POWER_SUPPLY_STATUS_CHARGING; - else if (power_supply_am_i_supplied(&di->bat)) - status = POWER_SUPPLY_STATUS_NOT_CHARGING; else - status = POWER_SUPPLY_STATUS_UNKNOWN; + status = POWER_SUPPLY_STATUS_CHARGING; } else { if (di->cache.flags & BQ27000_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; -- cgit v0.10.2 From 7a0a27d2ce38aee19a31fee8c12095f586eed393 Mon Sep 17 00:00:00 2001 From: Che-Liang Chiou Date: Wed, 1 Feb 2012 09:25:35 -0800 Subject: Input: serio_raw - return proper result when serio_raw_read fails serio_raw_read now returns (sometimes partially) successful number of bytes transferred to the caller, and only returns error code to the caller on completely failed transfers. Signed-off-by: Che-Liang Chiou Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 8250299..4494233 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -164,7 +164,8 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, struct serio_raw_client *client = file->private_data; struct serio_raw *serio_raw = client->serio_raw; char uninitialized_var(c); - ssize_t retval = 0; + ssize_t read = 0; + int retval; if (serio_raw->dead) return -ENODEV; @@ -180,13 +181,15 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, if (serio_raw->dead) return -ENODEV; - while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) { - if (put_user(c, buffer++)) - return -EFAULT; - retval++; + while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) { + retval = -EFAULT; + break; + } + read++; } - return retval; + return read ?: retval; } static ssize_t serio_raw_write(struct file *file, const char __user *buffer, -- cgit v0.10.2 From 4d4036591b3de279a8c93a3cb010b0bc1264703c Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 14 Jan 2012 22:50:49 +0200 Subject: bq27x00_battery: Fix flag register read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading flags, bq27x00_read() argument is inverted and causes reads 2 of bytes for bq27200 and 1 byte for bq27500, while their register sizes are 1 and 2 bytes respectively. This causes bq27500 upper flag bits always to be returned as 0, causing full charge state to never be reported correctly, so fix it. Cc: Lars-Peter Clausen Signed-off-by: Grazvydas Ignotas Acked-by: Pali Rohár Signed-off-by: Anton Vorontsov diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index d4b5281..1ed6ea0 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -311,7 +311,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) struct bq27x00_reg_cache cache = {0, }; bool is_bq27500 = di->chip == BQ27500; - cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); + cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); if (cache.flags >= 0) { if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); -- cgit v0.10.2 From da4dc0af6eeb131927fb602846437a50a42ca246 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 25 Jan 2012 20:00:21 -0800 Subject: lp8727_chager: Fix permissions on a header file The 'x(execute)' permission is removed. (chmod from 0755 to 0644) Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Anton Vorontsov diff --git a/include/linux/lp8727.h b/include/linux/lp8727.h old mode 100755 new mode 100644 -- cgit v0.10.2 From 1bbe24d465db626fed050e0128a7244b9cb407f4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 11 Jan 2012 17:19:45 +0800 Subject: power_supply: Fix modalias for charger-manager Since 43cc71eed1250755986da4c0f9898f9a635cb3bf, the platform modalias is prefixed with "platform:". Current code has the id_table, thus add MODULE_DEVICE_TABLE will automatically setup the modalias. Also make charger_manager_id static as it is only used in this driver. Signed-off-by: Axel Lin Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 0378d01..88fd971 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -974,10 +974,11 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) return 0; } -const struct platform_device_id charger_manager_id[] = { +static const struct platform_device_id charger_manager_id[] = { { "charger-manager", 0 }, { }, }; +MODULE_DEVICE_TABLE(platform, charger_manager_id); static int cm_suspend_prepare(struct device *dev) { @@ -1069,4 +1070,3 @@ module_exit(charger_manager_cleanup); MODULE_AUTHOR("MyungJoo Ham "); MODULE_DESCRIPTION("Charger Manager"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("charger-manager"); -- cgit v0.10.2 From 455a0e2cd80f7a2849b2e6d3be85c053ee44446b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 16 Jan 2012 13:48:20 +0800 Subject: lp8727_charger: Add terminating entry for i2c_device_id table The i2c_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Acked-by: Milo(Woogyom) Kim Signed-off-by: Anton Vorontsov diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index b15b575..c53dd12 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c @@ -464,6 +464,7 @@ static int __devexit lp8727_remove(struct i2c_client *cl) static const struct i2c_device_id lp8727_ids[] = { {"lp8727", 0}, + { } }; static struct i2c_driver lp8727_driver = { -- cgit v0.10.2 From 8dc6ae4d448758a30cf5fa822d6fe6f4e15a04c6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 1 Feb 2012 18:11:40 +0100 Subject: pinctrl: restore pin naming Commit ca53c5f1ca5c936777caca46b7c716a40682ce83 ("pinctrl: conjure names for unnamed pins") made pins lose their identity and only get autogenerated names. Acked-by: Stephen Warren Signed-off-by: Linus Walleij diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 8fe15cf..894cd5e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -189,7 +189,7 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev, pindesc->pctldev = pctldev; /* Copy basic pin info */ - if (pindesc->name) { + if (name) { pindesc->name = name; } else { pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", number); -- cgit v0.10.2 From 7d731019218e49a9811f6d0adec4b1cfcb752bed Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 1 Feb 2012 11:10:24 -0800 Subject: mtd: fix merge conflict resolution breakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes merge conflict resolution breakage introduced by merge d3712b9dfcf4 ("Merge tag 'for-linus' of git://github.com/prasad-joshi/logfs_upstream"). The commit changed 'mtd_can_have_bb()' function and made it always return zero, which is incorrect. Instead, we need it to return whether the underlying flash device can have bad eraseblocks or not. UBI needs this information because it affects how it handles the underlying flash. E.g., if the underlying flash is NOR, it cannot have bad blocks and any write or erase error is fatal, and all we can do is to switch to R/O mode. We do not need to reserve a pool of good eraseblocks for bad eraseblocks handling, and so on. This patch also removes 'mtd_can_have_bb()' invocations from Logfs to ensure correct Logfs behavior. I've tested that with this patch UBI works on top of NOR and NAND flashes emulated by mtdram and nandsim correspondingly. This patch is based on patch from Linus Torvalds. Signed-off-by: Artem Bityutskiy Acked-by: Jörn Engel Acked-by: Prasad Joshi Acked-by: Brian Norris Signed-off-by: Linus Torvalds diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c index e97404d..9c50144 100644 --- a/fs/logfs/dev_mtd.c +++ b/fs/logfs/dev_mtd.c @@ -152,9 +152,6 @@ static struct page *logfs_mtd_find_first_sb(struct super_block *sb, u64 *ofs) filler_t *filler = logfs_mtd_readpage; struct mtd_info *mtd = super->s_mtd; - if (!mtd_can_have_bb(mtd)) - return NULL; - *ofs = 0; while (mtd_block_isbad(mtd, *ofs)) { *ofs += mtd->erasesize; @@ -172,9 +169,6 @@ static struct page *logfs_mtd_find_last_sb(struct super_block *sb, u64 *ofs) filler_t *filler = logfs_mtd_readpage; struct mtd_info *mtd = super->s_mtd; - if (!mtd_can_have_bb(mtd)) - return NULL; - *ofs = mtd->size - mtd->erasesize; while (mtd_block_isbad(mtd, *ofs)) { *ofs -= mtd->erasesize; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 2212952..887ebe3 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -489,7 +489,7 @@ static inline int mtd_has_oob(const struct mtd_info *mtd) static inline int mtd_can_have_bb(const struct mtd_info *mtd) { - return 0; + return !!mtd->block_isbad; } /* Kernel-side ioctl definitions */ -- cgit v0.10.2 From 879a5a001b62a020e074d460b3a7c0fd993f9832 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Tue, 31 Jan 2012 20:02:00 -0800 Subject: MAINTAINERS: Greg's suse email address is dead My email address has changed, the suse.de one is now dead, so update all of my MAINTAINER entries with the correct one so that patches don't get lost. Also change the status of some of my entries as I'm supposed to be doing this stuff now for real. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index a1fce9a..252972b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -159,7 +159,7 @@ S: Maintained F: drivers/net/ethernet/realtek/r8169.c 8250/16?50 (AND CLONE UARTS) SERIAL DRIVER -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-serial@vger.kernel.org W: http://serial.sourceforge.net S: Maintained @@ -1783,9 +1783,9 @@ X: net/wireless/wext* CHAR and MISC DRIVERS M: Arnd Bergmann -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git -S: Maintained +S: Supported F: drivers/char/* F: drivers/misc/* @@ -2320,7 +2320,7 @@ F: lib/lru_cache.c F: Documentation/blockdev/drbd/ DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6.git S: Supported F: Documentation/kobject.txt @@ -6276,15 +6276,15 @@ S: Maintained F: arch/alpha/kernel/srm_env.c STABLE BRANCH -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: stable@vger.kernel.org -S: Maintained +S: Supported STAGING SUBSYSTEM -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git L: devel@driverdev.osuosl.org -S: Maintained +S: Supported F: drivers/staging/ STAGING - AGERE HERMES II and II.5 WIRELESS DRIVERS @@ -6669,8 +6669,8 @@ S: Maintained K: ^Subject:.*(?i)trivial TTY LAYER -M: Greg Kroah-Hartman -S: Maintained +M: Greg Kroah-Hartman +S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6.git F: drivers/tty/ F: drivers/tty/serial/serial_core.c @@ -6958,7 +6958,7 @@ S: Maintained F: drivers/usb/serial/digi_acceleport.c USB SERIAL DRIVER -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org S: Supported F: Documentation/usb/usb-serial.txt @@ -6973,9 +6973,8 @@ S: Maintained F: drivers/usb/serial/empeg.c USB SERIAL KEYSPAN DRIVER -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org -W: http://www.kroah.com/linux/ S: Maintained F: drivers/usb/serial/*keyspan* @@ -7003,7 +7002,7 @@ F: Documentation/video4linux/sn9c102.txt F: drivers/media/video/sn9c102/ USB SUBSYSTEM -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman L: linux-usb@vger.kernel.org W: http://www.linux-usb.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6.git @@ -7090,7 +7089,7 @@ F: fs/hppfs/ USERSPACE I/O (UIO) M: "Hans J. Koch" -M: Greg Kroah-Hartman +M: Greg Kroah-Hartman S: Maintained F: Documentation/DocBook/uio-howto.tmpl F: drivers/uio/ -- cgit v0.10.2 From 48c3883999cb06246911e29356d194f96f1c75ef Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 29 Jan 2012 22:14:02 +0000 Subject: net/hyperv: rx_bytes should account the ether header size skb->len after call eth_type_trans() does not include the ether header size, but rx_bytes should account it. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1a1ca6c..9dccc7a 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -298,7 +298,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, skb->ip_summed = CHECKSUM_NONE; net->stats.rx_packets++; - net->stats.rx_bytes += skb->len; + net->stats.rx_bytes += packet->total_data_buflen; /* * Pass the skb back up. Network stack will deallocate the skb when it -- cgit v0.10.2 From efcdbf24fd5daa88060869e51ed49f68b7ac8708 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Mon, 30 Jan 2012 14:16:06 -0800 Subject: net: Disambiguate kernel message Some of our machines were reporting: TCP: too many of orphaned sockets even when the number of orphaned sockets was well below the limit. We print a different message depending on whether we're out of TCP memory or there are too many orphaned sockets. Also move the check out of line and cleanup the messages that were printed. Signed-off-by: Arun Sharma Suggested-by: Mohan Srinivasan Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: David Miller Cc: Glauber Costa Cc: Ingo Molnar Cc: Joe Perches Signed-off-by: David S. Miller diff --git a/include/net/tcp.h b/include/net/tcp.h index d49db01..42c29bf 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -273,6 +273,14 @@ static inline int between(__u32 seq1, __u32 seq2, __u32 seq3) return seq3 - seq2 >= seq1 - seq2; } +static inline bool tcp_out_of_memory(struct sock *sk) +{ + if (sk->sk_wmem_queued > SOCK_MIN_SNDBUF && + sk_memory_allocated(sk) > sk_prot_mem_limits(sk, 2)) + return true; + return false; +} + static inline bool tcp_too_many_orphans(struct sock *sk, int shift) { struct percpu_counter *ocp = sk->sk_prot->orphan_count; @@ -283,13 +291,11 @@ static inline bool tcp_too_many_orphans(struct sock *sk, int shift) if (orphans << shift > sysctl_tcp_max_orphans) return true; } - - if (sk->sk_wmem_queued > SOCK_MIN_SNDBUF && - sk_memory_allocated(sk) > sk_prot_mem_limits(sk, 2)) - return true; return false; } +extern bool tcp_check_oom(struct sock *sk, int shift); + /* syncookies: remember time of last synqueue overflow */ static inline void tcp_synq_overflow(struct sock *sk) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 06373b4..a34f5cf 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1876,6 +1876,20 @@ void tcp_shutdown(struct sock *sk, int how) } EXPORT_SYMBOL(tcp_shutdown); +bool tcp_check_oom(struct sock *sk, int shift) +{ + bool too_many_orphans, out_of_socket_memory; + + too_many_orphans = tcp_too_many_orphans(sk, shift); + out_of_socket_memory = tcp_out_of_memory(sk); + + if (too_many_orphans && net_ratelimit()) + pr_info("TCP: too many orphaned sockets\n"); + if (out_of_socket_memory && net_ratelimit()) + pr_info("TCP: out of memory -- consider tuning tcp_mem\n"); + return too_many_orphans || out_of_socket_memory; +} + void tcp_close(struct sock *sk, long timeout) { struct sk_buff *skb; @@ -2015,10 +2029,7 @@ adjudge_to_death: } if (sk->sk_state != TCP_CLOSE) { sk_mem_reclaim(sk); - if (tcp_too_many_orphans(sk, 0)) { - if (net_ratelimit()) - printk(KERN_INFO "TCP: too many of orphaned " - "sockets\n"); + if (tcp_check_oom(sk, 0)) { tcp_set_state(sk, TCP_CLOSE); tcp_send_active_reset(sk, GFP_ATOMIC); NET_INC_STATS_BH(sock_net(sk), diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index a516d1e..cd2e072 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -77,10 +77,7 @@ static int tcp_out_of_resources(struct sock *sk, int do_reset) if (sk->sk_err_soft) shift++; - if (tcp_too_many_orphans(sk, shift)) { - if (net_ratelimit()) - printk(KERN_INFO "Out of socket memory\n"); - + if (tcp_check_oom(sk, shift)) { /* Catch exceptional cases, when connection requires reset. * 1. Last segment was sent recently. */ if ((s32)(tcp_time_stamp - tp->lsndtime) <= TCP_TIMEWAIT_LEN || -- cgit v0.10.2 From 786f528119722f564a22ad953411374e06116333 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 1 Feb 2012 09:32:25 +0000 Subject: ethtool: Null-terminate filename passed to ethtool_ops::flash_device The parameters for ETHTOOL_FLASHDEV include a filename, which ought to be null-terminated. Currently the only driver that implements ethtool_ops::flash_device attempts to add a null terminator if necessary, but does it wrongly. Do it in the ethtool core instead. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 6db6b6a..802e5dd 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -716,12 +716,8 @@ static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) { struct be_adapter *adapter = netdev_priv(netdev); - char file_name[ETHTOOL_FLASH_MAX_FILENAME]; - file_name[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; - strcpy(file_name, efl->data); - - return be_load_fw(adapter, file_name); + return be_load_fw(adapter, efl->data); } static int diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 369b418..3f79db1 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1190,6 +1190,8 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev, if (!dev->ethtool_ops->flash_device) return -EOPNOTSUPP; + efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; + return dev->ethtool_ops->flash_device(dev, &efl); } -- cgit v0.10.2 From f9721ed2707661af75a414d09cdcd71f99a13f62 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 29 Jan 2012 21:34:04 +0100 Subject: bcma: Fix mem leak in bcma_bus_scan() bcma_bus_scan() leaks 'struct bcma_device' bytes if bcma_get_next_core() returns error. Restructure the code so we always kfree() the memory we allocate to the variable 'core' before it goes out of scope. Signed-off-by: Jesper Juhl Signed-off-by: John W. Linville diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index cad9948..3a2f672 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -399,15 +399,18 @@ int bcma_bus_scan(struct bcma_bus *bus) core->bus = bus; err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core); - if (err == -ENODEV) { - core_num++; - continue; - } else if (err == -ENXIO) - continue; - else if (err == -ESPIPE) - break; - else if (err < 0) + if (err < 0) { + kfree(core); + if (err == -ENODEV) { + core_num++; + continue; + } else if (err == -ENXIO) { + continue; + } else if (err == -ESPIPE) { + break; + } return err; + } core->core_index = core_num++; bus->nr_cores++; -- cgit v0.10.2 From 7fc417556473f15efac269523c17eb92103c6484 Mon Sep 17 00:00:00 2001 From: Luigi Tarenga Date: Tue, 31 Jan 2012 18:51:23 +0100 Subject: rt2800lib: fix wrong -128dBm when signal is stronger than -12dBm This patch correct the type of variables containing the rssi values read from the rxwi. In function rt2800_agc_to_rssi() 3 variables (rssi0, rssi1, rss2) defined as int was assigned a 16bit signed values as unsigned. From a test with a hi-gain antenna I verified that the rxwi contains signed rssi values in the range -13/+81 (inclusive) with 0 as an error condition. In case of negative values a condition is triggered and the function return -128dBm while the signal is at its maximum. This patch correct the cast so negative values are not treated as very high positive values (ex. -13 does not become 243). Signed-off-by: Luigi Tarenga Reviewed-by: Stanislaw Gruszka Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 22a1a8f..7bef66d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -514,9 +514,9 @@ EXPORT_SYMBOL_GPL(rt2800_write_tx_data); static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) { - int rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); - int rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); - int rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); + s8 rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); + s8 rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); + s8 rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); u16 eeprom; u8 offset0; u8 offset1; @@ -552,7 +552,7 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) * which gives less energy... */ rssi0 = max(rssi0, rssi1); - return max(rssi0, rssi2); + return (int)max(rssi0, rssi2); } void rt2800_process_rxwi(struct queue_entry *entry, -- cgit v0.10.2 From 5fb8c182d26472ea24cbbadb6a3e355d7621cca4 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 31 Jan 2012 14:40:49 -0500 Subject: iwlwifi: make "Tx aggregation enabled on ra =" be at DEBUG level Average users either don't care or they think that message indicates a problem... Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index c664c27..339e8d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -620,7 +620,7 @@ int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = sta_priv->max_agg_bufsize; - IWL_INFO(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", + IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", sta->addr, tid); return iwl_send_lq_cmd(priv, ctx, -- cgit v0.10.2 From a6c84622b7fa3ea5417a9d1d0ce0bc3e7fbe3be1 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 1 Feb 2012 00:13:53 +0100 Subject: ssb: fix cardbus slot in hostmode ssb supports one extra device on the cardbus. This results in two devices in total, one beeing the host controller itself and the other the external device connected to the hostcontroller over cardbus. This makes the cardbus slot work on the Linksys WRT150N. Reported-by: Aaron Z Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c index 520e828..49d2091 100644 --- a/drivers/ssb/driver_pcicore.c +++ b/drivers/ssb/driver_pcicore.c @@ -75,7 +75,7 @@ static u32 get_cfgspace_addr(struct ssb_pcicore *pc, u32 tmp; /* We do only have one cardbus device behind the bridge. */ - if (pc->cardbusmode && (dev >= 1)) + if (pc->cardbusmode && (dev > 1)) goto out; if (bus == 0) { -- cgit v0.10.2 From 3d29dd9b5b160ba4542a9b8f869a220559e633a0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 1 Feb 2012 07:01:32 -0800 Subject: iwlwifi: don't mess up QoS counters with non-QoS frames In my AMPDU rework, I rely on the sequence numbers of frames. But I didn't check that the frame has a valid tid before updating the tracking counters. As a result, the Tx queues were stalled. People who hit this bug saw that we simply didn't let any data out. This bug was introduced in 3.3. This patch fixes that and checks that the frame is a QoS frame before looking at its tid and changing the counters. Signed-off-by: Emmanuel Grumbach Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 339e8d9..63bbc60 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -91,6 +91,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { + tx_cmd->tid_tspec = IWL_TID_NON_QOS; if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; else @@ -808,6 +809,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, u32 status = le16_to_cpu(tx_resp->status.status); int i; + WARN_ON(tid == IWL_TID_NON_QOS); + if (agg->wait_for_ba) IWL_DEBUG_TX_REPLY(priv, "got tx response w/o block-ack\n"); @@ -1035,10 +1038,13 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, } __skb_queue_head_init(&skbs); - priv->tid_data[sta_id][tid].next_reclaimed = next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", - next_reclaimed); + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", + next_reclaimed); + } /*we can free until ssn % q.n_bd not inclusive */ WARN_ON(iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 265de39..f822ac4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -815,6 +815,7 @@ struct iwl_qosparam_cmd { #define IWL_INVALID_STATION 255 #define IWL_MAX_TID_COUNT 8 +#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 67d6e32..324d06d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -1262,6 +1262,7 @@ static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, txq->time_stamp = jiffies; if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE && + tid != IWL_TID_NON_QOS && txq_id != trans_pcie->agg_txq[sta_id][tid])) { /* * FIXME: this is a uCode bug which need to be addressed, -- cgit v0.10.2 From 07ae2dfcf4f7143ce191c6436da1c33f179af0d6 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 1 Feb 2012 18:48:09 +0200 Subject: mac80211: timeout a single frame in the rx reorder buffer The current code checks for stored_mpdu_num > 1, causing the reorder_timer to be triggered indefinitely, but the frame is never timed-out (until the next packet is received) Signed-off-by: Eliad Peller Cc: Acked-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 7514091..5a5e504 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -611,7 +611,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; if (!tid_agg_rx->reorder_buf[index] && - tid_agg_rx->stored_mpdu_num > 1) { + tid_agg_rx->stored_mpdu_num) { /* * No buffers ready to be released, but check whether any * frames in the reorder buffer have timed out. -- cgit v0.10.2 From fe9161db2e6053da21e4649d77bbefaf3030b11d Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Wed, 1 Feb 2012 22:16:36 +0100 Subject: PM / Hibernate: Thaw kernel threads in SNAPSHOT_CREATE_IMAGE ioctl path In the SNAPSHOT_CREATE_IMAGE ioctl, if the call to hibernation_snapshot() fails, the frozen tasks are not thawed. And in the case of success, if we happen to exit due to a successful freezer test, all tasks (including those of userspace) are thawed, whereas actually we should have thawed only the kernel threads at that point. Fix both these issues. Signed-off-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki Cc: stable@vger.kernel.org diff --git a/kernel/power/user.c b/kernel/power/user.c index e5a21a8..3e10007 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -249,13 +249,15 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, } pm_restore_gfp_mask(); error = hibernation_snapshot(data->platform_support); - if (!error) { + if (error) { + thaw_kernel_threads(); + } else { error = put_user(in_suspend, (int __user *)arg); if (!error && !freezer_test_done) data->ready = 1; if (freezer_test_done) { freezer_test_done = false; - thaw_processes(); + thaw_kernel_threads(); } } break; -- cgit v0.10.2 From 71879d3cb3dd8f2dfdefb252775c1b3ea04a3dd4 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 31 Jan 2012 17:14:38 +0100 Subject: proc: mem_release() should check mm != NULL mem_release() can hit mm == NULL, add the necessary check. Cc: stable@kernel.org Signed-off-by: Oleg Nesterov Signed-off-by: Linus Torvalds diff --git a/fs/proc/base.c b/fs/proc/base.c index 9cde9edf..c3617ea 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -822,8 +822,8 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) static int mem_release(struct inode *inode, struct file *file) { struct mm_struct *mm = file->private_data; - - mmput(mm); + if (mm) + mmput(mm); return 0; } -- cgit v0.10.2 From 572d34b946bae070debd42db1143034d9687e13f Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 31 Jan 2012 17:14:54 +0100 Subject: proc: unify mem_read() and mem_write() No functional changes, cleanup and preparation. mem_read() and mem_write() are very similar. Move this code into the new common helper, mem_rw(), which takes the additional "int write" argument. Cc: stable@kernel.org Signed-off-by: Oleg Nesterov Signed-off-by: Linus Torvalds diff --git a/fs/proc/base.c b/fs/proc/base.c index c3617ea..be19090 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -718,57 +718,13 @@ static int mem_open(struct inode* inode, struct file* file) return 0; } -static ssize_t mem_read(struct file * file, char __user * buf, - size_t count, loff_t *ppos) +static ssize_t mem_rw(struct file *file, char __user *buf, + size_t count, loff_t *ppos, int write) { - int ret; - char *page; - unsigned long src = *ppos; struct mm_struct *mm = file->private_data; - - if (!mm) - return 0; - - page = (char *)__get_free_page(GFP_TEMPORARY); - if (!page) - return -ENOMEM; - - ret = 0; - - while (count > 0) { - int this_len, retval; - - this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; - retval = access_remote_vm(mm, src, page, this_len, 0); - if (!retval) { - if (!ret) - ret = -EIO; - break; - } - - if (copy_to_user(buf, page, retval)) { - ret = -EFAULT; - break; - } - - ret += retval; - src += retval; - buf += retval; - count -= retval; - } - *ppos = src; - - free_page((unsigned long) page); - return ret; -} - -static ssize_t mem_write(struct file * file, const char __user *buf, - size_t count, loff_t *ppos) -{ - int copied; + unsigned long addr = *ppos; + ssize_t copied; char *page; - unsigned long dst = *ppos; - struct mm_struct *mm = file->private_data; if (!mm) return 0; @@ -779,30 +735,48 @@ static ssize_t mem_write(struct file * file, const char __user *buf, copied = 0; while (count > 0) { - int this_len, retval; + int this_len = min_t(int, count, PAGE_SIZE); - this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; - if (copy_from_user(page, buf, this_len)) { + if (write && copy_from_user(page, buf, this_len)) { copied = -EFAULT; break; } - retval = access_remote_vm(mm, dst, page, this_len, 1); - if (!retval) { + + this_len = access_remote_vm(mm, addr, page, this_len, write); + if (!this_len) { if (!copied) copied = -EIO; break; } - copied += retval; - buf += retval; - dst += retval; - count -= retval; + + if (!write && copy_to_user(buf, page, this_len)) { + copied = -EFAULT; + break; + } + + buf += this_len; + addr += this_len; + copied += this_len; + count -= this_len; } - *ppos = dst; + *ppos = addr; free_page((unsigned long) page); return copied; } +static ssize_t mem_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return mem_rw(file, buf, count, ppos, 0); +} + +static ssize_t mem_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return mem_rw(file, (char __user*)buf, count, ppos, 1); +} + loff_t mem_lseek(struct file *file, loff_t offset, int orig) { switch (orig) { -- cgit v0.10.2 From 6d08f2c7139790c268820a2e590795cb8333181a Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 31 Jan 2012 17:15:11 +0100 Subject: proc: make sure mem_open() doesn't pin the target's memory Once /proc/pid/mem is opened, the memory can't be released until mem_release() even if its owner exits. Change mem_open() to do atomic_inc(mm_count) + mmput(), this only pins mm_struct. Change mem_rw() to do atomic_inc_not_zero(mm_count) before access_remote_vm(), this verifies that this mm is still alive. I am not sure what should mem_rw() return if atomic_inc_not_zero() fails. With this patch it returns zero to match the "mm == NULL" case, may be it should return -EINVAL like it did before e268337d. Perhaps it makes sense to add the additional fatal_signal_pending() check into the main loop, to ensure we do not hold this memory if the target task was oom-killed. Cc: stable@kernel.org Signed-off-by: Oleg Nesterov Signed-off-by: Linus Torvalds diff --git a/fs/proc/base.c b/fs/proc/base.c index be19090..d9512bd 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -711,6 +711,13 @@ static int mem_open(struct inode* inode, struct file* file) if (IS_ERR(mm)) return PTR_ERR(mm); + if (mm) { + /* ensure this mm_struct can't be freed */ + atomic_inc(&mm->mm_count); + /* but do not pin its memory */ + mmput(mm); + } + /* OK to pass negative loff_t, we can catch out-of-range */ file->f_mode |= FMODE_UNSIGNED_OFFSET; file->private_data = mm; @@ -734,6 +741,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf, return -ENOMEM; copied = 0; + if (!atomic_inc_not_zero(&mm->mm_users)) + goto free; + while (count > 0) { int this_len = min_t(int, count, PAGE_SIZE); @@ -761,6 +771,8 @@ static ssize_t mem_rw(struct file *file, char __user *buf, } *ppos = addr; + mmput(mm); +free: free_page((unsigned long) page); return copied; } @@ -797,7 +809,7 @@ static int mem_release(struct inode *inode, struct file *file) { struct mm_struct *mm = file->private_data; if (mm) - mmput(mm); + mmdrop(mm); return 0; } -- cgit v0.10.2 From c6df4b17c8539f737a6a2d7b797eac41e8e34cdc Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 2 Feb 2012 00:17:54 +0200 Subject: lib: Fix multiple definitions of clz_tab Both sparc 32-bit's software divide assembler and MPILIB provide clz_tab[] with identical contents. Break it out into a seperate object file and select it when SPARC32 or MPILIB is set. Reported-by: Al Viro Signed-off-by: David S. Miller Signed-off-by: James Morris diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 9665799..ca5580e 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -33,6 +33,7 @@ config SPARC config SPARC32 def_bool !64BIT select GENERIC_ATOMIC64 + select CLZ_TAB config SPARC64 def_bool 64BIT diff --git a/arch/sparc/lib/divdi3.S b/arch/sparc/lib/divdi3.S index 681b368..d74bc09 100644 --- a/arch/sparc/lib/divdi3.S +++ b/arch/sparc/lib/divdi3.S @@ -17,23 +17,9 @@ along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - .data - .align 8 - .globl __clz_tab -__clz_tab: - .byte 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 - .byte 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 - .byte 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 - .byte 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 - .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 - .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 - .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 - .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 - .size __clz_tab,256 - .global .udiv - .text .align 4 + .global .udiv .globl __divdi3 __divdi3: save %sp,-104,%sp diff --git a/lib/Kconfig b/lib/Kconfig index 169eb7c..d69d321 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -279,6 +279,9 @@ config AVERAGE If unsure, say N. +config CLZ_TAB + bool + config CORDIC tristate "CORDIC algorithm" help @@ -287,6 +290,7 @@ config CORDIC config MPILIB tristate + select CLZ_TAB help Multiprecision maths library from GnuPG. It is used to implement RSA digital signature verification, diff --git a/lib/Makefile b/lib/Makefile index d71aae1..18515f0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -121,6 +121,8 @@ obj-$(CONFIG_DQL) += dynamic_queue_limits.o obj-$(CONFIG_MPILIB) += mpi/ obj-$(CONFIG_SIGNATURE) += digsig.o +obj-$(CONFIG_CLZ_TAB) += clz_tab.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/clz_tab.c b/lib/clz_tab.c new file mode 100644 index 0000000..7287b4a --- /dev/null +++ b/lib/clz_tab.c @@ -0,0 +1,18 @@ +const unsigned char __clz_tab[] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, +}; diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c index 854c9c6..2f52662 100644 --- a/lib/mpi/mpi-bit.c +++ b/lib/mpi/mpi-bit.c @@ -21,25 +21,6 @@ #include "mpi-internal.h" #include "longlong.h" -const unsigned char __clz_tab[] = { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, -}; - #define A_LIMB_1 ((mpi_limb_t) 1) /**************** -- cgit v0.10.2 From a99e7e5f36c55e561a64280f5099078c31839076 Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 2 Feb 2012 00:17:55 +0200 Subject: lib: Fix 32-bit sparc udiv_qrnnd() definition in mpilib's longlong.h This copy of longlong.h is extremely dated and results in compile errors on sparc32 when MPILIB is enabled, copy over the more uptodate implementation from arch/sparc/math/sfp-util_32.h Reported-by: Al Viro Signed-off-by: David S. Miller Signed-off-by: James Morris diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h index b87487b..29f9862 100644 --- a/lib/mpi/longlong.h +++ b/lib/mpi/longlong.h @@ -1200,18 +1200,40 @@ do { \ "r" ((USItype)(v)) \ : "%g1", "%g2" __AND_CLOBBER_CC) #define UMUL_TIME 39 /* 39 instructions */ -#endif -#ifndef udiv_qrnnd -#ifndef LONGLONG_STANDALONE +/* It's quite necessary to add this much assembler for the sparc. + The default udiv_qrnnd (in C) is more than 10 times slower! */ #define udiv_qrnnd(q, r, n1, n0, d) \ -do { USItype __r; \ - (q) = __udiv_qrnnd(&__r, (n1), (n0), (d)); \ - (r) = __r; \ -} while (0) - extern USItype __udiv_qrnnd(); -#define UDIV_TIME 140 -#endif /* LONGLONG_STANDALONE */ -#endif /* udiv_qrnnd */ + __asm__ ("! Inlined udiv_qrnnd\n\t" \ + "mov 32,%%g1\n\t" \ + "subcc %1,%2,%%g0\n\t" \ + "1: bcs 5f\n\t" \ + "addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n\t" \ + "sub %1,%2,%1 ! this kills msb of n\n\t" \ + "addx %1,%1,%1 ! so this can't give carry\n\t" \ + "subcc %%g1,1,%%g1\n\t" \ + "2: bne 1b\n\t" \ + "subcc %1,%2,%%g0\n\t" \ + "bcs 3f\n\t" \ + "addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n\t" \ + "b 3f\n\t" \ + "sub %1,%2,%1 ! this kills msb of n\n\t" \ + "4: sub %1,%2,%1\n\t" \ + "5: addxcc %1,%1,%1\n\t" \ + "bcc 2b\n\t" \ + "subcc %%g1,1,%%g1\n\t" \ + "! Got carry from n. Subtract next step to cancel this carry.\n\t" \ + "bne 4b\n\t" \ + "addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb\n\t" \ + "sub %1,%2,%1\n\t" \ + "3: xnor %0,0,%0\n\t" \ + "! End of inline udiv_qrnnd\n" \ + : "=&r" ((USItype)(q)), \ + "=&r" ((USItype)(r)) \ + : "r" ((USItype)(d)), \ + "1" ((USItype)(n1)), \ + "0" ((USItype)(n0)) : "%g1", "cc") +#define UDIV_TIME (3+7*32) /* 7 instructions/iteration. 32 iterations. */ +#endif #endif /* __sparc__ */ /*************************************** -- cgit v0.10.2 From 43b6cec27e1e50a1de3eff47e66e502f3fe7e66e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 1 Feb 2012 23:46:58 +0000 Subject: ASoC: wm_hubs: Correct line input to line output 2 paths The second line output mixer has the controls for the line input bypasses in the opposite order. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index c1a3f8c..8a68cea 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -586,8 +586,8 @@ SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), }; static const struct snd_kcontrol_new line2_mix[] = { -SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 2, 1, 0), -SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 1, 1, 0), SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), }; -- cgit v0.10.2 From 2db4d87070e87d198ab630e66a898b45eff316d9 Mon Sep 17 00:00:00 2001 From: Thomas Tuttle Date: Wed, 1 Feb 2012 16:07:17 -0500 Subject: USB: qcserial: add several new serial devices Signed-off-by: Thomas Tuttle Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 1d5deee..26e3e30 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -36,6 +36,11 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x413c, 0x8171)}, /* Dell Gobi QDL device */ {USB_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */ {USB_DEVICE(0x1410, 0xa008)}, /* Novatel Gobi QDL device */ + {USB_DEVICE(0x1410, 0xa010)}, /* Novatel Gobi QDL device */ + {USB_DEVICE(0x1410, 0xa011)}, /* Novatel Gobi QDL device */ + {USB_DEVICE(0x1410, 0xa012)}, /* Novatel Gobi QDL device */ + {USB_DEVICE(0x1410, 0xa013)}, /* Novatel Gobi QDL device */ + {USB_DEVICE(0x1410, 0xa014)}, /* Novatel Gobi QDL device */ {USB_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */ {USB_DEVICE(0x0b05, 0x1774)}, /* Asus Gobi QDL device */ {USB_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */ @@ -86,7 +91,16 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ + + {USB_DEVICE(0x05c6, 0x920c)}, /* Gobi 3000 QDL */ + {USB_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */ + {USB_DEVICE(0x1410, 0xa020)}, /* Novatel Gobi 3000 QDL */ + {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */ + {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */ + {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ + {USB_DEVICE(0x12D1, 0x14F0)}, /* Sony Gobi 3000 QDL */ + {USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v0.10.2 From 0a365706f2d6062d3756806447c1bbcd7e315ffa Mon Sep 17 00:00:00 2001 From: Thomas Tuttle Date: Wed, 1 Feb 2012 16:07:45 -0500 Subject: USB: qcserial: don't enable autosuspend We've found that qcserial devices will not always wake up reliably from autosuspend, so disable it. Signed-off-by: Thomas Tuttle Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 26e3e30..f98800f 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -137,8 +137,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) spin_lock_init(&data->susp_lock); - usb_enable_autosuspend(serial->dev); - switch (nintf) { case 1: /* QDL mode */ -- cgit v0.10.2 From 504b61630ab65296b6c9113cce834574e8cc01de Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 31 Jan 2012 16:43:50 -0800 Subject: usb: ch9.h: usb_endpoint_maxp() uses __le16_to_cpu() The usb/ch9.h will be installed to /usr/include/linux, and be used from user space. But le16_to_cpu() is only defined for kernel code. Without this patch, user space compile will be broken. Special thanks to Stefan Becker Reported-by: Stefan Becker Signed-off-by: Kuninori Morimoto Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 61b2905..3b6f628 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -589,7 +589,7 @@ static inline int usb_endpoint_is_isoc_out( */ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) { - return le16_to_cpu(epd->wMaxPacketSize); + return __le16_to_cpu(epd->wMaxPacketSize); } /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 876cf5e7b9e4bae014b0fa2cc18b21bce6d99073 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 1 Feb 2012 18:32:32 +0530 Subject: gpio: samsung: adapt to changes in gpio specifier translator function declaration Commit 15c9a0acc3f7 (of: create of_phandle_args to simplify return of phandle parsing data) modifies the parameter list of of_xlate function pointer declaration in gpio_chip. Adapt the gpio specifier translate function for this change. Reported-by: Tushar Behera Signed-off-by: Thomas Abraham Tested-by: Karol Lewandowski Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index a766177..0a79a11 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -2387,27 +2387,30 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = { }; #if defined(CONFIG_ARCH_EXYNOS4) && defined(CONFIG_OF) -static int exynos4_gpio_xlate(struct gpio_chip *gc, struct device_node *np, - const void *gpio_spec, u32 *flags) +static int exynos4_gpio_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) { - const __be32 *gpio = gpio_spec; - const u32 n = be32_to_cpup(gpio); - unsigned int pin = gc->base + be32_to_cpu(gpio[0]); + unsigned int pin; if (WARN_ON(gc->of_gpio_n_cells < 4)) return -EINVAL; - if (n > gc->ngpio) + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) return -EINVAL; - if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(be32_to_cpu(gpio[1])))) + if (gpiospec->args[0] > gc->ngpio) + return -EINVAL; + + pin = gc->base + gpiospec->args[0]; + + if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1]))) pr_warn("gpio_xlate: failed to set pin function\n"); - if (s3c_gpio_setpull(pin, be32_to_cpu(gpio[2]))) + if (s3c_gpio_setpull(pin, gpiospec->args[2])) pr_warn("gpio_xlate: failed to set pin pull up/down\n"); - if (s5p_gpio_set_drvstr(pin, be32_to_cpu(gpio[3]))) + if (s5p_gpio_set_drvstr(pin, gpiospec->args[3])) pr_warn("gpio_xlate: failed to set pin drive strength\n"); - return n; + return gpiospec->args[0]; } static const struct of_device_id exynos4_gpio_dt_match[] __initdata = { -- cgit v0.10.2 From 1608ea5f4b5d6262cd6e808839491cfb2a67405a Mon Sep 17 00:00:00 2001 From: Rui li Date: Tue, 31 Jan 2012 15:27:33 +0800 Subject: USB: add new zte 3g-dongle's pid to option.c As ZTE have and will use more pid for new products this year, so we need to add some new zte 3g-dongle's pid on option.c , and delete one pid 0x0154 because it use for mass-storage port. Signed-off-by: Rui li Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ea126a4..39ed1f4 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -855,6 +855,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0088, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0089, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0090, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0091, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0092, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0093, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0095, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0098, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0099, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, @@ -883,7 +895,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0154, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) }, @@ -892,6 +903,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, @@ -1066,6 +1083,116 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1403, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1404, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1405, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1406, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1407, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1408, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1409, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1410, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1411, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1412, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1413, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1414, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1415, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1416, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1417, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1418, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1419, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1420, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1421, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1422, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1423, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1427, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1429, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1430, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1431, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1432, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1433, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1434, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1435, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1436, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1437, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1438, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1439, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1440, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1441, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1442, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1443, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1444, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1445, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1446, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1447, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1448, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1449, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1450, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1451, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1452, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1453, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1454, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1455, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1456, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1457, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1458, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1459, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1460, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1461, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1462, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1463, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1464, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1465, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1466, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1467, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1468, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1469, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1470, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1471, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1472, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1473, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1474, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1475, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1476, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1477, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1478, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1479, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1480, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1482, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1483, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1484, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1485, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1486, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1487, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1488, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1489, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1490, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1491, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1492, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1493, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1494, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1495, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1496, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1497, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1498, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1499, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1500, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1501, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1502, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1503, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1504, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1505, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1506, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1507, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1508, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1509, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1510, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, -- cgit v0.10.2 From 13405059e8148259a0d5b66f42e93d641a63e66a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 1 Feb 2012 18:15:49 -0800 Subject: docbook: fix fatal errors in device-drivers docbook and add DMA Management section Fix 2 fatal errors in the device-drivers docbook. Also add some missing files from drivers/base/; since several of these are DMA-related, add a section for DMA Management. docproc: drivers/base/sys.c: No such file or directory docproc: drivers/tty/serial/8250.c: No such file or directory Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 2f7fd43..9c27e51 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -102,9 +102,12 @@ X!Iinclude/linux/kobject.h !Iinclude/linux/device.h Device Drivers Base +!Idrivers/base/init.c !Edrivers/base/driver.c !Edrivers/base/core.c +!Edrivers/base/syscore.c !Edrivers/base/class.c +!Idrivers/base/node.c !Edrivers/base/firmware_class.c !Edrivers/base/transport_class.c -!Edrivers/base/sys.c +!Edrivers/base/dd.c @@ -121,6 +124,11 @@ X!Edrivers/base/interface.c !Edrivers/base/platform.c !Edrivers/base/bus.c + Device Drivers DMA Management +!Edrivers/base/dma-buf.c +!Edrivers/base/dma-coherent.c +!Edrivers/base/dma-mapping.c + Device Drivers Power Management !Edrivers/base/power/main.c @@ -219,7 +227,7 @@ X!Isound/sound_firmware.c 16x50 UART Driver !Edrivers/tty/serial/serial_core.c -!Edrivers/tty/serial/8250.c +!Edrivers/tty/serial/8250/8250.c -- cgit v0.10.2 From da46d7dd530c5af7cff049145f0c088f5e75bdc6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 1 Feb 2012 19:04:02 -0800 Subject: staging: fix go7007-usb license Add MODULE_LICENSE() as per the license in the comment at the top of the file for this source module to fix build warning: WARNING: modpost: missing MODULE_LICENSE() in drivers/staging/media/go7007/go7007-usb.o see include/linux/module.h for more information Signed-off-by: Randy Dunlap Cc: Ross Cohen Signed-off-by: Linus Torvalds diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 70e006b..5443e25 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1279,3 +1279,4 @@ static struct usb_driver go7007_usb_driver = { }; module_usb_driver(go7007_usb_driver); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From d166370ad86b33b1111af3a0cdd7de94e03789a6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 1 Feb 2012 10:51:53 +0800 Subject: gpio: Add missing spin_lock_init in gpio-pch driver This bug was introduced by commit d568a681 "gpio-pch: add spinlock in suspend/resume processing" which adds a spinlock to struct pch_gpio but never init the spinlock. Reported-by: Tomoya MORINAGA Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 68fa55e..e8729cc 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -392,6 +392,7 @@ static int __devinit pch_gpio_probe(struct pci_dev *pdev, chip->reg = chip->base; pci_set_drvdata(pdev, chip); mutex_init(&chip->lock); + spin_lock_init(&chip->spinlock); pch_gpio_setup(chip); ret = gpiochip_add(&chip->gpio); if (ret) { -- cgit v0.10.2 From 7e3a70fb7bccada029c188c89bfbf3c0a63c1500 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 1 Feb 2012 10:50:05 +0800 Subject: gpio: Add missing spin_lock_init in gpio-ml-ioh driver This bug was introduced by commit 54be5663 "gpio-ml-ioh: Support interrupt function" which adds a spinlock to struct ioh_gpio but never init the spinlock. Signed-off-by: Axel Lin Signed-off-by: Grant Likely diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 03d6dd5..f0febe5 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -448,6 +448,7 @@ static int __devinit ioh_gpio_probe(struct pci_dev *pdev, chip->reg = chip->base; chip->ch = i; mutex_init(&chip->lock); + spin_lock_init(&chip->spinlock); ioh_gpio_setup(chip, num_ports[i]); ret = gpiochip_add(&chip->gpio); if (ret) { -- cgit v0.10.2 From 94ac27a54be6a14948f0a9b3f542b4ff1faac232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20R=C3=A9tornaz?= Date: Tue, 24 Jan 2012 14:22:01 +0100 Subject: i.MX SDMA: Fix burstsize settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 6584cb88 (ARM i.MX dma: Fix burstsize settings) fixed the mxcmmc driver but forgot to fix the SDMA driver to handle the correct burtsize. This make the SD card access works again with DMA on i.MX31 boards. Signed-off-by: Philippe Rétornaz Tested-by: Sascha Hauer Acked-by: Sascha Hauer Signed-off-by: Vinod Koul diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index a8af379..8bc5acf 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1102,11 +1102,13 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_SLAVE_CONFIG: if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; - sdmac->watermark_level = dmaengine_cfg->src_maxburst; + sdmac->watermark_level = dmaengine_cfg->src_maxburst * + dmaengine_cfg->src_addr_width; sdmac->word_size = dmaengine_cfg->src_addr_width; } else { sdmac->per_address = dmaengine_cfg->dst_addr; - sdmac->watermark_level = dmaengine_cfg->dst_maxburst; + sdmac->watermark_level = dmaengine_cfg->dst_maxburst * + dmaengine_cfg->dst_addr_width; sdmac->word_size = dmaengine_cfg->dst_addr_width; } sdmac->direction = dmaengine_cfg->direction; -- cgit v0.10.2 From ba18311dff7933ccb9c41bbbb1ad3d70840069b5 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 12 Jan 2012 17:42:22 +0800 Subject: HID: usbhid: fix dead lock between open and disconect There is no reason to hold hiddev->existancelock before calling usb_deregister_dev, so move it out of the lock. The patch fixes the lockdep warning below. [ 5733.386271] ====================================================== [ 5733.386274] [ INFO: possible circular locking dependency detected ] [ 5733.386278] 3.2.0-custom-next-20120111+ #1 Not tainted [ 5733.386281] ------------------------------------------------------- [ 5733.386284] khubd/186 is trying to acquire lock: [ 5733.386288] (minor_rwsem){++++.+}, at: [] usb_deregister_dev+0x37/0x9e [usbcore] [ 5733.386311] [ 5733.386312] but task is already holding lock: [ 5733.386315] (&hiddev->existancelock){+.+...}, at: [] hiddev_disconnect+0x26/0x87 [usbhid] [ 5733.386328] [ 5733.386329] which lock already depends on the new lock. [ 5733.386330] [ 5733.386333] [ 5733.386334] the existing dependency chain (in reverse order) is: [ 5733.386336] [ 5733.386337] -> #1 (&hiddev->existancelock){+.+...}: [ 5733.386346] [] lock_acquire+0xcb/0x10e [ 5733.386357] [] __mutex_lock_common+0x60/0x465 [ 5733.386366] [] mutex_lock_nested+0x36/0x3b [ 5733.386371] [] hiddev_open+0x113/0x193 [usbhid] [ 5733.386378] [] usb_open+0x66/0xc2 [usbcore] [ 5733.386390] [] chrdev_open+0x12b/0x154 [ 5733.386402] [] __dentry_open.isra.16+0x20b/0x355 [ 5733.386408] [] nameidata_to_filp+0x43/0x4a [ 5733.386413] [] do_last+0x536/0x570 [ 5733.386419] [] path_openat+0xce/0x301 [ 5733.386423] [] do_filp_open+0x33/0x81 [ 5733.386427] [] do_sys_open+0x6a/0xfc [ 5733.386431] [] sys_open+0x1c/0x1e [ 5733.386434] [] system_call_fastpath+0x16/0x1b [ 5733.386441] [ 5733.386441] -> #0 (minor_rwsem){++++.+}: [ 5733.386448] [] __lock_acquire+0xa80/0xd74 [ 5733.386454] [] lock_acquire+0xcb/0x10e [ 5733.386458] [] down_write+0x44/0x77 [ 5733.386464] [] usb_deregister_dev+0x37/0x9e [usbcore] [ 5733.386475] [] hiddev_disconnect+0x3c/0x87 [usbhid] [ 5733.386483] [] hid_disconnect+0x3f/0x54 [ 5733.386491] [] hid_device_remove+0x4e/0x7a [ 5733.386496] [] __device_release_driver+0x81/0xcd [ 5733.386502] [] device_release_driver+0x20/0x2d [ 5733.386507] [] bus_remove_device+0x114/0x128 [ 5733.386512] [] device_del+0x131/0x183 [ 5733.386519] [] hid_destroy_device+0x1e/0x3d [ 5733.386525] [] usbhid_disconnect+0x36/0x42 [usbhid] [ 5733.386530] [] usb_unbind_interface+0x57/0x11f [usbcore] [ 5733.386542] [] __device_release_driver+0x81/0xcd [ 5733.386547] [] device_release_driver+0x20/0x2d [ 5733.386552] [] bus_remove_device+0x114/0x128 [ 5733.386557] [] device_del+0x131/0x183 [ 5733.386562] [] usb_disable_device+0xa8/0x1d8 [usbcore] [ 5733.386573] [] usb_disconnect+0xab/0x11f [usbcore] [ 5733.386583] [] hub_thread+0x73b/0x1157 [usbcore] [ 5733.386593] [] kthread+0x95/0x9d [ 5733.386601] [] kernel_thread_helper+0x4/0x10 [ 5733.386607] [ 5733.386608] other info that might help us debug this: [ 5733.386609] [ 5733.386612] Possible unsafe locking scenario: [ 5733.386613] [ 5733.386615] CPU0 CPU1 [ 5733.386618] ---- ---- [ 5733.386620] lock(&hiddev->existancelock); [ 5733.386625] lock(minor_rwsem); [ 5733.386630] lock(&hiddev->existancelock); [ 5733.386635] lock(minor_rwsem); [ 5733.386639] [ 5733.386640] *** DEADLOCK *** [ 5733.386641] [ 5733.386644] 6 locks held by khubd/186: [ 5733.386646] #0: (&__lockdep_no_validate__){......}, at: [] hub_thread+0x14a/0x1157 [usbcore] [ 5733.386661] #1: (&__lockdep_no_validate__){......}, at: [] usb_disconnect+0x50/0x11f [usbcore] [ 5733.386677] #2: (hcd->bandwidth_mutex){+.+.+.}, at: [] usb_disconnect+0xa1/0x11f [usbcore] [ 5733.386693] #3: (&__lockdep_no_validate__){......}, at: [] device_release_driver+0x18/0x2d [ 5733.386704] #4: (&__lockdep_no_validate__){......}, at: [] device_release_driver+0x18/0x2d [ 5733.386714] #5: (&hiddev->existancelock){+.+...}, at: [] hiddev_disconnect+0x26/0x87 [usbhid] [ 5733.386727] [ 5733.386727] stack backtrace: [ 5733.386731] Pid: 186, comm: khubd Not tainted 3.2.0-custom-next-20120111+ #1 [ 5733.386734] Call Trace: [ 5733.386741] [] ? up+0x34/0x3b [ 5733.386747] [] print_circular_bug+0x1f8/0x209 [ 5733.386752] [] __lock_acquire+0xa80/0xd74 [ 5733.386756] [] ? trace_hardirqs_on_caller+0x15d/0x1a3 [ 5733.386763] [] ? vprintk+0x3f4/0x419 [ 5733.386774] [] ? usb_deregister_dev+0x37/0x9e [usbcore] [ 5733.386779] [] lock_acquire+0xcb/0x10e [ 5733.386789] [] ? usb_deregister_dev+0x37/0x9e [usbcore] [ 5733.386797] [] down_write+0x44/0x77 [ 5733.386807] [] ? usb_deregister_dev+0x37/0x9e [usbcore] [ 5733.386818] [] usb_deregister_dev+0x37/0x9e [usbcore] [ 5733.386825] [] hiddev_disconnect+0x3c/0x87 [usbhid] [ 5733.386830] [] hid_disconnect+0x3f/0x54 [ 5733.386834] [] hid_device_remove+0x4e/0x7a [ 5733.386839] [] __device_release_driver+0x81/0xcd [ 5733.386844] [] device_release_driver+0x20/0x2d [ 5733.386848] [] bus_remove_device+0x114/0x128 [ 5733.386854] [] device_del+0x131/0x183 [ 5733.386859] [] hid_destroy_device+0x1e/0x3d [ 5733.386865] [] usbhid_disconnect+0x36/0x42 [usbhid] [ 5733.386876] [] usb_unbind_interface+0x57/0x11f [usbcore] [ 5733.386882] [] __device_release_driver+0x81/0xcd [ 5733.386886] [] device_release_driver+0x20/0x2d [ 5733.386890] [] bus_remove_device+0x114/0x128 [ 5733.386895] [] device_del+0x131/0x183 [ 5733.386905] [] usb_disable_device+0xa8/0x1d8 [usbcore] [ 5733.386916] [] usb_disconnect+0xab/0x11f [usbcore] [ 5733.386921] [] ? __mutex_unlock_slowpath+0x130/0x141 [ 5733.386929] [] hub_thread+0x73b/0x1157 [usbcore] [ 5733.386935] [] ? finish_task_switch+0x78/0x150 [ 5733.386941] [] ? __init_waitqueue_head+0x4c/0x4c [ 5733.386950] [] ? usb_remote_wakeup+0x56/0x56 [usbcore] [ 5733.386955] [] kthread+0x95/0x9d [ 5733.386961] [] kernel_thread_helper+0x4/0x10 [ 5733.386966] [] ? retint_restore_args+0x13/0x13 [ 5733.386970] [] ? __init_kthread_worker+0x55/0x55 [ 5733.386974] [] ? gs_change+0x13/0x13 Signed-off-by: Ming Lei Signed-off-by: Jiri Kosina diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 7c297d3..b1ec0e2 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -922,11 +922,11 @@ void hiddev_disconnect(struct hid_device *hid) struct hiddev *hiddev = hid->hiddev; struct usbhid_device *usbhid = hid->driver_data; + usb_deregister_dev(usbhid->intf, &hiddev_class); + mutex_lock(&hiddev->existancelock); hiddev->exist = 0; - usb_deregister_dev(usbhid->intf, &hiddev_class); - if (hiddev->open) { mutex_unlock(&hiddev->existancelock); usbhid_close(hiddev->hid); -- cgit v0.10.2 From 054d867e032daf55c3343fc6d36c5c5f1e7954db Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Jan 2012 12:25:50 +0100 Subject: ALSA: hda - Check power-state before changing in patch_via.c Instead of always writing AC_VERB_SET_POWER_STATE, check the current power-state and don't write again if the value is already set. This may reduce the click noise upon the dynamic power-state change (e.g. in analog-input mixer). Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 03e63fe..fb1f0ff 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -687,6 +687,15 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } +static void update_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int parm) +{ + if (snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0) == parm) + return; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); +} + static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -709,7 +718,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, } else parm = AC_PWRST_D3; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, nid, parm); } static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, @@ -2295,10 +2304,7 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, if (mux) { /* switch to D0 beofre change index */ - if (snd_hda_codec_read(codec, mux, 0, - AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) - snd_hda_codec_write(codec, mux, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, mux, AC_PWRST_D0); snd_hda_codec_write(codec, mux, 0, AC_VERB_SET_CONNECT_SEL, spec->inputs[cur].mux_idx); @@ -2922,9 +2928,9 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* SW0 (17h), AIW 0/1 (13h/14h) */ - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x17, parm); + update_power_state(codec, 0x13, parm); + update_power_state(codec, 0x14, parm); /* outputs */ /* PW0 (19h), SW1 (18h), AOW1 (11h) */ @@ -2932,8 +2938,8 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) set_pin_power_state(codec, 0x19, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x11, parm); /* PW6 (22h), SW2 (26h), AOW2 (24h) */ if (is_8ch) { @@ -2941,20 +2947,16 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) set_pin_power_state(codec, 0x22, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); - snd_hda_codec_write(codec, 0x26, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x24, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x26, parm); + update_power_state(codec, 0x24, parm); } else if (codec->vendor_id == 0x11064397) { /* PW7(23h), SW2(27h), AOW2(25h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); - snd_hda_codec_write(codec, 0x27, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x25, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x27, parm); + update_power_state(codec, 0x25, parm); } /* PW 3/4/7 (1ch/1dh/23h) */ @@ -2966,17 +2968,13 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) set_pin_power_state(codec, 0x23, &parm); /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x10, parm); if (is_8ch) { - snd_hda_codec_write(codec, 0x25, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x27, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x25, parm); + update_power_state(codec, 0x27, parm); } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) - snd_hda_codec_write(codec, 0x25, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x25, parm); } static int patch_vt1708S(struct hda_codec *codec); @@ -3149,10 +3147,10 @@ static void set_widgets_power_state_vt1702(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ - snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x13, parm); + update_power_state(codec, 0x12, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x20, parm); /* outputs */ /* PW 3/4 (16h/17h) */ @@ -3160,10 +3158,9 @@ static void set_widgets_power_state_vt1702(struct hda_codec *codec) set_pin_power_state(codec, 0x17, &parm); set_pin_power_state(codec, 0x16, &parm); /* MW0 (1ah), AOW 0/1 (10h/1dh) */ - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x1d, parm); } static int patch_vt1702(struct hda_codec *codec) @@ -3228,52 +3225,48 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); /* outputs */ /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x27, &parm); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1a, parm); + update_power_state(codec, 0xb, parm); /* PW2 (26h), AOW2 (ah) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x2b, &parm); - snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0xa, parm); /* PW0 (24h), AOW0 (8h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (!spec->hp_independent_mode) /* check for redirected HP */ set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x8, parm); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); /* PW1 (25h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); if (spec->smart51_enabled) set_pin_power_state(codec, 0x2a, &parm); - snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x9, parm); if (spec->hp_independent_mode) { /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0xc, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1b, parm); + update_power_state(codec, 0x34, parm); + update_power_state(codec, 0xc, parm); } } @@ -3433,8 +3426,8 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) if (imux_is_smixer) parm = AC_PWRST_D0; /* SW0 (17h), AIW0(13h) */ - snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x17, parm); + update_power_state(codec, 0x13, parm); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x1e, &parm); @@ -3442,12 +3435,11 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) if (spec->dmic_enabled) set_pin_power_state(codec, 0x22, &parm); else - snd_hda_codec_write(codec, 0x22, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + update_power_state(codec, 0x22, AC_PWRST_D3); /* SW2(26h), AIW1(14h) */ - snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x26, parm); + update_power_state(codec, 0x14, parm); /* outputs */ /* PW0 (19h), SW1 (18h), AOW1 (11h) */ @@ -3456,8 +3448,8 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) /* Smart 5.1 PW2(1bh) */ if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x11, parm); /* PW7 (23h), SW3 (27h), AOW3 (25h) */ parm = AC_PWRST_D3; @@ -3465,12 +3457,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) /* Smart 5.1 PW1(1ah) */ if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); - snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x27, parm); /* Smart 5.1 PW5(1eh) */ if (spec->smart51_enabled) set_pin_power_state(codec, 0x1e, &parm); - snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x25, parm); /* Mono out */ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ @@ -3486,9 +3478,9 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) mono_out = 1; } parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; - snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x28, parm); + update_power_state(codec, 0x29, parm); + update_power_state(codec, 0x2a, parm); /* PW 3/4 (1ch/1dh) */ parm = AC_PWRST_D3; @@ -3496,15 +3488,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) set_pin_power_state(codec, 0x1d, &parm); /* HP Independent Mode, power on AOW3 */ if (spec->hp_independent_mode) - snd_hda_codec_write(codec, 0x25, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x25, parm); /* force to D0 for internal Speaker */ /* MW0 (16h), AOW0 (10h) */ - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, - imux_is_smixer ? AC_PWRST_D0 : parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, - mono_out ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); + update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); } static int patch_vt1716S(struct hda_codec *codec) @@ -3580,54 +3569,45 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) set_pin_power_state(codec, 0x2b, &parm); parm = AC_PWRST_D0; /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); /* outputs */ /* AOW0 (8h)*/ - snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x8, parm); if (spec->codec_type == VT1802) { /* PW4 (28h), MW4 (18h), MUX4(38h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x18, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x38, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x38, parm); } else { /* PW4 (26h), MW4 (1ch), MUX4(37h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x37, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1c, parm); + update_power_state(codec, 0x37, parm); } if (spec->codec_type == VT1802) { /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - snd_hda_codec_write(codec, 0x15, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x15, parm); + update_power_state(codec, 0x35, parm); } else { /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - snd_hda_codec_write(codec, 0x19, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x35, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x19, parm); + update_power_state(codec, 0x35, parm); } if (spec->hp_independent_mode) - snd_hda_codec_write(codec, 0x9, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, 0x9, AC_PWRST_D0); /* Class-D */ /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ @@ -3637,12 +3617,10 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) set_pin_power_state(codec, 0x24, &parm); parm = present ? AC_PWRST_D3 : AC_PWRST_D0; if (spec->codec_type == VT1802) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x14, parm); else - snd_hda_codec_write(codec, 0x18, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x34, parm); /* Mono Out */ present = snd_hda_jack_detect(codec, 0x26); @@ -3650,28 +3628,20 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) parm = present ? AC_PWRST_D3 : AC_PWRST_D0; if (spec->codec_type == VT1802) { /* PW15 (33h), MW8(1ch), MUX8(3ch) */ - snd_hda_codec_write(codec, 0x33, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x3c, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x33, parm); + update_power_state(codec, 0x1c, parm); + update_power_state(codec, 0x3c, parm); } else { /* PW15 (31h), MW8(17h), MUX8(3bh) */ - snd_hda_codec_write(codec, 0x31, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x17, 0, - AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x3b, 0, - AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x31, parm); + update_power_state(codec, 0x17, parm); + update_power_state(codec, 0x3b, parm); } /* MW9 (21h) */ if (imux_is_smixer || !is_aa_path_mute(codec)) - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, 0x21, AC_PWRST_D0); else - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + update_power_state(codec, 0x21, AC_PWRST_D3); } /* patch for vt2002P */ @@ -3731,30 +3701,28 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) set_pin_power_state(codec, 0x2b, &parm); parm = AC_PWRST_D0; /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1e, parm); + update_power_state(codec, 0x1f, parm); + update_power_state(codec, 0x10, parm); + update_power_state(codec, 0x11, parm); /* outputs */ /* AOW0 (8h)*/ - snd_hda_codec_write(codec, 0x8, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, 0x8, AC_PWRST_D0); /* PW4 (28h), MW4 (18h), MUX4(38h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x18, parm); + update_power_state(codec, 0x38, parm); /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x15, parm); + update_power_state(codec, 0x35, parm); if (spec->hp_independent_mode) - snd_hda_codec_write(codec, 0x9, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, 0x9, AC_PWRST_D0); /* Internal Speaker */ /* PW0 (24h), MW0(14h), MUX0(34h) */ @@ -3763,15 +3731,11 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (present) { - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + update_power_state(codec, 0x14, AC_PWRST_D3); + update_power_state(codec, 0x34, AC_PWRST_D3); } else { - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - snd_hda_codec_write(codec, 0x34, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, 0x14, AC_PWRST_D0); + update_power_state(codec, 0x34, AC_PWRST_D0); } @@ -3782,26 +3746,20 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x31, &parm); if (present) { - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, 0x3c, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, 0x3e, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + update_power_state(codec, 0x1c, AC_PWRST_D3); + update_power_state(codec, 0x3c, AC_PWRST_D3); + update_power_state(codec, 0x3e, AC_PWRST_D3); } else { - snd_hda_codec_write(codec, 0x1c, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - snd_hda_codec_write(codec, 0x3c, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - snd_hda_codec_write(codec, 0x3e, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + update_power_state(codec, 0x1c, AC_PWRST_D0); + update_power_state(codec, 0x3c, AC_PWRST_D0); + update_power_state(codec, 0x3e, AC_PWRST_D0); } /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x33, &parm); - snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); - snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); + update_power_state(codec, 0x1d, parm); + update_power_state(codec, 0x3d, parm); } -- cgit v0.10.2 From 924339239fd5ba3e505f9420d41f0939196f3530 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Jan 2012 13:58:36 +0100 Subject: ALSA: hda - Fix the logic to detect VIA analog low-current mode The analog low-current mode must be enabled when the no stream is running but the current detection checks it in a wrong way. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=741128 Cc: [v3.1+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fb1f0ff..de43cd9 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1051,7 +1051,7 @@ static void analog_low_current_mode(struct hda_codec *codec) bool enable; unsigned int verb, parm; - enable = is_aa_path_mute(codec) && (spec->opened_streams != 0); + enable = is_aa_path_mute(codec) && !spec->opened_streams; /* decide low current mode's verb & parameter */ switch (spec->codec_type) { -- cgit v0.10.2 From e9d010c2e8f03952e67a6fd8aed0f0dc92084ccc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 1 Feb 2012 10:33:23 +0100 Subject: ALSA: hda - Allow analog low-current mode when dynamic power-control is on VIA codecs have several different power-saving features, and one of them is the analog low-current mode. But it turned out that the ALC mode causes pop-noises at each on/off time on some machines. As a quick workaround, disable the ALC when another power-saving feature, the dynamic pin power-control, is turned off, too, since the dynamic power-control is already exposed as a mixer enum element so that user can turn it on/off freely. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=741128 Cc: [v3.1+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index de43cd9..79166fb 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -199,6 +199,9 @@ struct via_spec { unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; + /* analog low-power control */ + bool alc_mode; + /* smart51 setup */ unsigned int smart51_nums; hda_nid_t smart51_pins[2]; @@ -758,6 +761,7 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, return 0; spec->no_pin_power_ctl = val; set_widgets_power_state(codec); + analog_low_current_mode(codec); return 1; } @@ -1045,13 +1049,19 @@ static bool is_aa_path_mute(struct hda_codec *codec) } /* enter/exit analog low-current mode */ -static void analog_low_current_mode(struct hda_codec *codec) +static void __analog_low_current_mode(struct hda_codec *codec, bool force) { struct via_spec *spec = codec->spec; bool enable; unsigned int verb, parm; - enable = is_aa_path_mute(codec) && !spec->opened_streams; + if (spec->no_pin_power_ctl) + enable = false; + else + enable = is_aa_path_mute(codec) && !spec->opened_streams; + if (enable == spec->alc_mode && !force) + return; + spec->alc_mode = enable; /* decide low current mode's verb & parameter */ switch (spec->codec_type) { @@ -1083,6 +1093,11 @@ static void analog_low_current_mode(struct hda_codec *codec) snd_hda_codec_write(codec, codec->afg, 0, verb, parm); } +static void analog_low_current_mode(struct hda_codec *codec) +{ + return __analog_low_current_mode(codec, false); +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -1508,10 +1523,6 @@ static int via_build_controls(struct hda_codec *codec) return err; } - /* init power states */ - set_widgets_power_state(codec); - analog_low_current_mode(codec); - via_free_kctls(codec); /* no longer needed */ err = snd_hda_jack_add_kctls(codec, &spec->autocfg); @@ -2782,6 +2793,10 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + /* init power states */ + set_widgets_power_state(codec); + __analog_low_current_mode(codec, true); + via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_speaker_out(codec); -- cgit v0.10.2 From b5bcc189401c815988b7dd37611fc56f40c9139d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 2 Feb 2012 10:30:17 +0100 Subject: ALSA: hda - Disable dynamic-power control for VIA as default Since the dynamic pin power-control and the analog low-current mode may lead to pop-noise, it's safer to set it off as default. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=741128 Cc: [v3.1+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 79166fb..284e311 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1470,6 +1470,7 @@ static int via_build_controls(struct hda_codec *codec) struct snd_kcontrol *kctl; int err, i; + spec->no_pin_power_ctl = 1; if (spec->set_widgets_power_state) if (!via_clone_control(spec, &via_pin_power_ctl_enum)) return -ENOMEM; -- cgit v0.10.2 From c1c454b8691cc95aa83f19273ed7845914c70e83 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Jan 2012 08:57:14 -0800 Subject: HID: hyperv: Properly disconnect the input device When we unload the mouse driver, properly disconnect the input device. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Reported-by: Fuzhou Chen Cc: stable Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 0c33ae9..4066324 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -548,6 +548,7 @@ static int mousevsc_remove(struct hv_device *dev) struct mousevsc_dev *input_dev = hv_get_drvdata(dev); vmbus_close(dev->channel); + hid_hw_stop(input_dev->hid_device); hid_destroy_device(input_dev->hid_device); mousevsc_free_device(input_dev); -- cgit v0.10.2 From de47a9cd62771e3e78954d855d2304fbad4c5a44 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 2 Feb 2012 15:25:16 +0000 Subject: drm/radeon: fix use after free in ATRM bios reading code. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=45503 Reported-and-Debugged-by: mlambda@gmail.com Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index 13ac63b..98724fc 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -59,8 +59,9 @@ static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, obj = (union acpi_object *)buffer.pointer; memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); + len = obj->buffer.length; kfree(buffer.pointer); - return obj->buffer.length; + return len; } bool radeon_atrm_supported(struct pci_dev *pdev) -- cgit v0.10.2 From 304a48400d9718f74ec35ae46f30868a5f4c4516 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 2 Feb 2012 10:18:00 -0500 Subject: drm/radeon/kms: fix TRAVIS panel setup Different versions of the DP to LVDS bridge chip need different panel mode settings depending on the chip version used. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=41569 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index a71557c..552b436 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -564,9 +564,21 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder, ENCODER_OBJECT_ID_NUTMEG) panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) == - ENCODER_OBJECT_ID_TRAVIS) - panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; - else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + ENCODER_OBJECT_ID_TRAVIS) { + u8 id[6]; + int i; + for (i = 0; i < 6; i++) + id[i] = radeon_read_dpcd_reg(radeon_connector, 0x503 + i); + if (id[0] == 0x73 && + id[1] == 0x69 && + id[2] == 0x76 && + id[3] == 0x61 && + id[4] == 0x72 && + id[5] == 0x54) + panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE; + else + panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; + } else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP); if (tmp & 1) panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE; -- cgit v0.10.2 From 52b53a0bf8026a322cfa6cfec6a10dd31fef8752 Mon Sep 17 00:00:00 2001 From: Ilija Hadzic Date: Thu, 2 Feb 2012 10:26:24 -0500 Subject: drm/radeon/kms/blit: fix blit copy for very large buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evergreen and NI blit copy was broken if the buffer maps to a rectangle whose one dimension is 16384 (max dimension allowed by these chips). In the mainline kernel, the problem is exposed only when buffers are very large (1G), but it's still a problem. The problem could be exposed for smaller buffers if anyone modifies the algorithm for rectangle construction in r600_blit_create_rect() (the reason why someone would modify that algorithm is to tune the performance of buffer moves). The root cause was in i2f() function which only operated on range between 0 and 16383. Fix this by extending the range of i2f() function to 0 to 32767. While at it improve the function so that the range can be easily extended in the future (if it becomes necessary), cleanup lines over 80 characters, and replace in-line comments with one strategic comment that explains the crux of the function. Credits to michel@daenzer.net for pointing out the root cause of the bug. v2: Fix I2F_MAX_INPUT constant definition goof and warn only once if input argument is out of range. Edit the comment a little bit to avoid some linguistic confusion and make it look better in general. Signed-off-by: Ilija Hadzic Reviewed-by: Alex Deucher Reviewed-by: Michel Dänzer Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c index d996f43..accc032 100644 --- a/drivers/gpu/drm/radeon/r600_blit_kms.c +++ b/drivers/gpu/drm/radeon/r600_blit_kms.c @@ -468,27 +468,42 @@ set_default_state(struct radeon_device *rdev) radeon_ring_write(ring, sq_stack_resource_mgmt_2); } +#define I2F_MAX_BITS 15 +#define I2F_MAX_INPUT ((1 << I2F_MAX_BITS) - 1) +#define I2F_SHIFT (24 - I2F_MAX_BITS) + +/* + * Converts unsigned integer into 32-bit IEEE floating point representation. + * Conversion is not universal and only works for the range from 0 + * to 2^I2F_MAX_BITS-1. Currently we only use it with inputs between + * 0 and 16384 (inclusive), so I2F_MAX_BITS=15 is enough. If necessary, + * I2F_MAX_BITS can be increased, but that will add to the loop iterations + * and slow us down. Conversion is done by shifting the input and counting + * down until the first 1 reaches bit position 23. The resulting counter + * and the shifted input are, respectively, the exponent and the fraction. + * The sign is always zero. + */ static uint32_t i2f(uint32_t input) { u32 result, i, exponent, fraction; - if ((input & 0x3fff) == 0) - result = 0; /* 0 is a special case */ + WARN_ON_ONCE(input > I2F_MAX_INPUT); + + if ((input & I2F_MAX_INPUT) == 0) + result = 0; else { - exponent = 140; /* exponent biased by 127; */ - fraction = (input & 0x3fff) << 10; /* cheat and only - handle numbers below 2^^15 */ - for (i = 0; i < 14; i++) { + exponent = 126 + I2F_MAX_BITS; + fraction = (input & I2F_MAX_INPUT) << I2F_SHIFT; + + for (i = 0; i < I2F_MAX_BITS; i++) { if (fraction & 0x800000) break; else { - fraction = fraction << 1; /* keep - shifting left until top bit = 1 */ + fraction = fraction << 1; exponent = exponent - 1; } } - result = exponent << 23 | (fraction & 0x7fffff); /* mask - off top bit; assumed 1 */ + result = exponent << 23 | (fraction & 0x7fffff); } return result; } -- cgit v0.10.2 From 97f1040982d7935716e9a45a26ccd5cc8fe92f8c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 29 Jan 2012 14:55:21 +0000 Subject: Revert "ARM: 7304/1: ioremap: fix boundary check when reusing static mapping" This reverts commit 3c424f359898aff48c3d5bed608ac706f8a528c3. Joachim Eastwood reports: | "ARM: 7304/1: ioremap: fix boundary check when reusing static mapping" | Commit: 3c424f359898aff48c3d5bed608ac706f8a528c3 in Linus master | | Breaks booting on my custom AT91RM9200 board. | There isn't any error messages or anything that indicates what goes | wrong it just stops after; Uncompressing Linux... done, booting the | kernel. | | Reverting it makes my board boot again. and further debugging reveals: ioremap: pfn=fffff phys=fffff000 offset=400 size=1000 ioremap: area c3ffdfc0: phys_addr=200000 pfn=200 size=4000 ioremap: found: addr fef74000 => fed73000 => fed73400 Clearly, an area for pfn 0x200, 16K can't ever satisfy a request for pfn 0xfffff. This happens because the changed if statement becomes: if (0x00200 > 0xfffff || 0xfffff000 + 0x400 + 0x1000-1 > 0x00200000 + 0x4000-1) and therefore: if (0x00200 > 0xfffff || 0x000003ff > 0x00203fff) The if condition fails, and so we _believe_ that the SRAM mapping fits our request. Clearly that's totally bogus. Moreover, the original premise of the 'fix' patch was wrong: | The condition checking boundaries of the requested and existing | mappings didn't take in-page offset into consideration though, | which lead to obscure and hard to debug problems when requested | mapping crossed end of the static one. as the code immediately above this loop does: size = PAGE_ALIGN(offset + size); so 'size' already contains the requested offset into the page. So, revert the broken 'fix'. Acked-by: Nicolas Pitre diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index ba15937..80632e8 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -225,8 +225,7 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, if ((area->flags & VM_ARM_MTYPE_MASK) != VM_ARM_MTYPE(mtype)) continue; if (__phys_to_pfn(area->phys_addr) > pfn || - __pfn_to_phys(pfn) + offset + size-1 > - area->phys_addr + area->size-1) + __pfn_to_phys(pfn) + size-1 > area->phys_addr + area->size-1) continue; /* we can drop the lock here as we know *area is static */ read_unlock(&vmlist_lock); -- cgit v0.10.2 From 2af276dfb1722e97b190bd2e646b079a2aa674db Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jan 2012 20:21:42 +0100 Subject: ARM: 7306/1: vfp: flush thread hwstate before restoring context from sigframe Following execution of a signal handler, we currently restore the VFP context from the ucontext in the signal frame. This involves copying from the user stack into the current thread's vfp_hard_struct and then flushing the new data out to the hardware registers. This is problematic when using a preemptible kernel because we could be context switched whilst updating the vfp_hard_struct. If the current thread has made use of VFP since the last context switch, the VFP notifier will copy from the hardware registers into the vfp_hard_struct, overwriting any data that had been partially copied by the signal code. Disabling preemption across copy_from_user calls is a terrible idea, so instead we move the VFP thread flush *before* we update the vfp_hard_struct. Since the flushing is performed lazily, this has the effect of disabling VFP and clearing the CPU's VFP state pointer, therefore preventing the thread from being updated with stale data on the next context switch. Cc: stable Tested-by: Peter Maydell Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 0340224..9e617bd 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -227,6 +227,8 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) return -EINVAL; + vfp_flush_hwstate(thread); + /* * Copy the floating point registers. There can be unused * registers see asm/hwcap.h for details. @@ -251,9 +253,6 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame) __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); - if (!err) - vfp_flush_hwstate(thread); - return err ? -EFAULT : 0; } -- cgit v0.10.2 From 247f4993a5974e6759606c4d380748eecfd273ff Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Mon, 30 Jan 2012 20:22:28 +0100 Subject: ARM: 7307/1: vfp: fix ptrace regset modification race In a preemptible kernel, vfp_set() can be preempted, causing the hardware VFP context to be switched while the thread vfp state is being read and modified. This leads to a race condition which can cause the thread vfp state to become corrupted if lazy VFP context save occurs due to preemption in between the time thread->vfpstate is read and the time the modified state is written back. This may occur if preemption occurs during the execution of a ptrace() call which modifies the VFP register state of a thread. Such instances should be very rare in most realistic scenarios -- none has been reported, so far as I am aware. Only uniprocessor systems should be affected, since VFP context save is not currently lazy in SMP kernels. The problem was introduced by my earlier patch migrating to use regsets to implement ptrace. This patch does a vfp_sync_hwstate() before reading thread->vfpstate, to make sure that the thread's VFP state is not live in the hardware registers while the registers are modified. Thanks to Will Deacon for spotting this. Cc: stable Signed-off-by: Dave Martin Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index e1d5e19..d001be4 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -699,10 +699,13 @@ static int vfp_set(struct task_struct *target, { int ret; struct thread_info *thread = task_thread_info(target); - struct vfp_hard_struct new_vfp = thread->vfpstate.hard; + struct vfp_hard_struct new_vfp; const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); + vfp_sync_hwstate(thread); + new_vfp = thread->vfpstate.hard; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &new_vfp.fpregs, user_fpregs_offset, @@ -723,7 +726,6 @@ static int vfp_set(struct task_struct *target, if (ret) return ret; - vfp_sync_hwstate(thread); thread->vfpstate.hard = new_vfp; vfp_flush_hwstate(thread); -- cgit v0.10.2 From 8130b9d7b9d858aa04ce67805e8951e3cb6e9b2f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jan 2012 20:23:29 +0100 Subject: ARM: 7308/1: vfp: flush thread hwstate before copying ptrace registers If we are context switched whilst copying into a thread's vfp_hard_struct then the partial copy may be corrupted by the VFP context switching code (see "ARM: vfp: flush thread hwstate before restoring context from sigframe"). This patch updates the ptrace VFP set code so that the thread state is flushed before the copy, therefore disabling VFP and preventing corruption from occurring. Cc: stable Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index d001be4..e33870f 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -726,8 +726,8 @@ static int vfp_set(struct task_struct *target, if (ret) return ret; - thread->vfpstate.hard = new_vfp; vfp_flush_hwstate(thread); + thread->vfpstate.hard = new_vfp; return 0; } -- cgit v0.10.2 From 91756acb58b17aee68d055fc15b1e2550ff00801 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 25 Jan 2012 19:36:28 +0100 Subject: ARM: 7303/1: perf: add empty NODE event definitions for Cortex-A5 and Cortex-A15 Commit 89d6c0b5 ("perf, arch: Add generic NODE cache events") added empty NODE event definitions for the ARM PMU implementations. This was merged along with Cortex-A5 and Cortex-A15 PMU support, so they missed out on the original patch. This patch adds the empty definitions to Cortex-A5 and Cortex-A15. Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 460bbbb..6933244 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -469,6 +469,20 @@ static const unsigned armv7_a5_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, }, }, + [C(NODE)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, }; /* @@ -579,6 +593,20 @@ static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, }, }, + [C(NODE)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, }; /* -- cgit v0.10.2 From 6d3ec1ae6cdcda185bd9452b2daed5145e2493a5 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 25 Jan 2012 11:54:22 +0100 Subject: ARM: 7302/1: Add TLB flushing for both entries in a PMD Linux uses two PMD entries for a PTE with the classic page table format, covering 2MB range. However, the __pte_free_tlb() function only adds a single TLB flush corresponding to 1MB range covering 'addr'. On Cortex-A15, level 1 entries can be cached by the TLB independently of the level 2 entries and without additional flushing a PMD entry would be left pointing at the wrong PTE. The patch limits the TLB flushing range to two 4KB pages around the 1MB boundary within PMD. Signed-off-by: Catalin Marinas Signed-off-by: Russell King diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h index 5d3ed7e3..314d466 100644 --- a/arch/arm/include/asm/tlb.h +++ b/arch/arm/include/asm/tlb.h @@ -198,7 +198,15 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, unsigned long addr) { pgtable_page_dtor(pte); - tlb_add_flush(tlb, addr); + + /* + * With the classic ARM MMU, a pte page has two corresponding pmd + * entries, each covering 1MB. + */ + addr &= PMD_MASK; + tlb_add_flush(tlb, addr + SZ_1M - PAGE_SIZE); + tlb_add_flush(tlb, addr + SZ_1M); + tlb_remove_page(tlb, pte); } -- cgit v0.10.2 From 2885e25c422fb68208f677f944a45fce8eda2a3c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2012 10:36:33 -0800 Subject: driver core: cpu: remove kernel warning when removing a cpu With the movement of the cpu sysdev code to be real stuct devices, now when we remove a cpu from the system, the driver core rightfully complains that there is not a release method for this device. For now, paper over this issue by quieting the driver core, but comment this in detail. This will be resolved in future kernels to be solved properly. Reported-by: Konrad Rzeszutek Wilk Tested-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index db87e78..23f2c4c 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -208,6 +208,25 @@ static ssize_t print_cpus_offline(struct device *dev, } static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); +static void cpu_device_release(struct device *dev) +{ + /* + * This is an empty function to prevent the driver core from spitting a + * warning at us. Yes, I know this is directly opposite of what the + * documentation for the driver core and kobjects say, and the author + * of this code has already been publically ridiculed for doing + * something as foolish as this. However, at this point in time, it is + * the only way to handle the issue of statically allocated cpu + * devices. The different architectures will have their cpu device + * code reworked to properly handle this in the near future, so this + * function will then be changed to correctly free up the memory held + * by the cpu device. + * + * Never copy this way of doing things, or you too will be made fun of + * on the linux-kerenl list, you have been warned. + */ +} + /* * register_cpu - Setup a sysfs device for a CPU. * @cpu - cpu->hotpluggable field set to 1 will generate a control file in @@ -223,6 +242,7 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) cpu->node_id = cpu_to_node(num); cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; + cpu->dev.release = cpu_device_release; error = device_register(&cpu->dev); if (!error && cpu->hotpluggable) register_cpu_control(cpu); -- cgit v0.10.2 From c43b874d5d714f271b80d4c3f49e05d0cbf51ed2 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 2 Feb 2012 00:07:00 +0000 Subject: tcp: properly initialize tcp memory limits Commit 4acb4190 tries to fix the using uninitialized value introduced by commit 3dc43e3, but it would make the per-socket memory limits too small. This patch fixes this and also remove the redundant codes introduced in 4acb4190. Signed-off-by: Jason Wang Acked-by: Glauber Costa Signed-off-by: David S. Miller diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 4cb9cd2..7a7724d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -778,7 +778,6 @@ EXPORT_SYMBOL_GPL(net_ipv4_ctl_path); static __net_init int ipv4_sysctl_init_net(struct net *net) { struct ctl_table *table; - unsigned long limit; table = ipv4_net_table; if (!net_eq(net, &init_net)) { @@ -815,11 +814,6 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) net->ipv4.sysctl_rt_cache_rebuild_count = 4; tcp_init_mem(net); - limit = nr_free_buffer_pages() / 8; - limit = max(limit, 128UL); - net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; - net->ipv4.sysctl_tcp_mem[1] = limit; - net->ipv4.sysctl_tcp_mem[2] = net->ipv4.sysctl_tcp_mem[0] * 2; net->ipv4.ipv4_hdr = register_net_sysctl_table(net, net_ipv4_ctl_path, table); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a34f5cf..37755cc 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3229,7 +3229,6 @@ __setup("thash_entries=", set_thash_entries); void tcp_init_mem(struct net *net) { - /* Set per-socket limits to no more than 1/128 the pressure threshold */ unsigned long limit = nr_free_buffer_pages() / 8; limit = max(limit, 128UL); net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; @@ -3298,7 +3297,8 @@ void __init tcp_init(void) sysctl_max_syn_backlog = max(128, cnt / 256); tcp_init_mem(&init_net); - limit = nr_free_buffer_pages() / 8; + /* Set per-socket limits to no more than 1/128 the pressure threshold */ + limit = nr_free_buffer_pages() << (PAGE_SHIFT - 10); limit = max(limit, 128UL); max_share = min(4UL*1024*1024, limit); -- cgit v0.10.2 From 4b8a8bc9249f144803d840f2f7608ee9bbf1ea51 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 1 Feb 2012 20:17:23 +0000 Subject: net/hyperv: fix the issue that large packets be dropped under bridge The packets with size larger than 1452 will be dropped by bridge which with two hyperv netdevice ports. This cause by hyperv netvsc driver always copy the trailer padding to the data packet, and then the skb received from netdevice may include wrong skb->len (20 bytes larger than the real size normally). The captured packet may like this: Ethernet II, Src: Microsof_00:00:07 (00:15:5d:00:00:07), Dst: HewlettP_00:00:4e (00:1f:29:00:00:4e) Destination: HewlettP_e6:00:4e (00:1f:29:00:00:4e) Source: Microsof_f6:6d:07 (00:15:5d:f6:6d:07) Type: IP (0x0800) Trailer: 1415161718191A1B1C1D1E1F20212223 Frame check sequence: 0x24252627 [incorrect, should be 0x7c2e5a5e] The following command help to reproduction it, and the ping ICMP packets will be dropped by bridge. $ ping ip -s 1453 This patch fixed it by removing the trailer padding from the data packet. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index da181f9..dc2e384 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -321,6 +321,25 @@ static void rndis_filter_receive_data(struct rndis_device *dev, data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; pkt->total_data_buflen -= data_offset; + + /* + * Make sure we got a valid RNDIS message, now total_data_buflen + * should be the data packet size plus the trailer padding size + */ + if (pkt->total_data_buflen < rndis_pkt->data_len) { + netdev_err(dev->net_dev->ndev, "rndis message buffer " + "overflow detected (got %u, min %u)" + "...dropping this message!\n", + pkt->total_data_buflen, rndis_pkt->data_len); + return; + } + + /* + * Remove the rndis trailer padding from rndis packet message + * rndis_pkt->data_len tell us the real data length, we only copy + * the data packet to the stack, without the rndis trailer padding + */ + pkt->total_data_buflen = rndis_pkt->data_len; pkt->data = (void *)((unsigned long)pkt->data + data_offset); pkt->is_data_pkt = true; -- cgit v0.10.2 From cb0a178c787d2bdf787d56e364becdb0eb4b4fab Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 2 Feb 2012 04:53:01 +0000 Subject: drivers/net/ethernet/ti: Move call to PTR_ERR after reassignment PTR_ERR should be called before its argument is cleared. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e,e1; constant c; @@ *e = c ... when != e = e1 when != &e when != true IS_ERR(e) *PTR_ERR(e) // Signed-off-by: Julia Lawall Reported-by: Josh Triplett Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 794ac30..4fa0bcb 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1600,8 +1600,9 @@ static int emac_dev_open(struct net_device *ndev) if (IS_ERR(priv->phydev)) { dev_err(emac_dev, "could not connect to phy %s\n", priv->phy_id); + ret = PTR_ERR(priv->phydev); priv->phydev = NULL; - return PTR_ERR(priv->phydev); + return ret; } priv->link = 0; diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index ef7c9c1..af8b8fc 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -318,9 +318,9 @@ static int __devinit davinci_mdio_probe(struct platform_device *pdev) data->clk = clk_get(dev, NULL); if (IS_ERR(data->clk)) { - data->clk = NULL; dev_err(dev, "failed to get device clock\n"); ret = PTR_ERR(data->clk); + data->clk = NULL; goto bail_out; } -- cgit v0.10.2 From 0a282538cc1977655004cdb2eb25dd2b63f20637 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 2 Feb 2012 07:17:59 +0000 Subject: net/hyperv: Use netif_tx_disable() instead of netif_stop_queue() when necessary For code path not on the xmit, use netif_tx_disable() instead of netif_stop_queue() to ensure other CPUs are not doing xmit. Signed-off-by: Haiyang Zhang Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 9dccc7a..69193fc 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -123,7 +123,7 @@ static int netvsc_close(struct net_device *net) struct hv_device *device_obj = net_device_ctx->device_ctx; int ret; - netif_stop_queue(net); + netif_tx_disable(net); ret = rndis_filter_close(device_obj); if (ret != 0) @@ -256,7 +256,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); } else { netif_carrier_off(net); - netif_stop_queue(net); + netif_tx_disable(net); } } @@ -337,7 +337,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) nvdev->start_remove = true; cancel_delayed_work_sync(&ndevctx->dwork); - netif_stop_queue(ndev); + netif_tx_disable(ndev); rndis_filter_device_remove(hdev); ndev->mtu = mtu; @@ -460,7 +460,7 @@ static int netvsc_remove(struct hv_device *dev) cancel_delayed_work_sync(&ndev_ctx->dwork); /* Stop outbound asap */ - netif_stop_queue(net); + netif_tx_disable(net); unregister_netdev(net); -- cgit v0.10.2 From c31c151b1c4a29da4dc92212aa8648fb4f8557b9 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 2 Feb 2012 07:18:00 +0000 Subject: net/hyperv: Fix the page buffer when an RNDIS message goes beyond page boundary There is a possible data corruption if an RNDIS message goes beyond page boundary in the sending code path. This patch fixes the problem. Signed-off-by: Haiyang Zhang Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 69193fc..466c58a 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -151,10 +151,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) int ret; unsigned int i, num_pages, npg_data; - /* Add multipage for skb->data and additional one for RNDIS */ + /* Add multipages for skb->data and additional 2 for RNDIS */ npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; - num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1; + num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; /* Allocate a netvsc packet based on # of frags. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + @@ -173,8 +173,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)); - /* Setup the rndis header */ - packet->page_buf_cnt = num_pages; + /* If the rndis msg goes beyond 1 page, we will add 1 later */ + packet->page_buf_cnt = num_pages - 1; /* Initialize it from the skb */ packet->total_data_buflen = skb->len; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index dc2e384..133b7fb 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -797,6 +797,19 @@ int rndis_filter_send(struct hv_device *dev, (unsigned long)rndisMessage & (PAGE_SIZE-1); pkt->page_buf[0].len = rndisMessageSize; + /* Add one page_buf if the rndis msg goes beyond page boundary */ + if (pkt->page_buf[0].offset + rndisMessageSize > PAGE_SIZE) { + int i; + for (i = pkt->page_buf_cnt; i > 1; i--) + pkt->page_buf[i] = pkt->page_buf[i-1]; + pkt->page_buf_cnt++; + pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; + pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) + rndisMessage + pkt->page_buf[0].len)) >> PAGE_SHIFT; + pkt->page_buf[1].offset = 0; + pkt->page_buf[1].len = rndisMessageSize - pkt->page_buf[0].len; + } + /* Save the packet send completion and context */ filterPacket->completion = pkt->completion.send.send_completion; filterPacket->completion_ctx = diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 62b908e..0ae065a 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -35,7 +35,7 @@ #include -#define MAX_PAGE_BUFFER_COUNT 18 +#define MAX_PAGE_BUFFER_COUNT 19 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ #pragma pack(push, 1) -- cgit v0.10.2 From b01377a4200d0dfc7b04a8daabb4739727353703 Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Thu, 2 Feb 2012 01:21:02 +0000 Subject: caif: Bugfix list_del_rcu race in cfmuxl_ctrlcmd. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always use cfmuxl_remove_uplayer when removing a up-layer. cfmuxl_ctrlcmd() can be called independently and in parallel with cfmuxl_remove_uplayer(). The race between them could cause list_del_rcu to be called on a node which has been already taken out from the list. That lead to a (rare) crash on accessing poisoned node->prev inside list_del_rcu. This fix ensures that deletion are done holding the same lock. Reported-by: Dmitry Tarnyagin Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c index b36f24a..94b0861 100644 --- a/net/caif/cfmuxl.c +++ b/net/caif/cfmuxl.c @@ -248,7 +248,6 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, { struct cfmuxl *muxl = container_obj(layr); struct cflayer *layer; - int idx; rcu_read_lock(); list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { @@ -257,14 +256,9 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && - layer->id != 0) { - - idx = layer->id % UP_CACHE_SIZE; - spin_lock_bh(&muxl->receive_lock); - RCU_INIT_POINTER(muxl->up_cache[idx], NULL); - list_del_rcu(&layer->node); - spin_unlock_bh(&muxl->receive_lock); - } + layer->id != 0) + cfmuxl_remove_uplayer(layr, layer->id); + /* NOTE: ctrlcmd is not allowed to block */ layer->ctrlcmd(layer, ctrl, phyid); } -- cgit v0.10.2 From ba7605745d5c99f0e71b3ec6c7cb5ed6afe540ad Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 2 Feb 2012 01:21:03 +0000 Subject: caif: Bugfix double kfree_skb upon xmit failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SKB is freed twice upon send error. The Network stack consumes SKB even when it returns error code. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index a986280..a97d97a 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -539,8 +539,10 @@ static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); memset(skb->cb, 0, sizeof(struct caif_payload_info)); - if (cf_sk->layer.dn == NULL) + if (cf_sk->layer.dn == NULL) { + kfree_skb(skb); return -EINVAL; + } return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); } @@ -683,10 +685,10 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, } err = transmit_skb(skb, cf_sk, msg->msg_flags&MSG_DONTWAIT, timeo); - if (err < 0) { - kfree_skb(skb); + if (err < 0) + /* skb is already freed */ goto pipe_err; - } + sent += size; } -- cgit v0.10.2 From c0ca9bc508c1d8d612863c2481f28168e517da7d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 2 Feb 2012 14:23:34 -0200 Subject: usb: host: Distinguish Kconfig text for Freescale controllers Distinguish Kconfig text by providing the Freescale family name. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 91413ca..bc90769 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -130,7 +130,7 @@ config USB_FSL_MPH_DR_OF tristate config USB_EHCI_FSL - bool "Support for Freescale on-chip EHCI USB controller" + bool "Support for Freescale PPC on-chip EHCI USB controller" depends on USB_EHCI_HCD && FSL_SOC select USB_EHCI_ROOT_HUB_TT select USB_FSL_MPH_DR_OF if OF @@ -138,7 +138,7 @@ config USB_EHCI_FSL Variation of ARC USB block used in some Freescale chips. config USB_EHCI_MXC - bool "Support for Freescale on-chip EHCI USB controller" + bool "Support for Freescale i.MX on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MXC select USB_EHCI_ROOT_HUB_TT ---help--- -- cgit v0.10.2 From ed2833ac7ee86ee00f6dd3085a32752209fd8b6e Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Mon, 30 Jan 2012 11:44:23 +0800 Subject: usb: otg: mv_otg: Add dependence This otg driver depends on marvell EHCI driver, so add the dependence. It can fix the following build error on i386: ERROR: "usb_remove_hcd" [drivers/usb/otg/mv_otg.ko] undefined! ERROR: "usb_add_hcd" [drivers/usb/otg/mv_otg.ko] undefined! Signed-off-by: Neil Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 76d6293..735ef4c 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -118,7 +118,7 @@ config FSL_USB2_OTG config USB_MV_OTG tristate "Marvell USB OTG support" - depends on USB_MV_UDC && USB_SUSPEND + depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help -- cgit v0.10.2 From 529febeee680dc22416fca033151a5e8bc620447 Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Thu, 2 Feb 2012 11:23:14 +0800 Subject: powerpc/usb: fix issue of CPU halt when missing USB PHY clock when missing USB PHY clock, kernel booting up will halt during USB initialization. We should check USBGP[PHY_CLK_VALID] bit to avoid CPU hang in this case. Signed-off-by: Shengzhou Liu Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index b556a72..c26a82e 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -239,7 +239,7 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); } -static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) +static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) { struct usb_hcd *hcd = ehci_to_hcd(ehci); struct fsl_usb2_platform_data *pdata; @@ -299,12 +299,19 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) #endif out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); } + + if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & CTRL_PHY_CLK_VALID)) { + printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n"); + return -ENODEV; + } + return 0; } /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_fsl_reinit(struct ehci_hcd *ehci) { - ehci_fsl_usb_setup(ehci); + if (ehci_fsl_usb_setup(ehci)) + return -ENODEV; ehci_port_power(ehci, 0); return 0; diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 4918062..bdf43e2 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -45,5 +45,6 @@ #define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ #define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ #define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#define CTRL_PHY_CLK_VALID (1 << 17) #define SNOOP_SIZE_2GB 0x1e #endif /* _EHCI_FSL_H */ -- cgit v0.10.2 From e4436a7c17ac2b5e138f93f83a541cba9b311685 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Fri, 27 Jan 2012 20:27:32 +0530 Subject: usb: Skip PCI USB quirk handling for Netlogic XLP The Netlogic XLP SoC's on-chip USB controller appears as a PCI USB device, but does not need the EHCI/OHCI handoff done in usb/host/pci-quirks.c. The pci-quirks.c is enabled for all vendors and devices, and is enabled if USB and PCI are configured. If we do not skip the qurik handling on XLP, the readb() call in ehci_bios_handoff() will cause a crash since byte access is not supported for EHCI registers in XLP. Signed-off-by: Jayachandran C Cc: stable Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index caf8742..ac53a66 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -867,6 +867,12 @@ hc_init: static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) { + /* Skip Netlogic mips SoC's internal PCI USB controller. + * This device does not need/support EHCI/OHCI handoff + */ + if (pdev->vendor == 0x184e) /* vendor Netlogic */ + return; + if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) quirk_usb_handoff_uhci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) -- cgit v0.10.2 From 36f8ecbf717e343393643fe2302a1dfee14e780d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 27 Jan 2012 15:47:03 -0800 Subject: uwb & wusb & usb wireless controllers: fix kconfig error & build errors Fix kconfig warnings and build errors in UWB/WUSB/USB_HWA etc. by making all of these related symbols depend on UWB. warning: (USB_WHCI_HCD && USB_HWA_HCD) selects USB_WUSB which has unmet direct dependencies (USB_SUPPORT && EXPERIMENTAL && USB && PCI && UWB) warning: (USB_HWA_HCD) selects UWB_HWA which has unmet direct dependencies (UWB && USB) which lead to: ERROR: "uwb_rsv_establish" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_pal_register" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_rsv_get_usable_mas" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_rsv_destroy" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_radio_stop" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_rsv_terminate" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_pal_unregister" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_pal_init" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_rc_reset_all" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_radio_start" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_rsv_create" [drivers/usb/wusbcore/wusbcore.ko] undefined! ERROR: "uwb_rc_put" [drivers/usb/host/whci/whci-hcd.ko] undefined! ERROR: "uwb_rc_get_by_grandpa" [drivers/usb/host/whci/whci-hcd.ko] undefined! ERROR: "__umc_driver_register" [drivers/usb/host/whci/whci-hcd.ko] undefined! ERROR: "umc_driver_unregister" [drivers/usb/host/whci/whci-hcd.ko] undefined! ERROR: "whci_wait_for" [drivers/usb/host/whci/whci-hcd.ko] undefined! ERROR: "uwb_rc_get_by_grandpa" [drivers/usb/host/hwa-hc.ko] undefined! ERROR: "uwb_rc_put" [drivers/usb/host/hwa-hc.ko] undefined! Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index bc90769..353cdd4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -546,7 +546,7 @@ config USB_RENESAS_USBHS_HCD config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on PCI && USB + depends on PCI && USB && UWB select USB_WUSB select UWB_WHCI help @@ -559,7 +559,7 @@ config USB_WHCI_HCD config USB_HWA_HCD tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on USB + depends on USB && UWB select USB_WUSB select UWB_HWA help -- cgit v0.10.2 From 114fc47492e23d93653e4a16664833e98d62a563 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 11 Jan 2012 17:41:01 -0800 Subject: ceph: change "ceph.layout" xattr to be "ceph.file.layout" The virtual extended attribute named "ceph.layout" is meaningful only for regular files. Change its name to be "ceph.file.layout" to more directly reflect that in the ceph xattr namespace. Preserve the old "ceph.layout" name for the time being (until we decide it's safe to get rid of it entirely). Add a missing initializer for "readonly" in the terminating entry. Signed-off-by: Alex Elder Reviewed-by: Sage Weil diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index a5e36e4..9e6734e 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -111,8 +111,10 @@ static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, } static struct ceph_vxattr_cb ceph_file_vxattrs[] = { + { true, "ceph.file.layout", ceph_vxattrcb_layout}, + /* The following extended attribute name is deprecated */ { true, "ceph.layout", ceph_vxattrcb_layout}, - { NULL, NULL } + { true, NULL, NULL } }; static struct ceph_vxattr_cb *ceph_inode_vxattrs(struct inode *inode) -- cgit v0.10.2 From ab434b60ab07f8c44246b6fb0cddee436687a09a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 13 Jan 2012 22:22:03 -0800 Subject: ceph: initialize client debugfs outside of monc->mutex Initializing debufs under monc->mutex introduces a lock dependency for sb->s_type->i_mutex_key, which (combined with several other dependencies) leads to an annoying lockdep warning. There's no particular reason to do the debugfs setup under this lock, so move it out. It used to be the case that our first monmap could come from the OSD; that is no longer the case with recent servers, so we will reliably set up the client entry during the initial authentication. We don't have to worry about racing with debugfs teardown by ceph_debugfs_client_cleanup() because ceph_destroy_client() calls ceph_msgr_flush() first, which will wait for the message dispatch work to complete (and the debugfs init to complete). Fixes: #1940 Signed-off-by: Sage Weil diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 97f70e5..761ad9d 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -85,8 +85,6 @@ int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) } else { pr_info("client%lld fsid %pU\n", ceph_client_id(client), fsid); memcpy(&client->fsid, fsid, sizeof(*fsid)); - ceph_debugfs_client_init(client); - client->have_fsid = true; } return 0; } diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 0b62dea..1845cde 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -8,8 +8,8 @@ #include #include +#include #include - #include /* @@ -340,8 +340,19 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc, client->monc.monmap = monmap; kfree(old); + if (!client->have_fsid) { + client->have_fsid = true; + mutex_unlock(&monc->mutex); + /* + * do debugfs initialization without mutex to avoid + * creating a locking dependency + */ + ceph_debugfs_client_init(client); + goto out_unlocked; + } out: mutex_unlock(&monc->mutex); +out_unlocked: wake_up_all(&client->auth_wq); } -- cgit v0.10.2 From 32852a81bccd9e3d1953b894966393d1b546576d Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Sat, 14 Jan 2012 22:20:59 -0500 Subject: ceph: fix length validation in parse_reply_info() "len" is read from network and thus needs validation. Otherwise, given a bogus "len" value, p+len could be an out-of-bounds pointer, which is used in further parsing. Signed-off-by: Xi Wang Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6203d80..be1415fc 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -262,6 +262,7 @@ static int parse_reply_info(struct ceph_msg *msg, /* trace */ ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { + ceph_decode_need(&p, end, len, bad); err = parse_reply_info_trace(&p, p+len, info, features); if (err < 0) goto out_bad; @@ -270,6 +271,7 @@ static int parse_reply_info(struct ceph_msg *msg, /* extra */ ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { + ceph_decode_need(&p, end, len, bad); err = parse_reply_info_extra(&p, p+len, info, features); if (err < 0) goto out_bad; -- cgit v0.10.2 From d8fb02abdc39f92a1066313e2b17047876afa8f9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 12 Jan 2012 17:48:10 -0800 Subject: ceph: create a new session lock to avoid lock inversion Lockdep was reporting a possible circular lock dependency in dentry_lease_is_valid(). That function needs to sample the session's s_cap_gen and and s_cap_ttl fields coherently, but needs to do so while holding a dentry lock. The s_cap_lock field was being used to protect the two fields, but that can't be taken while holding a lock on a dentry within the session. In most cases, the s_cap_gen and s_cap_ttl fields only get operated on separately. But in three cases they need to be updated together. Implement a new lock to protect the spots updating both fields atomically is required. Signed-off-by: Alex Elder Reviewed-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 8b53193..90d789d 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -641,10 +641,10 @@ static int __cap_is_valid(struct ceph_cap *cap) unsigned long ttl; u32 gen; - spin_lock(&cap->session->s_cap_lock); + spin_lock(&cap->session->s_gen_ttl_lock); gen = cap->session->s_cap_gen; ttl = cap->session->s_cap_ttl; - spin_unlock(&cap->session->s_cap_lock); + spin_unlock(&cap->session->s_gen_ttl_lock); if (cap->cap_gen < gen || time_after_eq(jiffies, ttl)) { dout("__cap_is_valid %p cap %p issued %s " diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 9895400..63c52f3 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -975,10 +975,10 @@ static int dentry_lease_is_valid(struct dentry *dentry) di = ceph_dentry(dentry); if (di && di->lease_session) { s = di->lease_session; - spin_lock(&s->s_cap_lock); + spin_lock(&s->s_gen_ttl_lock); gen = s->s_cap_gen; ttl = s->s_cap_ttl; - spin_unlock(&s->s_cap_lock); + spin_unlock(&s->s_gen_ttl_lock); if (di->lease_gen == gen && time_before(jiffies, dentry->d_time) && diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index be1415fc..a4fdf93 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -400,9 +400,11 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; s->s_con.peer_name.num = cpu_to_le64(mds); - spin_lock_init(&s->s_cap_lock); + spin_lock_init(&s->s_gen_ttl_lock); s->s_cap_gen = 0; s->s_cap_ttl = 0; + + spin_lock_init(&s->s_cap_lock); s->s_renew_requested = 0; s->s_renew_seq = 0; INIT_LIST_HEAD(&s->s_caps); @@ -2328,10 +2330,10 @@ static void handle_session(struct ceph_mds_session *session, case CEPH_SESSION_STALE: pr_info("mds%d caps went stale, renewing\n", session->s_mds); - spin_lock(&session->s_cap_lock); + spin_lock(&session->s_gen_ttl_lock); session->s_cap_gen++; session->s_cap_ttl = 0; - spin_unlock(&session->s_cap_lock); + spin_unlock(&session->s_gen_ttl_lock); send_renew_caps(mdsc, session); break; diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index a50ca0e..8c7c04e 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -117,10 +117,13 @@ struct ceph_mds_session { void *s_authorizer_buf, *s_authorizer_reply_buf; size_t s_authorizer_buf_len, s_authorizer_reply_buf_len; - /* protected by s_cap_lock */ - spinlock_t s_cap_lock; + /* protected by s_gen_ttl_lock */ + spinlock_t s_gen_ttl_lock; u32 s_cap_gen; /* inc each time we get mds stale msg */ unsigned long s_cap_ttl; /* when session caps expire */ + + /* protected by s_cap_lock */ + spinlock_t s_cap_lock; struct list_head s_caps; /* all caps issued by this session */ int s_nr_caps, s_trim_caps; int s_num_cap_releases; -- cgit v0.10.2 From 97bb59a03dd6767fcc00be09b0c6d9e5294eeea6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 24 Jan 2012 10:08:36 -0600 Subject: rbd: fix a memory leak in rbd_get_client() If an existing rbd client is found to be suitable for use in rbd_get_client(), the rbd_options structure is not being freed as it should. Fix that. Signed-off-by: Alex Elder Signed-off-by: Sage Weil diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 148ab94..7d8f8dd 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -380,6 +380,7 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr, rbdc = __rbd_client_find(opt); if (rbdc) { ceph_destroy_options(opt); + kfree(rbd_opts); /* using an existing client */ kref_get(&rbdc->kref); -- cgit v0.10.2 From 8cdb878dcb359fd1137e9abdee9322f5e9bcfdf8 Mon Sep 17 00:00:00 2001 From: Christopher Yeoh Date: Thu, 2 Feb 2012 11:34:09 +1030 Subject: Fix race in process_vm_rw_core This fixes the race in process_vm_core found by Oleg (see http://article.gmane.org/gmane.linux.kernel/1235667/ for details). This has been updated since I last sent it as the creation of the new mm_access() function did almost exactly the same thing as parts of the previous version of this patch did. In order to use mm_access() even when /proc isn't enabled, we move it to kernel/fork.c where other related process mm access functions already are. Signed-off-by: Chris Yeoh Signed-off-by: Linus Torvalds diff --git a/fs/proc/base.c b/fs/proc/base.c index d9512bd..d4548dd 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -198,26 +198,6 @@ static int proc_root_link(struct dentry *dentry, struct path *path) return result; } -static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) -{ - struct mm_struct *mm; - int err; - - err = mutex_lock_killable(&task->signal->cred_guard_mutex); - if (err) - return ERR_PTR(err); - - mm = get_task_mm(task); - if (mm && mm != current->mm && - !ptrace_may_access(task, mode)) { - mmput(mm); - mm = ERR_PTR(-EACCES); - } - mutex_unlock(&task->signal->cred_guard_mutex); - - return mm; -} - struct mm_struct *mm_for_maps(struct task_struct *task) { return mm_access(task, PTRACE_MODE_READ); diff --git a/include/linux/sched.h b/include/linux/sched.h index 2234985..7d379a6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2259,6 +2259,12 @@ static inline void mmdrop(struct mm_struct * mm) extern void mmput(struct mm_struct *); /* Grab a reference to a task's mm, if it is not already going away */ extern struct mm_struct *get_task_mm(struct task_struct *task); +/* + * Grab a reference to a task's mm, if it is not already going away + * and ptrace_may_access with the mode parameter passed to it + * succeeds. + */ +extern struct mm_struct *mm_access(struct task_struct *task, unsigned int mode); /* Remove the current tasks stale references to the old mm_struct */ extern void mm_release(struct task_struct *, struct mm_struct *); /* Allocate a new mm structure and copy contents from tsk->mm */ diff --git a/kernel/fork.c b/kernel/fork.c index 051f090..1b2ef3c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -647,6 +647,26 @@ struct mm_struct *get_task_mm(struct task_struct *task) } EXPORT_SYMBOL_GPL(get_task_mm); +struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) +{ + struct mm_struct *mm; + int err; + + err = mutex_lock_killable(&task->signal->cred_guard_mutex); + if (err) + return ERR_PTR(err); + + mm = get_task_mm(task); + if (mm && mm != current->mm && + !ptrace_may_access(task, mode)) { + mmput(mm); + mm = ERR_PTR(-EACCES); + } + mutex_unlock(&task->signal->cred_guard_mutex); + + return mm; +} + /* Please note the differences between mmput and mm_release. * mmput is called whenever we stop holding onto a mm_struct, * error success whatever. diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c index e920aa3..c20ff48 100644 --- a/mm/process_vm_access.c +++ b/mm/process_vm_access.c @@ -298,23 +298,18 @@ static ssize_t process_vm_rw_core(pid_t pid, const struct iovec *lvec, goto free_proc_pages; } - task_lock(task); - if (__ptrace_may_access(task, PTRACE_MODE_ATTACH)) { - task_unlock(task); - rc = -EPERM; - goto put_task_struct; - } - mm = task->mm; - - if (!mm || (task->flags & PF_KTHREAD)) { - task_unlock(task); - rc = -EINVAL; + mm = mm_access(task, PTRACE_MODE_ATTACH); + if (!mm || IS_ERR(mm)) { + rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; + /* + * Explicitly map EACCES to EPERM as EPERM is a more a + * appropriate error code for process_vw_readv/writev + */ + if (rc == -EACCES) + rc = -EPERM; goto put_task_struct; } - atomic_inc(&mm->mm_users); - task_unlock(task); - for (i = 0; i < riovcnt && iov_l_curr_idx < liovcnt; i++) { rc = process_vm_rw_single_vec( (unsigned long)rvec[i].iov_base, rvec[i].iov_len, -- cgit v0.10.2 From 486d8b9f88cd0871a716e2f16873e811ee6c1ece Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Thu, 2 Feb 2012 12:55:58 -0800 Subject: IB/srpt: Use DEFINE_SPINLOCK()/LIST_HEAD() Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index cd5d05e..284cb50 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -69,8 +69,8 @@ MODULE_LICENSE("Dual BSD/GPL"); */ static u64 srpt_service_guid; -static spinlock_t srpt_dev_lock; /* Protects srpt_dev_list. */ -static struct list_head srpt_dev_list; /* List of srpt_device structures. */ +static DEFINE_SPINLOCK(srpt_dev_lock); /* Protects srpt_dev_list. */ +static LIST_HEAD(srpt_dev_list); /* List of srpt_device structures. */ static unsigned srp_max_req_size = DEFAULT_MAX_REQ_SIZE; module_param(srp_max_req_size, int, 0444); @@ -4010,9 +4010,6 @@ static int __init srpt_init_module(void) goto out; } - spin_lock_init(&srpt_dev_lock); - INIT_LIST_HEAD(&srpt_dev_list); - ret = -ENODEV; srpt_target = target_fabric_configfs_init(THIS_MODULE, "srpt"); if (!srpt_target) { -- cgit v0.10.2 From f225066b64eaffe3a51ee488fb750c82fbcd971c Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Thu, 2 Feb 2012 12:55:58 -0800 Subject: IB/srpt: Use ARRAY_SIZE() instead of open-coding Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 284cb50..38ef61d 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -3264,8 +3264,7 @@ static void srpt_add_one(struct ib_device *device) for (i = 0; i < sdev->srq_size; ++i) srpt_post_recv(sdev, sdev->ioctx_ring[i]); - WARN_ON(sdev->device->phys_port_cnt - > sizeof(sdev->port)/sizeof(sdev->port[0])); + WARN_ON(sdev->device->phys_port_cnt > ARRAY_SIZE(sdev->port)); for (i = 1; i <= sdev->device->phys_port_cnt; i++) { sport = &sdev->port[i - 1]; -- cgit v0.10.2 From ebfded8c4b34caea450709ce467e67483fa4d8df Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 2 Feb 2012 12:55:59 -0800 Subject: IB/srpt: Remove unneeded include Signed-off-by: Jesper Juhl Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h index b4b4bbc..61e52b8 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.h +++ b/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -35,7 +35,6 @@ #ifndef IB_SRPT_H #define IB_SRPT_H -#include #include #include #include -- cgit v0.10.2 From d23a4b3fd6ef70b80411b39b8c8bc548a219ce70 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 29 Jan 2012 13:57:43 -0600 Subject: rbd: fix safety of rbd_put_client() The rbd_client structure uses a kref to arrange for cleaning up and freeing an instance when its last reference is dropped. The cleanup routine is rbd_client_release(), and one of the things it does is delete the rbd_client from rbd_client_list. It acquires node_lock to do so, but the way it is done is still not safe. The problem is that when attempting to reuse an existing rbd_client, the structure found might already be in the process of getting destroyed and cleaned up. Here's the scenario, with "CLIENT" representing an existing rbd_client that's involved in the race: Thread on CPU A | Thread on CPU B --------------- | --------------- rbd_put_client(CLIENT) | rbd_get_client() kref_put() | (acquires node_lock) kref->refcount becomes 0 | __rbd_client_find() returns CLIENT calls rbd_client_release() | kref_get(&CLIENT->kref); | (releases node_lock) (acquires node_lock) | deletes CLIENT from list | ...and starts using CLIENT... (releases node_lock) | and frees CLIENT | <-- but CLIENT gets freed here Fix this by having rbd_put_client() acquire node_lock. The result could still be improved, but at least it avoids this problem. Signed-off-by: Alex Elder Signed-off-by: Sage Weil diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7d8f8dd..7f40cb4 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -407,15 +407,15 @@ done_err: /* * Destroy ceph client + * + * Caller must hold node_lock. */ static void rbd_client_release(struct kref *kref) { struct rbd_client *rbdc = container_of(kref, struct rbd_client, kref); dout("rbd_release_client %p\n", rbdc); - spin_lock(&node_lock); list_del(&rbdc->node); - spin_unlock(&node_lock); ceph_destroy_client(rbdc->client); kfree(rbdc->rbd_opts); @@ -428,7 +428,9 @@ static void rbd_client_release(struct kref *kref) */ static void rbd_put_client(struct rbd_device *rbd_dev) { + spin_lock(&node_lock); kref_put(&rbd_dev->rbd_client->kref, rbd_client_release); + spin_unlock(&node_lock); rbd_dev->rbd_client = NULL; rbd_dev->client = NULL; } -- cgit v0.10.2 From de47a4176c532ef5961b8a46a2d541a3517412d3 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Thu, 2 Feb 2012 15:28:28 -0600 Subject: cifs: Fix oops in session setup code for null user mounts For null user mounts, do not invoke string length function during session setup. Cc: Acked-by: Jeff Layton Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index eb76741..551d0c2 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -246,16 +246,15 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, /* copy user */ /* BB what about null user mounts - check that we do this BB */ /* copy user */ - if (ses->user_name != NULL) + if (ses->user_name != NULL) { strncpy(bcc_ptr, ses->user_name, MAX_USERNAME_SIZE); + bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); + } /* else null user mount */ - - bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); *bcc_ptr = 0; bcc_ptr++; /* account for null termination */ /* copy domain */ - if (ses->domainName != NULL) { strncpy(bcc_ptr, ses->domainName, 256); bcc_ptr += strnlen(ses->domainName, 256); -- cgit v0.10.2 From 6e9d554fa6e481a848358c215f129432262123c0 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 12 Dec 2011 16:09:28 +0100 Subject: can: flexcan: fix irq flooding by clearing all interrupt sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As pointed out by Reuben Dowle and Lothar Waßmann, the TWRN_INT, RWRN_INT, BOFF_INT interrupt sources need to be cleared as well to avoid interrupt flooding, at least for the Flexcan on i.MX28 SOCs. Furthermore, the interrupts are only cleared, if really one of those interrupt sources are pending (which is not the case for rx and tx done). Cc: Reuben Dowle Cc: Lothar Waßmann Signed-off-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 7fd8089..96d2357 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -118,6 +118,9 @@ (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | FLEXCAN_ESR_BOFF_INT) #define FLEXCAN_ESR_ERR_ALL \ (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) +#define FLEXCAN_ESR_ALL_INT \ + (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ + FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) /* FLEXCAN interrupt flag register (IFLAG) bits */ #define FLEXCAN_TX_BUF_ID 8 @@ -577,7 +580,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) reg_iflag1 = flexcan_read(®s->iflag1); reg_esr = flexcan_read(®s->esr); - flexcan_write(FLEXCAN_ESR_ERR_INT, ®s->esr); /* ACK err IRQ */ + /* ACK all bus error and state change IRQ sources */ + if (reg_esr & FLEXCAN_ESR_ALL_INT) + flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); /* * schedule NAPI in case of: -- cgit v0.10.2 From 7bb4db93ae59e0faf810a83a8578f56bc968ab01 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 12 Dec 2011 16:07:16 +0100 Subject: can: cc770: store echo skb before starting the transfer On slow systems and high CAN bitrates, the error message "can_put_echo_skb: BUG! echo_skb is occupied!" did show up because can_put_echo_skb() was called after starting the transfer. Signed-off-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c index 7668967..c30f0e6 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c @@ -440,12 +440,14 @@ static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) for (i = 0; i < dlc; i++) cc770_write_reg(priv, msgobj[mo].data[i], cf->data[i]); + /* Store echo skb before starting the transfer */ + can_put_echo_skb(skb, dev, 0); + cc770_write_reg(priv, msgobj[mo].ctrl1, RMTPND_RES | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); stats->tx_bytes += dlc; - can_put_echo_skb(skb, dev, 0); /* * HM: We had some cases of repeated IRQs so make sure the -- cgit v0.10.2 From 2d5091e08c684912ed6b9ca03d1f7b01501b7bf6 Mon Sep 17 00:00:00 2001 From: Wolfgang Zarre Date: Sun, 15 Jan 2012 13:21:34 +0100 Subject: can: cc770: Fix indirect access deadlock on ISA cards This fix avoids a deadlock if an interrupt occurs during consecutive port operations on ISA cards utilising indirect access via address and data port. Tested on a B&R ISA card. Cc: linux-can@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Wolfgang Zarre Acked-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c index 4be5fe2..9f3a25c 100644 --- a/drivers/net/can/cc770/cc770_isa.c +++ b/drivers/net/can/cc770/cc770_isa.c @@ -110,6 +110,11 @@ MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])"); #define CC770_IOSIZE 0x20 #define CC770_IOSIZE_INDIRECT 0x02 +/* Spinlock for cc770_isa_port_write_reg_indirect + * and cc770_isa_port_read_reg_indirect + */ +static DEFINE_SPINLOCK(cc770_isa_port_lock); + static struct platform_device *cc770_isa_devs[MAXDEV]; static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg) @@ -138,18 +143,27 @@ static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv, int reg) { unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags; + u8 val; + spin_lock_irqsave(&cc770_isa_port_lock, flags); outb(reg, base); - return inb(base + 1); + val = inb(base + 1); + spin_unlock_irqrestore(&cc770_isa_port_lock, flags); + + return val; } static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv, int reg, u8 val) { unsigned long base = (unsigned long)priv->reg_base; + unsigned long flags; + spin_lock_irqsave(&cc770_isa_port_lock, flags); outb(reg, base); outb(val, base + 1); + spin_unlock_irqrestore(&cc770_isa_port_lock, flags); } static int __devinit cc770_isa_probe(struct platform_device *pdev) -- cgit v0.10.2 From e3f240f460a36b158217944b52a85f304914c1a6 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Wed, 1 Feb 2012 10:50:23 +0100 Subject: can: ti_hecc: use netif_rx in the interrupt This patch fixes locking problems by using netif_rx() instead of netif_receive_skb() in ti_hecc_error() called from the interrupt context. Signed-off-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index df809e3..5a2e1e3 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -745,9 +745,10 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, } } - netif_receive_skb(skb); + netif_rx(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + return 0; } -- cgit v0.10.2 From 44b0052c5cb4e75389ed3eb9e98c29295a7dadfb Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Mon, 12 Dec 2011 02:16:20 -0500 Subject: can: pch_can: fix error passive level test The test (((errc & PCH_REC) >> 8) > 127) would always be false because the receive error counter ((errc & PCH_REC) >> 8) is at most 127, where PCH_REC is defined as 0x7f00. To test whether the receive error counter has reached the error passive level, the RP bit (15) should be used. Signed-off-by: Xi Wang Acked-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c index d11fbb2..6edc25e 100644 --- a/drivers/net/can/pch_can.c +++ b/drivers/net/can/pch_can.c @@ -66,6 +66,7 @@ #define PCH_IF_CREQ_BUSY BIT(15) #define PCH_STATUS_INT 0x8000 +#define PCH_RP 0x00008000 #define PCH_REC 0x00007f00 #define PCH_TEC 0x000000ff @@ -527,7 +528,7 @@ static void pch_can_error(struct net_device *ndev, u32 status) priv->can.can_stats.error_passive++; state = CAN_STATE_ERROR_PASSIVE; cf->can_id |= CAN_ERR_CRTL; - if (((errc & PCH_REC) >> 8) > 127) + if (errc & PCH_RP) cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; if ((errc & PCH_TEC) > 127) cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; -- cgit v0.10.2 From d0a71a7e6de0e0ce9f86c8ba6e13414a9df63e0b Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Thu, 22 Dec 2011 23:57:49 +0100 Subject: can: ems_usb: Removed double netif_device_detach netif_device_attched is called twice when ems_usb_start fails with -ENODEV Signed-off-by: Sebastian Haas Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 9697c14..7dae64d 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -627,9 +627,6 @@ static int ems_usb_start(struct ems_usb *dev) err = usb_submit_urb(urb, GFP_KERNEL); if (err) { - if (err == -ENODEV) - netif_device_detach(dev->netdev); - usb_unanchor_urb(urb); usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, urb->transfer_dma); @@ -659,9 +656,6 @@ static int ems_usb_start(struct ems_usb *dev) err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); if (err) { - if (err == -ENODEV) - netif_device_detach(dev->netdev); - dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n", err); @@ -692,9 +686,6 @@ static int ems_usb_start(struct ems_usb *dev) return 0; failed: - if (err == -ENODEV) - netif_device_detach(dev->netdev); - dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err); return err; -- cgit v0.10.2 From 29830406415c227a54af429d7b300aabd4754237 Mon Sep 17 00:00:00 2001 From: Stephane Grosjean Date: Wed, 1 Feb 2012 11:05:48 +0100 Subject: can: peak_pci: Fix the way channels are linked together Change the way channels objects are linked together by peak_pci_probe() avoiding any kernel oops when driver is removed. Side effect is that the list is now browsed from last to first channel. Signed-off-by: Stephane Grosjean Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index 2c7f503..2147959 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -39,9 +39,9 @@ MODULE_LICENSE("GPL v2"); #define DRV_NAME "peak_pci" struct peak_pci_chan { - void __iomem *cfg_base; /* Common for all channels */ - struct net_device *next_dev; /* Chain of network devices */ - u16 icr_mask; /* Interrupt mask for fast ack */ + void __iomem *cfg_base; /* Common for all channels */ + struct net_device *prev_dev; /* Chain of network devices */ + u16 icr_mask; /* Interrupt mask for fast ack */ }; #define PEAK_PCI_CAN_CLOCK (16000000 / 2) @@ -98,7 +98,7 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, { struct sja1000_priv *priv; struct peak_pci_chan *chan; - struct net_device *dev, *dev0 = NULL; + struct net_device *dev; void __iomem *cfg_base, *reg_base; u16 sub_sys_id, icr; int i, err, channels; @@ -196,18 +196,14 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, } /* Create chain of SJA1000 devices */ - if (i == 0) - dev0 = dev; - else - chan->next_dev = dev; + chan->prev_dev = pci_get_drvdata(pdev); + pci_set_drvdata(pdev, dev); dev_info(&pdev->dev, "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", dev->name, priv->reg_base, chan->cfg_base, dev->irq); } - pci_set_drvdata(pdev, dev0); - /* Enable interrupts */ writew(icr, cfg_base + PITA_ICR + 2); @@ -217,12 +213,11 @@ failure_remove_channels: /* Disable interrupts */ writew(0x0, cfg_base + PITA_ICR + 2); - for (dev = dev0; dev; dev = chan->next_dev) { + for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { unregister_sja1000dev(dev); free_sja1000dev(dev); priv = netdev_priv(dev); chan = priv->priv; - dev = chan->next_dev; } pci_iounmap(pdev, reg_base); @@ -241,7 +236,7 @@ failure_disable_pci: static void __devexit peak_pci_remove(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); /* First device */ + struct net_device *dev = pci_get_drvdata(pdev); /* Last device */ struct sja1000_priv *priv = netdev_priv(dev); struct peak_pci_chan *chan = priv->priv; void __iomem *cfg_base = chan->cfg_base; @@ -255,7 +250,7 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) dev_info(&pdev->dev, "removing device %s\n", dev->name); unregister_sja1000dev(dev); free_sja1000dev(dev); - dev = chan->next_dev; + dev = chan->prev_dev; if (!dev) break; priv = netdev_priv(dev); -- cgit v0.10.2 From ff05f603c3238010769787f3ba54c48c290ed3e5 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 2 Feb 2012 15:29:08 -0800 Subject: include/linux/lp8727.h: Remove executable bit Signed-off-by: Josh Triplett Signed-off-by: Linus Torvalds diff --git a/include/linux/lp8727.h b/include/linux/lp8727.h old mode 100755 new mode 100644 -- cgit v0.10.2 From 321bf4ed5ff5f7c62ef59f33b7eec5b154391f0a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 30 Jan 2012 13:57:12 -0800 Subject: drivers/base/memory.c: fix memory_dev_init() long delay One system with 2048g ram, reported soft lockup on recent kernel. [ 34.426749] cpu_dev_init done [ 61.166399] BUG: soft lockup - CPU#0 stuck for 22s! [swapper/0:1] [ 61.166733] Modules linked in: [ 61.166904] irq event stamp: 1935610 [ 61.178431] hardirqs last enabled at (1935609): [] mutex_lock_nested+0x299/0x2b4 [ 61.178923] hardirqs last disabled at (1935610): [] apic_timer_interrupt+0x6b/0x80 [ 61.198767] softirqs last enabled at (1935476): [] __do_softirq+0x195/0x1ab [ 61.218604] softirqs last disabled at (1935471): [] call_softirq+0x1c/0x30 [ 61.238408] CPU 0 [ 61.238549] Modules linked in: [ 61.238744] [ 61.238825] Pid: 1, comm: swapper/0 Not tainted 3.3.0-rc1-tip-yh-02076-g962f689-dirty #171 [ 61.278212] RIP: 0010:[] [] lock_release+0x90/0x9c [ 61.278627] RSP: 0018:ffff883f64dbfd70 EFLAGS: 00000246 [ 61.298287] RAX: ffff883f64dc0000 RBX: 0000000000000000 RCX: 000000000000008b [ 61.298690] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 61.318383] RBP: ffff883f64dbfda0 R08: 0000000000000001 R09: 000000000000008b [ 61.338215] R10: 0000000000000000 R11: 0000000000000000 R12: ffff883f64dbfd10 [ 61.338610] R13: ffff883f64dc0708 R14: ffff883f64dc0708 R15: ffffffff81095657 [ 61.358299] FS: 0000000000000000(0000) GS:ffff883f7d600000(0000) knlGS:0000000000000000 [ 61.378118] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 61.378450] CR2: 0000000000000000 CR3: 00000000024af000 CR4: 00000000000007f0 [ 61.398144] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 61.417918] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 61.418260] Process swapper/0 (pid: 1, threadinfo ffff883f64dbe000, task ffff883f64dc0000) [ 61.445358] Stack: [ 61.445511] 0000000000000002 ffff897f649ba168 ffff883f64dbfe10 ffff88ff64bb57a8 [ 61.458040] 0000000000000000 0000000000000000 ffff883f64dbfdc0 ffffffff81ceb1b4 [ 61.458491] 000000000011608c ffff88ff64bb58a8 ffff883f64dbfdf0 ffffffff81c57638 [ 61.478215] Call Trace: [ 61.478367] [] _raw_spin_unlock+0x21/0x2e [ 61.497994] [] klist_next+0x9e/0xbc [ 61.498264] [] next_device+0xe/0x1e [ 61.517867] [] subsys_find_device_by_id+0xb7/0xd6 [ 61.518197] [] find_memory_block_hinted+0x3d/0x66 [ 61.537927] [] find_memory_block+0x10/0x12 [ 61.538193] [] add_memory_section+0x35/0x9e [ 61.557932] [] memory_dev_init+0x68/0xda [ 61.558227] [] driver_init+0x97/0xa7 [ 61.577853] [] kernel_init+0xf6/0x1c0 [ 61.578140] [] kernel_thread_helper+0x4/0x10 [ 61.597850] [] ? retint_restore_args+0xe/0xe [ 61.598144] [] ? start_kernel+0x3ab/0x3ab [ 61.617826] [] ? gs_change+0xb/0xb [ 61.618060] Code: 10 48 83 3b 00 eb e8 4c 89 f2 44 89 fe 4c 89 ef e8 e1 fe ff ff 65 48 8b 04 25 40 bc 00 00 c7 80 cc 06 00 00 00 00 00 00 41 54 9d <5e> 5b 41 5c 41 5d 41 5e 41 5f 5d c3 55 48 89 e5 41 57 41 89 cf [ 89.285380] memory_dev_init done Finally it takes about 55s to create 16400 memory entries. Root cause: for x86_64, 2048g (with 2g hole at [2g,4g), and TOP2 will be 2050g), will have 16400 memory block. find_memory_block/subsys_find_device_by_id will be expensive with that many entries. Actually, we don't need to find that memory block for BOOT path. Skip that finding make it get back to normal. [ 34.466696] cpu_dev_init done [ 35.290080] memory_dev_init done Also solved the delay with topology_init when sections_per_block is not 1. Signed-off-by: Yinghai Lu Cc: Kay Sievers Cc: Nathan Fontenot Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/memory.c b/drivers/base/memory.c index ed5de58..9e60dbe 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -572,19 +572,36 @@ static int init_memory_block(struct memory_block **memory, } static int add_memory_section(int nid, struct mem_section *section, + struct memory_block **mem_p, unsigned long state, enum mem_add_context context) { - struct memory_block *mem; + struct memory_block *mem = NULL; + int scn_nr = __section_nr(section); int ret = 0; mutex_lock(&mem_sysfs_mutex); - mem = find_memory_block(section); + if (context == BOOT) { + /* same memory block ? */ + if (mem_p && *mem_p) + if (scn_nr >= (*mem_p)->start_section_nr && + scn_nr <= (*mem_p)->end_section_nr) { + mem = *mem_p; + kobject_get(&mem->dev.kobj); + } + } else + mem = find_memory_block(section); + if (mem) { mem->section_count++; kobject_put(&mem->dev.kobj); - } else + } else { ret = init_memory_block(&mem, section, state); + /* store memory_block pointer for next loop */ + if (!ret && context == BOOT) + if (mem_p) + *mem_p = mem; + } if (!ret) { if (context == HOTPLUG && @@ -627,7 +644,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, */ int register_new_memory(int nid, struct mem_section *section) { - return add_memory_section(nid, section, MEM_OFFLINE, HOTPLUG); + return add_memory_section(nid, section, NULL, MEM_OFFLINE, HOTPLUG); } int unregister_memory_section(struct mem_section *section) @@ -647,6 +664,7 @@ int __init memory_dev_init(void) int ret; int err; unsigned long block_sz; + struct memory_block *mem = NULL; ret = subsys_system_register(&memory_subsys, NULL); if (ret) @@ -662,7 +680,10 @@ int __init memory_dev_init(void) for (i = 0; i < NR_MEM_SECTIONS; i++) { if (!present_section_nr(i)) continue; - err = add_memory_section(0, __nr_to_section(i), MEM_ONLINE, + /* don't need to reuse memory_block if only one per block */ + err = add_memory_section(0, __nr_to_section(i), + (sections_per_block == 1) ? NULL : &mem, + MEM_ONLINE, BOOT); if (!ret) ret = err; diff --git a/drivers/base/node.c b/drivers/base/node.c index 44f427a..90aa2a1 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -456,7 +456,15 @@ static int link_mem_sections(int nid) if (!present_section_nr(section_nr)) continue; mem_sect = __nr_to_section(section_nr); + + /* same memblock ? */ + if (mem_blk) + if ((section_nr >= mem_blk->start_section_nr) && + (section_nr <= mem_blk->end_section_nr)) + continue; + mem_blk = find_memory_block_hinted(mem_sect, mem_blk); + ret = register_mem_sect_under_node(mem_blk, nid); if (!err) err = ret; -- cgit v0.10.2 From 26417d475709adeddf1eedfc05ad5129b3ba7da6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 1 Feb 2012 18:15:49 -0800 Subject: docbook: fix fatal errors in device-drivers docbook and add DMA Management section Fix 2 fatal errors in the device-drivers docbook. Also add some missing files from drivers/base/; since several of these are DMA-related, add a section for DMA Management. docproc: drivers/base/sys.c: No such file or directory docproc: drivers/tty/serial/8250.c: No such file or directory Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index 2f7fd43..9c27e51 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -102,9 +102,12 @@ X!Iinclude/linux/kobject.h !Iinclude/linux/device.h Device Drivers Base +!Idrivers/base/init.c !Edrivers/base/driver.c !Edrivers/base/core.c +!Edrivers/base/syscore.c !Edrivers/base/class.c +!Idrivers/base/node.c !Edrivers/base/firmware_class.c !Edrivers/base/transport_class.c -!Edrivers/base/sys.c +!Edrivers/base/dd.c @@ -121,6 +124,11 @@ X!Edrivers/base/interface.c !Edrivers/base/platform.c !Edrivers/base/bus.c + Device Drivers DMA Management +!Edrivers/base/dma-buf.c +!Edrivers/base/dma-coherent.c +!Edrivers/base/dma-mapping.c + Device Drivers Power Management !Edrivers/base/power/main.c @@ -219,7 +227,7 @@ X!Isound/sound_firmware.c 16x50 UART Driver !Edrivers/tty/serial/serial_core.c -!Edrivers/tty/serial/8250.c +!Edrivers/tty/serial/8250/8250.c -- cgit v0.10.2 From 1bab08c075e250b0a9bec63fd4c06812bd413a3f Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Mon, 30 Jan 2012 13:08:11 -0800 Subject: drivers/tty/vt/vt_ioctl.c: fix KDFONTOP 32bit compatibility layer KDFONTOP(GET) currently fails with EIO when being run in a 32bit userland with a 64bit kernel if the font width is not 8. This is because the compatibility layer introduced by e9216651 ("tty: handle VT specific compat ioctls in vt driver") forces the addition of the KD_FONT_FLAG_OLD flag, which makes con_font_get return EIO in such case. This flag should *not* be set for KDFONTOP, since it's actually the whole point of this flag (see comment in con_font_set for instance). Signed-off-by: Samuel Thibault Cc: Arnd Bergmann Cc: Arthur Taylor Cc: Jiri Slaby Cc: Jiri Olsa Cc: stable Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 5e096f4..65447c5 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -1463,7 +1463,6 @@ compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, if (!perm && op->op != KD_FONT_OP_GET) return -EPERM; op->data = compat_ptr(((struct compat_console_font_op *)op)->data); - op->flags |= KD_FONT_FLAG_OLD; i = con_font_op(vc, op); if (i) return i; -- cgit v0.10.2 From 5f7b6d19723427a1d406c9640fe6f48fa97b5e1a Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Wed, 1 Feb 2012 00:11:23 +0900 Subject: serial: samsung: Add support for EXYNOS4212 and EXYNOS4412 This should be added for EXYNOS4212 and EXYNOS4412 SoCs. Cc: Thomas Abraham Cc: Greg Kroah-Hartman Cc: stable Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index f96f37b..3b07fb9 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1593,7 +1593,8 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif -#ifdef CONFIG_CPU_EXYNOS4210 +#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \ + defined(CONFIG_SOC_EXYNOS4412) static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung Exynos4 UART", -- cgit v0.10.2 From 7ccfe015a6e7f01734963f51b0f67126e21068ce Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Wed, 1 Feb 2012 00:39:11 +0900 Subject: serial: samsung: Add support for EXYNOS5250 Cc: Thomas Abraham Cc: Greg Kroah-Hartman Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 3b07fb9..c55e5fb 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1594,7 +1594,7 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #endif #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \ - defined(CONFIG_SOC_EXYNOS4412) + defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250) static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung Exynos4 UART", -- cgit v0.10.2 From 3e5ba466d515eb3fb1e0e975a3d8e499126ae2b5 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Mon, 30 Jan 2012 23:00:08 +0100 Subject: vmw_balloon: fix for a -Wuninitialized warning Fix for a -Wuninitialized compiler warning. Changed return value of vmballoon_send_lock_page() from bool to int to be able to distinguish between the error cases to avoid uninitialized use of hv_status in vmballoon_reserve_page() Signed-off-by: Danny Kukawka Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index cd41d40..cb56e27 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -314,7 +314,7 @@ static bool vmballoon_send_get_target(struct vmballoon *b, u32 *new_target) * fear that guest will need it. Host may reject some pages, we need to * check the return value and maybe submit a different page. */ -static bool vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, +static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, unsigned int *hv_status) { unsigned long status, dummy; @@ -322,17 +322,17 @@ static bool vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, pfn32 = (u32)pfn; if (pfn32 != pfn) - return false; + return -1; STATS_INC(b->stats.lock); *hv_status = status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy); if (vmballoon_check_status(b, status)) - return true; + return 0; pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); STATS_INC(b->stats.lock_fail); - return false; + return 1; } /* @@ -411,7 +411,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) struct page *page; gfp_t flags; unsigned int hv_status; - bool locked = false; + int locked; flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP; do { @@ -431,7 +431,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) /* inform monitor */ locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status); - if (!locked) { + if (locked > 0) { STATS_INC(b->stats.refused_alloc); if (hv_status == VMW_BALLOON_ERROR_RESET || @@ -449,7 +449,7 @@ static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) if (++b->n_refused_pages >= VMW_BALLOON_MAX_REFUSED) return -EIO; } - } while (!locked); + } while (locked != 0); /* track allocated page */ list_add(&page->lru, &b->pages); -- cgit v0.10.2 From 474de3bbadd9cb75ffc32cc759c40d868343d46c Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Thu, 2 Feb 2012 14:20:29 +0100 Subject: cs5535-mfgpt: don't call __init function from __devinit Fix scan_timers() to be __devinit and not __init since the function get called from cs5535_mfgpt_probe which is __devinit. Signed-off-by: Danny Kukawka Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c index bc685bf..87a390d 100644 --- a/drivers/misc/cs5535-mfgpt.c +++ b/drivers/misc/cs5535-mfgpt.c @@ -262,7 +262,7 @@ static void __init reset_all_timers(void) * In other cases (such as with VSAless OpenFirmware), the system firmware * leaves timers available for us to use. */ -static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt) +static int __devinit scan_timers(struct cs5535_mfgpt_chip *mfgpt) { struct cs5535_mfgpt_timer timer = { .chip = mfgpt }; unsigned long flags; -- cgit v0.10.2 From b5266ea675c5a041e2852c7ccec4cf2d4f5e0cf4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 1 Feb 2012 12:31:47 +0800 Subject: mmc: cb710 core: Add missing spin_lock_init for irq_lock of struct cb710_chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Axel Lin Acked-by: MichaÅ‚ MirosÅ‚aw Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 68cd05b..85cc771 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -245,6 +245,7 @@ static int __devinit cb710_probe(struct pci_dev *pdev, if (err) return err; + spin_lock_init(&chip->irq_lock); chip->pdev = pdev; chip->iobase = pcim_iomap_table(pdev)[0]; -- cgit v0.10.2 From 84f2b9b2edc09595569c7397cc3c888764ffd78b Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 2 Feb 2012 12:04:01 +0100 Subject: perf: Remove deprecated WARN_ON_ONCE() With the new throttling/unthrottling code introduced with commit: e050e3f0a71b ("perf: Fix broken interrupt rate throttling") we occasionally hit two WARN_ON_ONCE() checks in: - intel_pmu_pebs_enable() - intel_pmu_lbr_enable() - x86_pmu_start() The assertions are no longer problematic. There is a valid path where they can trigger but it is harmless. The assertion can be triggered with: $ perf record -e instructions:pp .... Leading to paths: intel_pmu_pebs_enable intel_pmu_enable_event x86_perf_event_set_period x86_pmu_start perf_adjust_freq_unthr_context perf_event_task_tick scheduler_tick And: intel_pmu_lbr_enable intel_pmu_enable_event x86_perf_event_set_period x86_pmu_start perf_adjust_freq_unthr_context. perf_event_task_tick scheduler_tick cpuc->enabled is always on because when we get to perf_adjust_freq_unthr_context() the PMU is not totally disabled. Furthermore when we need to adjust a period, we only stop the event we need to change and not the entire PMU. Thus, when we re-enable, cpuc->enabled is already set. Note that when we stop the event, both pebs and lbr are stopped if necessary (and possible). Signed-off-by: Stephane Eranian Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/20120202110401.GA30911@quad Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 5adce10..2a30e5a 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -986,9 +986,6 @@ static void x86_pmu_start(struct perf_event *event, int flags) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx = event->hw.idx; - if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) - return; - if (WARN_ON_ONCE(idx == -1)) return; diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 73da6b6..d6bd49f 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -439,7 +439,6 @@ void intel_pmu_pebs_enable(struct perf_event *event) hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; cpuc->pebs_enabled |= 1ULL << hwc->idx; - WARN_ON_ONCE(cpuc->enabled); if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1) intel_pmu_lbr_enable(event); diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 3fab3de..47a7e63 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -72,8 +72,6 @@ void intel_pmu_lbr_enable(struct perf_event *event) if (!x86_pmu.lbr_nr) return; - WARN_ON_ONCE(cpuc->enabled); - /* * Reset the LBR stack if we changed task context to * avoid data leaks. -- cgit v0.10.2 From 976d98cb726601972fafb28173d4526ce3603415 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 3 Feb 2012 15:14:17 +0800 Subject: usb: musb: fix a build error on mips On mips, we got: drivers/usb/musb/musb_io.h:44: error: conflicting types for 'readsl' arch/mips/include/asm/io.h:529: error: previous definition of 'readsl' was here drivers/usb/musb/musb_io.h:46: error: conflicting types for 'readsw' arch/mips/include/asm/io.h:528: error: previous definition of 'readsw' was here drivers/usb/musb/musb_io.h:48: error: conflicting types for 'readsb' so, should add !defined(CONFIG_MIPS) too. Cc: Felipe Balbi Cc: Greg Kroah-Hartman Signed-off-by: WANG Cong Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index e61aa95..1d5eda2 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -39,7 +39,8 @@ #if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \ && !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \ - && !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) + && !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) \ + && !defined(CONFIG_MIPS) static inline void readsl(const void __iomem *addr, void *buf, int len) { insl((unsigned long)addr, buf, len); } static inline void readsw(const void __iomem *addr, void *buf, int len) -- cgit v0.10.2 From 9c0a835a9d9aed41bcf9c287f5069133a6e2a87b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 31 Jan 2012 16:43:50 -0800 Subject: usb: ch9.h: usb_endpoint_maxp() uses __le16_to_cpu() The usb/ch9.h will be installed to /usr/include/linux, and be used from user space. But le16_to_cpu() is only defined for kernel code. Without this patch, user space compile will be broken. Special thanks to Stefan Becker Cc: stable@vger.kernel.org # 3.2 Reported-by: Stefan Becker Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 61b2905..3b6f628 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -589,7 +589,7 @@ static inline int usb_endpoint_is_isoc_out( */ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) { - return le16_to_cpu(epd->wMaxPacketSize); + return __le16_to_cpu(epd->wMaxPacketSize); } /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 82b982c9a697e7be0745523a53334fe38a4582c8 Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Fri, 3 Feb 2012 00:19:07 -0800 Subject: Input: i8042 - add Lenovo Ideapad U455 to 'reset' blacklist From 2d5a38a56453421e82428155f4b00303f3fb19b2 Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Wed, 1 Feb 2012 03:11:53 +0400 Subject: [PATCH] Input: i8042 - add Lenovo Ideapad U455 to 'reset' blacklist Lenovo Ideapad U455 needs to be in the reset quirk list for its touchpad's proper function. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=40672 Signed-off-by: Igor Murzov Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index b4cfc6c..5ec774d 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -512,6 +512,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), }, }, + { + /* Lenovo Ideapad U455 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20046"), + }, + }, { } }; -- cgit v0.10.2 From 08bc3d4e67d486a9b2a5065c1c2ebaf13048a465 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 30 Jan 2012 12:05:55 +0100 Subject: drivers/gpu/drm/drm_ioc32.c: initialize all fields The c32 structure is allocated on the stack and its idx field is not initialized before copying it to user level. This patch takes the value from the result of the ioctl, as done for the other fields. Signed-off-by: Julia Lawall Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index ddd70db..637fcc3 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -315,7 +315,8 @@ static int compat_drm_getclient(struct file *file, unsigned int cmd, if (err) return err; - if (__get_user(c32.auth, &client->auth) + if (__get_user(c32.idx, &client->idx) + || __get_user(c32.auth, &client->auth) || __get_user(c32.pid, &client->pid) || __get_user(c32.uid, &client->uid) || __get_user(c32.magic, &client->magic) -- cgit v0.10.2 From 3fe89a0c799c62de68e9e6d6e33899cd7e0da901 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 2 Feb 2012 21:23:11 +0100 Subject: drm/radeon: do not continue after error from r600_ib_test This return statement got dropped while fixing the conflicts introduced in 7a7e8734ac3. Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index ae09fe8..9be353b 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -3191,6 +3191,7 @@ static int evergreen_startup(struct radeon_device *rdev) if (r) { DRM_ERROR("radeon: failed testing IB (%d).\n", r); rdev->accel_working = false; + return r; } r = r600_audio_init(rdev); -- cgit v0.10.2 From 9497182051f261fe688bb2d672fdbc07ab3d5348 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 6 Jan 2012 03:24:16 +0000 Subject: ixgbe: update copyright to 2012 New year so bump the copyright date. Signed-off-by: Don Skidmore Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/Makefile b/drivers/net/ethernet/intel/ixgbe/Makefile index 7d7387f..7a16177 100644 --- a/drivers/net/ethernet/intel/ixgbe/Makefile +++ b/drivers/net/ethernet/intel/ixgbe/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel 10 Gigabit PCI Express Linux driver -# Copyright(c) 1999 - 2010 Intel Corporation. +# Copyright(c) 1999 - 2012 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 258164d..e6aeb64 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index ef2afef..b406c36 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index 7720721..4e59083 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index a3aa633..383b941 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 863f9c1..5ccea80 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index 318caf4..8bfaaee 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h index e162775..24333b7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c index fcd0e47..d3695ed 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h index 2f31893..ba83570 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c index 32cd97b..888a419 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h index a59d5dc..4dec47f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index da31735..005e5f2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index da7e580..1fd510c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index d18d615..4bc7942 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h index 261fd62..1dbed17 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 1ee5d0f..92192c6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -64,7 +64,7 @@ char ixgbe_default_device_descr[] = __stringify(BUILD) "-k" const char ixgbe_driver_version[] = DRV_VERSION; static const char ixgbe_copyright[] = - "Copyright (c) 1999-2011 Intel Corporation."; + "Copyright (c) 1999-2012 Intel Corporation."; static const struct ixgbe_info *ixgbe_info_tbl[] = { [board_82598] = &ixgbe_82598_info, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c index 3f725d4..1f3e32b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h index b239bda..310bdd9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 7cf1e1f..b917735 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index 197bdd1..cc18165 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index cf6812d..b76339b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index e8badab..2ab38d5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 775602e..9b95bef 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 8cc5ecc..f838a2b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2011 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, -- cgit v0.10.2 From 68d6d4ab9a963cfaece88f9b4ea23be517ce46b0 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Thu, 5 Jan 2012 07:58:11 +0000 Subject: ixgbe: Add warning when no space left for more MAC filters Send message to system log when the VF requests another MAC filter but there is no space left on the device for it. Signed-off-by: Greg Rose Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index b76339b..8d8cdbc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -646,6 +646,9 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf) ixgbe_ndo_set_vf_spoofchk(adapter->netdev, vf, false); retval = ixgbe_set_vf_macvlan(adapter, vf, index, (unsigned char *)(&msgbuf[1])); + if (retval == -ENOSPC) + e_warn(drv, "VF %d has requested a MACVLAN filter " + "but there is no space for it\n", vf); break; default: e_err(drv, "Unhandled Msg %8.8x\n", msgbuf[0]); -- cgit v0.10.2 From 1bba2e81f5575825b59146f982004415d3751e6b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 5 Jan 2012 06:29:54 +0000 Subject: ixgbe: make ethtool strings table const Signed-off-by: Stephen Hemminger Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 1fd510c..1f31a04 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -58,7 +58,7 @@ struct ixgbe_stats { sizeof(((struct rtnl_link_stats64 *)0)->m), \ offsetof(struct rtnl_link_stats64, m) -static struct ixgbe_stats ixgbe_gstrings_stats[] = { +static const struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_packets", IXGBE_NETDEV_STAT(rx_packets)}, {"tx_packets", IXGBE_NETDEV_STAT(tx_packets)}, {"rx_bytes", IXGBE_NETDEV_STAT(rx_bytes)}, -- cgit v0.10.2 From 3a2c40336b5ee112f6a2bc784e79aa6124d7acf6 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Wed, 1 Feb 2012 01:28:15 +0000 Subject: ixgbevf: Fix mailbox interrupt ack bug Remove jump to out label that was skipping over code to replace the ack bit in the mailbox cache variable. This was causing driver mailbox routines that poll for the PF to ack a message to time out which would in turn cause all other mailbox messages to fail. Also replace the mailbox cache variable message indication bit when a message is found so that other functions that choose to poll for a mailbox message from the PF won't miss it. Signed-off-by: Greg Rose Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index bed411b..3044a56 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -935,7 +935,11 @@ static irqreturn_t ixgbevf_msix_mbx(int irq, void *data) if (msg & IXGBE_VT_MSGTYPE_NACK) pr_warn("Last Request of type %2.2x to PF Nacked\n", msg & 0xFF); - goto out; + /* + * Restore the PFSTS bit in case someone is polling for a + * return message from the PF + */ + hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFSTS; } /* @@ -945,7 +949,7 @@ static irqreturn_t ixgbevf_msix_mbx(int irq, void *data) */ if (got_ack) hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFACK; -out: + return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index d0138d7..77a19d9 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -283,6 +283,17 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr, return ret_val; } +static void ixgbevf_write_msg_read_ack(struct ixgbe_hw *hw, + u32 *msg, u16 size) +{ + struct ixgbe_mbx_info *mbx = &hw->mbx; + u32 retmsg[IXGBE_VFMAILBOX_SIZE]; + s32 retval = mbx->ops.write_posted(hw, msg, size); + + if (!retval) + mbx->ops.read_posted(hw, retmsg, size); +} + /** * ixgbevf_update_mc_addr_list_vf - Update Multicast addresses * @hw: pointer to the HW structure @@ -294,7 +305,6 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, struct net_device *netdev) { struct netdev_hw_addr *ha; - struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[IXGBE_VFMAILBOX_SIZE]; u16 *vector_list = (u16 *)&msgbuf[1]; u32 cnt, i; @@ -321,7 +331,7 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, vector_list[i++] = ixgbevf_mta_vector(hw, ha->addr); } - mbx->ops.write_posted(hw, msgbuf, IXGBE_VFMAILBOX_SIZE); + ixgbevf_write_msg_read_ack(hw, msgbuf, IXGBE_VFMAILBOX_SIZE); return 0; } @@ -336,7 +346,6 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, static s32 ixgbevf_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) { - struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[2]; msgbuf[0] = IXGBE_VF_SET_VLAN; @@ -344,7 +353,9 @@ static s32 ixgbevf_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, /* Setting the 8 bit field MSG INFO to TRUE indicates "add" */ msgbuf[0] |= vlan_on << IXGBE_VT_MSGINFO_SHIFT; - return mbx->ops.write_posted(hw, msgbuf, 2); + ixgbevf_write_msg_read_ack(hw, msgbuf, 2); + + return 0; } /** -- cgit v0.10.2 From 5c47a2b6211316ee9cd8740db453af7c5c01d0d6 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Fri, 6 Jan 2012 02:53:30 +0000 Subject: ixgbevf: Update copyright notices Signed-off-by: Greg Rose Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbevf/Makefile b/drivers/net/ethernet/intel/ixgbevf/Makefile index 1f35d22..4ce4c97 100644 --- a/drivers/net/ethernet/intel/ixgbevf/Makefile +++ b/drivers/net/ethernet/intel/ixgbevf/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel 82599 Virtual Function driver -# Copyright(c) 1999 - 2010 Intel Corporation. +# Copyright(c) 1999 - 2012 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/defines.h b/drivers/net/ethernet/intel/ixgbevf/defines.h index 2eb89cb..947b5c8 100644 --- a/drivers/net/ethernet/intel/ixgbevf/defines.h +++ b/drivers/net/ethernet/intel/ixgbevf/defines.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index c857003..2bfe0d1 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2009 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index 9075c1d..dfed420 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 3044a56..e51d552 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -60,7 +60,7 @@ static const char ixgbevf_driver_string[] = #define DRV_VERSION "2.2.0-k" const char ixgbevf_driver_version[] = DRV_VERSION; static char ixgbevf_copyright[] = - "Copyright (c) 2009 - 2010 Intel Corporation."; + "Copyright (c) 2009 - 2012 Intel Corporation."; static const struct ixgbevf_info *ixgbevf_info_tbl[] = { [board_82599_vf] = &ixgbevf_82599_vf_info, diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.c b/drivers/net/ethernet/intel/ixgbevf/mbx.c index 13532d9..9c95590 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.c +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.h b/drivers/net/ethernet/intel/ixgbevf/mbx.h index 9d38a94..cf9131c 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.h +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/regs.h b/drivers/net/ethernet/intel/ixgbevf/regs.h index 5e4d5e5..debd8c0 100644 --- a/drivers/net/ethernet/intel/ixgbevf/regs.h +++ b/drivers/net/ethernet/intel/ixgbevf/regs.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 77a19d9..74be741 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h index d556619..25c951d 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 82599 Virtual Function driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, -- cgit v0.10.2 From 2a06ed9297e927bb928897661940c47ea4f41707 Mon Sep 17 00:00:00 2001 From: Mitch A Williams Date: Tue, 17 Jan 2012 04:09:05 +0000 Subject: igbvf: change copyright date Change copyright date to 2012 in the source files. Signed-off-by: Mitch Williams Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/igbvf/Makefile b/drivers/net/ethernet/intel/igbvf/Makefile index 0fa3db3..044b0ad 100644 --- a/drivers/net/ethernet/intel/igbvf/Makefile +++ b/drivers/net/ethernet/intel/igbvf/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel(R) 82576 Virtual Function Linux driver -# Copyright(c) 2009 - 2010 Intel Corporation. +# Copyright(c) 2009 - 2012 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h index 79f2604..33f40d3 100644 --- a/drivers/net/ethernet/intel/igbvf/defines.h +++ b/drivers/net/ethernet/intel/igbvf/defines.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 2dba534..db7dce2 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index fd4a7b7..2c6d87e 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/mbx.c b/drivers/net/ethernet/intel/igbvf/mbx.c index 048aae2..b4b65bc 100644 --- a/drivers/net/ethernet/intel/igbvf/mbx.c +++ b/drivers/net/ethernet/intel/igbvf/mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/mbx.h b/drivers/net/ethernet/intel/igbvf/mbx.h index c2883c4..24370bc 100644 --- a/drivers/net/ethernet/intel/igbvf/mbx.h +++ b/drivers/net/ethernet/intel/igbvf/mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index a4b20c8..4e9141c 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -53,7 +53,7 @@ const char igbvf_driver_version[] = DRV_VERSION; static const char igbvf_driver_string[] = "Intel(R) Gigabit Virtual Function Network Driver"; static const char igbvf_copyright[] = - "Copyright (c) 2009 - 2011 Intel Corporation."; + "Copyright (c) 2009 - 2012 Intel Corporation."; static int igbvf_poll(struct napi_struct *napi, int budget); static void igbvf_reset(struct igbvf_adapter *); diff --git a/drivers/net/ethernet/intel/igbvf/regs.h b/drivers/net/ethernet/intel/igbvf/regs.h index 77e18d3..7dc6341 100644 --- a/drivers/net/ethernet/intel/igbvf/regs.h +++ b/drivers/net/ethernet/intel/igbvf/regs.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index af3822f..1955197 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ethernet/intel/igbvf/vf.h b/drivers/net/ethernet/intel/igbvf/vf.h index d7ed58f..57db3c6 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.h +++ b/drivers/net/ethernet/intel/igbvf/vf.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel(R) 82576 Virtual Function Linux driver - Copyright(c) 2009 - 2010 Intel Corporation. + Copyright(c) 2009 - 2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, -- cgit v0.10.2 From 542a398080ff4a2fcf6a8f3ae642dad5fbc8877e Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 23 Jan 2012 12:47:32 +0000 Subject: ixgbe: Fix typo in ixgbe_common.h Correct spelling "packtetbuf_num" to "packetbuf_num" in drivers/net/ethernet/intel/ixgbe/ixgbe_common.h Signed-off-by: Masanari Iida Tested-by: Phil Schmitt diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 5ccea80..2c834c4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -75,7 +75,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw); s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw); s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval); -s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packtetbuf_num); +s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num); s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw); s32 ixgbe_validate_mac_addr(u8 *mac_addr); -- cgit v0.10.2 From b544d1e0e233f83a2e6d20ee96b54ea272d5d5ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 3 Feb 2012 11:56:35 +0100 Subject: ALSA: hda/realtek - Add missing Bass and CLFE as vmaster slaves The recent changes in Realtek auto-parser added the new "Bass Speaker" and "CLFE" mixer elements which should be tracked as vmaster slaves, too. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42720 Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a8e82be..33b6077 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1855,6 +1855,8 @@ static const char * const alc_slave_vols[] = { "Speaker Playback Volume", "Mono Playback Volume", "Line-Out Playback Volume", + "CLFE Playback Volume", + "Bass Speaker Playback Volume", "PCM Playback Volume", NULL, }; @@ -1870,6 +1872,8 @@ static const char * const alc_slave_sws[] = { "Mono Playback Switch", "IEC958 Playback Switch", "Line-Out Playback Switch", + "CLFE Playback Switch", + "Bass Speaker Playback Switch", "PCM Playback Switch", NULL, }; -- cgit v0.10.2 From 5a97d0ae5b5d78727b87965cba84194a9f1e06ce Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 3 Feb 2012 11:08:05 +0100 Subject: ARM: 7314/1: kuser: consistently use usr_ret for returning from helpers __kuser_cmpxchg64 has a return path using bx lr to get back to the caller. This is actually ok since the code in question is predicated on CONFIG_CPU_32v6K, but for the sake of consistency using the usr_ret macro is probably better. Acked-by: Dave Martin Acked-by: Nicolas Pitre Signed-off-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 3a456c6..be16a48 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -790,7 +790,7 @@ __kuser_cmpxchg64: @ 0xffff0f60 smp_dmb arm rsbs r0, r3, #0 @ set returned val and C flag ldmfd sp!, {r4, r5, r6, r7} - bx lr + usr_ret lr #elif !defined(CONFIG_SMP) -- cgit v0.10.2 From 3af336376f77859da84bb1156ef29d5337b316a9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Nov 2011 21:27:32 +0300 Subject: IB/srpt: Fix ERR_PTR() vs. NULL checking confusion transport_init_session() and target_fabric_configfs_init() don't return NULL pointers, they only return ERR_PTRs or valid pointers. Signed-off-by: Dan Carpenter Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 38ef61d..c125614 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -2595,7 +2595,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, } ch->sess = transport_init_session(); - if (!ch->sess) { + if (IS_ERR(ch->sess)) { rej->reason = __constant_cpu_to_be32( SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); pr_debug("Failed to create session\n"); @@ -4009,10 +4009,10 @@ static int __init srpt_init_module(void) goto out; } - ret = -ENODEV; srpt_target = target_fabric_configfs_init(THIS_MODULE, "srpt"); - if (!srpt_target) { + if (IS_ERR(srpt_target)) { printk(KERN_ERR "couldn't register\n"); + ret = PTR_ERR(srpt_target); goto out; } -- cgit v0.10.2 From f8447d6c213273b444c81eaa2449f55510229d4f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Sat, 14 Jan 2012 20:58:43 +0100 Subject: mfd: Store twl6040-codec mclk configuration Store the last used mclk configuration for the PLL. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index dda8629..c2088f4 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -282,6 +282,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* Default PLL configuration after power up */ twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->sysclk = 19200000; + twl6040->mclk = 32768; } else { /* already powered-down */ if (!twl6040->power_count) { @@ -305,6 +306,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) twl6040_power_down(twl6040); } twl6040->sysclk = 0; + twl6040->mclk = 0; } out: @@ -421,6 +423,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, } twl6040->sysclk = freq_out; + twl6040->mclk = freq_in; twl6040->pll = pll_id; pll_out: diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 2463c261..9bc9ac6 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -187,8 +187,10 @@ struct twl6040 { int rev; u8 vibra_ctrl_cache[2]; + /* PLL configuration */ int pll; unsigned int sysclk; + unsigned int mclk; unsigned int irq; unsigned int irq_base; -- cgit v0.10.2 From 2bd05db718cf452fe235c7c08083b2e60f787f3a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Sat, 14 Jan 2012 20:58:44 +0100 Subject: mfd: Avoid twl6040-codec PLL reconfiguration when not needed Do not reconfigure the PLL in case it has been configured already with the requested parameters. In case of different PLL configuration do only the needed changes. This can save considerable amount of time since we can avoid the defined protocol (with delays) for the PLL configuration. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index c2088f4..b2d8e51 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -326,23 +326,38 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); + /* Force full reconfiguration when switching between PLL */ + if (pll_id != twl6040->pll) { + twl6040->sysclk = 0; + twl6040->mclk = 0; + } + switch (pll_id) { case TWL6040_SYSCLK_SEL_LPPLL: /* low-power PLL divider */ - switch (freq_out) { - case 17640000: - lppllctl |= TWL6040_LPLLFIN; - break; - case 19200000: - lppllctl &= ~TWL6040_LPLLFIN; - break; - default: - dev_err(twl6040->dev, - "freq_out %d not supported\n", freq_out); - ret = -EINVAL; - goto pll_out; + /* Change the sysclk configuration only if it has been canged */ + if (twl6040->sysclk != freq_out) { + switch (freq_out) { + case 17640000: + lppllctl |= TWL6040_LPLLFIN; + break; + case 19200000: + lppllctl &= ~TWL6040_LPLLFIN; + break; + default: + dev_err(twl6040->dev, + "freq_out %d not supported\n", + freq_out); + ret = -EINVAL; + goto pll_out; + } + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, + lppllctl); } - twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + + /* The PLL in use has not been change, we can exit */ + if (twl6040->pll == pll_id) + break; switch (freq_in) { case 32768: @@ -373,48 +388,56 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, goto pll_out; } - hppllctl &= ~TWL6040_MCLK_MSK; + if (twl6040->mclk != freq_in) { + hppllctl &= ~TWL6040_MCLK_MSK; + + switch (freq_in) { + case 12000000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_12000KHZ | + TWL6040_HPLLENA; + break; + case 19200000: + /* + * PLL disabled + * (enable PLL if MCLK jitter quality + * doesn't meet specification) + */ + hppllctl |= TWL6040_MCLK_19200KHZ; + break; + case 26000000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_26000KHZ | + TWL6040_HPLLENA; + break; + case 38400000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_38400KHZ | + TWL6040_HPLLENA; + break; + default: + dev_err(twl6040->dev, + "freq_in %d not supported\n", freq_in); + ret = -EINVAL; + goto pll_out; + } - switch (freq_in) { - case 12000000: - /* PLL enabled, active mode */ - hppllctl |= TWL6040_MCLK_12000KHZ | - TWL6040_HPLLENA; - break; - case 19200000: /* - * PLL disabled - * (enable PLL if MCLK jitter quality - * doesn't meet specification) + * enable clock slicer to ensure input waveform is + * square */ - hppllctl |= TWL6040_MCLK_19200KHZ; - break; - case 26000000: - /* PLL enabled, active mode */ - hppllctl |= TWL6040_MCLK_26000KHZ | - TWL6040_HPLLENA; - break; - case 38400000: - /* PLL enabled, active mode */ - hppllctl |= TWL6040_MCLK_38400KHZ | - TWL6040_HPLLENA; - break; - default: - dev_err(twl6040->dev, - "freq_in %d not supported\n", freq_in); - ret = -EINVAL; - goto pll_out; - } + hppllctl |= TWL6040_HPLLSQRENA; - /* enable clock slicer to ensure input waveform is square */ - hppllctl |= TWL6040_HPLLSQRENA; - - twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl); - usleep_range(500, 700); - lppllctl |= TWL6040_HPLLSEL; - twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); - lppllctl &= ~TWL6040_LPLLENA; - twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, + hppllctl); + usleep_range(500, 700); + lppllctl |= TWL6040_HPLLSEL; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, + lppllctl); + lppllctl &= ~TWL6040_LPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, + lppllctl); + } break; default: dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); -- cgit v0.10.2 From 8149415efa033ca138c0080ded77329e98697c7b Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 30 Jan 2012 10:55:13 -0500 Subject: ath9k: use WARN_ON_ONCE in ath_rc_get_highest_rix The device seems to survive the issue, so no need to flood the logs about it... Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index b3c3798..635b592 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -694,7 +694,7 @@ static u8 ath_rc_get_highest_rix(struct ath_softc *sc, return rate; /* This should not happen */ - WARN_ON(1); + WARN_ON_ONCE(1); rate = ath_rc_priv->valid_rate_index[0]; -- cgit v0.10.2 From b7097eb75fa11c302dcdec83f1dbfa874e0af0d1 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 1 Feb 2012 20:41:43 -0800 Subject: mwifiex: handle association failure case correctly Currently even if association is failed "iw link" shows some information about connected BSS and "Tx timeout" error is seen in dmesg log. This patch fixes below issues in the code to handle assoc failure case correctly. 1) "status" variable in mwifiex_wait_queue_complete() is not correctly updated. Hence driver doesn't inform cfg80211 stack about association failure. 2) During association network queues are stopped but carrier is not cleared, which gives Tx timeout error in failure case Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 470ca75..b0fbf5d 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -54,7 +54,7 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) { bool cancel_flag = false; - int status = adapter->cmd_wait_q.status; + int status; struct cmd_ctrl_node *cmd_queued; if (!adapter->cmd_queued) @@ -79,6 +79,8 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) mwifiex_cancel_pending_ioctl(adapter); dev_dbg(adapter->dev, "cmd cancel\n"); } + + status = adapter->cmd_wait_q.status; adapter->cmd_wait_q.status = 0; return status; @@ -240,6 +242,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); /* Clear any past association response stored for * application retrieval */ @@ -271,6 +275,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" -- cgit v0.10.2 From 07445f688218a48bde72316aed9de4fdcc173131 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 2 Feb 2012 16:29:05 +0530 Subject: ath9k: Fix kernel panic during driver initilization all works need to be initialized before ieee80211_register_hw to prevent mac80211 call backs such as drv_start, drv_config getting started. otherwise we would queue/cancel works before initializing them and it leads to kernel panic. this issue can be recreated with the following script in Chrome laptops with AR928X cards, with background scan running (or) Network manager is running while true do sudo modprobe -v ath9k sleep 3 sudo modprobe -r ath9k sleep 3 done EIP: [<81040a47>] __cancel_work_timer+0xb8/0xe1 SS:ESP 0068:f6be9d70 ---[ end trace 4f86d6139a9900ef ]--- Registered led device: ath9k-phy0 ieee80211 phy0: Atheros AR9280 Rev:2 mem=0xf88a0000, irq=16 Kernel panic - not syncing: Fatal exception Pid: 456, comm: wpa_supplicant Tainted: G D 3.0.13 #1 Call Trace: [<81379e21>] panic+0x53/0x14a [<81004a30>] oops_end+0x73/0x81 [<81004b53>] die+0x4c/0x55 [<81002710>] do_trap+0x7c/0x83 [<81002855>] ? do_bounds+0x58/0x58 [<810028cc>] do_invalid_op+0x77/0x81 [<81040a47>] ? __cancel_work_timer+0xb8/0xe1 [<810489ec>] ? sched_clock_cpu+0x81/0x11f [<8103f809>] ? wait_on_work+0xe2/0xf7 [<8137f807>] error_code+0x67/0x6c [<810300d8>] ? wait_consider_task+0x4ba/0x84c [<81040a47>] ? __cancel_work_timer+0xb8/0xe1 [<810380c9>] ? try_to_del_timer_sync+0x5f/0x67 [<81040a91>] cancel_work_sync+0xf/0x11 [] ath_set_channel+0x62/0x25c [ath9k] [] ? ath9k_tx_last_beacon+0x26a/0x85c [ath9k] [] ath_radio_disable+0x3f1/0x68e [ath9k] [] ieee80211_hw_config+0x111/0x116 [mac80211] [] __ieee80211_recalc_idle+0x919/0xa37 [mac80211] [] __ieee80211_recalc_idle+0xa33/0xa37 [mac80211] [<812dbed8>] __dev_open+0x82/0xab Cc: Cc: Gary Morain Cc: Paul Stewart Cc: Vasanthakumar Thiagarajan Tested-by: Mohammed Shafi Shajakhan Signed-off-by: Rajkumar Manoharan Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index abf9435..53a005d 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -822,6 +822,11 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, ARRAY_SIZE(ath9k_tpt_blink)); #endif + INIT_WORK(&sc->hw_reset_work, ath_reset_work); + INIT_WORK(&sc->hw_check_work, ath_hw_check); + INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); + INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); + /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) @@ -840,10 +845,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, goto error_world; } - INIT_WORK(&sc->hw_reset_work, ath_reset_work); - INIT_WORK(&sc->hw_check_work, ath_hw_check); - INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); - INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; ath_init_leds(sc); -- cgit v0.10.2 From 90451e6973a5da155c6f315a409ca0a8d3ce6b76 Mon Sep 17 00:00:00 2001 From: Milan Kocian Date: Fri, 3 Feb 2012 14:28:00 +0100 Subject: USB: usbserial: add new PID number (0xa951) to the ftdi driver Signed-off-by: Milan Kocian Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ad654f8..f770415 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -839,6 +839,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index f994503..6f6058f 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1187,3 +1187,10 @@ */ /* ZigBee controller */ #define FTDI_RF_R106 0x8A28 + +/* + * Product: HCP HIT GPRS modem + * Manufacturer: HCP d.o.o. + * ATI command output: Cinterion MC55i + */ +#define FTDI_CINTERION_MC55I_PID 0xA951 -- cgit v0.10.2 From e7c9bba7999a8e55efb96d4279c025ca587c5ecd Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 3 Feb 2012 13:22:10 +0800 Subject: tty: fix a build failure on sparc On sparc, there is a build failure: drivers/tty/serial/8250/8250.c:48:21: error: suncore.h: No such file or directory drivers/tty/serial/8250/8250.c:3275: error: implicit declaration of function 'sunserial_register_minors' drivers/tty/serial/8250/8250.c:3305: error: implicit declaration of function 'sunserial_unregister_minors' this is due to commit 9bef3d4197379a995fa80f81950bbbf8d32e9e8b (serial: group all the 8250 related code together) moved these files into 8250/ subdirectory, but forgot to change the reference to drivers/tty/serial/suncore.h. Cc: Paul Gortmaker Signed-off-by: WANG Cong Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 9f50c4e..9b7336f 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -45,7 +45,7 @@ #include "8250.h" #ifdef CONFIG_SPARC -#include "suncore.h" +#include "../suncore.h" #endif /* -- cgit v0.10.2 From 5b02aa1e6e7cf7d3bbf8027dc7e05e0eb2f1e828 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 1 Feb 2012 16:07:41 -0500 Subject: xen/bootup: During bootup suppress XENBUS: Unable to read cpu state When the initial domain starts, it prints (depending on the amount of CPUs) a slew of XENBUS: Unable to read cpu state XENBUS: Unable to read cpu state XENBUS: Unable to read cpu state XENBUS: Unable to read cpu state which provide no useful information - as the error is a valid issue - but not on the initial domain. The reason is that the XenStore is not accessible at that time (it is after all the first guest) so the CPU hotplug watch cannot parse "availability/cpu" attribute. Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index 14e2d99..4dcfced 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -30,7 +30,8 @@ static int vcpu_online(unsigned int cpu) sprintf(dir, "cpu/%u", cpu); err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state); if (err != 1) { - printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); + if (!xen_initial_domain()) + printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); return err; } -- cgit v0.10.2 From 41bd956de3dfdc3a43708fe2e0c8096c69064a1e Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 1 Feb 2012 15:56:54 -0500 Subject: xen/smp: Fix CPU online/offline bug triggering a BUG: scheduling while atomic. When a user offlines a VCPU and then onlines it, we get: NMI watchdog disabled (cpu2): hardware events not enabled BUG: scheduling while atomic: swapper/2/0/0x00000002 Modules linked in: dm_multipath dm_mod xen_evtchn iscsi_boot_sysfs iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi scsi_mod libcrc32c crc32c radeon fbco ttm bitblit softcursor drm_kms_helper xen_blkfront xen_netfront xen_fbfront fb_sys_fops sysimgblt sysfillrect syscopyarea xen_kbdfront xenfs [last unloaded: Pid: 0, comm: swapper/2 Tainted: G O 3.2.0phase15.1-00003-gd6f7f5b-dirty #4 Call Trace: [] __schedule_bug+0x61/0x70 [] __schedule+0x798/0x850 [] schedule+0x3a/0x50 [] cpu_idle+0xbe/0xe0 [] cpu_bringup_and_idle+0xe/0x10 The reason for this should be obvious from this call-chain: cpu_bringup_and_idle: \- cpu_bringup | \-[preempt_disable] | |- cpu_idle \- play_dead [assuming the user offlined the VCPU] | \ | +- (xen_play_dead) | \- HYPERVISOR_VCPU_off [so VCPU is dead, once user | | onlines it starts from here] | \- cpu_bringup [preempt_disable] | +- preempt_enable_no_reschedule() +- schedule() \- preempt_enable() So we have two preempt_disble() and one preempt_enable(). Calling preempt_enable() after the cpu_bringup() in the xen_play_dead fixes the imbalance. Signed-off-by: Konrad Rzeszutek Wilk diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 041d4fe..501d4e0 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -409,6 +409,13 @@ static void __cpuinit xen_play_dead(void) /* used only with HOTPLUG_CPU */ play_dead_common(); HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); cpu_bringup(); + /* + * Balance out the preempt calls - as we are running in cpu_idle + * loop which has been called at bootup from cpu_bringup_and_idle. + * The cpucpu_bringup_and_idle called cpu_bringup which made a + * preempt_disable() So this preempt_enable will balance it out. + */ + preempt_enable(); } #else /* !CONFIG_HOTPLUG_CPU */ -- cgit v0.10.2 From 207d543f472c1ac9552df79838dc807cbcaa9740 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 30 Jan 2012 14:31:46 +0000 Subject: xen pvhvm: do not remap pirqs onto evtchns if !xen_have_vector_callback CC: stable@kernel.org #2.6.37 and onwards Signed-off-by: Stefano Stabellini Signed-off-by: Konrad Rzeszutek Wilk diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 492ade8..d99346e 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -374,7 +374,7 @@ int __init pci_xen_init(void) int __init pci_xen_hvm_init(void) { - if (!xen_feature(XENFEAT_hvm_pirqs)) + if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs)) return 0; #ifdef CONFIG_ACPI -- cgit v0.10.2 From e4de866a834620ef974f5ba86d394a13d2f0cf66 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 25 Jan 2012 16:00:00 -0500 Subject: xen/pci[front|back]: Use %d instead of %1x for displaying PCI devfn. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .. as the rest of the kernel is using that format. Suggested-by: Марк Коренберг Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 7cf3d2f..1620088 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -189,7 +189,7 @@ static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn, if (verbose_request) dev_info(&pdev->xdev->dev, - "read dev=%04x:%02x:%02x.%01x - offset %x size %d\n", + "read dev=%04x:%02x:%02x.%d - offset %x size %d\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size); @@ -228,7 +228,7 @@ static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn, if (verbose_request) dev_info(&pdev->xdev->dev, - "write dev=%04x:%02x:%02x.%01x - " + "write dev=%04x:%02x:%02x.%d - " "offset %x size %d val %x\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val); @@ -432,7 +432,7 @@ static int __devinit pcifront_scan_bus(struct pcifront_device *pdev, d = pci_scan_single_device(b, devfn); if (d) dev_info(&pdev->xdev->dev, "New device on " - "%04x:%02x:%02x.%02x found.\n", domain, bus, + "%04x:%02x:%02x.%d found.\n", domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); } @@ -1041,7 +1041,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func)); if (!pci_dev) { dev_dbg(&pdev->xdev->dev, - "Cannot get PCI device %04x:%02x:%02x.%02x\n", + "Cannot get PCI device %04x:%02x:%02x.%d\n", domain, bus, slot, func); continue; } @@ -1049,7 +1049,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) pci_dev_put(pci_dev); dev_dbg(&pdev->xdev->dev, - "PCI device %04x:%02x:%02x.%02x removed.\n", + "PCI device %04x:%02x:%02x.%d removed.\n", domain, bus, slot, func); } diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index 7944a17..19834d1 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -884,7 +884,7 @@ static inline int str_to_quirk(const char *buf, int *domain, int *bus, int int err; err = - sscanf(buf, " %04x:%02x:%02x.%1x-%08x:%1x:%08x", domain, bus, slot, + sscanf(buf, " %04x:%02x:%02x.%d-%08x:%1x:%08x", domain, bus, slot, func, reg, size, mask); if (err == 7) return 0; @@ -904,7 +904,7 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func) pci_dev_id->bus = bus; pci_dev_id->devfn = PCI_DEVFN(slot, func); - pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%01x\n", + pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%d\n", domain, bus, slot, func); spin_lock_irqsave(&device_ids_lock, flags); @@ -934,7 +934,7 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func) err = 0; - pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%01x from " + pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%d from " "seize list\n", domain, bus, slot, func); } } @@ -1029,7 +1029,7 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf) break; count += scnprintf(buf + count, PAGE_SIZE - count, - "%04x:%02x:%02x.%01x\n", + "%04x:%02x:%02x.%d\n", pci_dev_id->domain, pci_dev_id->bus, PCI_SLOT(pci_dev_id->devfn), PCI_FUNC(pci_dev_id->devfn)); diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index d5dcf8d..64b11f9 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -206,6 +206,7 @@ static int xen_pcibk_publish_pci_dev(struct xen_pcibk_device *pdev, goto out; } + /* Note: The PV protocol uses %02x, don't change it */ err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str, "%04x:%02x:%02x.%02x", domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); @@ -229,7 +230,7 @@ static int xen_pcibk_export_device(struct xen_pcibk_device *pdev, err = -EINVAL; xenbus_dev_fatal(pdev->xdev, err, "Couldn't locate PCI device " - "(%04x:%02x:%02x.%01x)! " + "(%04x:%02x:%02x.%d)! " "perhaps already in-use?", domain, bus, slot, func); goto out; @@ -274,7 +275,7 @@ static int xen_pcibk_remove_device(struct xen_pcibk_device *pdev, if (!dev) { err = -EINVAL; dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device " - "(%04x:%02x:%02x.%01x)! not owned by this domain\n", + "(%04x:%02x:%02x.%d)! not owned by this domain\n", domain, bus, slot, func); goto out; } -- cgit v0.10.2 From a43a5ccdfa5bd5b2f00aa9b2321df268c2e5d6e2 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 24 Jan 2012 13:52:42 +0000 Subject: xenbus_dev: add missing error check to watch handling So far only the watch path was checked to be zero terminated, while the watch token was merely assumed to be. Signed-off-by: Jan Beulich Signed-off-by: Konrad Rzeszutek Wilk diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index 527dc2a..89f7625 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -369,6 +369,10 @@ static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u) goto out; } token++; + if (memchr(token, 0, u->u.msg.len - (token - path)) == NULL) { + rc = -EILSEQ; + goto out; + } if (msg_type == XS_WATCH) { watch = alloc_watch_adapter(path, token); -- cgit v0.10.2 From 331818f1c468a24e581aedcbe52af799366a9dfe Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 3 Feb 2012 18:30:53 -0500 Subject: NFSv4: Fix an Oops in the NFSv4 getacl code Commit bf118a342f10dafe44b14451a1392c3254629a1f (NFSv4: include bitmap in nfsv4 get acl data) introduces the 'acl_scratch' page for the case where we may need to decode multi-page data. However it fails to take into account the fact that the variable may be NULL (for the case where we're not doing multi-page decode), and it also attaches it to the encoding xdr_stream rather than the decoding one. The immediate result is an Oops in nfs4_xdr_enc_getacl due to the call to page_address() with a NULL page pointer. Signed-off-by: Trond Myklebust Cc: Andy Adamson Cc: stable@vger.kernel.org diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f0c849c..d202e04 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3575,8 +3575,8 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu } if (npages > 1) { /* for decoding across pages */ - args.acl_scratch = alloc_page(GFP_KERNEL); - if (!args.acl_scratch) + res.acl_scratch = alloc_page(GFP_KERNEL); + if (!res.acl_scratch) goto out_free; } args.acl_len = npages * PAGE_SIZE; @@ -3612,8 +3612,8 @@ out_free: for (i = 0; i < npages; i++) if (pages[i]) __free_page(pages[i]); - if (args.acl_scratch) - __free_page(args.acl_scratch); + if (res.acl_scratch) + __free_page(res.acl_scratch); return ret; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 95e92e4..33bd8d0 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -2522,7 +2522,6 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr, xdr_inline_pages(&req->rq_rcv_buf, replen << 2, args->acl_pages, args->acl_pgbase, args->acl_len); - xdr_set_scratch_buffer(xdr, page_address(args->acl_scratch), PAGE_SIZE); encode_nops(&hdr); } @@ -6032,6 +6031,10 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, struct compound_hdr hdr; int status; + if (res->acl_scratch != NULL) { + void *p = page_address(res->acl_scratch); + xdr_set_scratch_buffer(xdr, p, PAGE_SIZE); + } status = decode_compound_hdr(xdr, &hdr); if (status) goto out; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a764cef..d6ba9a1 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -614,7 +614,6 @@ struct nfs_getaclargs { size_t acl_len; unsigned int acl_pgbase; struct page ** acl_pages; - struct page * acl_scratch; struct nfs4_sequence_args seq_args; }; @@ -624,6 +623,7 @@ struct nfs_getaclres { size_t acl_len; size_t acl_data_offset; int acl_flags; + struct page * acl_scratch; struct nfs4_sequence_res seq_res; }; -- cgit v0.10.2 From 20d16fef95fb2924228dce0078fdfec5429f84f2 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 3 Feb 2012 15:37:11 -0800 Subject: MAINTAINERS: update partitions block F: patterns Commit 9be96f3fd101 ("move fs/partitions to block/") moved the files, update the patterns. Signed-off-by: Joe Perches Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 252972b..8c2086f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4136,7 +4136,7 @@ L: linux-ntfs-dev@lists.sourceforge.net W: http://www.linux-ntfs.org/content/view/19/37/ S: Maintained F: Documentation/ldm.txt -F: fs/partitions/ldm.* +F: block/partitions/ldm.* LogFS M: Joern Engel @@ -5633,7 +5633,7 @@ W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: arch/s390/ F: drivers/s390/ -F: fs/partitions/ibm.c +F: block/partitions/ibm.c F: Documentation/s390/ F: Documentation/DocBook/s390* -- cgit v0.10.2 From 0960e7d13f5d33e4a8201b6c546ce60a2c6faba4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 3 Feb 2012 15:37:12 -0800 Subject: MAINTAINERS: remove iMX5 section commit 784a90c0a7d8f5 ("ARM i.MX: Merge i.MX5 support into mach-imx") merged the files, remove the iMX5 section. Signed-off-by: Joe Perches Cc: Amit Kucheria Cc: Sascha Hauer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 8c2086f..a9fd6ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -789,12 +789,6 @@ F: arch/arm/mach-mx*/ F: arch/arm/mach-imx/ F: arch/arm/plat-mxc/ -ARM/FREESCALE IMX51 -M: Amit Kucheria -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained -F: arch/arm/mach-mx5/ - ARM/FREESCALE IMX6 M: Shawn Guo L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -- cgit v0.10.2 From cdb7232b29229ecf1cc89764d75074eaee63ce32 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 3 Feb 2012 15:37:12 -0800 Subject: MAINTAINERS: remove staging sections Two commits moved files from staging to drivers/media/ commit d6ce55de3abcc ("[media] move cx25821 out of staging") commit be30497085080 ("[media] move tm6000 to drivers/media/video") Remove the sections. Signed-off-by: Joe Perches Cc: Mauro Carvalho Chehab Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index a9fd6ca..51767d5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6390,11 +6390,6 @@ M: Omar Ramirez Luna S: Odd Fixes F: drivers/staging/tidspbridge/ -STAGING - TRIDENT TVMASTER TMxxxx USB VIDEO CAPTURE DRIVERS -L: linux-media@vger.kernel.org -S: Odd Fixes -F: drivers/staging/tm6000/ - STAGING - USB ENE SM/MS CARD READER DRIVER M: Al Cho S: Odd Fixes -- cgit v0.10.2 From 070f420b35a2c078ca2bd2a0d31dd7b5001d411c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 3 Feb 2012 15:37:13 -0800 Subject: MAINTAINERS: update lguest F: patterns commit 07fe9977b623 ("lguest: move the lguest tool to the tools directory") moved the files, update the patterns. Sort F: patterns alphabetically too. Signed-off-by: Joe Perches Cc: Davidlohr Bueso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 51767d5..8591c03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3986,11 +3986,11 @@ M: Rusty Russell L: lguest@lists.ozlabs.org W: http://lguest.ozlabs.org/ S: Odd Fixes -F: Documentation/virtual/lguest/ +F: arch/x86/include/asm/lguest*.h F: arch/x86/lguest/ F: drivers/lguest/ F: include/linux/lguest*.h -F: arch/x86/include/asm/lguest*.h +F: tools/lguest/ LINUX FOR IBM pSERIES (RS/6000) M: Paul Mackerras -- cgit v0.10.2 From a1b58c237b73f10221b31e05b47a6565558207ef Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 3 Feb 2012 15:37:13 -0800 Subject: xtensa: fix memscan() Defining memscan() as memchr() is wrong, because the return values of memscan() and memchr() are different when the character is not found. So use the generic memscan() implementation to fix this. Signed-off-by: Akinobu Mita Cc: Chris Zankel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/xtensa/include/asm/string.h b/arch/xtensa/include/asm/string.h index 5fb8c27..405a8c4 100644 --- a/arch/xtensa/include/asm/string.h +++ b/arch/xtensa/include/asm/string.h @@ -118,7 +118,4 @@ extern void *memmove(void *__dest, __const__ void *__src, size_t __n); /* Don't build bcopy at all ... */ #define __HAVE_ARCH_BCOPY -#define __HAVE_ARCH_MEMSCAN -#define memscan memchr - #endif /* _XTENSA_STRING_H */ -- cgit v0.10.2 From 35512ecaef03250fe50ad81430dd467f01d9a96b Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Fri, 3 Feb 2012 15:37:13 -0800 Subject: mm: postpone migrated page mapping reset Postpone resetting page->mapping until the final remove_migration_ptes(). Otherwise the expression PageAnon(migration_entry_to_page(entry)) does not work. Signed-off-by: Konstantin Khlebnikov Cc: Hugh Dickins Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/migrate.c b/mm/migrate.c index 9871a56..df141f6 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -445,7 +445,6 @@ void migrate_page_copy(struct page *newpage, struct page *page) ClearPageSwapCache(page); ClearPagePrivate(page); set_page_private(page, 0); - page->mapping = NULL; /* * If any waiters have accumulated on the new page then @@ -667,6 +666,7 @@ static int move_to_new_page(struct page *newpage, struct page *page, } else { if (remap_swapcache) remove_migration_ptes(page, newpage); + page->mapping = NULL; } unlock_page(newpage); -- cgit v0.10.2 From 31e0017e6f6fb5cfdfaf932c1f98c9bef8d57688 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 3 Feb 2012 15:37:14 -0800 Subject: avr32: select generic atomic64_t support Enable use of the generic atomic64 implementation on AVR32 platforms. Without this the kernel fails to build as the architecture does not provide its version. Signed-off-by: Fabio Baltieri Acked-by: Hans-Christian Egtvedt Cc: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 197e96f..3dea7231 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -8,6 +8,7 @@ config AVR32 select HAVE_KPROBES select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE + select GENERIC_ATOMIC64 select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW select ARCH_HAVE_NMI_SAFE_CMPXCHG -- cgit v0.10.2 From 82b3f2a7171731cce62f25058d25afb91a14710c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 3 Feb 2012 15:37:14 -0800 Subject: mm/memcontrol.c: fix warning with CONFIG_NUMA=n mm/memcontrol.c: In function 'memcg_check_events': mm/memcontrol.c:779: warning: unused variable 'do_numainfo' Acked-by: Michal Hocko Cc: Li Zefan Cc: Hiroyuki KAMEZAWA Cc: Johannes Weiner Acked-by: "Kirill A. Shutemov" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 556859f..6728a7a 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -776,7 +776,8 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) /* threshold event is triggered in finer grain than soft limit */ if (unlikely(mem_cgroup_event_ratelimit(memcg, MEM_CGROUP_TARGET_THRESH))) { - bool do_softlimit, do_numainfo; + bool do_softlimit; + bool do_numainfo __maybe_unused; do_softlimit = mem_cgroup_event_ratelimit(memcg, MEM_CGROUP_TARGET_SOFTLIMIT); -- cgit v0.10.2 From 99f02ef1f18631eb0a4e0ea0a3d56878dbcb4b90 Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Fri, 3 Feb 2012 15:37:14 -0800 Subject: mm/filemap_xip.c: fix race condition in xip_file_fault() Fix a race condition that shows in conjunction with xip_file_fault() when two threads of the same user process fault on the same memory page. In this case, the race winner will install the page table entry and the unlucky loser will cause an oops: xip_file_fault calls vm_insert_pfn (via vm_insert_mixed) which drops out at this check: retval = -EBUSY; if (!pte_none(*pte)) goto out_unlock; The resulting -EBUSY return value will trigger a BUG_ON() in xip_file_fault. This fix simply considers the fault as fixed in this case, because the race winner has successfully installed the pte. [akpm@linux-foundation.org: use conventional (and consistent) comment layout] Reported-by: David Sadler Signed-off-by: Carsten Otte Reported-by: Louis Alex Eisner Cc: Hugh Dickins Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index f91b2f6..a4eb311 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -263,7 +263,12 @@ found: xip_pfn); if (err == -ENOMEM) return VM_FAULT_OOM; - BUG_ON(err); + /* + * err == -EBUSY is fine, we've raced against another thread + * that faulted-in the same page + */ + if (err != -EBUSY) + BUG_ON(err); return VM_FAULT_NOPAGE; } else { int err, ret = VM_FAULT_OOM; -- cgit v0.10.2 From 92618184cb92c5b39d4d8573572d576f9ccb3c28 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 3 Feb 2012 15:37:15 -0800 Subject: lkdtm: avoid calling lkdtm_do_action() with spinlock held lkdtm_do_action() may call sleeping functions like kmalloc(), so do not call it with spin lock held. Signed-off-by: WANG Cong Cc: Prarit Bhargava Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Reviewed-by: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 150cd70..28adefe 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -354,6 +354,7 @@ static void lkdtm_do_action(enum ctype which) static void lkdtm_handler(void) { unsigned long flags; + bool do_it = false; spin_lock_irqsave(&count_lock, flags); count--; @@ -361,10 +362,13 @@ static void lkdtm_handler(void) cp_name_to_str(cpoint), cp_type_to_str(cptype), count); if (count == 0) { - lkdtm_do_action(cptype); + do_it = true; count = cpoint_count; } spin_unlock_irqrestore(&count_lock, flags); + + if (do_it) + lkdtm_do_action(cptype); } static int lkdtm_register_cpoint(enum cname which) -- cgit v0.10.2 From cbcb8346054073d000ecac324763372d6abd44ac Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 3 Feb 2012 15:37:15 -0800 Subject: drivers/tty/vt/vt_ioctl.c: fix KDFONTOP 32bit compatibility layer KDFONTOP(GET) currently fails with EIO when being run in a 32bit userland with a 64bit kernel if the font width is not 8. This is because of the setting of the KD_FONT_FLAG_OLD flag, which makes con_font_get return EIO in such case. This flag should *not* be set for KDFONTOP, since it's actually the whole point of this flag (see comment in con_font_set for instance). Signed-off-by: Samuel Thibault Reviewed-by: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Arthur Taylor Cc: Jiri Slaby Cc: Jiri Olsa Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 5e096f4..65447c5 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -1463,7 +1463,6 @@ compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, if (!perm && op->op != KD_FONT_OP_GET) return -EPERM; op->data = compat_ptr(((struct compat_console_font_op *)op)->data); - op->flags |= KD_FONT_FLAG_OLD; i = con_font_op(vc, op); if (i) return i; -- cgit v0.10.2 From 55ca6140e9bb307efc97a9301a4f501de02a6fd6 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 3 Feb 2012 15:37:16 -0800 Subject: kprobes: fix a memory leak in function pre_handler_kretprobe() In function pre_handler_kretprobe(), the allocated kretprobe_instance object will get leaked if the entry_handler callback returns non-zero. This may cause all the preallocated kretprobe_instance objects exhausted. This issue can be reproduced by changing samples/kprobes/kretprobe_example.c to probe "mutex_unlock". And the fix is straightforward: just put the allocated kretprobe_instance object back onto the free_instances list. [akpm@linux-foundation.org: use raw_spin_lock/unlock] Signed-off-by: Jiang Liu Acked-by: Jim Keniston Acked-by: Ananth N Mavinakayanahalli Cc: Masami Hiramatsu Cc: Anil S Keshavamurthy Cc: "David S. Miller" Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 29f5b65..9788c0e 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1673,8 +1673,12 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, ri->rp = rp; ri->task = current; - if (rp->entry_handler && rp->entry_handler(ri, regs)) + if (rp->entry_handler && rp->entry_handler(ri, regs)) { + raw_spin_lock_irqsave(&rp->lock, flags); + hlist_add_head(&ri->hlist, &rp->free_instances); + raw_spin_unlock_irqrestore(&rp->lock, flags); return 0; + } arch_prepare_kretprobe(ri, regs); -- cgit v0.10.2 From 3deaa7190a8da38453c4fabd9dec7f66d17fff67 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Fri, 3 Feb 2012 15:37:17 -0800 Subject: readahead: fix pipeline break caused by block plug Herbert Poetzl reported a performance regression since 2.6.39. The test is a simple dd read, but with big block size. The reason is: T1: ra (A, A+128k), (A+128k, A+256k) T2: lock_page for page A, submit the 256k T3: hit page A+128K, ra (A+256k, A+384). the range isn't submitted because of plug and there isn't any lock_page till we hit page A+256k because all pages from A to A+256k is in memory T4: hit page A+256k, ra (A+384, A+ 512). Because of plug, the range isn't submitted again. T5: lock_page A+256k, so (A+256k, A+512k) will be submitted. The task is waitting for (A+256k, A+512k) finish. There is no request to disk in T3 and T4, so readahead pipeline breaks. We really don't need block plug for generic_file_aio_read() for buffered I/O. The readahead already has plug and has fine grained control when I/O should be submitted. Deleting plug for buffered I/O fixes the regression. One side effect is plug makes the request size 256k, the size is 128k without it. This is because default ra size is 128k and not a reason we need plug here. Vivek said: : We submit some readahead IO to device request queue but because of nested : plug, queue never gets unplugged. When read logic reaches a page which is : not in page cache, it waits for page to be read from the disk : (lock_page_killable()) and that time we flush the plug list. : : So effectively read ahead logic is kind of broken in parts because of : nested plugging. Removing top level plug (generic_file_aio_read()) for : buffered reads, will allow unplugging queue earlier for readahead. Signed-off-by: Shaohua Li Signed-off-by: Wu Fengguang Reported-by: Herbert Poetzl Tested-by: Eric Dumazet Cc: Christoph Hellwig Cc: Jens Axboe Cc: Vivek Goyal Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/filemap.c b/mm/filemap.c index 97f49ed..b662757 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1400,15 +1400,12 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long seg = 0; size_t count; loff_t *ppos = &iocb->ki_pos; - struct blk_plug plug; count = 0; retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); if (retval) return retval; - blk_start_plug(&plug); - /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ if (filp->f_flags & O_DIRECT) { loff_t size; @@ -1424,8 +1421,12 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, retval = filemap_write_and_wait_range(mapping, pos, pos + iov_length(iov, nr_segs) - 1); if (!retval) { + struct blk_plug plug; + + blk_start_plug(&plug); retval = mapping->a_ops->direct_IO(READ, iocb, iov, pos, nr_segs); + blk_finish_plug(&plug); } if (retval > 0) { *ppos = pos + retval; @@ -1481,7 +1482,6 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, break; } out: - blk_finish_plug(&plug); return retval; } EXPORT_SYMBOL(generic_file_aio_read); -- cgit v0.10.2 From 0bf380bc70ecba68cb4d74dc656cc2fa8c4d801a Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Fri, 3 Feb 2012 15:37:18 -0800 Subject: mm: compaction: check pfn_valid when entering a new MAX_ORDER_NR_PAGES block during isolation for migration When isolating for migration, migration starts at the start of a zone which is not necessarily pageblock aligned. Further, it stops isolating when COMPACT_CLUSTER_MAX pages are isolated so migrate_pfn is generally not aligned. This allows isolate_migratepages() to call pfn_to_page() on an invalid PFN which can result in a crash. This was originally reported against a 3.0-based kernel with the following trace in a crash dump. PID: 9902 TASK: d47aecd0 CPU: 0 COMMAND: "memcg_process_s" #0 [d72d3ad0] crash_kexec at c028cfdb #1 [d72d3b24] oops_end at c05c5322 #2 [d72d3b38] __bad_area_nosemaphore at c0227e60 #3 [d72d3bec] bad_area at c0227fb6 #4 [d72d3c00] do_page_fault at c05c72ec #5 [d72d3c80] error_code (via page_fault) at c05c47a4 EAX: 00000000 EBX: 000c0000 ECX: 00000001 EDX: 00000807 EBP: 000c0000 DS: 007b ESI: 00000001 ES: 007b EDI: f3000a80 GS: 6f50 CS: 0060 EIP: c030b15a ERR: ffffffff EFLAGS: 00010002 #6 [d72d3cb4] isolate_migratepages at c030b15a #7 [d72d3d14] zone_watermark_ok at c02d26cb #8 [d72d3d2c] compact_zone at c030b8de #9 [d72d3d68] compact_zone_order at c030bba1 #10 [d72d3db4] try_to_compact_pages at c030bc84 #11 [d72d3ddc] __alloc_pages_direct_compact at c02d61e7 #12 [d72d3e08] __alloc_pages_slowpath at c02d66c7 #13 [d72d3e78] __alloc_pages_nodemask at c02d6a97 #14 [d72d3eb8] alloc_pages_vma at c030a845 #15 [d72d3ed4] do_huge_pmd_anonymous_page at c03178eb #16 [d72d3f00] handle_mm_fault at c02f36c6 #17 [d72d3f30] do_page_fault at c05c70ed #18 [d72d3fb0] error_code (via page_fault) at c05c47a4 EAX: b71ff000 EBX: 00000001 ECX: 00001600 EDX: 00000431 DS: 007b ESI: 08048950 ES: 007b EDI: bfaa3788 SS: 007b ESP: bfaa36e0 EBP: bfaa3828 GS: 6f50 CS: 0073 EIP: 080487c8 ERR: ffffffff EFLAGS: 00010202 It was also reported by Herbert van den Bergh against 3.1-based kernel with the following snippet from the console log. BUG: unable to handle kernel paging request at 01c00008 IP: [] isolate_migratepages+0x119/0x390 *pdpt = 000000002f7ce001 *pde = 0000000000000000 It is expected that it also affects 3.2.x and current mainline. The problem is that pfn_valid is only called on the first PFN being checked and that PFN is not necessarily aligned. Lets say we have a case like this H = MAX_ORDER_NR_PAGES boundary | = pageblock boundary m = cc->migrate_pfn f = cc->free_pfn o = memory hole H------|------H------|----m-Hoooooo|ooooooH-f----|------H The migrate_pfn is just below a memory hole and the free scanner is beyond the hole. When isolate_migratepages started, it scans from migrate_pfn to migrate_pfn+pageblock_nr_pages which is now in a memory hole. It checks pfn_valid() on the first PFN but then scans into the hole where there are not necessarily valid struct pages. This patch ensures that isolate_migratepages calls pfn_valid when necessary. Reported-by: Herbert van den Bergh Tested-by: Herbert van den Bergh Signed-off-by: Mel Gorman Acked-by: Michal Nazarewicz Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/compaction.c b/mm/compaction.c index 71a58f6..bd939a5 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -313,6 +313,19 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, } else if (!locked) spin_lock_irq(&zone->lru_lock); + /* + * migrate_pfn does not necessarily start aligned to a + * pageblock. Ensure that pfn_valid is called when moving + * into a new MAX_ORDER_NR_PAGES range in case of large + * memory holes within the zone + */ + if ((low_pfn & (MAX_ORDER_NR_PAGES - 1)) == 0) { + if (!pfn_valid(low_pfn)) { + low_pfn += MAX_ORDER_NR_PAGES - 1; + continue; + } + } + if (!pfn_valid_within(low_pfn)) continue; nr_scanned++; -- cgit v0.10.2 From 500823195d0c9eec2a4637484f30cc93ec633d4a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 2 Feb 2012 13:54:25 +0200 Subject: Revert "mtd: atmel_nand: optimize read/write buffer functions" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit fb5427508abbd635e877fabdf55795488119c2d6. The reason is that it breaks 16 bits NAND flash as it was reported by Nikolaus Voss and confirmed by Eric Bénard. Nicolas Ferre alco confirmed: "After double checking with designers, I must admit that I misunderstood the way of optimizing accesses to SMC. 16 bit nand is not so common those days..." Reported-by: Nikolaus Voss Acked-by: Nicolas Ferre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse Cc: stable@kernel.org [3.1+] diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 4dd056e..35b4fb5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -161,6 +161,37 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) !!host->board->rdy_pin_active_low; } +/* + * Minimal-overhead PIO for data access. + */ +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_readsb(nand_chip->IO_ADDR_R, buf, len); +} + +static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); +} + +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_writesb(nand_chip->IO_ADDR_W, buf, len); +} + +static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2); +} + static void dma_complete_func(void *completion) { complete(completion); @@ -235,27 +266,33 @@ err_buf: static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; if (use_dma && len > mtd->oobsize) /* only use DMA for bigger than oob size: better performances */ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) return; - /* if no DMA operation possible, use PIO */ - memcpy_fromio(buf, chip->IO_ADDR_R, len); + if (host->board->bus_width_16) + atmel_read_buf16(mtd, buf, len); + else + atmel_read_buf8(mtd, buf, len); } static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) { struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; if (use_dma && len > mtd->oobsize) /* only use DMA for bigger than oob size: better performances */ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) return; - /* if no DMA operation possible, use PIO */ - memcpy_toio(chip->IO_ADDR_W, buf, len); + if (host->board->bus_width_16) + atmel_write_buf16(mtd, buf, len); + else + atmel_write_buf8(mtd, buf, len); } /* -- cgit v0.10.2 From 864e5e360ebb5acc7a41c6d6ac738a62aa1aa609 Mon Sep 17 00:00:00 2001 From: JD Zheng Date: Mon, 30 Jan 2012 10:59:01 -0800 Subject: ARM: bcmring: remove unused DMA map code Remove BCMRING DMA map code which is no longer used. This also fixes a build error with dma.c introduced by bfcd2ea6a40b33270564d706396f1b514a988d3c. Signed-off-by: Jiandong Zheng Signed-off-by: Olof Johansson diff --git a/arch/arm/mach-bcmring/dma.c b/arch/arm/mach-bcmring/dma.c index 1a1a27d..1024396 100644 --- a/arch/arm/mach-bcmring/dma.c +++ b/arch/arm/mach-bcmring/dma.c @@ -33,17 +33,11 @@ #include -#include #include #include #include #include -/* I don't quite understand why dc4 fails when this is set to 1 and DMA is enabled */ -/* especially since dc4 doesn't use kmalloc'd memory. */ - -#define ALLOW_MAP_OF_KMALLOC_MEMORY 0 - /* ---- Public Variables ------------------------------------------------- */ /* ---- Private Constants and Types -------------------------------------- */ @@ -53,24 +47,12 @@ #define CONTROLLER_FROM_HANDLE(handle) (((handle) >> 4) & 0x0f) #define CHANNEL_FROM_HANDLE(handle) ((handle) & 0x0f) -#define DMA_MAP_DEBUG 0 - -#if DMA_MAP_DEBUG -# define DMA_MAP_PRINT(fmt, args...) printk("%s: " fmt, __func__, ## args) -#else -# define DMA_MAP_PRINT(fmt, args...) -#endif /* ---- Private Variables ------------------------------------------------ */ static DMA_Global_t gDMA; static struct proc_dir_entry *gDmaDir; -static atomic_t gDmaStatMemTypeKmalloc = ATOMIC_INIT(0); -static atomic_t gDmaStatMemTypeVmalloc = ATOMIC_INIT(0); -static atomic_t gDmaStatMemTypeUser = ATOMIC_INIT(0); -static atomic_t gDmaStatMemTypeCoherent = ATOMIC_INIT(0); - #include "dma_device.c" /* ---- Private Function Prototypes -------------------------------------- */ @@ -79,34 +61,6 @@ static atomic_t gDmaStatMemTypeCoherent = ATOMIC_INIT(0); /****************************************************************************/ /** -* Displays information for /proc/dma/mem-type -*/ -/****************************************************************************/ - -static int dma_proc_read_mem_type(char *buf, char **start, off_t offset, - int count, int *eof, void *data) -{ - int len = 0; - - len += sprintf(buf + len, "dma_map_mem statistics\n"); - len += - sprintf(buf + len, "coherent: %d\n", - atomic_read(&gDmaStatMemTypeCoherent)); - len += - sprintf(buf + len, "kmalloc: %d\n", - atomic_read(&gDmaStatMemTypeKmalloc)); - len += - sprintf(buf + len, "vmalloc: %d\n", - atomic_read(&gDmaStatMemTypeVmalloc)); - len += - sprintf(buf + len, "user: %d\n", - atomic_read(&gDmaStatMemTypeUser)); - - return len; -} - -/****************************************************************************/ -/** * Displays information for /proc/dma/channels */ /****************************************************************************/ @@ -846,8 +800,6 @@ int dma_init(void) dma_proc_read_channels, NULL); create_proc_read_entry("devices", 0, gDmaDir, dma_proc_read_devices, NULL); - create_proc_read_entry("mem-type", 0, gDmaDir, - dma_proc_read_mem_type, NULL); } out: @@ -1565,767 +1517,3 @@ int dma_set_device_handler(DMA_Device_t dev, /* Device to set the callback for. } EXPORT_SYMBOL(dma_set_device_handler); - -/****************************************************************************/ -/** -* Initializes a memory mapping structure -*/ -/****************************************************************************/ - -int dma_init_mem_map(DMA_MemMap_t *memMap) -{ - memset(memMap, 0, sizeof(*memMap)); - - sema_init(&memMap->lock, 1); - - return 0; -} - -EXPORT_SYMBOL(dma_init_mem_map); - -/****************************************************************************/ -/** -* Releases any memory currently being held by a memory mapping structure. -*/ -/****************************************************************************/ - -int dma_term_mem_map(DMA_MemMap_t *memMap) -{ - down(&memMap->lock); /* Just being paranoid */ - - /* Free up any allocated memory */ - - up(&memMap->lock); - memset(memMap, 0, sizeof(*memMap)); - - return 0; -} - -EXPORT_SYMBOL(dma_term_mem_map); - -/****************************************************************************/ -/** -* Looks at a memory address and categorizes it. -* -* @return One of the values from the DMA_MemType_t enumeration. -*/ -/****************************************************************************/ - -DMA_MemType_t dma_mem_type(void *addr) -{ - unsigned long addrVal = (unsigned long)addr; - - if (addrVal >= CONSISTENT_BASE) { - /* NOTE: DMA virtual memory space starts at 0xFFxxxxxx */ - - /* dma_alloc_xxx pages are physically and virtually contiguous */ - - return DMA_MEM_TYPE_DMA; - } - - /* Technically, we could add one more classification. Addresses between VMALLOC_END */ - /* and the beginning of the DMA virtual address could be considered to be I/O space. */ - /* Right now, nobody cares about this particular classification, so we ignore it. */ - - if (is_vmalloc_addr(addr)) { - /* Address comes from the vmalloc'd region. Pages are virtually */ - /* contiguous but NOT physically contiguous */ - - return DMA_MEM_TYPE_VMALLOC; - } - - if (addrVal >= PAGE_OFFSET) { - /* PAGE_OFFSET is typically 0xC0000000 */ - - /* kmalloc'd pages are physically contiguous */ - - return DMA_MEM_TYPE_KMALLOC; - } - - return DMA_MEM_TYPE_USER; -} - -EXPORT_SYMBOL(dma_mem_type); - -/****************************************************************************/ -/** -* Looks at a memory address and determines if we support DMA'ing to/from -* that type of memory. -* -* @return boolean - -* return value != 0 means dma supported -* return value == 0 means dma not supported -*/ -/****************************************************************************/ - -int dma_mem_supports_dma(void *addr) -{ - DMA_MemType_t memType = dma_mem_type(addr); - - return (memType == DMA_MEM_TYPE_DMA) -#if ALLOW_MAP_OF_KMALLOC_MEMORY - || (memType == DMA_MEM_TYPE_KMALLOC) -#endif - || (memType == DMA_MEM_TYPE_USER); -} - -EXPORT_SYMBOL(dma_mem_supports_dma); - -/****************************************************************************/ -/** -* Maps in a memory region such that it can be used for performing a DMA. -* -* @return -*/ -/****************************************************************************/ - -int dma_map_start(DMA_MemMap_t *memMap, /* Stores state information about the map */ - enum dma_data_direction dir /* Direction that the mapping will be going */ - ) { - int rc; - - down(&memMap->lock); - - DMA_MAP_PRINT("memMap: %p\n", memMap); - - if (memMap->inUse) { - printk(KERN_ERR "%s: memory map %p is already being used\n", - __func__, memMap); - rc = -EBUSY; - goto out; - } - - memMap->inUse = 1; - memMap->dir = dir; - memMap->numRegionsUsed = 0; - - rc = 0; - -out: - - DMA_MAP_PRINT("returning %d", rc); - - up(&memMap->lock); - - return rc; -} - -EXPORT_SYMBOL(dma_map_start); - -/****************************************************************************/ -/** -* Adds a segment of memory to a memory map. Each segment is both -* physically and virtually contiguous. -* -* @return 0 on success, error code otherwise. -*/ -/****************************************************************************/ - -static int dma_map_add_segment(DMA_MemMap_t *memMap, /* Stores state information about the map */ - DMA_Region_t *region, /* Region that the segment belongs to */ - void *virtAddr, /* Virtual address of the segment being added */ - dma_addr_t physAddr, /* Physical address of the segment being added */ - size_t numBytes /* Number of bytes of the segment being added */ - ) { - DMA_Segment_t *segment; - - DMA_MAP_PRINT("memMap:%p va:%p pa:0x%x #:%d\n", memMap, virtAddr, - physAddr, numBytes); - - /* Sanity check */ - - if (((unsigned long)virtAddr < (unsigned long)region->virtAddr) - || (((unsigned long)virtAddr + numBytes)) > - ((unsigned long)region->virtAddr + region->numBytes)) { - printk(KERN_ERR - "%s: virtAddr %p is outside region @ %p len: %d\n", - __func__, virtAddr, region->virtAddr, region->numBytes); - return -EINVAL; - } - - if (region->numSegmentsUsed > 0) { - /* Check to see if this segment is physically contiguous with the previous one */ - - segment = ®ion->segment[region->numSegmentsUsed - 1]; - - if ((segment->physAddr + segment->numBytes) == physAddr) { - /* It is - just add on to the end */ - - DMA_MAP_PRINT("appending %d bytes to last segment\n", - numBytes); - - segment->numBytes += numBytes; - - return 0; - } - } - - /* Reallocate to hold more segments, if required. */ - - if (region->numSegmentsUsed >= region->numSegmentsAllocated) { - DMA_Segment_t *newSegment; - size_t oldSize = - region->numSegmentsAllocated * sizeof(*newSegment); - int newAlloc = region->numSegmentsAllocated + 4; - size_t newSize = newAlloc * sizeof(*newSegment); - - newSegment = kmalloc(newSize, GFP_KERNEL); - if (newSegment == NULL) { - return -ENOMEM; - } - memcpy(newSegment, region->segment, oldSize); - memset(&((uint8_t *) newSegment)[oldSize], 0, - newSize - oldSize); - kfree(region->segment); - - region->numSegmentsAllocated = newAlloc; - region->segment = newSegment; - } - - segment = ®ion->segment[region->numSegmentsUsed]; - region->numSegmentsUsed++; - - segment->virtAddr = virtAddr; - segment->physAddr = physAddr; - segment->numBytes = numBytes; - - DMA_MAP_PRINT("returning success\n"); - - return 0; -} - -/****************************************************************************/ -/** -* Adds a region of memory to a memory map. Each region is virtually -* contiguous, but not necessarily physically contiguous. -* -* @return 0 on success, error code otherwise. -*/ -/****************************************************************************/ - -int dma_map_add_region(DMA_MemMap_t *memMap, /* Stores state information about the map */ - void *mem, /* Virtual address that we want to get a map of */ - size_t numBytes /* Number of bytes being mapped */ - ) { - unsigned long addr = (unsigned long)mem; - unsigned int offset; - int rc = 0; - DMA_Region_t *region; - dma_addr_t physAddr; - - down(&memMap->lock); - - DMA_MAP_PRINT("memMap:%p va:%p #:%d\n", memMap, mem, numBytes); - - if (!memMap->inUse) { - printk(KERN_ERR "%s: Make sure you call dma_map_start first\n", - __func__); - rc = -EINVAL; - goto out; - } - - /* Reallocate to hold more regions. */ - - if (memMap->numRegionsUsed >= memMap->numRegionsAllocated) { - DMA_Region_t *newRegion; - size_t oldSize = - memMap->numRegionsAllocated * sizeof(*newRegion); - int newAlloc = memMap->numRegionsAllocated + 4; - size_t newSize = newAlloc * sizeof(*newRegion); - - newRegion = kmalloc(newSize, GFP_KERNEL); - if (newRegion == NULL) { - rc = -ENOMEM; - goto out; - } - memcpy(newRegion, memMap->region, oldSize); - memset(&((uint8_t *) newRegion)[oldSize], 0, newSize - oldSize); - - kfree(memMap->region); - - memMap->numRegionsAllocated = newAlloc; - memMap->region = newRegion; - } - - region = &memMap->region[memMap->numRegionsUsed]; - memMap->numRegionsUsed++; - - offset = addr & ~PAGE_MASK; - - region->memType = dma_mem_type(mem); - region->virtAddr = mem; - region->numBytes = numBytes; - region->numSegmentsUsed = 0; - region->numLockedPages = 0; - region->lockedPages = NULL; - - switch (region->memType) { - case DMA_MEM_TYPE_VMALLOC: - { - atomic_inc(&gDmaStatMemTypeVmalloc); - - /* printk(KERN_ERR "%s: vmalloc'd pages are not supported\n", __func__); */ - - /* vmalloc'd pages are not physically contiguous */ - - rc = -EINVAL; - break; - } - - case DMA_MEM_TYPE_KMALLOC: - { - atomic_inc(&gDmaStatMemTypeKmalloc); - - /* kmalloc'd pages are physically contiguous, so they'll have exactly */ - /* one segment */ - -#if ALLOW_MAP_OF_KMALLOC_MEMORY - physAddr = - dma_map_single(NULL, mem, numBytes, memMap->dir); - rc = dma_map_add_segment(memMap, region, mem, physAddr, - numBytes); -#else - rc = -EINVAL; -#endif - break; - } - - case DMA_MEM_TYPE_DMA: - { - /* dma_alloc_xxx pages are physically contiguous */ - - atomic_inc(&gDmaStatMemTypeCoherent); - - physAddr = (vmalloc_to_pfn(mem) << PAGE_SHIFT) + offset; - - dma_sync_single_for_cpu(NULL, physAddr, numBytes, - memMap->dir); - rc = dma_map_add_segment(memMap, region, mem, physAddr, - numBytes); - break; - } - - case DMA_MEM_TYPE_USER: - { - size_t firstPageOffset; - size_t firstPageSize; - struct page **pages; - struct task_struct *userTask; - - atomic_inc(&gDmaStatMemTypeUser); - -#if 1 - /* If the pages are user pages, then the dma_mem_map_set_user_task function */ - /* must have been previously called. */ - - if (memMap->userTask == NULL) { - printk(KERN_ERR - "%s: must call dma_mem_map_set_user_task when using user-mode memory\n", - __func__); - return -EINVAL; - } - - /* User pages need to be locked. */ - - firstPageOffset = - (unsigned long)region->virtAddr & (PAGE_SIZE - 1); - firstPageSize = PAGE_SIZE - firstPageOffset; - - region->numLockedPages = (firstPageOffset - + region->numBytes + - PAGE_SIZE - 1) / PAGE_SIZE; - pages = - kmalloc(region->numLockedPages * - sizeof(struct page *), GFP_KERNEL); - - if (pages == NULL) { - region->numLockedPages = 0; - return -ENOMEM; - } - - userTask = memMap->userTask; - - down_read(&userTask->mm->mmap_sem); - rc = get_user_pages(userTask, /* task */ - userTask->mm, /* mm */ - (unsigned long)region->virtAddr, /* start */ - region->numLockedPages, /* len */ - memMap->dir == DMA_FROM_DEVICE, /* write */ - 0, /* force */ - pages, /* pages (array of pointers to page) */ - NULL); /* vmas */ - up_read(&userTask->mm->mmap_sem); - - if (rc != region->numLockedPages) { - kfree(pages); - region->numLockedPages = 0; - - if (rc >= 0) { - rc = -EINVAL; - } - } else { - uint8_t *virtAddr = region->virtAddr; - size_t bytesRemaining; - int pageIdx; - - rc = 0; /* Since get_user_pages returns +ve number */ - - region->lockedPages = pages; - - /* We've locked the user pages. Now we need to walk them and figure */ - /* out the physical addresses. */ - - /* The first page may be partial */ - - dma_map_add_segment(memMap, - region, - virtAddr, - PFN_PHYS(page_to_pfn - (pages[0])) + - firstPageOffset, - firstPageSize); - - virtAddr += firstPageSize; - bytesRemaining = - region->numBytes - firstPageSize; - - for (pageIdx = 1; - pageIdx < region->numLockedPages; - pageIdx++) { - size_t bytesThisPage = - (bytesRemaining > - PAGE_SIZE ? PAGE_SIZE : - bytesRemaining); - - DMA_MAP_PRINT - ("pageIdx:%d pages[pageIdx]=%p pfn=%u phys=%u\n", - pageIdx, pages[pageIdx], - page_to_pfn(pages[pageIdx]), - PFN_PHYS(page_to_pfn - (pages[pageIdx]))); - - dma_map_add_segment(memMap, - region, - virtAddr, - PFN_PHYS(page_to_pfn - (pages - [pageIdx])), - bytesThisPage); - - virtAddr += bytesThisPage; - bytesRemaining -= bytesThisPage; - } - } -#else - printk(KERN_ERR - "%s: User mode pages are not yet supported\n", - __func__); - - /* user pages are not physically contiguous */ - - rc = -EINVAL; -#endif - break; - } - - default: - { - printk(KERN_ERR "%s: Unsupported memory type: %d\n", - __func__, region->memType); - - rc = -EINVAL; - break; - } - } - - if (rc != 0) { - memMap->numRegionsUsed--; - } - -out: - - DMA_MAP_PRINT("returning %d\n", rc); - - up(&memMap->lock); - - return rc; -} - -EXPORT_SYMBOL(dma_map_add_segment); - -/****************************************************************************/ -/** -* Maps in a memory region such that it can be used for performing a DMA. -* -* @return 0 on success, error code otherwise. -*/ -/****************************************************************************/ - -int dma_map_mem(DMA_MemMap_t *memMap, /* Stores state information about the map */ - void *mem, /* Virtual address that we want to get a map of */ - size_t numBytes, /* Number of bytes being mapped */ - enum dma_data_direction dir /* Direction that the mapping will be going */ - ) { - int rc; - - rc = dma_map_start(memMap, dir); - if (rc == 0) { - rc = dma_map_add_region(memMap, mem, numBytes); - if (rc < 0) { - /* Since the add fails, this function will fail, and the caller won't */ - /* call unmap, so we need to do it here. */ - - dma_unmap(memMap, 0); - } - } - - return rc; -} - -EXPORT_SYMBOL(dma_map_mem); - -/****************************************************************************/ -/** -* Setup a descriptor ring for a given memory map. -* -* It is assumed that the descriptor ring has already been initialized, and -* this routine will only reallocate a new descriptor ring if the existing -* one is too small. -* -* @return 0 on success, error code otherwise. -*/ -/****************************************************************************/ - -int dma_map_create_descriptor_ring(DMA_Device_t dev, /* DMA device (where the ring is stored) */ - DMA_MemMap_t *memMap, /* Memory map that will be used */ - dma_addr_t devPhysAddr /* Physical address of device */ - ) { - int rc; - int numDescriptors; - DMA_DeviceAttribute_t *devAttr; - DMA_Region_t *region; - DMA_Segment_t *segment; - dma_addr_t srcPhysAddr; - dma_addr_t dstPhysAddr; - int regionIdx; - int segmentIdx; - - devAttr = &DMA_gDeviceAttribute[dev]; - - down(&memMap->lock); - - /* Figure out how many descriptors we need */ - - numDescriptors = 0; - for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) { - region = &memMap->region[regionIdx]; - - for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed; - segmentIdx++) { - segment = ®ion->segment[segmentIdx]; - - if (memMap->dir == DMA_TO_DEVICE) { - srcPhysAddr = segment->physAddr; - dstPhysAddr = devPhysAddr; - } else { - srcPhysAddr = devPhysAddr; - dstPhysAddr = segment->physAddr; - } - - rc = - dma_calculate_descriptor_count(dev, srcPhysAddr, - dstPhysAddr, - segment-> - numBytes); - if (rc < 0) { - printk(KERN_ERR - "%s: dma_calculate_descriptor_count failed: %d\n", - __func__, rc); - goto out; - } - numDescriptors += rc; - } - } - - /* Adjust the size of the ring, if it isn't big enough */ - - if (numDescriptors > devAttr->ring.descriptorsAllocated) { - dma_free_descriptor_ring(&devAttr->ring); - rc = - dma_alloc_descriptor_ring(&devAttr->ring, - numDescriptors); - if (rc < 0) { - printk(KERN_ERR - "%s: dma_alloc_descriptor_ring failed: %d\n", - __func__, rc); - goto out; - } - } else { - rc = - dma_init_descriptor_ring(&devAttr->ring, - numDescriptors); - if (rc < 0) { - printk(KERN_ERR - "%s: dma_init_descriptor_ring failed: %d\n", - __func__, rc); - goto out; - } - } - - /* Populate the descriptors */ - - for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) { - region = &memMap->region[regionIdx]; - - for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed; - segmentIdx++) { - segment = ®ion->segment[segmentIdx]; - - if (memMap->dir == DMA_TO_DEVICE) { - srcPhysAddr = segment->physAddr; - dstPhysAddr = devPhysAddr; - } else { - srcPhysAddr = devPhysAddr; - dstPhysAddr = segment->physAddr; - } - - rc = - dma_add_descriptors(&devAttr->ring, dev, - srcPhysAddr, dstPhysAddr, - segment->numBytes); - if (rc < 0) { - printk(KERN_ERR - "%s: dma_add_descriptors failed: %d\n", - __func__, rc); - goto out; - } - } - } - - rc = 0; - -out: - - up(&memMap->lock); - return rc; -} - -EXPORT_SYMBOL(dma_map_create_descriptor_ring); - -/****************************************************************************/ -/** -* Maps in a memory region such that it can be used for performing a DMA. -* -* @return -*/ -/****************************************************************************/ - -int dma_unmap(DMA_MemMap_t *memMap, /* Stores state information about the map */ - int dirtied /* non-zero if any of the pages were modified */ - ) { - - int rc = 0; - int regionIdx; - int segmentIdx; - DMA_Region_t *region; - DMA_Segment_t *segment; - - down(&memMap->lock); - - for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) { - region = &memMap->region[regionIdx]; - - for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed; - segmentIdx++) { - segment = ®ion->segment[segmentIdx]; - - switch (region->memType) { - case DMA_MEM_TYPE_VMALLOC: - { - printk(KERN_ERR - "%s: vmalloc'd pages are not yet supported\n", - __func__); - rc = -EINVAL; - goto out; - } - - case DMA_MEM_TYPE_KMALLOC: - { -#if ALLOW_MAP_OF_KMALLOC_MEMORY - dma_unmap_single(NULL, - segment->physAddr, - segment->numBytes, - memMap->dir); -#endif - break; - } - - case DMA_MEM_TYPE_DMA: - { - dma_sync_single_for_cpu(NULL, - segment-> - physAddr, - segment-> - numBytes, - memMap->dir); - break; - } - - case DMA_MEM_TYPE_USER: - { - /* Nothing to do here. */ - - break; - } - - default: - { - printk(KERN_ERR - "%s: Unsupported memory type: %d\n", - __func__, region->memType); - rc = -EINVAL; - goto out; - } - } - - segment->virtAddr = NULL; - segment->physAddr = 0; - segment->numBytes = 0; - } - - if (region->numLockedPages > 0) { - int pageIdx; - - /* Some user pages were locked. We need to go and unlock them now. */ - - for (pageIdx = 0; pageIdx < region->numLockedPages; - pageIdx++) { - struct page *page = - region->lockedPages[pageIdx]; - - if (memMap->dir == DMA_FROM_DEVICE) { - SetPageDirty(page); - } - page_cache_release(page); - } - kfree(region->lockedPages); - region->numLockedPages = 0; - region->lockedPages = NULL; - } - - region->memType = DMA_MEM_TYPE_NONE; - region->virtAddr = NULL; - region->numBytes = 0; - region->numSegmentsUsed = 0; - } - memMap->userTask = NULL; - memMap->numRegionsUsed = 0; - memMap->inUse = 0; - -out: - up(&memMap->lock); - - return rc; -} - -EXPORT_SYMBOL(dma_unmap); diff --git a/arch/arm/mach-bcmring/include/mach/dma.h b/arch/arm/mach-bcmring/include/mach/dma.h index 1f2c531..7254378 100644 --- a/arch/arm/mach-bcmring/include/mach/dma.h +++ b/arch/arm/mach-bcmring/include/mach/dma.h @@ -26,15 +26,9 @@ /* ---- Include Files ---------------------------------------------------- */ #include -#include #include #include #include -#include -#include -#include -#include -#include /* ---- Constants and Types ---------------------------------------------- */ @@ -113,78 +107,6 @@ typedef struct { /**************************************************************************** * -* The DMA_MemType_t and DMA_MemMap_t are helper structures used to setup -* DMA chains from a variety of memory sources. -* -*****************************************************************************/ - -#define DMA_MEM_MAP_MIN_SIZE 4096 /* Pages less than this size are better */ - /* off not being DMA'd. */ - -typedef enum { - DMA_MEM_TYPE_NONE, /* Not a valid setting */ - DMA_MEM_TYPE_VMALLOC, /* Memory came from vmalloc call */ - DMA_MEM_TYPE_KMALLOC, /* Memory came from kmalloc call */ - DMA_MEM_TYPE_DMA, /* Memory came from dma_alloc_xxx call */ - DMA_MEM_TYPE_USER, /* Memory came from user space. */ - -} DMA_MemType_t; - -/* A segment represents a physically and virtually contiguous chunk of memory. */ -/* i.e. each segment can be DMA'd */ -/* A user of the DMA code will add memory regions. Each region may need to be */ -/* represented by one or more segments. */ - -typedef struct { - void *virtAddr; /* Virtual address used for this segment */ - dma_addr_t physAddr; /* Physical address this segment maps to */ - size_t numBytes; /* Size of the segment, in bytes */ - -} DMA_Segment_t; - -/* A region represents a virtually contiguous chunk of memory, which may be */ -/* made up of multiple segments. */ - -typedef struct { - DMA_MemType_t memType; - void *virtAddr; - size_t numBytes; - - /* Each region (virtually contiguous) consists of one or more segments. Each */ - /* segment is virtually and physically contiguous. */ - - int numSegmentsUsed; - int numSegmentsAllocated; - DMA_Segment_t *segment; - - /* When a region corresponds to user memory, we need to lock all of the pages */ - /* down before we can figure out the physical addresses. The lockedPage array contains */ - /* the pages that were locked, and which subsequently need to be unlocked once the */ - /* memory is unmapped. */ - - unsigned numLockedPages; - struct page **lockedPages; - -} DMA_Region_t; - -typedef struct { - int inUse; /* Is this mapping currently being used? */ - struct semaphore lock; /* Acquired when using this structure */ - enum dma_data_direction dir; /* Direction this transfer is intended for */ - - /* In the event that we're mapping user memory, we need to know which task */ - /* the memory is for, so that we can obtain the correct mm locks. */ - - struct task_struct *userTask; - - int numRegionsUsed; - int numRegionsAllocated; - DMA_Region_t *region; - -} DMA_MemMap_t; - -/**************************************************************************** -* * The DMA_DeviceAttribute_t contains information which describes a * particular DMA device (or peripheral). * @@ -570,124 +492,6 @@ int dma_alloc_double_dst_descriptors(DMA_Handle_t handle, /* DMA Handle */ /****************************************************************************/ /** -* Initializes a DMA_MemMap_t data structure -*/ -/****************************************************************************/ - -int dma_init_mem_map(DMA_MemMap_t *memMap /* Stores state information about the map */ - ); - -/****************************************************************************/ -/** -* Releases any memory currently being held by a memory mapping structure. -*/ -/****************************************************************************/ - -int dma_term_mem_map(DMA_MemMap_t *memMap /* Stores state information about the map */ - ); - -/****************************************************************************/ -/** -* Looks at a memory address and categorizes it. -* -* @return One of the values from the DMA_MemType_t enumeration. -*/ -/****************************************************************************/ - -DMA_MemType_t dma_mem_type(void *addr); - -/****************************************************************************/ -/** -* Sets the process (aka userTask) associated with a mem map. This is -* required if user-mode segments will be added to the mapping. -*/ -/****************************************************************************/ - -static inline void dma_mem_map_set_user_task(DMA_MemMap_t *memMap, - struct task_struct *task) -{ - memMap->userTask = task; -} - -/****************************************************************************/ -/** -* Looks at a memory address and determines if we support DMA'ing to/from -* that type of memory. -* -* @return boolean - -* return value != 0 means dma supported -* return value == 0 means dma not supported -*/ -/****************************************************************************/ - -int dma_mem_supports_dma(void *addr); - -/****************************************************************************/ -/** -* Initializes a memory map for use. Since this function acquires a -* sempaphore within the memory map, it is VERY important that dma_unmap -* be called when you're finished using the map. -*/ -/****************************************************************************/ - -int dma_map_start(DMA_MemMap_t *memMap, /* Stores state information about the map */ - enum dma_data_direction dir /* Direction that the mapping will be going */ - ); - -/****************************************************************************/ -/** -* Adds a segment of memory to a memory map. -* -* @return 0 on success, error code otherwise. -*/ -/****************************************************************************/ - -int dma_map_add_region(DMA_MemMap_t *memMap, /* Stores state information about the map */ - void *mem, /* Virtual address that we want to get a map of */ - size_t numBytes /* Number of bytes being mapped */ - ); - -/****************************************************************************/ -/** -* Creates a descriptor ring from a memory mapping. -* -* @return 0 on success, error code otherwise. -*/ -/****************************************************************************/ - -int dma_map_create_descriptor_ring(DMA_Device_t dev, /* DMA device (where the ring is stored) */ - DMA_MemMap_t *memMap, /* Memory map that will be used */ - dma_addr_t devPhysAddr /* Physical address of device */ - ); - -/****************************************************************************/ -/** -* Maps in a memory region such that it can be used for performing a DMA. -* -* @return -*/ -/****************************************************************************/ - -int dma_map_mem(DMA_MemMap_t *memMap, /* Stores state information about the map */ - void *addr, /* Virtual address that we want to get a map of */ - size_t count, /* Number of bytes being mapped */ - enum dma_data_direction dir /* Direction that the mapping will be going */ - ); - -/****************************************************************************/ -/** -* Maps in a memory region such that it can be used for performing a DMA. -* -* @return -*/ -/****************************************************************************/ - -int dma_unmap(DMA_MemMap_t *memMap, /* Stores state information about the map */ - int dirtied /* non-zero if any of the pages were modified */ - ); - -/****************************************************************************/ -/** * Initiates a transfer when the descriptors have already been setup. * * This is a special case, and normally, the dma_transfer_xxx functions should -- cgit v0.10.2 From ca43784daa7a400407d851799ac69d3de2b2ab4e Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sat, 4 Feb 2012 11:29:02 -0800 Subject: ARM: bcmring: fix build failure in mach-bcmring/arch.c Upstream commit d1fce9c115eeb93e01c01732dfe9a86cf76009cf "ARM: restart: bcmring: use new restart hook" breaks building of this platform, since what used to be the last field of the MACHINE_START/END block didn't have a trailing comma. Once another field was added below, we get: arch/arm/mach-bcmring/arch.c:198: error: request for member 'restart' in something not a structure or union Signed-off-by: Paul Gortmaker Acked-by: Jiandong Zheng Signed-off-by: Olof Johansson diff --git a/arch/arm/mach-bcmring/arch.c b/arch/arm/mach-bcmring/arch.c index 9e5e755..45c97b1 100644 --- a/arch/arm/mach-bcmring/arch.c +++ b/arch/arm/mach-bcmring/arch.c @@ -194,6 +194,6 @@ MACHINE_START(BCMRING, "BCMRING") .init_early = bcmring_init_early, .init_irq = bcmring_init_irq, .timer = &bcmring_timer, - .init_machine = bcmring_init_machine + .init_machine = bcmring_init_machine, .restart = bcmring_restart, MACHINE_END -- cgit v0.10.2 From 379e0be812ab8a2a351e784b0c987788f5123090 Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Fri, 3 Feb 2012 22:22:41 +0100 Subject: PM / Freezer: Thaw only kernel threads if freezing of kernel threads fails If freezing of kernel threads fails, we are expected to automatically thaw tasks in the error recovery path. However, at times, we encounter situations in which we would like the automatic error recovery path to thaw only the kernel threads, because we want to be able to do some more cleanup before we thaw userspace. Something like: error = freeze_kernel_threads(); if (error) { /* Do some cleanup */ /* Only then thaw userspace tasks*/ thaw_processes(); } An example of such a situation is where we freeze/thaw filesystems during suspend/hibernation. There, if freezing of kernel threads fails, we would like to thaw the frozen filesystems before thawing the userspace tasks. So, modify freeze_kernel_threads() to thaw only kernel threads in case of freezing failure. And change suspend_freeze_processes() accordingly. (At the same time, let us also get rid of the rather cryptic usage of the conditional operator (:?) in that function.) [rjw: In fact, this patch fixes a regression introduced during the 3.3 merge window, because without it thaw_processes() may be called before swsusp_free() in some situations and that may lead to massive memory allocation failures.] Signed-off-by: Srivatsa S. Bhat Acked-by: Tejun Heo Acked-by: Nigel Cunningham Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/power.h b/kernel/power/power.h index 0c4defe..21724ee 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -231,8 +231,28 @@ extern int pm_test_level; #ifdef CONFIG_SUSPEND_FREEZER static inline int suspend_freeze_processes(void) { - int error = freeze_processes(); - return error ? : freeze_kernel_threads(); + int error; + + error = freeze_processes(); + + /* + * freeze_processes() automatically thaws every task if freezing + * fails. So we need not do anything extra upon error. + */ + if (error) + goto Finish; + + error = freeze_kernel_threads(); + + /* + * freeze_kernel_threads() thaws only kernel threads upon freezing + * failure. So we have to thaw the userspace tasks ourselves. + */ + if (error) + thaw_processes(); + + Finish: + return error; } static inline void suspend_thaw_processes(void) diff --git a/kernel/power/process.c b/kernel/power/process.c index eeca003..7e42645 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -143,7 +143,10 @@ int freeze_processes(void) /** * freeze_kernel_threads - Make freezable kernel threads go to the refrigerator. * - * On success, returns 0. On failure, -errno and system is fully thawed. + * On success, returns 0. On failure, -errno and only the kernel threads are + * thawed, so as to give a chance to the caller to do additional cleanups + * (if any) before thawing the userspace tasks. So, it is the responsibility + * of the caller to thaw the userspace tasks, when the time is right. */ int freeze_kernel_threads(void) { @@ -159,7 +162,7 @@ int freeze_kernel_threads(void) BUG_ON(in_atomic()); if (error) - thaw_processes(); + thaw_kernel_threads(); return error; } -- cgit v0.10.2 From d020283dc694c9ec31b410f522252f7a8397e67d Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Fri, 3 Feb 2012 22:22:25 +0100 Subject: PM / QoS: CPU C-state breakage with PM Qos change Looks like change "PM QoS: Move and rename the implementation files" merged during the 3.2 development cycle made PM QoS depend on CONFIG_PM which depends on (PM_SLEEP || PM_RUNTIME). That breaks CPU C-states with kernels not having these CONFIGs, causing CPUs to spend time in Polling loop idle instead of going into deep C-states, consuming way way more power. This is with either acpi idle or intel idle enabled. Either CONFIG_PM should be enabled with any pm_qos users or the !CONFIG_PM pm_qos_request() should return sane defaults not to break the existing users. Here's is the patch for the latter option. [rjw: Modified the changelog slightly.] Signed-off-by: Venkatesh Pallipadi Signed-off-by: Rafael J. Wysocki Cc: stable@vger.kernel.org diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index e5bbcba..4d99e4e 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -110,7 +110,19 @@ static inline void pm_qos_remove_request(struct pm_qos_request *req) { return; } static inline int pm_qos_request(int pm_qos_class) - { return 0; } +{ + switch (pm_qos_class) { + case PM_QOS_CPU_DMA_LATENCY: + return PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + case PM_QOS_NETWORK_LATENCY: + return PM_QOS_NETWORK_LAT_DEFAULT_VALUE; + case PM_QOS_NETWORK_THROUGHPUT: + return PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE; + default: + return PM_QOS_DEFAULT_VALUE; + } +} + static inline int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { return 0; } -- cgit v0.10.2 From 5962b35c1de3254a2f03b95efd3b7854b874d7b7 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 3 Feb 2012 05:18:43 +0000 Subject: netprio_cgroup: Fix obo in get_prioidx It was recently pointed out to me that the get_prioidx function sets a bit in the prioidx map prior to checking to see if the index being set is out of bounds. This patch corrects that, avoiding the possiblity of us writing beyond the end of the array Signed-off-by: Neil Horman Reported-by: Stanislaw Gruszka CC: Stanislaw Gruszka CC: "David S. Miller" Signed-off-by: David S. Miller diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 3a9fd48..9ae183a 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -58,11 +58,12 @@ static int get_prioidx(u32 *prio) spin_lock_irqsave(&prioidx_map_lock, flags); prioidx = find_first_zero_bit(prioidx_map, sizeof(unsigned long) * PRIOIDX_SZ); + if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) { + spin_unlock_irqrestore(&prioidx_map_lock, flags); + return -ENOSPC; + } set_bit(prioidx, prioidx_map); spin_unlock_irqrestore(&prioidx_map_lock, flags); - if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) - return -ENOSPC; - atomic_set(&max_prioidx, prioidx); *prio = prioidx; return 0; -- cgit v0.10.2 From e2446eaab5585555a38ea0df4e01ff313dbb4ac9 Mon Sep 17 00:00:00 2001 From: Shawn Lu Date: Sat, 4 Feb 2012 12:38:09 +0000 Subject: tcp_v4_send_reset: binding oif to iif in no sock case Binding RST packet outgoing interface to incoming interface for tcp v4 when there is no socket associate with it. when sk is not NULL, using sk->sk_bound_dev_if instead. (suggested by Eric Dumazet). This has few benefits: 1. tcp_v6_send_reset already did that. 2. This helps tcp connect with SO_BINDTODEVICE set. When connection is lost, we still able to sending out RST using same interface. 3. we are sending reply, it is most likely to be succeed if iif is used Signed-off-by: Shawn Lu Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 337ba4c..94d683a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -651,6 +651,11 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len, IPPROTO_TCP, 0); arg.csumoffset = offsetof(struct tcphdr, check) / 2; arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0; + /* When socket is gone, all binding information is lost. + * routing might fail in this case. using iif for oif to + * make sure we can deliver it + */ + arg.bound_dev_if = sk ? sk->sk_bound_dev_if : inet_iif(skb); net = dev_net(skb_dst(skb)->dev); arg.tos = ip_hdr(skb)->tos; -- cgit v0.10.2 From e6b45241c57a83197e5de9166b3b0d32ac562609 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Sat, 4 Feb 2012 13:04:46 +0000 Subject: ipv4: reset flowi parameters on route connect Eric Dumazet found that commit 813b3b5db83 (ipv4: Use caller's on-stack flowi as-is in output route lookups.) that comes in 3.0 added a regression. The problem appears to be that resulting flowi4_oif is used incorrectly as input parameter to some routing lookups. The result is that when connecting to local port without listener if the IP address that is used is not on a loopback interface we incorrectly assign RTN_UNICAST to the output route because no route is matched by oif=lo. The RST packet can not be sent immediately by tcp_v4_send_reset because it expects RTN_LOCAL. So, change ip_route_connect and ip_route_newports to update the flowi4 fields that are input parameters because we do not want unnecessary binding to oif. To make it clear what are the input parameters that can be modified during lookup and to show which fields of floiw4 are reused add a new function to update the flowi4 structure: flowi4_update_output. Thanks to Yurij M. Plotnikov for providing a bug report including a program to reproduce the problem. Thanks to Eric Dumazet for tracking the problem down to tcp_v4_send_reset and providing initial fix. Reported-by: Yurij M. Plotnikov Signed-off-by: Julian Anastasov Acked-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/include/net/flow.h b/include/net/flow.h index 9b58243..6c469db 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -93,6 +93,16 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->fl4_dport = dport; fl4->fl4_sport = sport; } + +/* Reset some input parameters after previous lookup */ +static inline void flowi4_update_output(struct flowi4 *fl4, int oif, __u8 tos, + __be32 daddr, __be32 saddr) +{ + fl4->flowi4_oif = oif; + fl4->flowi4_tos = tos; + fl4->daddr = daddr; + fl4->saddr = saddr; +} struct flowi6 { diff --git a/include/net/route.h b/include/net/route.h index 91855d1..b1c0d5b 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -270,6 +270,7 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4, if (IS_ERR(rt)) return rt; ip_rt_put(rt); + flowi4_update_output(fl4, oif, tos, fl4->daddr, fl4->saddr); } security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -284,6 +285,9 @@ static inline struct rtable *ip_route_newports(struct flowi4 *fl4, struct rtable fl4->fl4_dport = dport; fl4->fl4_sport = sport; ip_rt_put(rt); + flowi4_update_output(fl4, sk->sk_bound_dev_if, + RT_CONN_FLAGS(sk), fl4->daddr, + fl4->saddr); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(sock_net(sk), fl4, sk); } -- cgit v0.10.2 From 585c0fd8216e0c9f98e2434092af7ec0f999522d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 27 Jan 2012 05:43:59 -0800 Subject: hwmon: (w83627ehf) Fix number of fans for NCT6776F NCT6776F can select fan input pins for fans 3 to 5 with a secondary set of chip register bits. Check that second set of bits in addition to the first set to detect if fans 3..5 are monitored. Signed-off-by: Guenter Roeck Cc: stable@vger.kernel.org # 3.0+ Acked-by: Jean Delvare diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 2dfae7d..4d383e7 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1920,9 +1920,26 @@ w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, fan4min = 0; fan5pin = 0; } else if (sio_data->kind == nct6776) { - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); - fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); - fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); + bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; + + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); + regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); + + if (regval & 0x80) + fan3pin = gpok; + else + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + + if (regval & 0x40) + fan4pin = gpok; + else + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); + + if (regval & 0x20) + fan5pin = gpok; + else + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); + fan4min = fan4pin; } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { fan3pin = 1; -- cgit v0.10.2 From 3a92d687c8015860a19213e3c102cad6b722f83c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 5 Feb 2012 15:09:28 +1100 Subject: crypto: sha512 - Avoid stack bloat on i386 Unfortunately in reducing W from 80 to 16 we ended up unrolling the loop twice. As gcc has issues dealing with 64-bit ops on i386 this means that we end up using even more stack space (>1K). This patch solves the W reduction by moving LOAD_OP/BLEND_OP into the loop itself, thus avoiding the need to duplicate it. While the stack space still isn't great (>0.5K) it is at least in the same ball park as the amount of stack used for our C sha1 implementation. Note that this patch basically reverts to the original code so the diff looks bigger than it really is. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 3edebfd..f04af93 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -89,46 +89,42 @@ sha512_transform(u64 *state, const u8 *input) int i; u64 W[16]; - /* load the input */ - for (i = 0; i < 16; i++) - LOAD_OP(i, W, input); - /* load the state into our registers */ a=state[0]; b=state[1]; c=state[2]; d=state[3]; e=state[4]; f=state[5]; g=state[6]; h=state[7]; -#define SHA512_0_15(i, a, b, c, d, e, f, g, h) \ - t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[i]; \ - t2 = e0(a) + Maj(a, b, c); \ - d += t1; \ - h = t1 + t2 - -#define SHA512_16_79(i, a, b, c, d, e, f, g, h) \ - BLEND_OP(i, W); \ - t1 = h + e1(e) + Ch(e, f, g) + sha512_K[i] + W[(i)&15]; \ - t2 = e0(a) + Maj(a, b, c); \ - d += t1; \ - h = t1 + t2 - - for (i = 0; i < 16; i += 8) { - SHA512_0_15(i, a, b, c, d, e, f, g, h); - SHA512_0_15(i + 1, h, a, b, c, d, e, f, g); - SHA512_0_15(i + 2, g, h, a, b, c, d, e, f); - SHA512_0_15(i + 3, f, g, h, a, b, c, d, e); - SHA512_0_15(i + 4, e, f, g, h, a, b, c, d); - SHA512_0_15(i + 5, d, e, f, g, h, a, b, c); - SHA512_0_15(i + 6, c, d, e, f, g, h, a, b); - SHA512_0_15(i + 7, b, c, d, e, f, g, h, a); - } - for (i = 16; i < 80; i += 8) { - SHA512_16_79(i, a, b, c, d, e, f, g, h); - SHA512_16_79(i + 1, h, a, b, c, d, e, f, g); - SHA512_16_79(i + 2, g, h, a, b, c, d, e, f); - SHA512_16_79(i + 3, f, g, h, a, b, c, d, e); - SHA512_16_79(i + 4, e, f, g, h, a, b, c, d); - SHA512_16_79(i + 5, d, e, f, g, h, a, b, c); - SHA512_16_79(i + 6, c, d, e, f, g, h, a, b); - SHA512_16_79(i + 7, b, c, d, e, f, g, h, a); + /* now iterate */ + for (i=0; i<80; i+=8) { + if (!(i & 8)) { + int j; + + if (i < 16) { + /* load the input */ + for (j = 0; j < 16; j++) + LOAD_OP(i + j, W, input); + } else { + for (j = 0; j < 16; j++) { + BLEND_OP(i + j, W); + } + } + } + + t1 = h + e1(e) + Ch(e,f,g) + sha512_K[i ] + W[(i & 15)]; + t2 = e0(a) + Maj(a,b,c); d+=t1; h=t1+t2; + t1 = g + e1(d) + Ch(d,e,f) + sha512_K[i+1] + W[(i & 15) + 1]; + t2 = e0(h) + Maj(h,a,b); c+=t1; g=t1+t2; + t1 = f + e1(c) + Ch(c,d,e) + sha512_K[i+2] + W[(i & 15) + 2]; + t2 = e0(g) + Maj(g,h,a); b+=t1; f=t1+t2; + t1 = e + e1(b) + Ch(b,c,d) + sha512_K[i+3] + W[(i & 15) + 3]; + t2 = e0(f) + Maj(f,g,h); a+=t1; e=t1+t2; + t1 = d + e1(a) + Ch(a,b,c) + sha512_K[i+4] + W[(i & 15) + 4]; + t2 = e0(e) + Maj(e,f,g); h+=t1; d=t1+t2; + t1 = c + e1(h) + Ch(h,a,b) + sha512_K[i+5] + W[(i & 15) + 5]; + t2 = e0(d) + Maj(d,e,f); g+=t1; c=t1+t2; + t1 = b + e1(g) + Ch(g,h,a) + sha512_K[i+6] + W[(i & 15) + 6]; + t2 = e0(c) + Maj(c,d,e); f+=t1; b=t1+t2; + t1 = a + e1(f) + Ch(f,g,h) + sha512_K[i+7] + W[(i & 15) + 7]; + t2 = e0(b) + Maj(b,c,d); e+=t1; a=t1+t2; } state[0] += a; state[1] += b; state[2] += c; state[3] += d; -- cgit v0.10.2 From b530b1930bbd9d005345133f0ff0c556d2a52b19 Mon Sep 17 00:00:00 2001 From: David Lv Date: Sat, 4 Feb 2012 23:22:26 +0000 Subject: via-velocity: S3 resume fix. Initially diagnosed on Ubuntu 11.04 with kernel 2.6.38. velocity_close is not called during a suspend / resume cycle in this driver and it has no business playing directly with power states. Signed-off-by: David Lv Acked-by: Francois Romieu Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 4128d6b..cb35b14 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -2491,9 +2491,6 @@ static int velocity_close(struct net_device *dev) if (dev->irq != 0) free_irq(dev->irq, dev); - /* Power down the chip */ - pci_set_power_state(vptr->pdev, PCI_D3hot); - velocity_free_rings(vptr); vptr->flags &= (~VELOCITY_FLAGS_OPENED); -- cgit v0.10.2 From 977b7e3a52a7421ad33a393a38ece59f3d41c2fa Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 4 Feb 2012 20:54:03 -0600 Subject: writeback: fix dereferencing NULL bdi->dev on trace_writeback_queue When a SD card is hot removed without umount, del_gendisk() will call bdi_unregister() without destroying/freeing it. This leaves the bdi in the bdi->dev = NULL, bdi->wb.task = NULL, bdi->bdi_list removed state. When sync(2) gets the bdi before bdi_unregister() and calls bdi_queue_work() after the unregister, trace_writeback_queue will be dereferencing the NULL bdi->dev. Fix it with a simple test for NULL. LKML-reference: http://lkml.org/lkml/2012/1/18/346 Cc: stable@kernel.org Reported-by: Rabin Vincent Tested-by: Namjae Jeon Signed-off-by: Wu Fengguang diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 06d302e..5973410 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -47,7 +47,10 @@ DECLARE_EVENT_CLASS(writeback_work_class, __field(int, reason) ), TP_fast_assign( - strncpy(__entry->name, dev_name(bdi->dev), 32); + struct device *dev = bdi->dev; + if (!dev) + dev = default_backing_dev_info.dev; + strncpy(__entry->name, dev_name(dev), 32); __entry->nr_pages = work->nr_pages; __entry->sb_dev = work->sb ? work->sb->s_dev : 0; __entry->sync_mode = work->sync_mode; -- cgit v0.10.2 From b1a4874e0d03cb65a1bde950d8f5f2d8743f66ed Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 25 Jan 2012 17:20:59 +0100 Subject: spi: Add spi-s3c64xx driver dependency on ARCH_EXYNOS4 The spi-s3c64xx driver is also used on Exynos4 so update the dependency to enable build on those platforms. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park [grant.likely: relax depends to ARCH_EXYNOS instead of ARCH_EXYNOS4] Signed-off-by: Grant Likely diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3f9a47e..8293658 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -299,7 +299,7 @@ config SPI_S3C24XX_FIQ config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" - depends on (ARCH_S3C64XX || ARCH_S5P64X0) + depends on (ARCH_S3C64XX || ARCH_S5P64X0 || ARCH_EXYNOS) select S3C64XX_DMA if ARCH_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. -- cgit v0.10.2 From c88db233251b026fda775428f0250c760553e216 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Thu, 2 Feb 2012 14:20:30 +0100 Subject: spi-topcliff-pch: rename pch_spi_pcidev to pch_spi_pcidev_driver Rename static struct pci_driver pch_spi_pcidev to pch_spi_pcidev_driver to get rid of warnings from modpost checks. Signed-off-by: Danny Kukawka Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 2a6429d..10182eb 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1720,7 +1720,7 @@ static int pch_spi_resume(struct pci_dev *pdev) #endif -static struct pci_driver pch_spi_pcidev = { +static struct pci_driver pch_spi_pcidev_driver = { .name = "pch_spi", .id_table = pch_spi_pcidev_id, .probe = pch_spi_probe, @@ -1736,7 +1736,7 @@ static int __init pch_spi_init(void) if (ret) return ret; - ret = pci_register_driver(&pch_spi_pcidev); + ret = pci_register_driver(&pch_spi_pcidev_driver); if (ret) return ret; @@ -1746,7 +1746,7 @@ module_init(pch_spi_init); static void __exit pch_spi_exit(void) { - pci_unregister_driver(&pch_spi_pcidev); + pci_unregister_driver(&pch_spi_pcidev_driver); platform_driver_unregister(&pch_spi_pd_driver); } module_exit(pch_spi_exit); -- cgit v0.10.2 From 9fa73472ddbcd3da87d35a7f4566eaaf345f798e Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Mon, 6 Feb 2012 08:57:29 +0100 Subject: block: fix ioc locking warning Meelis reported a warning: WARNING: at kernel/timer.c:1122 run_timer_softirq+0x199/0x1ec() Hardware name: 939Dual-SATA2 timer: cfq_idle_slice_timer+0x0/0xaa preempt leak: 00000102 -> 00000103 Modules linked in: sr_mod cdrom videodev media drm_kms_helper ohci_hcd ehci_hcd v4l2_compat_ioctl32 usbcore i2c_ali15x3 snd_seq drm snd_timer snd_seq Pid: 0, comm: swapper Not tainted 3.3.0-rc2-00110-gd125666 #176 Call Trace: [] warn_slowpath_common+0x7e/0x96 [] ? cfq_slice_expired+0x1d/0x1d [] warn_slowpath_fmt+0x41/0x43 [] ? cfq_idle_slice_timer+0xa1/0xaa [] ? cfq_slice_expired+0x1d/0x1d [] run_timer_softirq+0x199/0x1ec [] ? timekeeping_get_ns+0x12/0x31 [] ? apic_write+0x11/0x13 [] __do_softirq+0x74/0xfa [] call_softirq+0x1a/0x30 [] do_softirq+0x31/0x68 [] irq_exit+0x3d/0xa3 [] smp_apic_timer_interrupt+0x6b/0x77 [] apic_timer_interrupt+0x69/0x70 [] ? sched_clock_cpu+0x73/0x7d [] ? sched_clock_cpu+0x73/0x7d [] ? default_idle+0x1e/0x32 [] ? default_idle+0x18/0x32 [] cpu_idle+0x87/0xd1 [] rest_init+0x85/0x89 [] start_kernel+0x2eb/0x2f8 [] x86_64_start_reservations+0x7e/0x82 [] x86_64_start_kernel+0xf0/0xf7 this_q == locked_q is possible. There are two problems here: 1. In UP case, there is preemption counter issue as spin_trylock always successes. 2. In SMP case, the loop breaks too earlier. Signed-off-by: Shaohua Li Reported-by: Meelis Roos Reported-by: Knut Petersen Tested-by: Knut Petersen Signed-off-by: Jens Axboe diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 27a06e0..7490b6d 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -204,7 +204,9 @@ void put_io_context(struct io_context *ioc, struct request_queue *locked_q) spin_unlock(last_q->queue_lock); last_q = NULL; - if (!spin_trylock(this_q->queue_lock)) + /* spin_trylock() always successes in UP case */ + if (this_q != locked_q && + !spin_trylock(this_q->queue_lock)) break; last_q = this_q; continue; -- cgit v0.10.2 From 822bfa51ce44f2c63c300fdb76dc99c4d5a5ca9f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 6 Feb 2012 10:20:45 +0100 Subject: cdrom: use copy_to_user() without the underscores "nframes" comes from the user and "nframes * CD_FRAMESIZE_RAW" can wrap on 32 bit systems. That would have been ok if we used the same wrapped value for the copy, but we use a shifted value. We should just use the checked version of copy_to_user() because it's not going to make a difference to the speed. Cc: stable@vger.kernel.com Signed-off-by: Dan Carpenter Signed-off-by: Jens Axboe diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 55eaf47..8fefe59 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2119,11 +2119,6 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, if (!nr) return -ENOMEM; - if (!access_ok(VERIFY_WRITE, ubuf, nframes * CD_FRAMESIZE_RAW)) { - ret = -EFAULT; - goto out; - } - cgc.data_direction = CGC_DATA_READ; while (nframes > 0) { if (nr > nframes) @@ -2132,7 +2127,7 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW); if (ret) break; - if (__copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { + if (copy_to_user(ubuf, cgc.buffer, CD_FRAMESIZE_RAW * nr)) { ret = -EFAULT; break; } @@ -2140,7 +2135,6 @@ static int cdrom_read_cdda_old(struct cdrom_device_info *cdi, __u8 __user *ubuf, nframes -= nr; lba += nr; } -out: kfree(cgc.buffer); return ret; } -- cgit v0.10.2 From 226e01ef0da0b1a4c2c3922fb83ff3f9e4dfb508 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 5 Feb 2012 01:27:44 +0100 Subject: ALSA: emu8000: Remove duplicate linux/moduleparam.h include from emu8000_patch.c The header 'linux/moduleparam.h' is included twice in 'sound/isa/sb/emu8000_patch.c'. Once is enough. Signed-off-by: Jesper Juhl Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c index e09f144..c99c607 100644 --- a/sound/isa/sb/emu8000_patch.c +++ b/sound/isa/sb/emu8000_patch.c @@ -22,7 +22,6 @@ #include "emu8000_local.h" #include #include -#include static int emu8000_reset_addr; module_param(emu8000_reset_addr, int, 0444); -- cgit v0.10.2 From eedec3d3854a390fc14008f265930f8c22b0373f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 6 Feb 2012 10:24:04 +0100 Subject: ALSA: hda/realtek - Fix a wrong condition sparse complains that "spec->multiout.dac_nids" is a pointer. sound/pci/hda/patch_realtek.c:2321:37: error: incompatible types for operation (>) sound/pci/hda/patch_realtek.c:2321:37: left side has type unsigned short const [usertype] *dac_nids sound/pci/hda/patch_realtek.c:2321:37: right side has type int It was meant to be num_dacs instead of dac_nids. Although the current code still works as expected (when num_dacs is zero, dac_nids should be NULL, too), better to fix now, of course. Reported-by: Dan Carpenter Cc: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 33b6077..485a837 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2322,7 +2322,7 @@ static int alc_build_pcms(struct hda_codec *codec) "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; - if (spec->multiout.dac_nids > 0) { + if (spec->multiout.num_dacs > 0) { p = spec->stream_analog_playback; if (!p) p = &alc_pcm_analog_playback; -- cgit v0.10.2 From db966f8abb9ba74f7d5a7230f51572f52c31c4e5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 Feb 2012 12:07:08 +0000 Subject: ASoC: wm8994: Enabling VMID should take a runtime PM reference We can enable VMID independently of the bias in some use cases so we need to ensure that the core device is powered up. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 8623950..81795eb 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -770,6 +770,8 @@ static void vmid_reference(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + pm_runtime_get_sync(codec->dev); + wm8994->vmid_refcount++; dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n", @@ -837,6 +839,8 @@ static void vmid_dereference(struct snd_soc_codec *codec) WM8994_VMID_BUF_ENA | WM8994_VMID_RAMP_MASK, 0); } + + pm_runtime_put(codec->dev); } static int vmid_event(struct snd_soc_dapm_widget *w, -- cgit v0.10.2 From d7cb3dbd10c09081fafd05fcbef590ce04e402ff Mon Sep 17 00:00:00 2001 From: Przemo Firszt Date: Sun, 5 Feb 2012 22:35:24 +0000 Subject: HID: wacom: Fix invalid power_supply_powers calls power_supply_powers calls added in 35b4c01e2 ("power_supply: add "powers" links to self-powered HID devices") have to be called after power device is created. This patch also fixes the second call - it has to be "ac" instead of "battery" Signed-off-by: Przemo Firszt Signed-off-by: Chris Bagwell Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index b47e58b..acab74c 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -531,7 +531,6 @@ static int wacom_probe(struct hid_device *hdev, wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; wdata->battery.use_for_apm = 0; - power_supply_powers(&wdata->battery, &hdev->dev); ret = power_supply_register(&hdev->dev, &wdata->battery); if (ret) { @@ -540,6 +539,8 @@ static int wacom_probe(struct hid_device *hdev, goto err_battery; } + power_supply_powers(&wdata->battery, &hdev->dev); + wdata->ac.properties = wacom_ac_props; wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props); wdata->ac.get_property = wacom_ac_get_property; @@ -547,14 +548,14 @@ static int wacom_probe(struct hid_device *hdev, wdata->ac.type = POWER_SUPPLY_TYPE_MAINS; wdata->ac.use_for_apm = 0; - power_supply_powers(&wdata->battery, &hdev->dev); - ret = power_supply_register(&hdev->dev, &wdata->ac); if (ret) { hid_warn(hdev, "can't create ac battery attribute, err: %d\n", ret); goto err_ac; } + + power_supply_powers(&wdata->ac, &hdev->dev); #endif return 0; -- cgit v0.10.2 From 2da8cbf8a6b454296c9db7b57cac72d5a39e3aa7 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 3 Feb 2012 20:34:02 -0800 Subject: mwifiex: add NULL checks in driver unload path If driver load is failed, sometimes few pointers may remain uninitialized ex. priv->wdev, priv->netdev, adapter->sleep_cfm This will cause NULL pointer dereferance while unloading the driver. Signed-off-by: Amitkumar Karwar Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index e05b417..1d0ec57 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -382,7 +382,8 @@ mwifiex_free_adapter(struct mwifiex_adapter *adapter) adapter->if_ops.cleanup_if(adapter); - dev_kfree_skb_any(adapter->sleep_cfm); + if (adapter->sleep_cfm) + dev_kfree_skb_any(adapter->sleep_cfm); } /* diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 84be196..b728f54 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -822,7 +822,9 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) continue; rtnl_lock(); - mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev); + if (priv->wdev && priv->netdev) + mwifiex_del_virtual_intf(priv->wdev->wiphy, + priv->netdev); rtnl_unlock(); } @@ -830,9 +832,11 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) if (!priv) goto exit_remove; - wiphy_unregister(priv->wdev->wiphy); - wiphy_free(priv->wdev->wiphy); - kfree(priv->wdev); + if (priv->wdev) { + wiphy_unregister(priv->wdev->wiphy); + wiphy_free(priv->wdev->wiphy); + kfree(priv->wdev); + } mwifiex_terminate_workqueue(adapter); -- cgit v0.10.2 From f88373fa47f3ce6590fdfaa742d0ddacc2ae017f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 5 Feb 2012 21:15:17 +0100 Subject: ath9k: fix a WEP crypto related regression commit b4a82a0 "ath9k_hw: fix interpretation of the rx KeyMiss flag" fixed the interpretation of the KeyMiss flag for keycache based lookups, however WEP encryption uses a static index, so KeyMiss is always asserted for it, even though frames are decrypted properly. Fix this by clearing the ATH9K_RXERR_KEYMISS flag if no keycache based lookup was performed. Signed-off-by: Felix Fietkau Cc: stable@vger.kernel.org Reported-by: Laurent Bonnans Reported-by: Jurica Vukadin Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 0e666fb..7e1a91a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -822,6 +822,14 @@ static bool ath9k_rx_accept(struct ath_common *common, (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | ATH9K_RXERR_KEYMISS)); + /* + * Key miss events are only relevant for pairwise keys where the + * descriptor does contain a valid key index. This has been observed + * mostly with CCMP encryption. + */ + if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID) + rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; + if (!rx_stats->rs_datalen) return false; /* -- cgit v0.10.2 From 55a2bb4a6d5e8c7b324d003e130fd9aaf33be4e6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 5 Feb 2012 21:15:18 +0100 Subject: ath9k_hw: fix a RTS/CTS timeout regression commit adb5066 "ath9k_hw: do not apply the 2.4 ghz ack timeout workaround to cts" reduced the hardware CTS timeout to the normal values specified by the standard, but it turns out while it doesn't need the same extra time that it needs for the ACK timeout, it does need more than the value specified in the standard, but only for 2.4 GHz. This patch brings the CTS timeout value in sync with the initialization values, while still allowing adjustment for bigger distances. Signed-off-by: Felix Fietkau Cc: stable@vger.kernel.org Reported-by: Seth Forshee Reported-by: Marek Lindner Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ee77595..87db1ee 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1037,13 +1037,16 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) /* * Workaround for early ACK timeouts, add an offset to match the - * initval's 64us ack timeout value. + * initval's 64us ack timeout value. Use 48us for the CTS timeout. * This was initially only meant to work around an issue with delayed * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ - if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) + if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) { acktimeout += 64 - sifstime - ah->slottime; + ctstimeout += 48 - sifstime - ah->slottime; + } + ath9k_hw_set_sifs_time(ah, sifstime); ath9k_hw_setslottime(ah, slottime); -- cgit v0.10.2 From 715252d41912941efb791a7b7bad94d2614dc5c3 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 4 Feb 2012 23:49:40 +0100 Subject: IB/srpt: Don't return freed pointer from srpt_alloc_ioctx_ring() Signed-off-by: Jesper Juhl Signed-off-by: Roland Dreier diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index c125614..2b73d43 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -687,6 +687,7 @@ err: while (--i >= 0) srpt_free_ioctx(sdev, ring[i], dma_size, dir); kfree(ring); + ring = NULL; out: return ring; } -- cgit v0.10.2 From f08ad06c05bf58ac38ad7b2fc97b243c12459ff2 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Mon, 6 Feb 2012 06:26:17 +0000 Subject: mlx4_core: fix memory leak at multi_func_cleanup Perform cleanup also in non-master flow. The VFs use communication channel as well. Signed-off-by: Eugenia Emantayev Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 405e6ac..eaf09d4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1616,12 +1616,12 @@ void mlx4_multi_func_cleanup(struct mlx4_dev *dev) kfree(priv->mfunc.master.slave_state[i].vlan_filter[port]); } kfree(priv->mfunc.master.slave_state); - iounmap(priv->mfunc.comm); - dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, - priv->mfunc.vhcr, - priv->mfunc.vhcr_dma); - priv->mfunc.vhcr = NULL; } + + iounmap(priv->mfunc.comm); + dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, + priv->mfunc.vhcr, priv->mfunc.vhcr_dma); + priv->mfunc.vhcr = NULL; } void mlx4_cmd_cleanup(struct mlx4_dev *dev) -- cgit v0.10.2 From 4df99504065fcc75c70cde0aa4a342fb32c829b8 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Mon, 6 Feb 2012 06:26:29 +0000 Subject: mlx4_core: use correct flag for unicast_promisc Use MLX4_DEV_CAP_FLAG_VEP_UC_STEER for unicast_promisc_add/remove Unicast entries were managed in wrong data structures. Signed-off-by: Eugenia Emantayev Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 0785d9b..00794cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1004,7 +1004,7 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove); int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) + if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) return 0; if (mlx4_is_mfunc(dev)) @@ -1016,7 +1016,7 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add); int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) + if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) return 0; if (mlx4_is_mfunc(dev)) -- cgit v0.10.2 From 4c41b3673759d096106e68bce586f103c51d4119 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Mon, 6 Feb 2012 06:26:36 +0000 Subject: mlx4_core: use correct port for steering Use port number for correct steering (list per port). Before the fix all steering entries (for both physical ports) were managed in first port structures, so we had leakage of resources for port 2. Signed-off-by: Eugenia Emantayev Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 00794cb..ca574d8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -136,7 +136,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port, u32 prot; int err; - s_steer = &mlx4_priv(dev)->steer[0]; + s_steer = &mlx4_priv(dev)->steer[port - 1]; new_entry = kzalloc(sizeof *new_entry, GFP_KERNEL); if (!new_entry) return -ENOMEM; @@ -220,7 +220,7 @@ static int existing_steering_entry(struct mlx4_dev *dev, u8 port, struct mlx4_promisc_qp *pqp; struct mlx4_promisc_qp *dqp; - s_steer = &mlx4_priv(dev)->steer[0]; + s_steer = &mlx4_priv(dev)->steer[port - 1]; pqp = get_promisc_qp(dev, 0, steer, qpn); if (!pqp) @@ -265,7 +265,7 @@ static bool check_duplicate_entry(struct mlx4_dev *dev, u8 port, struct mlx4_steer_index *tmp_entry, *entry = NULL; struct mlx4_promisc_qp *dqp, *tmp_dqp; - s_steer = &mlx4_priv(dev)->steer[0]; + s_steer = &mlx4_priv(dev)->steer[port - 1]; /* if qp is not promisc, it cannot be duplicated */ if (!get_promisc_qp(dev, 0, steer, qpn)) @@ -306,7 +306,7 @@ static bool can_remove_steering_entry(struct mlx4_dev *dev, u8 port, bool ret = false; int i; - s_steer = &mlx4_priv(dev)->steer[0]; + s_steer = &mlx4_priv(dev)->steer[port - 1]; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) @@ -361,7 +361,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port, int err; struct mlx4_priv *priv = mlx4_priv(dev); - s_steer = &mlx4_priv(dev)->steer[0]; + s_steer = &mlx4_priv(dev)->steer[port - 1]; mutex_lock(&priv->mcg_table.mutex); @@ -466,7 +466,7 @@ static int remove_promisc_qp(struct mlx4_dev *dev, u8 port, int loc, i; int err; - s_steer = &mlx4_priv(dev)->steer[0]; + s_steer = &mlx4_priv(dev)->steer[port - 1]; mutex_lock(&priv->mcg_table.mutex); pqp = get_promisc_qp(dev, 0, steer, qpn); -- cgit v0.10.2 From 2e6b411971de727a5bcea4323331fdaa70a81b11 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 31 Jan 2012 14:22:15 -0500 Subject: bcma: don't fail for bad SPROM CRC The brcmsmac driver is now using the bcma SPROM CRC check, which does not recognize all chipsets that were functional prior to the switch. In particular, the current code bails out on odd CRC errors in recent Macbooks. This patch ignores those errors, with the argument that an unrecognized SPROM should be treated similarly to a non-existing one. Signed-off-by: Henrik Rydberg Signed-off-by: John W. Linville diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index febbc0a..ec31f7d 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -169,10 +169,8 @@ int bcma_bus_register(struct bcma_bus *bus) err = bcma_sprom_get(bus); if (err == -ENOENT) { pr_err("No SPROM available\n"); - } else if (err) { + } else if (err) pr_err("Failed to get SPROM: %d\n", err); - return -ENOENT; - } /* Register found cores */ bcma_register_cores(bus); -- cgit v0.10.2 From 68355f71132bec25e0d89b2d8d3ed01286307d31 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 6 Feb 2012 08:39:49 +0000 Subject: mlx4: allow device removal by fixing dma unmap size After opening the network interface, Mellanox ConnectX device cannot be removed by hotplug because it has not properly unmapped all DMA memory. It happens that mlx4_en_activate_rx_rings overrides the variable that keeps the size of the memory mapped. This is fixed by passing to mlx4_en_destroy_rx_ring the same size that is given to mlx4_en_create_rx_ring. After applying this patch, hot unplugging the device works after opening the interface. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 467ae58..149e60d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -892,7 +892,8 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv) for (i = 0; i < priv->rx_ring_num; i++) { if (priv->rx_ring[i].rx_info) - mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i]); + mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i], + priv->prof->rx_ring_size, priv->stride); if (priv->rx_cq[i].buf) mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 971d4b6..d1c631e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -380,12 +380,12 @@ err_allocator: } void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring) + struct mlx4_en_rx_ring *ring, u32 size, u16 stride) { struct mlx4_en_dev *mdev = priv->mdev; mlx4_en_unmap_buffer(&ring->wqres.buf); - mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size + TXBB_SIZE); + mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE); vfree(ring->rx_info); ring->rx_info = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 35f0884..d60335f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -528,7 +528,8 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, u32 size, u16 stride); void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring); + struct mlx4_en_rx_ring *ring, + u32 size, u16 stride); int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv); void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring); -- cgit v0.10.2 From 7e2eb99cc6207e4464f018f72c67c55f2b1c93a4 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Mon, 6 Feb 2012 08:39:50 +0000 Subject: mlx4: fix DMA mapping leak when allocation fails mlx4_en_prepare_rx_desc does not correctly clean up after it finds an allocation failure. It should unmap a page before calling put_page, but it only calls the later. This bug would prevent a device removal using hotplug after setting the device MTU to 9000 and opening the network interface. After the fix, we still see the allocation failure with MTU 9000, but we are able to remove the device. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index d1c631e..d4ad8c2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -168,8 +168,12 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, return 0; err: - while (i--) + while (i--) { + dma_addr_t dma = be64_to_cpu(rx_desc->data[i].addr); + pci_unmap_single(priv->mdev->pdev, dma, skb_frags[i].size, + PCI_DMA_FROMDEVICE); put_page(skb_frags[i].page); + } return -ENOMEM; } -- cgit v0.10.2 From 7a0153ee15575a4d07b5da8c96b79e0b0fd41a12 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 6 Feb 2012 18:54:06 -0200 Subject: perf tools: Fix perf stack to non executable on x86_64 By adding following objects: bench/mem-memcpy-x86-64-asm.o the x86_64 perf binary ended up with executable stack. The reason was that above object are assembler sourced and is missing the GNU-stack note section. In such case the linker assumes that the final binary should not be restricted at all and mark the stack as RWX. Adding section ".note.GNU-stack" definition to mentioned object, with all flags disabled, thus omiting this object from linker stack flags decision. Problem introduced in: $ git describe ea7872b v2.6.37-rc2-19-gea7872b Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=783570 Reported-by: Clark Williams Acked-by: Eric Dumazet Cc: Corey Ashford Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: stable@kernel.org Link: http://lkml.kernel.org/r/1328100848-5630-1-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa [ committer note: Backported fix to perf/urgent (3.3-rc2+) ] Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S index a57b66e..185a96d 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm.S +++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S @@ -1,2 +1,8 @@ #include "../../../arch/x86/lib/memcpy_64.S" +/* + * We need to provide note.GNU-stack section, saying that we want + * NOT executable stack. Otherwise the final linking will assume that + * the ELF stack should not be restricted at all and set it RWX. + */ +.section .note.GNU-stack,"",@progbits -- cgit v0.10.2 From bf32c9ebc9890764c7a9984e3a0c8a57a059753d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 5 Feb 2012 18:11:05 +0100 Subject: perf tools: Fix prefix matching for kernel maps In some perf ancient versions we used '[kernel.kallsyms._text]' as the name for the kernel map. This got changed with commit: perf: 'perf kvm' tool for monitoring guest performance from host commit a1645ce12adb6c9cc9e19d7695466204e3f017fe Author: Zhang, Yanmin and we started to use following name '[kernel.kallsyms]_text'. This name change is important for the report code dealing with ancient perf data. When processing the kernel map event, we need to recognize the old naming (dont match the last ']') and initialize the kernel map correctly. The subsequent call to maps__set_kallsyms_ref_reloc_sym deals with the superfluous ']' to get correct symbol name. Cc: Corey Ashford Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1328461865-6127-1-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 73ddaf0..2044324 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -554,7 +554,7 @@ static int perf_event__process_kernel_mmap(struct perf_tool *tool __used, is_kernel_mmap = memcmp(event->mmap.filename, kmmap_prefix, - strlen(kmmap_prefix)) == 0; + strlen(kmmap_prefix) - 1) == 0; if (event->mmap.filename[0] == '/' || (!is_kernel_mmap && event->mmap.filename[0] == '[')) { -- cgit v0.10.2 From a4a03fc7ef89020baca4f19174e6a43767c6d78a Mon Sep 17 00:00:00 2001 From: "Naveen N. Rao" Date: Fri, 3 Feb 2012 22:31:13 +0530 Subject: perf evsel: Fix an issue where perf report fails to show the proper percentage This patch fixes an issue where perf report shows nan% for certain perf.data files. The below is from a report for a do_fork probe: -nan% sshd [kernel.kallsyms] [k] do_fork -nan% packagekitd [kernel.kallsyms] [k] do_fork -nan% dbus-daemon [kernel.kallsyms] [k] do_fork -nan% bash [kernel.kallsyms] [k] do_fork A git bisect shows commit f3bda2c as the cause. However, looking back through the git history, I saw commit 640c03c which seems to have removed the required initialization for perf_sample->period. The problem only started showing after commit f3bda2c. The below patch re-introduces the initialization and it fixes the problem for me. With the below patch, for the same perf.data: 73.08% bash [kernel.kallsyms] [k] do_fork 8.97% 11-dhclient [kernel.kallsyms] [k] do_fork 6.41% sshd [kernel.kallsyms] [k] do_fork 3.85% 20-chrony [kernel.kallsyms] [k] do_fork 2.56% sendmail [kernel.kallsyms] [k] do_fork This patch applies over current linux-tip commit 9949284. Problem introduced in: $ git describe 640c03c v2.6.37-rc3-83-g640c03c Cc: Ananth N Mavinakayanahalli Cc: Ingo Molnar Cc: Robert Richter Cc: Srikar Dronamraju Cc: stable@kernel.org Link: http://lkml.kernel.org/r/20120203170113.5190.25558.stgit@localhost6.localdomain6 Signed-off-by: Naveen N. Rao Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 667f3b7..7132ee8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -463,6 +463,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, memset(data, 0, sizeof(*data)); data->cpu = data->pid = data->tid = -1; data->stream_id = data->id = data->time = -1ULL; + data->period = 1; if (event->header.type != PERF_RECORD_SAMPLE) { if (!sample_id_all) -- cgit v0.10.2 From c898261c0dad617f0f1080bedc02d507a2fcfb92 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 25 Jan 2012 08:16:25 -0800 Subject: drm/i915: Force explicit bpp selection for intel_dp_link_required It is never correct to use intel_crtc->bpp in intel_dp_link_required, so instead pass an explicit bpp in to this function. This patch only supports 18bpp and 24bpp modes, which means that 10bpc modes will be computed incorrectly. Fixing that will require more extensive changes, and so must be addressed separately from this bugfix. intel_dp_link_required is called from intel_dp_mode_valid and intel_dp_mode_fixup. * intel_dp_mode_valid is called to list supported modes; in this case, the current crtc values cannot be relevant as the modes in question may never be selected. Thus, using intel_crtc->bpp is never right. * intel_dp_mode_fixup is called during mode setting, but it is run well before ironlake_crtc_mode_set is called to set intel_crtc->bpp, so using intel_crtc-bpp in this path can only ever get a stale value. Cc: Lubos Kolouch Cc: Adam Jackson Cc: stable@vger.kernel.org Reviewed-by: Daniel Vetter Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=42263 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44881 Tested-by: Dave Airlie Tested-by: camalot@picnicpark.org (Dell Latitude 6510) Tested-by: Roland Dreier Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index db3b461..94f860c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -208,17 +208,8 @@ intel_dp_link_clock(uint8_t link_bw) */ static int -intel_dp_link_required(struct intel_dp *intel_dp, int pixel_clock, int check_bpp) +intel_dp_link_required(int pixel_clock, int bpp) { - struct drm_crtc *crtc = intel_dp->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int bpp = 24; - - if (check_bpp) - bpp = check_bpp; - else if (intel_crtc) - bpp = intel_crtc->bpp; - return (pixel_clock * bpp + 9) / 10; } @@ -245,12 +236,11 @@ intel_dp_mode_valid(struct drm_connector *connector, return MODE_PANEL; } - mode_rate = intel_dp_link_required(intel_dp, mode->clock, 0); + mode_rate = intel_dp_link_required(mode->clock, 24); max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); if (mode_rate > max_rate) { - mode_rate = intel_dp_link_required(intel_dp, - mode->clock, 18); + mode_rate = intel_dp_link_required(mode->clock, 18); if (mode_rate > max_rate) return MODE_CLOCK_HIGH; else @@ -683,7 +673,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; - int bpp = mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 0; + int bpp = mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { @@ -701,7 +691,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(intel_dp, mode->clock, bpp) + if (intel_dp_link_required(mode->clock, bpp) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; -- cgit v0.10.2 From 96e02d1586782eadf051fa3d6bc4132d2447ac2c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 4 Feb 2012 10:47:10 +0100 Subject: exec: fix use-after-free bug in setup_new_exec() Setting the task name is done within setup_new_exec() by accessing bprm->filename. However this happens after flush_old_exec(). This may result in a use after free bug, flush_old_exec() may "complete" vfork_done, which will wake up the parent which in turn may free the passed in filename. To fix this add a new tcomm field in struct linux_binprm which contains the now early generated task name until it is used. Fixes this bug on s390: Unable to handle kernel pointer dereference at virtual kernel address 0000000039768000 Process kworker/u:3 (pid: 245, task: 000000003a3dc840, ksp: 0000000039453818) Krnl PSW : 0704000180000000 0000000000282e94 (setup_new_exec+0xa0/0x374) Call Trace: ([<0000000000282e2c>] setup_new_exec+0x38/0x374) [<00000000002dd12e>] load_elf_binary+0x402/0x1bf4 [<0000000000280a42>] search_binary_handler+0x38e/0x5bc [<0000000000282b6c>] do_execve_common+0x410/0x514 [<0000000000282cb6>] do_execve+0x46/0x58 [<00000000005bce58>] kernel_execve+0x28/0x70 [<000000000014ba2e>] ____call_usermodehelper+0x102/0x140 [<00000000005bc8da>] kernel_thread_starter+0x6/0xc [<00000000005bc8d4>] kernel_thread_starter+0x0/0xc Last Breaking-Event-Address: [<00000000002830f0>] setup_new_exec+0x2fc/0x374 Kernel panic - not syncing: Fatal exception: panic_on_oops Reported-by: Sebastian Ott Signed-off-by: Heiko Carstens Signed-off-by: Linus Torvalds diff --git a/fs/exec.c b/fs/exec.c index aeb135c..92ce83a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1071,6 +1071,21 @@ void set_task_comm(struct task_struct *tsk, char *buf) perf_event_comm(tsk); } +static void filename_to_taskname(char *tcomm, const char *fn, unsigned int len) +{ + int i, ch; + + /* Copies the binary name from after last slash */ + for (i = 0; (ch = *(fn++)) != '\0';) { + if (ch == '/') + i = 0; /* overwrite what we wrote */ + else + if (i < len - 1) + tcomm[i++] = ch; + } + tcomm[i] = '\0'; +} + int flush_old_exec(struct linux_binprm * bprm) { int retval; @@ -1085,6 +1100,7 @@ int flush_old_exec(struct linux_binprm * bprm) set_mm_exe_file(bprm->mm, bprm->file); + filename_to_taskname(bprm->tcomm, bprm->filename, sizeof(bprm->tcomm)); /* * Release all of the old mmap stuff */ @@ -1116,10 +1132,6 @@ EXPORT_SYMBOL(would_dump); void setup_new_exec(struct linux_binprm * bprm) { - int i, ch; - const char *name; - char tcomm[sizeof(current->comm)]; - arch_pick_mmap_layout(current->mm); /* This is the point of no return */ @@ -1130,18 +1142,7 @@ void setup_new_exec(struct linux_binprm * bprm) else set_dumpable(current->mm, suid_dumpable); - name = bprm->filename; - - /* Copies the binary name from after last slash */ - for (i=0; (ch = *(name++)) != '\0';) { - if (ch == '/') - i = 0; /* overwrite what we wrote */ - else - if (i < (sizeof(tcomm) - 1)) - tcomm[i++] = ch; - } - tcomm[i] = '\0'; - set_task_comm(current, tcomm); + set_task_comm(current, bprm->tcomm); /* Set the new mm task size. We have to do that late because it may * depend on TIF_32BIT which is only updated in flush_thread() on diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index fd88a39..0092102 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -18,7 +18,7 @@ struct pt_regs; #define BINPRM_BUF_SIZE 128 #ifdef __KERNEL__ -#include +#include #define CORENAME_MAX_SIZE 128 @@ -58,6 +58,7 @@ struct linux_binprm { unsigned interp_flags; unsigned interp_data; unsigned long loader, exec; + char tcomm[TASK_COMM_LEN]; }; #define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 -- cgit v0.10.2 From f5fe184b08daf0c34b0de0d02c7033fe119dbf0a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 6 Feb 2012 09:49:50 -0800 Subject: Documentation: add missing tainted bits to Documentation/sysctl/kernel.txt Two of the bits in the tainted flag are not documented. Signed-off-by: Larry Finger Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 8c20fbd..6d78841 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -601,6 +601,8 @@ can be ORed together: instead of using the one provided by the hardware. 512 - A kernel warning has occurred. 1024 - A module from drivers/staging was loaded. +2048 - The system is working around a severe firmware bug. +4096 - An out-of-tree module has been loaded. ============================================================== -- cgit v0.10.2 From 71ea4efe4fc3319ab577be00d1bbcb76d3ffaf63 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 6 Feb 2012 09:50:00 -0800 Subject: Documentation: update quilt tree location for Documentation patches Update quilt tree location for Documentation/ patches. Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 8591c03..55ca0be 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2281,7 +2281,7 @@ F: drivers/acpi/dock.c DOCUMENTATION M: Randy Dunlap L: linux-doc@vger.kernel.org -T: quilt http://userweb.kernel.org/~rdunlap/kernel-doc-patches/current/ +T: quilt http://xenotime.net/kernel-doc-patches/current/ S: Maintained F: Documentation/ -- cgit v0.10.2 From db91ff55bdf06736b849afc1b1fce5763bbb8d5d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 7 Feb 2012 12:01:51 +1100 Subject: md: two small fixes to handling interrupt resync. 1/ If a resync is aborted we should record how far we got (recovery_cp) the last request that we know has completed (->curr_resync_completed) rather than the last request that was submitted (->curr_resync). 2/ When a resync aborts we still want to update the metadata with any changes, so set MD_CHANGE_DEVS even if we 'skip'. Signed-off-by: NeilBrown diff --git a/drivers/md/md.c b/drivers/md/md.c index 33aa06f..81d1c43 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7333,7 +7333,8 @@ void md_do_sync(struct mddev *mddev) printk(KERN_INFO "md: checkpointing %s of %s.\n", desc, mdname(mddev)); - mddev->recovery_cp = mddev->curr_resync; + mddev->recovery_cp = + mddev->curr_resync_completed; } } else mddev->recovery_cp = MaxSector; @@ -7351,9 +7352,9 @@ void md_do_sync(struct mddev *mddev) rcu_read_unlock(); } } + skip: set_bit(MD_CHANGE_DEVS, &mddev->flags); - skip: if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { /* We completed so min/max setting can be forgotten if used. */ if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) -- cgit v0.10.2 From 8b3262c00d6feccdccd6567d27687ae4b64c54bd Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Mon, 16 Jan 2012 16:46:04 +1000 Subject: m68knommu: fix syscall tracing stuck process The return path from an exception was checking too many bits in the thread_info->flags, and getting stuck calling do_signal(). There was no work to do, we should only be checking the low 8 bits (as per comments and definitions in arch/m68k/include/asm/thread_info.h). This fixes the stuck process problem when using strace. Signed-off-by: Greg Ungerer diff --git a/arch/m68k/platform/coldfire/entry.S b/arch/m68k/platform/coldfire/entry.S index 863889f..281e38c 100644 --- a/arch/m68k/platform/coldfire/entry.S +++ b/arch/m68k/platform/coldfire/entry.S @@ -136,7 +136,7 @@ Luser_return: movel %sp,%d1 /* get thread_info pointer */ andl #-THREAD_SIZE,%d1 /* at base of kernel stack */ movel %d1,%a0 - movel %a0@(TINFO_FLAGS),%d1 /* get thread_info->flags */ + moveb %a0@(TINFO_FLAGS+3),%d1 /* thread_info->flags (low 8 bits) */ jne Lwork_to_do /* still work to do */ Lreturn: @@ -148,8 +148,6 @@ Lwork_to_do: btst #TIF_NEED_RESCHED,%d1 jne reschedule - /* GERG: do we need something here for TRACEing?? */ - Lsignal_return: subql #4,%sp /* dummy return address */ SAVE_SWITCH_STACK -- cgit v0.10.2 From 57e00098cc0e43d001c9c8a018a1f8396faa0d16 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 10 Jan 2012 14:06:07 +0100 Subject: m68k: Add shared bit to Coldfire kernel page entries We had problems accessing our NOR flash trough mtd. The system always got stuck at attaching UBI using ubiattach if booted from NFS or after mounting squashfs as rootfs directly from NOR flash. After some testing of the new changes introduced from v3.2-rc1 to v3.2-rc7 we had to apply the following patch to get mtd working again. [gerg: The problem was ultimately caused by allocated kernel pages not having the shared (SG) bit set. Without the SG bit set the MMU will look for page matches incorporating the ASID as well. Things like module regions allocated using vmalloc would fault when other processes run. ] Signed-off-by: Alexander Stein Signed-off-by: Greg Ungerer diff --git a/arch/m68k/include/asm/mcf_pgtable.h b/arch/m68k/include/asm/mcf_pgtable.h index 756bde4..3c79368 100644 --- a/arch/m68k/include/asm/mcf_pgtable.h +++ b/arch/m68k/include/asm/mcf_pgtable.h @@ -78,7 +78,8 @@ | CF_PAGE_READABLE \ | CF_PAGE_WRITABLE \ | CF_PAGE_EXEC \ - | CF_PAGE_SYSTEM) + | CF_PAGE_SYSTEM \ + | CF_PAGE_SHARED) #define PAGE_COPY __pgprot(CF_PAGE_VALID \ | CF_PAGE_ACCESSED \ -- cgit v0.10.2 From 3372f5a7d005dd42e754490fed6a0171c4a018c6 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 23 Jan 2012 15:45:56 +0100 Subject: m68k: Do not set global share for non-kernel shared pages If the SG bit is set in MMUTR the page is accessible for all userspace processes (ignoring the ASID). So a process might randomly access a page from a different process which had a shared page (from shared memory) in its context. Signed-off-by: Alexander Stein Signed-off-by: Greg Ungerer diff --git a/arch/m68k/mm/mcfmmu.c b/arch/m68k/mm/mcfmmu.c index babd5a9..875b800 100644 --- a/arch/m68k/mm/mcfmmu.c +++ b/arch/m68k/mm/mcfmmu.c @@ -87,7 +87,7 @@ void __init paging_init(void) int cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) { - unsigned long flags, mmuar; + unsigned long flags, mmuar, mmutr; struct mm_struct *mm; pgd_t *pgd; pmd_t *pmd; @@ -137,9 +137,10 @@ int cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) if (!pte_dirty(*pte) && !KMAPAREA(mmuar)) set_pte(pte, pte_wrprotect(*pte)); - mmu_write(MMUTR, (mmuar & PAGE_MASK) | (asid << MMUTR_IDN) | - (((int)(pte->pte) & (int)CF_PAGE_MMUTR_MASK) - >> CF_PAGE_MMUTR_SHIFT) | MMUTR_V); + mmutr = (mmuar & PAGE_MASK) | (asid << MMUTR_IDN) | MMUTR_V; + if ((mmuar < TASK_UNMAPPED_BASE) || (mmuar >= TASK_SIZE)) + mmutr |= (pte->pte & CF_PAGE_MMUTR_MASK) >> CF_PAGE_MMUTR_SHIFT; + mmu_write(MMUTR, mmutr); mmu_write(MMUDR, (pte_val(*pte) & PAGE_MASK) | ((pte->pte) & CF_PAGE_MMUDR_MASK) | MMUDR_SZ_8KB | MMUDR_X); -- cgit v0.10.2 From 218d06d79468ca2e6abf3679eea12d7d93d251ef Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 20 Dec 2011 11:45:08 +0530 Subject: i2c: tegra: Add devexit_p() for remove It was originally missed in the __devinit/__devexit annotations. Signed-off-by: Shubhrajyoti D Acked-by: Stephen Warren Acked-by: Ben Dooks Signed-off-by: Olof Johansson diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 6381604..0ab4a95 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -755,7 +755,7 @@ MODULE_DEVICE_TABLE(of, tegra_i2c_of_match); static struct platform_driver tegra_i2c_driver = { .probe = tegra_i2c_probe, - .remove = tegra_i2c_remove, + .remove = __devexit_p(tegra_i2c_remove), #ifdef CONFIG_PM .suspend = tegra_i2c_suspend, .resume = tegra_i2c_resume, -- cgit v0.10.2 From 5f21f1240c5d1dad82edb21d38566da85085f530 Mon Sep 17 00:00:00 2001 From: Marc Dietrich Date: Sat, 28 Jan 2012 20:03:04 +0100 Subject: ARM: tegra: paz00: fix wrong SD1 power gpio The power gpio for the external memory card was specified wrongly. Replace it with the correct value (tested with warmboot with fastboot). Signed-off-by: Marc Dietrich Acked-by: Stephen Warren Signed-off-by: Olof Johansson diff --git a/arch/arm/boot/dts/tegra-paz00.dts b/arch/arm/boot/dts/tegra-paz00.dts index 1a1d702..a94e92c 100644 --- a/arch/arm/boot/dts/tegra-paz00.dts +++ b/arch/arm/boot/dts/tegra-paz00.dts @@ -60,7 +60,7 @@ sdhci@c8000000 { cd-gpios = <&gpio 173 0>; /* gpio PV5 */ wp-gpios = <&gpio 57 0>; /* gpio PH1 */ - power-gpios = <&gpio 155 0>; /* gpio PT3 */ + power-gpios = <&gpio 169 0>; /* gpio PV1 */ }; sdhci@c8000200 { diff --git a/arch/arm/mach-tegra/board-paz00.h b/arch/arm/mach-tegra/board-paz00.h index ffa83f5..3c9f8da 100644 --- a/arch/arm/mach-tegra/board-paz00.h +++ b/arch/arm/mach-tegra/board-paz00.h @@ -22,7 +22,7 @@ /* SDCARD */ #define TEGRA_GPIO_SD1_CD TEGRA_GPIO_PV5 #define TEGRA_GPIO_SD1_WP TEGRA_GPIO_PH1 -#define TEGRA_GPIO_SD1_POWER TEGRA_GPIO_PT3 +#define TEGRA_GPIO_SD1_POWER TEGRA_GPIO_PV1 /* ULPI */ #define TEGRA_ULPI_RST TEGRA_GPIO_PV0 -- cgit v0.10.2 From 0783a9bf4a5207e9d57b8c110022957f8dc88e2a Mon Sep 17 00:00:00 2001 From: Marc Dietrich Date: Sat, 28 Jan 2012 20:03:05 +0100 Subject: ARM: tegra: paz00: fix wrong UART port on mini-pcie plug UARTC is connected to the mini-pcie port. Signed-off-by: Marc Dietrich Acked-by: Stephen Warren Signed-off-by: Olof Johansson diff --git a/arch/arm/boot/dts/tegra-paz00.dts b/arch/arm/boot/dts/tegra-paz00.dts index a94e92c..825d295 100644 --- a/arch/arm/boot/dts/tegra-paz00.dts +++ b/arch/arm/boot/dts/tegra-paz00.dts @@ -46,11 +46,11 @@ }; serial@70006200 { - status = "disable"; + clock-frequency = <216000000>; }; serial@70006300 { - clock-frequency = <216000000>; + status = "disable"; }; serial@70006400 { diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index fcf4f37..330afdf 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -60,9 +60,9 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .uartclk = 216000000, }, { /* serial port on mini-pcie */ - .membase = IO_ADDRESS(TEGRA_UARTD_BASE), - .mapbase = TEGRA_UARTD_BASE, - .irq = INT_UARTD, + .membase = IO_ADDRESS(TEGRA_UARTC_BASE), + .mapbase = TEGRA_UARTC_BASE, + .irq = INT_UARTC, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, .iotype = UPIO_MEM, @@ -174,7 +174,7 @@ static void __init tegra_paz00_fixup(struct tag *tags, char **cmdline, static __initdata struct tegra_clk_init_table paz00_clk_init_table[] = { /* name parent rate enabled */ { "uarta", "pll_p", 216000000, true }, - { "uartd", "pll_p", 216000000, true }, + { "uartc", "pll_p", 216000000, true }, { "pll_p_out4", "pll_p", 24000000, true }, { "usbd", "clk_m", 12000000, false }, -- cgit v0.10.2 From 95fe1ee41e23fa271416da67483594dde74bc6ca Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 18 Jan 2012 14:04:29 +0100 Subject: target: accept REQUEST_SENSE with 18bytes WindowsXP+BOT issues a MODE_SENSE request with page 0x1c which is not suppoerted by target. Target rejects that command with TCM_INVALID_CDB_FIELD, so far so good. On BOT I can't send the SENSE response back, instead I can only reply that an error occured. The next thing happens is a REQUEST_SENSE request with 18 bytes length. Since the check here is more than 18 bytes I have to NACK that request as well. This is not really required: We check for some additional room, but we never use it. The additional length is set to 0xa so the total length is 0xa + 8 = 18 which is fine with my 18 bytes. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index a9bbf5a..f3d71fa 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -1058,11 +1058,8 @@ int target_emulate_request_sense(struct se_task *task) */ buf[0] = 0x70; buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; - /* - * Make sure request data length is enough for additional - * sense data. - */ - if (cmd->data_length <= 18) { + + if (cmd->data_length < 18) { buf[7] = 0x00; err = -EINVAL; goto end; @@ -1079,11 +1076,8 @@ int target_emulate_request_sense(struct se_task *task) */ buf[0] = 0x70; buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE; - /* - * Make sure request data length is enough for additional - * sense data. - */ - if (cmd->data_length <= 18) { + + if (cmd->data_length < 18) { buf[7] = 0x00; err = -EINVAL; goto end; -- cgit v0.10.2 From 1edcdb497ef418122cd4f98e157660cf594b345a Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Thu, 19 Jan 2012 13:39:23 -0800 Subject: target: Change target_submit_cmd() to return void Retval not very useful, and may even be harmful. Once submitted, fabrics should expect a sense error if anything goes wrong. All fabrics checking of this retval are useless or broken: fc checks it just to emit more debug output. ib_srpt trickles retval up, then it is ignored. qla2xxx trickles it up, which then causes a bug because the abort goto in qla_target.c thinks cmd hasn't been sent to target. Just returning nothing is best. Signed-off-by: Andy Grover Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index cf996d8..aace7ee 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1657,7 +1657,7 @@ EXPORT_SYMBOL(transport_handle_cdb_direct); * This may only be called from process context, and also currently * assumes internal allocation of fabric payload buffer by target-core. **/ -int target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, +void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, unsigned char *cdb, unsigned char *sense, u32 unpacked_lun, u32 data_length, int task_attr, int data_dir, int flags) { @@ -1706,12 +1706,11 @@ int target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, * when fabric has filled the incoming buffer. */ transport_handle_cdb_direct(se_cmd); - return 0; + return; out_check_cond: transport_send_check_condition_and_sense(se_cmd, se_cmd->scsi_sense_reason, 0); - return 0; } EXPORT_SYMBOL(target_submit_cmd); diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index addc18f..9e7e26c 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -540,7 +540,6 @@ static void ft_send_work(struct work_struct *work) int data_dir = 0; u32 data_len; int task_attr; - int ret; fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp)); if (!fcp) @@ -603,14 +602,10 @@ static void ft_send_work(struct work_struct *work) * Use a single se_cmd->cmd_kref as we expect to release se_cmd * directly from ft_check_stop_free callback in response path. */ - ret = target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, cmd->cdb, + target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, cmd->cdb, &cmd->ft_sense_buffer[0], cmd->lun, data_len, task_attr, data_dir, 0); - pr_debug("r_ctl %x alloc target_submit_cmd %d\n", fh->fh_r_ctl, ret); - if (ret < 0) { - ft_dump_cmd(cmd, __func__); - return; - } + pr_debug("r_ctl %x alloc target_submit_cmd\n", fh->fh_r_ctl); return; err: diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 523e8bc..d36fad3 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -114,7 +114,7 @@ void transport_init_se_cmd(struct se_cmd *, struct target_core_fabric_ops *, struct se_session *, u32, int, int, unsigned char *); int transport_lookup_cmd_lun(struct se_cmd *, u32); int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *); -int target_submit_cmd(struct se_cmd *, struct se_session *, unsigned char *, +void target_submit_cmd(struct se_cmd *, struct se_session *, unsigned char *, unsigned char *, u32, u32, int, int, int); int transport_handle_cdb_direct(struct se_cmd *); int transport_generic_handle_cdb_map(struct se_cmd *); -- cgit v0.10.2 From 735703cac08f34a197e1c6331ca9adc8a52d6046 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 20 Jan 2012 19:02:56 -0800 Subject: target: Fix target_submit_cmd() exception handling This patch fixes a bug in target_submit_cmd() where the failure path for transport_generic_allocate_tasks() made a direct call to transport_send_check_condition_and_sense() and not calling the final target_put_sess_cmd() release callback. For transport_generic_allocate_tasks() failures, use the proper call to transport_generic_request_failure() to handle kref_put() along with potential internal queue full response processing. It also makes transport_lookup_cmd_lun() failures in target_submit_cmd() use transport_send_check_condition_and_sense() and target_put_sess_cmd() directly to avoid se_cmd->se_dev reference in transport_generic_request_failure() handling. Finally it drops the out_check_cond: label and use direct reference for allocate task failures, and per-se_device queue_full handling is currently not supported for transport_lookup_cmd_lun() failure descriptors due to se_device dependency. Reported-by: Roland Dreier Cc: Roland Dreier Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index aace7ee..799b5ef 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1690,15 +1690,21 @@ void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, /* * Locate se_lun pointer and attach it to struct se_cmd */ - if (transport_lookup_cmd_lun(se_cmd, unpacked_lun) < 0) - goto out_check_cond; + if (transport_lookup_cmd_lun(se_cmd, unpacked_lun) < 0) { + transport_send_check_condition_and_sense(se_cmd, + se_cmd->scsi_sense_reason, 0); + target_put_sess_cmd(se_sess, se_cmd); + return; + } /* * Sanitize CDBs via transport_generic_cmd_sequencer() and * allocate the necessary tasks to complete the received CDB+data */ rc = transport_generic_allocate_tasks(se_cmd, cdb); - if (rc != 0) - goto out_check_cond; + if (rc != 0) { + transport_generic_request_failure(se_cmd); + return; + } /* * Dispatch se_cmd descriptor to se_lun->lun_se_dev backend * for immediate execution of READs, otherwise wait for @@ -1707,10 +1713,6 @@ void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, */ transport_handle_cdb_direct(se_cmd); return; - -out_check_cond: - transport_send_check_condition_and_sense(se_cmd, - se_cmd->scsi_sense_reason, 0); } EXPORT_SYMBOL(target_submit_cmd); -- cgit v0.10.2 From 7347b5ff7090b2b399b7921a266255cd55bbd78f Mon Sep 17 00:00:00 2001 From: Marco Sanvido Date: Fri, 20 Jan 2012 15:49:27 -0800 Subject: target: Fix iblock se_dev_attrib.unmap_granularity The block layer keeps q->limits.discard_granularity in bytes, but iblock (and the SCSI Block Limits VPD page) keep unmap_granularity in blocks. Report the correct value when exporting block devices by dividing to convert bytes to blocks. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 628e877..d43d5eb 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -181,7 +181,7 @@ static struct se_device *iblock_create_virtdevice( */ dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count = 1; dev->se_sub_dev->se_dev_attrib.unmap_granularity = - q->limits.discard_granularity; + q->limits.discard_granularity >> 9; dev->se_sub_dev->se_dev_attrib.unmap_granularity_alignment = q->limits.discard_alignment; -- cgit v0.10.2 From c3bc93da24c8f315d9ece741f6e6414a38879ec6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 23 Jan 2012 11:39:49 +1100 Subject: iscsi_target: in_aton needs linux/inet.h Fixes this error after a recent nfs cleanup: drivers/target/iscsi/iscsi_target_configfs.c: In function 'lio_target_call_addnptotpg': drivers/target/iscsi/iscsi_target_configfs.c:214:3: error: implicit declaration of function 'in6_pton' [-Werror=implicit-function-declaration] drivers/target/iscsi/iscsi_target_configfs.c:239:3: error: implicit declaration of function 'in_aton' [-Werror=implicit-function-declaration] Signed-off-by: Stephen Rothwell Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 3468caa..6b35b37 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include -- cgit v0.10.2 From 3011684c0b0b77b0f88113ef1c15b18befc6734a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 27 Jan 2012 15:50:55 +0300 Subject: target: handle empty string writes in sysfs These are root only and we're not likely to hit the problem in practise, but it makes the static checkers happy. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 0955bb8..6e043ee 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1704,13 +1704,15 @@ static ssize_t target_core_store_dev_alias( return -EINVAL; } - se_dev->su_dev_flags |= SDF_USING_ALIAS; read_bytes = snprintf(&se_dev->se_dev_alias[0], SE_DEV_ALIAS_LEN, "%s", page); - + if (!read_bytes) + return -EINVAL; if (se_dev->se_dev_alias[read_bytes - 1] == '\n') se_dev->se_dev_alias[read_bytes - 1] = '\0'; + se_dev->su_dev_flags |= SDF_USING_ALIAS; + pr_debug("Target_Core_ConfigFS: %s/%s set alias: %s\n", config_item_name(&hba->hba_group.cg_item), config_item_name(&se_dev->se_dev_group.cg_item), @@ -1753,13 +1755,15 @@ static ssize_t target_core_store_dev_udev_path( return -EINVAL; } - se_dev->su_dev_flags |= SDF_USING_UDEV_PATH; read_bytes = snprintf(&se_dev->se_dev_udev_path[0], SE_UDEV_PATH_LEN, "%s", page); - + if (!read_bytes) + return -EINVAL; if (se_dev->se_dev_udev_path[read_bytes - 1] == '\n') se_dev->se_dev_udev_path[read_bytes - 1] = '\0'; + se_dev->su_dev_flags |= SDF_USING_UDEV_PATH; + pr_debug("Target_Core_ConfigFS: %s/%s set udev_path: %s\n", config_item_name(&hba->hba_group.cg_item), config_item_name(&se_dev->se_dev_group.cg_item), -- cgit v0.10.2 From 5c55125f4794f4e77574fc09839cb47b0eb45b06 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Feb 2012 16:51:24 -0500 Subject: iblock: fix handling of large requests Requesting to many bvecs upsets bio_alloc_bioset, so limit the number we ask for to the amount it can handle. Signed-off-by: Christoph Hellwig Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index d43d5eb..8572eae 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -488,6 +488,13 @@ iblock_get_bio(struct se_task *task, sector_t lba, u32 sg_num) struct iblock_req *ib_req = IBLOCK_REQ(task); struct bio *bio; + /* + * Only allocate as many vector entries as the bio code allows us to, + * we'll loop later on until we have handled the whole request. + */ + if (sg_num > BIO_MAX_PAGES) + sg_num = BIO_MAX_PAGES; + bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set); if (!bio) { pr_err("Unable to allocate memory for bio\n"); -- cgit v0.10.2 From 9f9ef6d3c08319defc29e4cf6ef2868d15ed0cc3 Mon Sep 17 00:00:00 2001 From: Dax Kelson Date: Fri, 3 Feb 2012 23:40:25 -0700 Subject: iscsi: use IP_FREEBIND socket option Use IP_FREEBIND socket option so that iscsi portal configuration with explicit IP addresses can happen during boot, before network interfaces have been assigned IPs. This is especially important on systemd based Linux boxes where system boot happens asynchronously and non-trivial configuration must be done to get targetcli.service to start synchronously after the network is configured. Reference: http://lists.fedoraproject.org/pipermail/devel/2011-October/158025.html Signed-off-by: Dax Kelson Cc: "Nicholas A. Bellinger" Cc: "Andy Grover" Cc: "Lennart Poettering" Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index ec47a7c..38cb7ce 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -842,6 +842,14 @@ int iscsi_target_setup_login_socket( goto fail; } + ret = kernel_setsockopt(sock, IPPROTO_IP, IP_FREEBIND, + (char *)&opt, sizeof(opt)); + if (ret < 0) { + pr_err("kernel_setsockopt() for IP_FREEBIND" + " failed\n"); + goto fail; + } + ret = kernel_bind(sock, (struct sockaddr *)&np->np_sockaddr, len); if (ret < 0) { pr_err("kernel_bind() failed: %d\n", ret); -- cgit v0.10.2 From 67236c44741e250199ccd77f1115568e68cf8848 Mon Sep 17 00:00:00 2001 From: Martin Svec Date: Mon, 6 Feb 2012 22:13:25 -0800 Subject: target: Fix unsupported WRITE_SAME sense payload This patch fixes a bug in target-core where unsupported WRITE_SAME ops from a target_check_write_same_discard() failure was incorrectly returning CHECK_CONDITION w/ TCM_INVALID_CDB_FIELD sense data. This was causing some clients to not properly fall back, so go ahead and use the correct TCM_UNSUPPORTED_SCSI_OPCODE sense for this case. Reported-by: Martin Svec Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 799b5ef..58cea07 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2697,7 +2697,7 @@ static int transport_generic_cmd_sequencer( cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; if (target_check_write_same_discard(&cdb[10], dev) < 0) - goto out_invalid_cdb_field; + goto out_unsupported_cdb; if (!passthrough) cmd->execute_task = target_emulate_write_same; break; @@ -2980,7 +2980,7 @@ static int transport_generic_cmd_sequencer( cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; if (target_check_write_same_discard(&cdb[1], dev) < 0) - goto out_invalid_cdb_field; + goto out_unsupported_cdb; if (!passthrough) cmd->execute_task = target_emulate_write_same; break; @@ -3003,7 +3003,7 @@ static int transport_generic_cmd_sequencer( * of byte 1 bit 3 UNMAP instead of original reserved field */ if (target_check_write_same_discard(&cdb[1], dev) < 0) - goto out_invalid_cdb_field; + goto out_unsupported_cdb; if (!passthrough) cmd->execute_task = target_emulate_write_same; break; -- cgit v0.10.2 From 11a3122f6cf2d988a77eb8883d0fc49cd013a6d5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 7 Feb 2012 07:51:30 +0100 Subject: block: strip out locking optimization in put_io_context() put_io_context() performed a complex trylock dancing to avoid deferring ioc release to workqueue. It was also broken on UP because trylock was always assumed to succeed which resulted in unbalanced preemption count. While there are ways to fix the UP breakage, even the most pathological microbench (forced ioc allocation and tight fork/exit loop) fails to show any appreciable performance benefit of the optimization. Strip it out. If there turns out to be workloads which are affected by this change, simpler optimization from the discussion thread can be applied later. Signed-off-by: Tejun Heo LKML-Reference: <1328514611.21268.66.camel@sli10-conroe> Signed-off-by: Jens Axboe diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index fa8f263..75642a3 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1659,7 +1659,7 @@ static void blkiocg_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); if (ioc) { ioc_cgroup_changed(ioc); - put_io_context(ioc, NULL); + put_io_context(ioc); } } } diff --git a/block/blk-core.c b/block/blk-core.c index 6367025..532b3a2 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -642,7 +642,7 @@ static inline void blk_free_request(struct request_queue *q, struct request *rq) if (rq->cmd_flags & REQ_ELVPRIV) { elv_put_request(q, rq); if (rq->elv.icq) - put_io_context(rq->elv.icq->ioc, q); + put_io_context(rq->elv.icq->ioc); } mempool_free(rq, q->rq.rq_pool); diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 7490b6d..9884fd7 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -29,21 +29,6 @@ void get_io_context(struct io_context *ioc) } EXPORT_SYMBOL(get_io_context); -/* - * Releasing ioc may nest into another put_io_context() leading to nested - * fast path release. As the ioc's can't be the same, this is okay but - * makes lockdep whine. Keep track of nesting and use it as subclass. - */ -#ifdef CONFIG_LOCKDEP -#define ioc_release_depth(q) ((q) ? (q)->ioc_release_depth : 0) -#define ioc_release_depth_inc(q) (q)->ioc_release_depth++ -#define ioc_release_depth_dec(q) (q)->ioc_release_depth-- -#else -#define ioc_release_depth(q) 0 -#define ioc_release_depth_inc(q) do { } while (0) -#define ioc_release_depth_dec(q) do { } while (0) -#endif - static void icq_free_icq_rcu(struct rcu_head *head) { struct io_cq *icq = container_of(head, struct io_cq, __rcu_head); @@ -75,11 +60,8 @@ static void ioc_exit_icq(struct io_cq *icq) if (rcu_dereference_raw(ioc->icq_hint) == icq) rcu_assign_pointer(ioc->icq_hint, NULL); - if (et->ops.elevator_exit_icq_fn) { - ioc_release_depth_inc(q); + if (et->ops.elevator_exit_icq_fn) et->ops.elevator_exit_icq_fn(icq); - ioc_release_depth_dec(q); - } /* * @icq->q might have gone away by the time RCU callback runs @@ -149,81 +131,29 @@ static void ioc_release_fn(struct work_struct *work) /** * put_io_context - put a reference of io_context * @ioc: io_context to put - * @locked_q: request_queue the caller is holding queue_lock of (hint) * * Decrement reference count of @ioc and release it if the count reaches - * zero. If the caller is holding queue_lock of a queue, it can indicate - * that with @locked_q. This is an optimization hint and the caller is - * allowed to pass in %NULL even when it's holding a queue_lock. + * zero. */ -void put_io_context(struct io_context *ioc, struct request_queue *locked_q) +void put_io_context(struct io_context *ioc) { - struct request_queue *last_q = locked_q; unsigned long flags; if (ioc == NULL) return; BUG_ON(atomic_long_read(&ioc->refcount) <= 0); - if (locked_q) - lockdep_assert_held(locked_q->queue_lock); - - if (!atomic_long_dec_and_test(&ioc->refcount)) - return; /* - * Destroy @ioc. This is a bit messy because icq's are chained - * from both ioc and queue, and ioc->lock nests inside queue_lock. - * The inner ioc->lock should be held to walk our icq_list and then - * for each icq the outer matching queue_lock should be grabbed. - * ie. We need to do reverse-order double lock dancing. - * - * Another twist is that we are often called with one of the - * matching queue_locks held as indicated by @locked_q, which - * prevents performing double-lock dance for other queues. - * - * So, we do it in two stages. The fast path uses the queue_lock - * the caller is holding and, if other queues need to be accessed, - * uses trylock to avoid introducing locking dependency. This can - * handle most cases, especially if @ioc was performing IO on only - * single device. - * - * If trylock doesn't cut it, we defer to @ioc->release_work which - * can do all the double-locking dancing. + * Releasing ioc requires reverse order double locking and we may + * already be holding a queue_lock. Do it asynchronously from wq. */ - spin_lock_irqsave_nested(&ioc->lock, flags, - ioc_release_depth(locked_q)); - - while (!hlist_empty(&ioc->icq_list)) { - struct io_cq *icq = hlist_entry(ioc->icq_list.first, - struct io_cq, ioc_node); - struct request_queue *this_q = icq->q; - - if (this_q != last_q) { - if (last_q && last_q != locked_q) - spin_unlock(last_q->queue_lock); - last_q = NULL; - - /* spin_trylock() always successes in UP case */ - if (this_q != locked_q && - !spin_trylock(this_q->queue_lock)) - break; - last_q = this_q; - continue; - } - ioc_exit_icq(icq); + if (atomic_long_dec_and_test(&ioc->refcount)) { + spin_lock_irqsave(&ioc->lock, flags); + if (!hlist_empty(&ioc->icq_list)) + schedule_work(&ioc->release_work); + spin_unlock_irqrestore(&ioc->lock, flags); } - - if (last_q && last_q != locked_q) - spin_unlock(last_q->queue_lock); - - spin_unlock_irqrestore(&ioc->lock, flags); - - /* if no icq is left, we're done; otherwise, kick release_work */ - if (hlist_empty(&ioc->icq_list)) - kmem_cache_free(iocontext_cachep, ioc); - else - schedule_work(&ioc->release_work); } EXPORT_SYMBOL(put_io_context); @@ -238,7 +168,7 @@ void exit_io_context(struct task_struct *task) task_unlock(task); atomic_dec(&ioc->nr_tasks); - put_io_context(ioc, NULL); + put_io_context(ioc); } /** diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index da21c24..5684df6 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1794,7 +1794,7 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, cfqd->active_queue = NULL; if (cfqd->active_cic) { - put_io_context(cfqd->active_cic->icq.ioc, cfqd->queue); + put_io_context(cfqd->active_cic->icq.ioc); cfqd->active_cic = NULL; } } diff --git a/fs/ioprio.c b/fs/ioprio.c index f84b380..0f1b951 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -51,7 +51,7 @@ int set_task_ioprio(struct task_struct *task, int ioprio) ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); if (ioc) { ioc_ioprio_changed(ioc, ioprio); - put_io_context(ioc, NULL); + put_io_context(ioc); } return err; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 6c6a1f0..606cf33 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -399,9 +399,6 @@ struct request_queue { /* Throttle data */ struct throtl_data *td; #endif -#ifdef CONFIG_LOCKDEP - int ioc_release_depth; -#endif }; #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 7e1371c..119773e 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -133,7 +133,7 @@ static inline struct io_context *ioc_task_link(struct io_context *ioc) struct task_struct; #ifdef CONFIG_BLOCK -void put_io_context(struct io_context *ioc, struct request_queue *locked_q); +void put_io_context(struct io_context *ioc); void exit_io_context(struct task_struct *task); struct io_context *get_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node); @@ -141,8 +141,7 @@ void ioc_ioprio_changed(struct io_context *ioc, int ioprio); void ioc_cgroup_changed(struct io_context *ioc); #else struct io_context; -static inline void put_io_context(struct io_context *ioc, - struct request_queue *locked_q) { } +static inline void put_io_context(struct io_context *ioc) { } static inline void exit_io_context(struct task_struct *task) { } #endif diff --git a/kernel/fork.c b/kernel/fork.c index 051f090..c574aef 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -890,7 +890,7 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) return -ENOMEM; new_ioc->ioprio = ioc->ioprio; - put_io_context(new_ioc, NULL); + put_io_context(new_ioc); } #endif return 0; -- cgit v0.10.2 From 4e8670e26135d8fbfd5e084fddc1a8ed9f8eb4cb Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Tue, 7 Feb 2012 07:54:31 +0100 Subject: mtip32xx: removed the irrelevant argument of mtip_hw_submit_io() and the unused member of struct driver_data Removed the following: * irrelevant argument 'barrier' of mtip_hw_submit_io() * unused member 'eh_active' of struct driver_data Signed-off-by: Asai Thambi S P Signed-off-by: Sam Bradshaw Signed-off-by: Jens Axboe diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index b74eab7..8eb81c9 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -2068,8 +2068,6 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, * when the read completes. * @data Callback data passed to the callback function * when the read completes. - * @barrier If non-zero, this command must be completed before - * issuing any other commands. * @dir Direction (read or write) * * return value @@ -2077,7 +2075,7 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, */ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, int nsect, int nents, int tag, void *callback, - void *data, int barrier, int dir) + void *data, int dir) { struct host_to_dev_fis *fis; struct mtip_port *port = dd->port; @@ -2108,8 +2106,6 @@ static void mtip_hw_submit_io(struct driver_data *dd, sector_t start, *((unsigned int *) &fis->lba_low) = (start & 0xFFFFFF); *((unsigned int *) &fis->lba_low_ex) = ((start >> 24) & 0xFFFFFF); fis->device = 1 << 6; - if (barrier) - fis->device |= FUA_BIT; fis->features = nsect & 0xFF; fis->features_ex = (nsect >> 8) & 0xFF; fis->sect_count = ((tag << 3) | (tag >> 5)); @@ -3087,7 +3083,6 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) tag, bio_endio, bio, - bio->bi_rw & REQ_FUA, bio_data_dir(bio)); } else bio_io_error(bio); @@ -3187,6 +3182,10 @@ skip_create_disk: blk_queue_max_segments(dd->queue, MTIP_MAX_SG); blk_queue_physical_block_size(dd->queue, 4096); blk_queue_io_min(dd->queue, 4096); + /* + * write back cache is not supported in the device. FUA depends on + * write back cache support, hence setting flush support to zero. + */ blk_queue_flush(dd->queue, 0); /* Set the capacity of the device in 512 byte sectors. */ diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index 723d7c4..e0554a8 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -104,9 +104,6 @@ /* BAR number used to access the HBA registers. */ #define MTIP_ABAR 5 -/* Forced Unit Access Bit */ -#define FUA_BIT 0x80 - #ifdef DEBUG #define dbg_printk(format, arg...) \ printk(pr_fmt(format), ##arg); @@ -415,8 +412,6 @@ struct driver_data { atomic_t resumeflag; /* Atomic variable to track suspend/resume */ - atomic_t eh_active; /* Flag for error handling tracking */ - struct task_struct *mtip_svc_handler; /* task_struct of svc thd */ }; -- cgit v0.10.2 From b97f6bfdd1af95681de5a9f652da644a6525e376 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 7 Feb 2012 11:00:53 +0100 Subject: ALSA: hda - Fix error handling in patch_ca0132.c In patch_ca0132.c, the error returned from chipio_write() isn't checked always. Also, the power-up/down sequence isn't tracked properly in some error paths. Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 35abe3c..21d91d5 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -728,18 +728,19 @@ static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol, err = chipio_read(codec, REG_CODEC_MUTE, &data); if (err < 0) - return err; + goto exit; /* *valp 0 is mute, 1 is unmute */ data = (data & 0x7f) | (*valp ? 0 : 0x80); - chipio_write(codec, REG_CODEC_MUTE, data); + err = chipio_write(codec, REG_CODEC_MUTE, data); if (err < 0) - return err; + goto exit; spec->curr_hp_switch = *valp; + exit: snd_hda_power_down(codec); - return 1; + return err < 0 ? err : 1; } static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol, @@ -770,18 +771,19 @@ static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol, err = chipio_read(codec, REG_CODEC_MUTE, &data); if (err < 0) - return err; + goto exit; /* *valp 0 is mute, 1 is unmute */ data = (data & 0xef) | (*valp ? 0 : 0x10); - chipio_write(codec, REG_CODEC_MUTE, data); + err = chipio_write(codec, REG_CODEC_MUTE, data); if (err < 0) - return err; + goto exit; spec->curr_speaker_switch = *valp; + exit: snd_hda_power_down(codec); - return 1; + return err < 0 ? err : 1; } static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol, @@ -819,25 +821,26 @@ static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol, err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data); if (err < 0) - return err; + goto exit; val = 31 - left_vol; data = (data & 0xe0) | val; - chipio_write(codec, REG_CODEC_HP_VOL_L, data); + err = chipio_write(codec, REG_CODEC_HP_VOL_L, data); if (err < 0) - return err; + goto exit; val = 31 - right_vol; data = (data & 0xe0) | val; - chipio_write(codec, REG_CODEC_HP_VOL_R, data); + err = chipio_write(codec, REG_CODEC_HP_VOL_R, data); if (err < 0) - return err; + goto exit; spec->curr_hp_volume[0] = left_vol; spec->curr_hp_volume[1] = right_vol; + exit: snd_hda_power_down(codec); - return 1; + return err < 0 ? err : 1; } static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid) @@ -936,6 +939,8 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; err = add_in_volume(codec, spec->dig_in, "IEC958"); + if (err < 0) + return err; } return 0; } -- cgit v0.10.2 From 217c8b2b1978aa4a02ce040a99c59ed3b6418fe5 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 7 Feb 2012 13:40:56 +0100 Subject: HID: wiimote: fix invalid power_supply_powers call Analogically to d7cb3dbd1 ("HID: wacom: Fix invalid power_supply_powers calls"), fix also the same occurence in wiimote driver. Reported-by: przemo@firszt.eu Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index fc253b4..cac3589 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1226,14 +1226,14 @@ static int wiimote_hid_probe(struct hid_device *hdev, wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; wdata->battery.use_for_apm = 0; - power_supply_powers(&wdata->battery, &hdev->dev); - ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); if (ret) { hid_err(hdev, "Cannot register battery device\n"); goto err_battery; } + power_supply_powers(&wdata->battery, &hdev->dev); + ret = wiimote_leds_create(wdata); if (ret) goto err_free; -- cgit v0.10.2 From 416846d2b31fc740ed9d5a5ec116964fb43c4358 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 7 Feb 2012 14:18:14 +0100 Subject: ALSA: hda - add support for Uniwill ECS M31EI notebook This hardware requires same fixup for the node 0x0f like Asus A6Rp. More information: https://bugzilla.redhat.com/show_bug.cgi?id=785417 Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 485a837..9350f3c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5627,6 +5627,7 @@ static const struct alc_fixup alc861_fixups[] = { static const struct snd_pci_quirk alc861_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP), + SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), {} -- cgit v0.10.2 From f39d47ff819ed52a2afbdbecbe35f23f7755f58d Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Tue, 7 Feb 2012 14:39:57 +0100 Subject: perf: Fix double start/stop in x86_pmu_start() The following patch fixes a bug introduced by the following commit: e050e3f0a71b ("perf: Fix broken interrupt rate throttling") The patch caused the following warning to pop up depending on the sampling frequency adjustments: ------------[ cut here ]------------ WARNING: at arch/x86/kernel/cpu/perf_event.c:995 x86_pmu_start+0x79/0xd4() It was caused by the following call sequence: perf_adjust_freq_unthr_context.part() { stop() if (delta > 0) { perf_adjust_period() { if (period > 8*...) { stop() ... start() } } } start() } Which caused a double start and a double stop, thus triggering the assert in x86_pmu_start(). The patch fixes the problem by avoiding the double calls. We pass a new argument to perf_adjust_period() to indicate whether or not the event is already stopped. We can't just remove the start/stop from that function because it's called from __perf_event_overflow where the event needs to be reloaded via a stop/start back-toback call. The patch reintroduces the assertion in x86_pmu_start() which was removed by commit: 84f2b9b ("perf: Remove deprecated WARN_ON_ONCE()") In this second version, we've added calls to disable/enable PMU during unthrottling or frequency adjustment based on bug report of spurious NMI interrupts from Eric Dumazet. Reported-and-tested-by: Eric Dumazet Signed-off-by: Stephane Eranian Acked-by: Peter Zijlstra Cc: markus@trippelsdorf.de Cc: paulus@samba.org Link: http://lkml.kernel.org/r/20120207133956.GA4932@quad [ Minor edits to the changelog and to the code ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 2a30e5a..5adce10 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -986,6 +986,9 @@ static void x86_pmu_start(struct perf_event *event, int flags) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx = event->hw.idx; + if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) + return; + if (WARN_ON_ONCE(idx == -1)) return; diff --git a/kernel/events/core.c b/kernel/events/core.c index ba36013..1b5c081 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2303,7 +2303,7 @@ do { \ static DEFINE_PER_CPU(int, perf_throttled_count); static DEFINE_PER_CPU(u64, perf_throttled_seq); -static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) +static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count, bool disable) { struct hw_perf_event *hwc = &event->hw; s64 period, sample_period; @@ -2322,9 +2322,13 @@ static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) hwc->sample_period = sample_period; if (local64_read(&hwc->period_left) > 8*sample_period) { - event->pmu->stop(event, PERF_EF_UPDATE); + if (disable) + event->pmu->stop(event, PERF_EF_UPDATE); + local64_set(&hwc->period_left, 0); - event->pmu->start(event, PERF_EF_RELOAD); + + if (disable) + event->pmu->start(event, PERF_EF_RELOAD); } } @@ -2350,6 +2354,7 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, return; raw_spin_lock(&ctx->lock); + perf_pmu_disable(ctx->pmu); list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (event->state != PERF_EVENT_STATE_ACTIVE) @@ -2381,13 +2386,17 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, /* * restart the event * reload only if value has changed + * we have stopped the event so tell that + * to perf_adjust_period() to avoid stopping it + * twice. */ if (delta > 0) - perf_adjust_period(event, period, delta); + perf_adjust_period(event, period, delta, false); event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); } + perf_pmu_enable(ctx->pmu); raw_spin_unlock(&ctx->lock); } @@ -4562,7 +4571,7 @@ static int __perf_event_overflow(struct perf_event *event, hwc->freq_time_stamp = now; if (delta > 0 && delta < 2*TICK_NSEC) - perf_adjust_period(event, delta, hwc->last_period); + perf_adjust_period(event, delta, hwc->last_period, true); } /* -- cgit v0.10.2 From aadf1f0fc851d71095d2773bcda73e1423e6b730 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 6 Feb 2012 15:04:23 +0000 Subject: Revert "skge: check for PCI dma mapping errors" As reported by several people... The code in rx_clean was panic'ing so revert commit d0249e44432aa0ffcf710b64449b8eaa3722547e. Will redo DMA mapping checks as new patches for a later release. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index edb9bda..33947ac 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -931,20 +931,17 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base) } /* Allocate and setup a new buffer for receiving */ -static int skge_rx_setup(struct pci_dev *pdev, - struct skge_element *e, - struct sk_buff *skb, unsigned int bufsize) +static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, + struct sk_buff *skb, unsigned int bufsize) { struct skge_rx_desc *rd = e->desc; - dma_addr_t map; + u64 map; - map = pci_map_single(pdev, skb->data, bufsize, + map = pci_map_single(skge->hw->pdev, skb->data, bufsize, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(pdev, map)) - goto mapping_error; - rd->dma_lo = lower_32_bits(map); - rd->dma_hi = upper_32_bits(map); + rd->dma_lo = map; + rd->dma_hi = map >> 32; e->skb = skb; rd->csum1_start = ETH_HLEN; rd->csum2_start = ETH_HLEN; @@ -956,13 +953,6 @@ static int skge_rx_setup(struct pci_dev *pdev, rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize; dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, bufsize); - return 0; - -mapping_error: - if (net_ratelimit()) - dev_warn(&pdev->dev, "%s: rx mapping error\n", - skb->dev->name); - return -EIO; } /* Resume receiving using existing skb, @@ -1024,11 +1014,7 @@ static int skge_rx_fill(struct net_device *dev) return -ENOMEM; skb_reserve(skb, NET_IP_ALIGN); - if (skge_rx_setup(skge->hw->pdev, e, skb, skge->rx_buf_size)) { - kfree_skb(skb); - return -ENOMEM; - } - + skge_rx_setup(skge, e, skb, skge->rx_buf_size); } while ((e = e->next) != ring->start); ring->to_clean = ring->start; @@ -2743,7 +2729,7 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, struct skge_tx_desc *td; int i; u32 control, len; - dma_addr_t map; + u64 map; if (skb_padto(skb, ETH_ZLEN)) return NETDEV_TX_OK; @@ -2757,14 +2743,11 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, e->skb = skb; len = skb_headlen(skb); map = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(hw->pdev, map)) - goto mapping_error; - dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, len); - td->dma_lo = lower_32_bits(map); - td->dma_hi = upper_32_bits(map); + td->dma_lo = map; + td->dma_hi = map >> 32; if (skb->ip_summed == CHECKSUM_PARTIAL) { const int offset = skb_checksum_start_offset(skb); @@ -2795,16 +2778,14 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, map = skb_frag_dma_map(&hw->pdev->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - if (dma_mapping_error(&hw->pdev->dev, map)) - goto mapping_unwind; e = e->next; e->skb = skb; tf = e->desc; BUG_ON(tf->control & BMU_OWN); - tf->dma_lo = lower_32_bits(map); - tf->dma_hi = upper_32_bits(map); + tf->dma_lo = map; + tf->dma_hi = (u64) map >> 32; dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, skb_frag_size(frag)); @@ -2834,28 +2815,6 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, } return NETDEV_TX_OK; - -mapping_unwind: - /* unroll any pages that were already mapped. */ - if (e != skge->tx_ring.to_use) { - struct skge_element *u; - - for (u = skge->tx_ring.to_use->next; u != e; u = u->next) - pci_unmap_page(hw->pdev, dma_unmap_addr(u, mapaddr), - dma_unmap_len(u, maplen), - PCI_DMA_TODEVICE); - e = skge->tx_ring.to_use; - } - /* undo the mapping for the skb header */ - pci_unmap_single(hw->pdev, dma_unmap_addr(e, mapaddr), - dma_unmap_len(e, maplen), - PCI_DMA_TODEVICE); -mapping_error: - /* mapping error causes error message and packet to be discarded. */ - if (net_ratelimit()) - dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name); - dev_kfree_skb(skb); - return NETDEV_TX_OK; } @@ -3099,17 +3058,13 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, if (!nskb) goto resubmit; - if (unlikely(skge_rx_setup(skge->hw->pdev, e, nskb, skge->rx_buf_size))) { - dev_kfree_skb(nskb); - goto resubmit; - } - pci_unmap_single(skge->hw->pdev, dma_unmap_addr(e, mapaddr), dma_unmap_len(e, maplen), PCI_DMA_FROMDEVICE); skb = e->skb; prefetch(skb->data); + skge_rx_setup(skge, e, nskb, skge->rx_buf_size); } skb_put(skb, len); -- cgit v0.10.2 From da0912868527913eba15f5ebcfb420b32a037f1a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 4 Feb 2012 20:31:39 +0000 Subject: CONFIG_TR/CONFIG_LLC: work around the problem with select As it is, with PCI/ISA/MCA/CCW all set to n and PCMCIA set to m setting TR to y will set LLC to m, with very unpleasant results - net/802/psnap gets picked into obj-y, resulting in the kernel that won't link - psnap calls functions from llc. The cause, AFAICS, is that kconfig gets rev_dep for LLC containing || TR && (deps for TR) and even though TR is boolean, both LLC and PCMCIA are tristate and that thing becomes || y && (n || m), i.e. || m. The reason for dependency on PCMCIA is that when none of PCI, ISA, MCA, CCW or PCMCIA is set there'll be no tokenring drivers, so there's no point building tokenring core. Proper fix probably belongs in kconfig (we need strict and, such that y m would be y, so that rev_deps added for tristate selected by bool would use that instead of &&; we'd have || TR (deps for TR) in this case), but it's a rather intrusive change. There's an easy workaround in case of TR -> LLC select, namely to have a def_bool y symbol sitting under if TR and have that symbol selecting LLC. Kudos to johill for suggesting that one... Signed-off-by: Al Viro Signed-off-by: David S. Miller diff --git a/drivers/net/tokenring/Kconfig b/drivers/net/tokenring/Kconfig index c7e0149..45550d4 100644 --- a/drivers/net/tokenring/Kconfig +++ b/drivers/net/tokenring/Kconfig @@ -7,7 +7,6 @@ menuconfig TR bool "Token Ring driver support" depends on NETDEVICES && !UML depends on (PCI || ISA || MCA || CCW || PCMCIA) - select LLC help Token Ring is IBM's way of communication on a local network; the rest of the world uses Ethernet. To participate on a Token Ring @@ -20,6 +19,10 @@ menuconfig TR if TR +config WANT_LLC + def_bool y + select LLC + config PCMCIA_IBMTR tristate "IBM PCMCIA tokenring adapter support" depends on IBMTR!=y && PCMCIA -- cgit v0.10.2 From 3f61cd879c2f112c468e8849949b6fc88c739679 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 6 Feb 2012 11:28:21 +0000 Subject: bnx2x: Fix mem leak in bnx2x_tpa_stop() if build_skb() fails. We allocate memory for 'new_data' with kmalloc(). If we get the memory we then try to build_skb() and if that should fail (which it can) we do not enter 'if (likely(skb)) {' and actually use 'new_data' but instead fall through to the 'drop:' label and end up returning from the function without ever assigning 'new'data' to anything or freeing it. That leaks the memory allocated to 'new_data'. This patch fixes the memory leak by doing a kfree(new_data) in the case where build_skb() fails (or where allocation of 'new_data' itself fails, but in taht case it's just a harmless kfree(NULL)). Signed-off-by: Jesper Juhl Acked-by: Eric Dumazet Acked-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 03f3935..7aee469 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -523,7 +523,6 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, skb = build_skb(data); if (likely(skb)) { - #ifdef BNX2X_STOP_ON_ERROR if (pad + len > fp->rx_buf_size) { BNX2X_ERR("skb_put is about to fail... " @@ -557,7 +556,7 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, return; } - + kfree(new_data); drop: /* drop the packet and keep the buffer in the bin */ DP(NETIF_MSG_RX_STATUS, -- cgit v0.10.2 From 6d25886ee2fbc05a7bf4dae5f5ae345cb73df2fd Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Tue, 7 Feb 2012 07:39:11 +0000 Subject: net: Fix build regression when INET_UDP_DIAG=y and IPV6=m Tested-by: Anisse Astier Signed-off-by: David S. Miller diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index aa2a2c7..d183262 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -409,7 +409,7 @@ config INET_TCP_DIAG config INET_UDP_DIAG tristate "UDP: socket monitoring interface" - depends on INET_DIAG + depends on INET_DIAG && (IPV6 || IPV6=n) default n ---help--- Support for UDP socket monitoring interface used by the ss tool. -- cgit v0.10.2 From fdb37a7f84a58ccad24abffd54ad46d23b763e13 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 6 Feb 2012 23:55:15 +0000 Subject: net: sh_eth: fix skb_over_panic happen When this GETHER controller received a large frame (about 1800 bytes or more), skb_over_panic() happened. This is because the previous driver set the RFLR to 0x1000 (4096 bytes) and the skb allocate size is smaller than 4096 bytes. So, the controller accepted such a frame. The controller can discard a large frame by the RFLR setting. So, the patch modifies the value of RFLR to mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN. Signed-off-by: Yoshihiro Shimoda Cc: Eric Dumazet Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 813d41c..87b6501 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "sh_eth.h" @@ -817,7 +818,8 @@ static int sh_eth_dev_init(struct net_device *ndev) sh_eth_write(ndev, 0, TRIMD); /* Recv frame limit set register */ - sh_eth_write(ndev, RFLR_VALUE, RFLR); + sh_eth_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, + RFLR); sh_eth_write(ndev, sh_eth_read(ndev, EESR), EESR); sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 47877b1..cdbd844 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -575,9 +575,6 @@ enum RPADIR_BIT { RPADIR_PADR = 0x0003f, }; -/* RFLR */ -#define RFLR_VALUE 0x1000 - /* FDR */ #define DEFAULT_FDR_INIT 0x00000707 -- cgit v0.10.2 From a1728800bed3b93b231d99e97c756f622b9991c2 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Mon, 6 Feb 2012 14:51:03 +0000 Subject: net: enable TC35815 for MIPS again 8<---------------------------------------------------------------------- From: Ralf Roesch Date: Wed, 16 Nov 2011 09:33:50 +0100 Subject: [PATCH] net: enable TC35815 for MIPS again TX493[8,9] MIPS SoCs support 2 Ethernet channels of type TC35815 which are connected to the internal PCI controller. And JMR3927 MIPS board has a TC35815 chip on board. These dependencies were lost on movement to drivers/net/ethernet/toshiba. Signed-off-by: Ralf Roesch Signed-off-by: Atsushi Nemoto CC: stable@vger.kernel.org [3.2+] Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/toshiba/Kconfig b/drivers/net/ethernet/toshiba/Kconfig index 0517647..74acb5c 100644 --- a/drivers/net/ethernet/toshiba/Kconfig +++ b/drivers/net/ethernet/toshiba/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_TOSHIBA bool "Toshiba devices" default y - depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) || PPC_PS3 + depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB || MIPS) || PPC_PS3 ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from -- cgit v0.10.2 From e81a7bd55531a77d8c16d44766db4089c2983f1b Mon Sep 17 00:00:00 2001 From: Tomas Vanek Date: Sun, 5 Feb 2012 15:51:53 +0200 Subject: zd1211rw: firmware needs duration_id set to zero for non-pspoll frames Some devices (iwl5100) cannot connect to zd1211rw based AP. It appears that zd1211 firmware messes up duration_id field if it is not set to zero by driver. Sniffing traffic shows that zd1211 is transmitting frames with duration_id bits 14 and 15 set and other bits appearing random. Setting duration_id at driver to zero results zd1211 outputting sane duration_id. This means that firmware is setting correct values itself and expects duration_id to be zero in first place. Looking at vendor driver shows that only PSPoll frames have duration_id set by driver, for other frames duration_id left zero. Original bug-report and attached patch at: http://sourceforge.net/mailarchive/message.php?msg_id=28759111 Reported-by: Tomas Vanek [modified original patch from bug-report, added check for pspoll frame] Signed-off-by: Jussi Kivilinna Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 0a70149..98a574a 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -866,6 +866,14 @@ static int fill_ctrlset(struct zd_mac *mac, ZD_ASSERT(frag_len <= 0xffff); + /* + * Firmware computes the duration itself (for all frames except PSPoll) + * and needs the field set to 0 at input, otherwise firmware messes up + * duration_id and sets bits 14 and 15 on. + */ + if (!ieee80211_is_pspoll(hdr->frame_control)) + hdr->duration_id = 0; + txrate = ieee80211_get_tx_rate(mac->hw, info); cs->modulation = txrate->hw_value; -- cgit v0.10.2 From 59c1204d464075fcb5d02e7daa6b23782dd4a088 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 7 Feb 2012 08:20:37 -0800 Subject: ACPI: remove duplicated lines of merging problems with acpi_processor_add Those lines have two copies. Signed-off-by: Yinghai Lu Cc: Thomas Renninger Signed-off-by: Linus Torvalds diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 2b805d7..8ae05ce 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -586,13 +586,6 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) if (pr->flags.need_hotplug_init) return 0; - /* - * Do not start hotplugged CPUs now, but when they - * are onlined the first time - */ - if (pr->flags.need_hotplug_init) - return 0; - result = acpi_processor_start(pr); if (result) goto err_remove_sysfs; -- cgit v0.10.2 From 8eef05dd3e70233f3e391774d612dab44c3f023b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 3 Feb 2012 15:20:39 -0800 Subject: checkpatch: Warn on code with 6+ tab indentation Overly indented code should be refactored. Suggest refactoring excessive indentation of of if/else/for/do/while/switch statements. For example: $ cat t.c #include #include int main(int argc, char **argv) { if (1) if (2) if (3) if (4) if (5) if (6) if (7) if (8) ; return 0; } $ ./scripts/checkpatch.pl -f t.c WARNING: Too many leading tabs - consider code refactoring #12: FILE: t.c:12: + if (6) WARNING: Too many leading tabs - consider code refactoring #13: FILE: t.c:13: + if (7) WARNING: Too many leading tabs - consider code refactoring #14: FILE: t.c:14: + if (8) total: 0 errors, 3 warnings, 17 lines checked t.c has style problems, please review. If any of these errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. Signed-off-by: Joe Perches Signed-off-by: Linus Torvalds diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index e3bfcbe..a3b9782 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1924,6 +1924,12 @@ sub process { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + + if ($line =~ /^\+\t{6,}/) { + WARN("DEEP_INDENTATION", + "Too many leading tabs - consider code refactoring\n" . $herecurr); + } + my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); -- cgit v0.10.2 From 4edc53c1f8cdd99d349165d6c61c45aa4e8e2564 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 7 Feb 2012 06:30:51 -0500 Subject: cifs: fix error handling when cifscreds key payload is an error Reported-by: Dan Carpenter Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9c28865..940189b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2125,7 +2125,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) down_read(&key->sem); upayload = key->payload.data; if (IS_ERR_OR_NULL(upayload)) { - rc = PTR_ERR(key); + rc = upayload ? PTR_ERR(upayload) : -EINVAL; goto out_key_put; } -- cgit v0.10.2 From 8b0192a5f478da1c1ae906bf3ffff53f26204f56 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 7 Feb 2012 06:30:52 -0500 Subject: cifs: request oplock when doing open on lookup Currently, it's always set to 0 (no oplock requested). Cc: Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index df8fecb..63a196b 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -492,7 +492,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, { int xid; int rc = 0; /* to get around spurious gcc warning, set to zero here */ - __u32 oplock = 0; + __u32 oplock = enable_oplocks ? REQ_OPLOCK : 0; __u16 fileHandle = 0; bool posix_open = false; struct cifs_sb_info *cifs_sb; -- cgit v0.10.2 From ff4fa4a25a33f92b5653bb43add0c63bea98d464 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 7 Feb 2012 06:31:05 -0500 Subject: cifs: don't return error from standard_receive3 after marking response malformed standard_receive3 will check the validity of the response from the server (via checkSMB). It'll pass the result of that check to handle_mid which will dequeue it and mark it with a status of MID_RESPONSE_MALFORMED if checkSMB returned an error. At that point, standard_receive3 will also return an error, which will make the demultiplex thread skip doing the callback for the mid. This is wrong -- if we were able to identify the request and the response is marked malformed, then we want the demultiplex thread to do the callback. Fix this by making standard_receive3 return 0 in this situation. Cc: stable@vger.kernel.org Reported-and-Tested-by: Mark Moseley Signed-off-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 940189b..602f77c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -773,10 +773,11 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) cifs_dump_mem("Bad SMB: ", buf, min_t(unsigned int, server->total_read, 48)); - if (mid) - handle_mid(mid, server, smb_buffer, length); + if (!mid) + return length; - return length; + handle_mid(mid, server, smb_buffer, length); + return 0; } static int -- cgit v0.10.2 From fef67c518349a4cd0eba6b38bde4afdc76538147 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Mon, 6 Feb 2012 15:49:47 +0530 Subject: ARM: OMAP2: Fix the OMAP2 only build break seen with 2011+ ARM tool-chains With the latest Sourcery G++ Lite 2011.03-41 and latest linaro tool-chains OMAP2 only build breaks with below error. arch/arm/mach-omap2/omap-smc.S: Assembler messages: arch/arm/mach-omap2/omap-smc.S:30: Error: selected processor does not support ARM mode `smc #0' arch/arm/mach-omap2/omap-smc.S:53: Error: selected processor does not support ARM mode `smc #0' arch/arm/mach-omap2/omap-smc.S:61: Error: selected processor does not support ARM mode `smc #0' arch/arm/mach-omap2/omap-smc.S:69: Error: selected processor does not support ARM mode `smc #0' arch/arm/mach-omap2/omap-smc.S:77: Error: selected processor does not support ARM mode `smc #0' make[1]: *** [arch/arm/mach-omap2/omap-smc.o] Error 1 OMAP2 devices doesn't have the security support but the security support was getting built because of OMAP2PLUS. Don't build security code for OMAP2 devices. While at it, fix the secure-common line in the Makefile to use tabs instead of spaces. Reported-by: Kevin Hilman Signed-off-by: Santosh Shilimkar Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index fc9b238..bd76394 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -11,9 +11,9 @@ hwmod-common = omap_hwmod.o \ omap_hwmod_common_data.o clock-common = clock.o clock_common_data.o \ clkt_dpll.o clkt_clksel.o -secure-common = omap-smc.o omap-secure.o +secure-common = omap-smc.o omap-secure.o -obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) $(secure-common) +obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common) obj-$(CONFIG_ARCH_OMAP4) += prm44xx.o $(hwmod-common) $(secure-common) diff --git a/arch/arm/plat-omap/include/plat/omap-secure.h b/arch/arm/plat-omap/include/plat/omap-secure.h index 64f9d1c..3047ff9 100644 --- a/arch/arm/plat-omap/include/plat/omap-secure.h +++ b/arch/arm/plat-omap/include/plat/omap-secure.h @@ -3,7 +3,7 @@ #include -#ifdef CONFIG_ARCH_OMAP2PLUS +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) extern int omap_secure_ram_reserve_memblock(void); #else static inline void omap_secure_ram_reserve_memblock(void) -- cgit v0.10.2 From 3686396410e41d97356924b246aced7c86e29ca0 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Sun, 5 Feb 2012 13:39:40 +0200 Subject: ARM: OMAP3: cm-t35: fix section mismatch warning WARNING: arch/arm/mach-omap2/built-in.o(.text+0xeae8): Section mismatch in reference from the function cm_t35_init_usbh() to the (unknown reference) .init.data:(unknown) The function cm_t35_init_usbh() references the (unknown reference) __initdata (unknown). This is often because cm_t35_init_usbh lacks a __initdata annotation or the annotation of (unknown) is wrong. Signed-off-by: Igor Grinberg Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index e921e3b..d73316e 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -437,7 +437,7 @@ static struct usbhs_omap_board_data usbhs_bdata __initdata = { .reset_gpio_port[2] = -EINVAL }; -static void cm_t35_init_usbh(void) +static void __init cm_t35_init_usbh(void) { int err; -- cgit v0.10.2 From 050c8ea80e3e90019d9e981c6a117ef614e882ed Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Feb 2012 09:19:38 +0100 Subject: block: separate out blk_rq_merge_ok() and blk_try_merge() from elevator functions blk_rq_merge_ok() is the elevator-neutral part of merge eligibility test. blk_try_merge() determines merge direction and expects the caller to have tested elv_rq_merge_ok() previously. elv_rq_merge_ok() now wraps blk_rq_merge_ok() and then calls elv_iosched_allow_merge(). elv_try_merge() is removed and the two callers are updated to call elv_rq_merge_ok() explicitly followed by blk_try_merge(). While at it, make rq_merge_ok() functions return bool. This is to prepare for plug merge update and doesn't introduce any behavior change. This is based on Jens' patch to skip elevator_allow_merge_fn() from plug merge. Signed-off-by: Tejun Heo LKML-Reference: <4F16F3CA.90904@kernel.dk> Original-patch-by: Jens Axboe Signed-off-by: Jens Axboe diff --git a/block/blk-core.c b/block/blk-core.c index 532b3a2..fa697bf 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1282,10 +1282,10 @@ static bool attempt_plug_merge(struct request_queue *q, struct bio *bio, (*request_count)++; - if (rq->q != q) + if (rq->q != q || !elv_rq_merge_ok(rq, bio)) continue; - el_ret = elv_try_merge(rq, bio); + el_ret = blk_try_merge(rq, bio); if (el_ret == ELEVATOR_BACK_MERGE) { ret = bio_attempt_back_merge(q, rq, bio); if (ret) diff --git a/block/blk-merge.c b/block/blk-merge.c index cfcc37c..160035f 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -471,3 +471,40 @@ int blk_attempt_req_merge(struct request_queue *q, struct request *rq, { return attempt_merge(q, rq, next); } + +bool blk_rq_merge_ok(struct request *rq, struct bio *bio) +{ + if (!rq_mergeable(rq)) + return false; + + /* don't merge file system requests and discard requests */ + if ((bio->bi_rw & REQ_DISCARD) != (rq->bio->bi_rw & REQ_DISCARD)) + return false; + + /* don't merge discard requests and secure discard requests */ + if ((bio->bi_rw & REQ_SECURE) != (rq->bio->bi_rw & REQ_SECURE)) + return false; + + /* different data direction or already started, don't merge */ + if (bio_data_dir(bio) != rq_data_dir(rq)) + return false; + + /* must be same device and not a special request */ + if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special) + return false; + + /* only merge integrity protected bio into ditto rq */ + if (bio_integrity(bio) != blk_integrity_rq(rq)) + return false; + + return true; +} + +int blk_try_merge(struct request *rq, struct bio *bio) +{ + if (blk_rq_pos(rq) + blk_rq_sectors(rq) == bio->bi_sector) + return ELEVATOR_BACK_MERGE; + else if (blk_rq_pos(rq) - bio_sectors(bio) == bio->bi_sector) + return ELEVATOR_FRONT_MERGE; + return ELEVATOR_NO_MERGE; +} diff --git a/block/blk.h b/block/blk.h index 7efd772..9c12f80 100644 --- a/block/blk.h +++ b/block/blk.h @@ -137,6 +137,8 @@ int blk_attempt_req_merge(struct request_queue *q, struct request *rq, struct request *next); void blk_recalc_rq_segments(struct request *rq); void blk_rq_set_mixed_merge(struct request *rq); +bool blk_rq_merge_ok(struct request *rq, struct bio *bio); +int blk_try_merge(struct request *rq, struct bio *bio); void blk_queue_congestion_threshold(struct request_queue *q); diff --git a/block/elevator.c b/block/elevator.c index 91e18f8..f016855 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -70,39 +70,9 @@ static int elv_iosched_allow_merge(struct request *rq, struct bio *bio) /* * can we safely merge with this request? */ -int elv_rq_merge_ok(struct request *rq, struct bio *bio) +bool elv_rq_merge_ok(struct request *rq, struct bio *bio) { - if (!rq_mergeable(rq)) - return 0; - - /* - * Don't merge file system requests and discard requests - */ - if ((bio->bi_rw & REQ_DISCARD) != (rq->bio->bi_rw & REQ_DISCARD)) - return 0; - - /* - * Don't merge discard requests and secure discard requests - */ - if ((bio->bi_rw & REQ_SECURE) != (rq->bio->bi_rw & REQ_SECURE)) - return 0; - - /* - * different data direction or already started, don't merge - */ - if (bio_data_dir(bio) != rq_data_dir(rq)) - return 0; - - /* - * must be same device and not a special request - */ - if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special) - return 0; - - /* - * only merge integrity protected bio into ditto rq - */ - if (bio_integrity(bio) != blk_integrity_rq(rq)) + if (!blk_rq_merge_ok(rq, bio)) return 0; if (!elv_iosched_allow_merge(rq, bio)) @@ -112,23 +82,6 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio) } EXPORT_SYMBOL(elv_rq_merge_ok); -int elv_try_merge(struct request *__rq, struct bio *bio) -{ - int ret = ELEVATOR_NO_MERGE; - - /* - * we can merge and sequence is ok, check if it's possible - */ - if (elv_rq_merge_ok(__rq, bio)) { - if (blk_rq_pos(__rq) + blk_rq_sectors(__rq) == bio->bi_sector) - ret = ELEVATOR_BACK_MERGE; - else if (blk_rq_pos(__rq) - bio_sectors(bio) == bio->bi_sector) - ret = ELEVATOR_FRONT_MERGE; - } - - return ret; -} - static struct elevator_type *elevator_find(const char *name) { struct elevator_type *e; @@ -478,8 +431,8 @@ int elv_merge(struct request_queue *q, struct request **req, struct bio *bio) /* * First try one-hit cache. */ - if (q->last_merge) { - ret = elv_try_merge(q->last_merge, bio); + if (q->last_merge && elv_rq_merge_ok(q->last_merge, bio)) { + ret = blk_try_merge(q->last_merge, bio); if (ret != ELEVATOR_NO_MERGE) { *req = q->last_merge; return ret; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index c24f3d7..d6dfb65 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -122,7 +122,6 @@ extern void elv_dispatch_add_tail(struct request_queue *, struct request *); extern void elv_add_request(struct request_queue *, struct request *, int); extern void __elv_add_request(struct request_queue *, struct request *, int); extern int elv_merge(struct request_queue *, struct request **, struct bio *); -extern int elv_try_merge(struct request *, struct bio *); extern void elv_merge_requests(struct request_queue *, struct request *, struct request *); extern void elv_merged_request(struct request_queue *, struct request *, int); @@ -155,7 +154,7 @@ extern ssize_t elv_iosched_store(struct request_queue *, const char *, size_t); extern int elevator_init(struct request_queue *, char *); extern void elevator_exit(struct elevator_queue *); extern int elevator_change(struct request_queue *, const char *); -extern int elv_rq_merge_ok(struct request *, struct bio *); +extern bool elv_rq_merge_ok(struct request *, struct bio *); /* * Helper functions. -- cgit v0.10.2 From 07c2bd37350c9b1af71b35d05f16e300a6602948 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 8 Feb 2012 09:19:42 +0100 Subject: block: don't call elevator callbacks for plug merges Plug merge calls two elevator callbacks outside queue lock - elevator_allow_merge_fn() and elevator_bio_merged_fn(). Although attempt_plug_merge() suggests that elevator is guaranteed to be there through the existing request on the plug list, nothing prevents plug merge from calling into dying or initializing elevator. For regular merges, bypass ensures elvpriv count to reach zero, which in turn prevents merges as all !ELVPRIV requests get REQ_SOFTBARRIER from forced back insertion. Plug merge doesn't check ELVPRIV, and, as the requests haven't gone through elevator insertion yet, it doesn't have SOFTBARRIER set allowing merges on a bypassed queue. This, for example, leads to the following crash during elevator switch. BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [] cfq_allow_merge+0x49/0xa0 PGD 112cbc067 PUD 115d5c067 PMD 0 Oops: 0000 [#1] PREEMPT SMP CPU 1 Modules linked in: deadline_iosched Pid: 819, comm: dd Not tainted 3.3.0-rc2-work+ #76 Bochs Bochs RIP: 0010:[] [] cfq_allow_merge+0x49/0xa0 RSP: 0018:ffff8801143a38f8 EFLAGS: 00010297 RAX: 0000000000000000 RBX: ffff88011817ce28 RCX: ffff880116eb6cc0 RDX: 0000000000000000 RSI: ffff880118056e20 RDI: ffff8801199512f8 RBP: ffff8801143a3908 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000000 R12: ffff880118195708 R13: ffff880118052aa0 R14: ffff8801143a3d50 R15: ffff880118195708 FS: 00007f19f82cb700(0000) GS:ffff88011fc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000008 CR3: 0000000112c6a000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process dd (pid: 819, threadinfo ffff8801143a2000, task ffff880116eb6cc0) Stack: ffff88011817ce28 ffff880118195708 ffff8801143a3928 ffffffff81391bba ffff88011817ce28 ffff880118195708 ffff8801143a3948 ffffffff81391bf1 ffff88011817ce28 0000000000000000 ffff8801143a39a8 ffffffff81398e3e Call Trace: [] elv_rq_merge_ok+0x4a/0x60 [] elv_try_merge+0x21/0x40 [] blk_queue_bio+0x8e/0x390 [] generic_make_request+0xca/0x100 [] submit_bio+0x74/0x100 [] __blockdev_direct_IO+0x1ce2/0x3450 [] blkdev_direct_IO+0x57/0x60 [] generic_file_aio_read+0x6d5/0x760 [] do_sync_read+0xe2/0x120 [] vfs_read+0xc5/0x180 [] sys_read+0x51/0x90 [] system_call_fastpath+0x16/0x1b There are multiple ways to fix this including making plug merge check ELVPRIV; however, * Calling into elevator outside queue lock is confusing and error-prone. * Requests on plug list aren't known to the elevator. They aren't on the elevator yet, so there's no elevator specific state to update. * Given the nature of plug merges - collecting bio's for the same purpose from the same issuer - elevator specific restrictions aren't applicable. So, simply don't call into elevator methods from plug merge by moving elv_bio_merged() from bio_attempt_*_merge() to blk_queue_bio(), and using blk_try_merge() in attempt_plug_merge(). This is based on Jens' patch to skip elevator_allow_merge_fn() from plug merge. Note that this makes per-cgroup merged stats skip plug merging. Signed-off-by: Tejun Heo LKML-Reference: <4F16F3CA.90904@kernel.dk> Original-patch-by: Jens Axboe Signed-off-by: Jens Axboe diff --git a/block/blk-core.c b/block/blk-core.c index fa697bf..3a78b00 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1212,7 +1212,6 @@ static bool bio_attempt_back_merge(struct request_queue *q, struct request *req, req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); drive_stat_acct(req, 0); - elv_bio_merged(q, req, bio); return true; } @@ -1243,7 +1242,6 @@ static bool bio_attempt_front_merge(struct request_queue *q, req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); drive_stat_acct(req, 0); - elv_bio_merged(q, req, bio); return true; } @@ -1257,13 +1255,12 @@ static bool bio_attempt_front_merge(struct request_queue *q, * on %current's plugged list. Returns %true if merge was successful, * otherwise %false. * - * This function is called without @q->queue_lock; however, elevator is - * accessed iff there already are requests on the plugged list which in - * turn guarantees validity of the elevator. - * - * Note that, on successful merge, elevator operation - * elevator_bio_merged_fn() will be called without queue lock. Elevator - * must be ready for this. + * Plugging coalesces IOs from the same issuer for the same purpose without + * going through @q->queue_lock. As such it's more of an issuing mechanism + * than scheduling, and the request, while may have elvpriv data, is not + * added on the elevator at this point. In addition, we don't have + * reliable access to the elevator outside queue lock. Only check basic + * merging parameters without querying the elevator. */ static bool attempt_plug_merge(struct request_queue *q, struct bio *bio, unsigned int *request_count) @@ -1282,7 +1279,7 @@ static bool attempt_plug_merge(struct request_queue *q, struct bio *bio, (*request_count)++; - if (rq->q != q || !elv_rq_merge_ok(rq, bio)) + if (rq->q != q || !blk_rq_merge_ok(rq, bio)) continue; el_ret = blk_try_merge(rq, bio); @@ -1347,12 +1344,14 @@ void blk_queue_bio(struct request_queue *q, struct bio *bio) el_ret = elv_merge(q, &req, bio); if (el_ret == ELEVATOR_BACK_MERGE) { if (bio_attempt_back_merge(q, req, bio)) { + elv_bio_merged(q, req, bio); if (!attempt_back_merge(q, req)) elv_merged_request(q, req, el_ret); goto out_unlock; } } else if (el_ret == ELEVATOR_FRONT_MERGE) { if (bio_attempt_front_merge(q, req, bio)) { + elv_bio_merged(q, req, bio); if (!attempt_front_merge(q, req)) elv_merged_request(q, req, el_ret); goto out_unlock; diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 5684df6..d0ba505 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1699,18 +1699,11 @@ static int cfq_allow_merge(struct request_queue *q, struct request *rq, /* * Lookup the cfqq that this bio will be queued with and allow - * merge only if rq is queued there. This function can be called - * from plug merge without queue_lock. In such cases, ioc of @rq - * and %current are guaranteed to be equal. Avoid lookup which - * requires queue_lock by using @rq's cic. + * merge only if rq is queued there. */ - if (current->io_context == RQ_CIC(rq)->icq.ioc) { - cic = RQ_CIC(rq); - } else { - cic = cfq_cic_lookup(cfqd, current->io_context); - if (!cic) - return false; - } + cic = cfq_cic_lookup(cfqd, current->io_context); + if (!cic) + return false; cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio)); return cfqq == RQ_CFQQ(rq); diff --git a/include/linux/elevator.h b/include/linux/elevator.h index d6dfb65..7d4e035 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -42,12 +42,6 @@ struct elevator_ops elevator_merged_fn *elevator_merged_fn; elevator_merge_req_fn *elevator_merge_req_fn; elevator_allow_merge_fn *elevator_allow_merge_fn; - - /* - * Used for both plugged list and elevator merging and in the - * former case called without queue_lock. Read comment on top of - * attempt_plug_merge() for details. - */ elevator_bio_merged_fn *elevator_bio_merged_fn; elevator_dispatch_fn *elevator_dispatch_fn; -- cgit v0.10.2 From 927c9423dd5f2d1c0b93d5e694ab84b4a5559713 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sat, 4 Feb 2012 20:51:43 +0100 Subject: ALSA: usb-audio: add Edirol UM-3G support Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 8edc503..d89ab4c 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1618,6 +1618,14 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + /* Edirol UM-3G */ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x0108), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = 0, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, +{ /* Boss JS-8 Jam Station */ USB_DEVICE(0x0582, 0x0109), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { -- cgit v0.10.2 From 2492250e4412c6411324c14ab289629360640b0a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sat, 4 Feb 2012 20:56:47 +0100 Subject: ALSA: oxygen, virtuoso: fix exchanged L/R volumes of aux and CD inputs The driver accidentally exchanged the left/right fields for stereo AC'97 mixer registers. This affected only the aux and CD inputs because the line input bypasses the AC'97 codec and the mic input is mono; cards without AC'97 (Xonar DS/DG/HDAV Slim, HG2PCI, HiFier) were not affected. Reported-and-tested-by: Abby Cedar Signed-off-by: Clemens Ladisch Cc: 2.6.31+ Signed-off-by: Takashi Iwai diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 26c7e8b..c0dbb52 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -618,9 +618,12 @@ static int ac97_volume_get(struct snd_kcontrol *ctl, mutex_lock(&chip->mutex); reg = oxygen_read_ac97(chip, codec, index); mutex_unlock(&chip->mutex); - value->value.integer.value[0] = 31 - (reg & 0x1f); - if (stereo) - value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); + if (!stereo) { + value->value.integer.value[0] = 31 - (reg & 0x1f); + } else { + value->value.integer.value[0] = 31 - ((reg >> 8) & 0x1f); + value->value.integer.value[1] = 31 - (reg & 0x1f); + } return 0; } @@ -636,14 +639,14 @@ static int ac97_volume_put(struct snd_kcontrol *ctl, mutex_lock(&chip->mutex); oldreg = oxygen_read_ac97(chip, codec, index); - newreg = oldreg; - newreg = (newreg & ~0x1f) | - (31 - (value->value.integer.value[0] & 0x1f)); - if (stereo) - newreg = (newreg & ~0x1f00) | - ((31 - (value->value.integer.value[1] & 0x1f)) << 8); - else - newreg = (newreg & ~0x1f00) | ((newreg & 0x1f) << 8); + if (!stereo) { + newreg = oldreg & ~0x1f; + newreg |= 31 - (value->value.integer.value[0] & 0x1f); + } else { + newreg = oldreg & ~0x1f1f; + newreg |= (31 - (value->value.integer.value[0] & 0x1f)) << 8; + newreg |= 31 - (value->value.integer.value[1] & 0x1f); + } change = newreg != oldreg; if (change) oxygen_write_ac97(chip, codec, index, newreg); -- cgit v0.10.2 From e57b6886f555ab57f40a01713304e2053efe51ec Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Feb 2012 16:42:52 +0100 Subject: drm/i915: no lvds quirk for AOpen MP45 According to a bug report, it doesn't have one. Cc: stable@kernel.org Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44263 Acked-by: Chris Wilson Signed-Off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 798f6e1..aa84832 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -694,6 +694,14 @@ static const struct dmi_system_id intel_no_lvds[] = { }, { .callback = intel_no_lvds_dmi_callback, + .ident = "AOpen i45GMx-I", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"), + }, + }, + { + .callback = intel_no_lvds_dmi_callback, .ident = "Aopen i945GTt-VFA", .matches = { DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"), -- cgit v0.10.2 From 37b40adf2d1b4a5e51323be73ccf8ddcf3f15dd3 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 8 Feb 2012 20:02:03 +0100 Subject: bsg: fix sysfs link remove warning We create "bsg" link if q->kobj.sd is not NULL, so remove it only when the same condition is true. Fixes: WARNING: at fs/sysfs/inode.c:323 sysfs_hash_and_remove+0x2b/0x77() sysfs: can not remove 'bsg', no directory Call Trace: [] warn_slowpath_common+0x6a/0x7f [] ? sysfs_hash_and_remove+0x2b/0x77 [] warn_slowpath_fmt+0x2b/0x2f [] sysfs_hash_and_remove+0x2b/0x77 [] sysfs_remove_link+0x20/0x23 [] bsg_unregister_queue+0x40/0x6d [] __scsi_remove_device+0x31/0x9d [] scsi_forget_host+0x41/0x52 [] scsi_remove_host+0x71/0xe0 [] quiesce_and_remove_host+0x51/0x83 [usb_storage] [] usb_stor_disconnect+0x18/0x22 [usb_storage] [] usb_unbind_interface+0x4e/0x109 [] __device_release_driver+0x6b/0xa6 [] device_release_driver+0x17/0x22 [] bus_remove_device+0xd6/0xe6 [] device_del+0xf2/0x137 [] usb_disable_device+0x94/0x1a0 Signed-off-by: Stanislaw Gruszka Signed-off-by: Jens Axboe diff --git a/block/bsg.c b/block/bsg.c index 4cf703f..ff64ae3 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -983,7 +983,8 @@ void bsg_unregister_queue(struct request_queue *q) mutex_lock(&bsg_mutex); idr_remove(&bsg_minor_idr, bcd->minor); - sysfs_remove_link(&q->kobj, "bsg"); + if (q->kobj.sd) + sysfs_remove_link(&q->kobj, "bsg"); device_unregister(bcd->class_dev); bcd->class_dev = NULL; kref_put(&bcd->ref, bsg_kref_release_function); -- cgit v0.10.2 From cdccaa9467b982d57b139818d15e1e994feca372 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Feb 2012 20:03:14 +0100 Subject: cdrom: move shared static to cdrom_device_info The keeplocked variable in the cdrom driver is shared across multiple drives, but set in per-device ioctls. Move it to the per-device struct, avoiding that the setting on one drive affects the driver's behavior when closing another. [ Impact: limit udev's confusion to one drive when a CD burning program unlocks the CD door at the end of burning. ] Signed-off-by: Paolo Bonzini Signed-off-by: Jens Axboe diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 8fefe59..d620b44 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -286,8 +286,6 @@ /* used to tell the module to turn on full debugging messages */ static bool debug; -/* used to keep tray locked at all times */ -static int keeplocked; /* default compatibility mode */ static bool autoclose=1; static bool autoeject; @@ -1204,7 +1202,7 @@ void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); cdrom_dvd_rw_close_write(cdi); - if ((cdo->capability & CDC_LOCK) && !keeplocked) { + if ((cdo->capability & CDC_LOCK) && !cdi->keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); cdo->lock_door(cdi, 0); } @@ -1371,7 +1369,7 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot) curslot = info->hdr.curslot; kfree(info); - if (cdi->use_count > 1 || keeplocked) { + if (cdi->use_count > 1 || cdi->keeplocked) { if (slot == CDSL_CURRENT) { return curslot; } else { @@ -2289,7 +2287,7 @@ static int cdrom_ioctl_eject(struct cdrom_device_info *cdi) if (!CDROM_CAN(CDC_OPEN_TRAY)) return -ENOSYS; - if (cdi->use_count != 1 || keeplocked) + if (cdi->use_count != 1 || cdi->keeplocked) return -EBUSY; if (CDROM_CAN(CDC_LOCK)) { int ret = cdi->ops->lock_door(cdi, 0); @@ -2316,7 +2314,7 @@ static int cdrom_ioctl_eject_sw(struct cdrom_device_info *cdi, if (!CDROM_CAN(CDC_OPEN_TRAY)) return -ENOSYS; - if (keeplocked) + if (cdi->keeplocked) return -EBUSY; cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); @@ -2447,7 +2445,7 @@ static int cdrom_ioctl_lock_door(struct cdrom_device_info *cdi, if (!CDROM_CAN(CDC_LOCK)) return -EDRIVE_CANT_DO_THIS; - keeplocked = arg ? 1 : 0; + cdi->keeplocked = arg ? 1 : 0; /* * Don't unlock the door on multiple opens by default, but allow diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 35eae4b..7c48029 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -952,7 +952,8 @@ struct cdrom_device_info { char name[20]; /* name of the device type */ /* per-device flags */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ - __u8 reserved : 6; /* not used yet */ + __u8 keeplocked : 1; /* CDROM_LOCKDOOR status */ + __u8 reserved : 5; /* not used yet */ int cdda_method; /* see flags */ __u8 last_sense; __u8 media_written; /* dirty flag, DVD+RW bookkeeping */ -- cgit v0.10.2 From 3f9a5aabd0a9fe0e0cd308506f48963d79169aa7 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 8 Feb 2012 20:03:38 +0100 Subject: floppy: Cleanup disk->queue before caling put_disk() if add_disk() was never called add_disk() takes gendisk reference on request queue. If driver failed during initialization and never called add_disk() then that extra reference is not taken. That reference is put in put_disk(). floppy driver allocates the disk, allocates queue, sets disk->queue and then relizes that floppy controller is not present. It tries to tear down everything and tries to put a reference down in put_disk() which was never taken. In such error cases cleanup disk->queue before calling put_disk() so that we never try to put down a reference which was never taken in first place. Reported-and-tested-by: Suresh Jayaraman Tested-by: Dirk Gouders Signed-off-by: Vivek Goyal Acked-by: Tejun Heo Signed-off-by: Jens Axboe diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 510fb10..401ba78 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4368,8 +4368,14 @@ out_unreg_blkdev: out_put_disk: while (dr--) { del_timer_sync(&motor_off_timer[dr]); - if (disks[dr]->queue) + if (disks[dr]->queue) { blk_cleanup_queue(disks[dr]->queue); + /* + * put_disk() is not paired with add_disk() and + * will put queue reference one extra time. fix it. + */ + disks[dr]->queue = NULL; + } put_disk(disks[dr]); } return err; -- cgit v0.10.2 From 4609dff6b5d11e1ed5ff935e15f9f6022acb312b Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 8 Feb 2012 20:03:39 +0100 Subject: floppy: Fix a crash during rmmod floppy driver does not call add_disk() on all the drives hence we don't take gendisk reference on request queue for these drives. Don't call put_disk() with disk->queue set, otherwise we try to put the reference we never took. Reported-and-tested-by: Dirk Gouders Signed-off-by: Vivek Goyal Acked-by: Tejun Heo Signed-off-by: Jens Axboe diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 401ba78..9baf11e 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4585,6 +4585,15 @@ static void __exit floppy_module_exit(void) platform_device_unregister(&floppy_device[drive]); } blk_cleanup_queue(disks[drive]->queue); + + /* + * These disks have not called add_disk(). Don't put down + * queue reference in put_disk(). + */ + if (!(allowed_drive_mask & (1 << drive)) || + fdc_state[FDC(drive)].version == FDC_NONE) + disks[drive]->queue = NULL; + put_disk(disks[drive]); } -- cgit v0.10.2 From f647e1526fd6c7c8ab720781c40d11e11f930e93 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 7 Feb 2012 17:24:19 +0000 Subject: ASoC: wm8994: Fix typo in VMID ramp setting The VMID ramp rate is supposed to be 0x3, not 11b. Fix that. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 81795eb..b75a652 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -785,7 +785,7 @@ static void vmid_reference(struct snd_soc_codec *codec) WM8994_VMID_RAMP_MASK, WM8994_STARTUP_BIAS_ENA | WM8994_VMID_BUF_ENA | - (0x11 << WM8994_VMID_RAMP_SHIFT)); + (0x3 << WM8994_VMID_RAMP_SHIFT)); /* Main bias enable, VMID=2x40k */ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, -- cgit v0.10.2 From a7c4183be2d6a7da8c97a9b671b5f3aed321127e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 7 Feb 2012 14:18:29 +0000 Subject: ASoC: wm8994: Disable line output discharge prior to ramping VMID Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index b75a652..ec69a6c 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -787,6 +787,11 @@ static void vmid_reference(struct snd_soc_codec *codec) WM8994_VMID_BUF_ENA | (0x3 << WM8994_VMID_RAMP_SHIFT)); + /* Remove discharge for line out */ + snd_soc_update_bits(codec, WM8994_ANTIPOP_1, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH, 0); + /* Main bias enable, VMID=2x40k */ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, WM8994_BIAS_ENA | -- cgit v0.10.2 From 5abebfdd02450fa1349daacf242e70b3736581e3 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Wed, 8 Feb 2012 22:07:18 +0100 Subject: bio: don't overflow in bio_get_nr_vecs() There were two places bio_get_nr_vecs() could overflow: First, it did a left shift to convert from sectors to bytes immediately before dividing by PAGE_SIZE. If PAGE_SIZE ever was less than 512 a great many things would break, so dividing by PAGE_SIZE >> 9 is safe and will generate smaller code too. The nastier overflow was in the DIV_ROUND_UP() (that's what the code was effectively doing, anyways). If n + d overflowed, the whole thing would return 0 which breaks things rather effectively. bio_get_nr_vecs() doesn't claim to give an exact value anyways, so the DIV_ROUND_UP() is silly; we could do a straight divide except if a device's queue_max_sectors was less than PAGE_SIZE we'd return 0. So we just add 1; this should always be safe - things will break badly if bio_get_nr_vecs() returns > BIO_MAX_PAGES (bio_alloc() will suddenly start failing) but it's queue_max_segments that must guard against this, if queue_max_sectors is preventing this from happen things are going to explode on architectures with different PAGE_SIZE. Signed-off-by: Kent Overstreet Cc: Tejun Heo Acked-by: Valdis Kletnieks Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/fs/bio.c b/fs/bio.c index b1fe82c..b980ecd 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -505,13 +505,9 @@ EXPORT_SYMBOL(bio_clone); int bio_get_nr_vecs(struct block_device *bdev) { struct request_queue *q = bdev_get_queue(bdev); - int nr_pages; - - nr_pages = ((queue_max_sectors(q) << 9) + PAGE_SIZE - 1) >> PAGE_SHIFT; - if (nr_pages > queue_max_segments(q)) - nr_pages = queue_max_segments(q); - - return nr_pages; + return min_t(unsigned, + queue_max_segments(q), + queue_max_sectors(q) / (PAGE_SIZE >> 9) + 1); } EXPORT_SYMBOL(bio_get_nr_vecs); -- cgit v0.10.2 From 306df0716aa285d378cc948fafed53c6ed895fe6 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Wed, 8 Feb 2012 22:07:19 +0100 Subject: loop: zero fill bio instead of return -EIO for partial read commit 8268f5a741 ("deny partial write for loop dev fd") tried to fix the loop device partial read information leak problem. But it changed the semantics of read behavior. When we read beyond the end of the device we should get 0 bytes, which is normal behavior, we should not just return -EIO Instead of returning -EIO, zero out the bio to avoid information leak in case of partail read. Signed-off-by: Dave Young Reviewed-by: Jeff Moyer Tested-by: Jeff Moyer Cc: Dmitry Monakhov Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe diff --git a/drivers/block/loop.c b/drivers/block/loop.c index f002577..cd50435 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -356,14 +356,14 @@ lo_direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd) return __splice_from_pipe(pipe, sd, lo_splice_actor); } -static int +static ssize_t do_lo_receive(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos) { struct lo_read_data cookie; struct splice_desc sd; struct file *file; - long retval; + ssize_t retval; cookie.lo = lo; cookie.page = bvec->bv_page; @@ -379,26 +379,28 @@ do_lo_receive(struct loop_device *lo, file = lo->lo_backing_file; retval = splice_direct_to_actor(file, &sd, lo_direct_splice_actor); - if (retval < 0) - return retval; - if (retval != bvec->bv_len) - return -EIO; - return 0; + return retval; } static int lo_receive(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos) { struct bio_vec *bvec; - int i, ret = 0; + ssize_t s; + int i; bio_for_each_segment(bvec, bio, i) { - ret = do_lo_receive(lo, bvec, bsize, pos); - if (ret < 0) + s = do_lo_receive(lo, bvec, bsize, pos); + if (s < 0) + return s; + + if (s != bvec->bv_len) { + zero_fill_bio(bio); break; + } pos += bvec->bv_len; } - return ret; + return 0; } static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) -- cgit v0.10.2 From 617cf884810b44384fe8e9431e9babeb80a2ff37 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 8 Feb 2012 13:53:38 -0800 Subject: drm/i915: fixup interlaced bits clearing in PIPECONF on PCH_SPLIT (v2) An identical patch has been merged for i9xx_crtc_mode_set: Commit 59df7b1771c150163e522f33c638096ab0efbf42 Author: Christian Schmidt Date: Mon Dec 19 20:03:33 2011 +0100 drm/intel: Fix initialization if startup happens in interlaced mode [v2] But that one neglected to fix up the ironlake+ path. This should fix the issue reported by Alfonso Fiore where booting with only a HDMI cable connected to his TV failed to display anything. The issue is that the bios set up things for 1080i and used the pannel fitter to scale up the lower progressive resolutions. We failed to clear the interlace bit in the PIPEACONF register, resulting in havoc. v2: Be more paranoid and just unconditionally clear the field before setting new values. Cc: Peter Ross Cc: Alfonso Fiore Signed-Off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f1744aa..00fbff5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5307,6 +5307,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, } } + pipeconf &= ~PIPECONF_INTERLACE_MASK; if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; /* the chip adds 2 halflines automatically */ @@ -5317,7 +5318,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, adjusted_mode->crtc_vsync_end -= 1; adjusted_mode->crtc_vsync_start -= 1; } else - pipeconf &= ~PIPECONF_INTERLACE_MASK; /* progressive */ + pipeconf |= PIPECONF_PROGRESSIVE; I915_WRITE(HTOTAL(pipe), (adjusted_mode->crtc_hdisplay - 1) | @@ -5902,6 +5903,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } } + pipeconf &= ~PIPECONF_INTERLACE_MASK; if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; /* the chip adds 2 halflines automatically */ @@ -5912,7 +5914,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, adjusted_mode->crtc_vsync_end -= 1; adjusted_mode->crtc_vsync_start -= 1; } else - pipeconf &= ~PIPECONF_INTERLACE_MASK; /* progressive */ + pipeconf |= PIPECONF_PROGRESSIVE; I915_WRITE(HTOTAL(pipe), (adjusted_mode->crtc_hdisplay - 1) | -- cgit v0.10.2 From b7cdd9e6323af368e26121c5b791eddc78e79fea Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 30 Jan 2012 14:12:59 -0800 Subject: staging: delete gma500 driver It's now "properly" merged into the DRM tree in the kernel, so delete the staging version of the driver as it is far obsolete and broken. Requested-by: Alan Cox Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 21e2f4b..b31194c 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -120,8 +120,6 @@ source "drivers/staging/cptm1217/Kconfig" source "drivers/staging/ste_rmi4/Kconfig" -source "drivers/staging/gma500/Kconfig" - source "drivers/staging/mei/Kconfig" source "drivers/staging/nvec/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 7c5808d..b5813c4 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_FT1000) += ft1000/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ -obj-$(CONFIG_DRM_PSB) += gma500/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ diff --git a/drivers/staging/gma500/Kconfig b/drivers/staging/gma500/Kconfig deleted file mode 100644 index c7a2b3b..0000000 --- a/drivers/staging/gma500/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -config DRM_PSB - tristate "Intel GMA5/600 KMS Framebuffer" - depends on DRM && PCI && X86 && BROKEN - select FB_CFB_COPYAREA - select FB_CFB_FILLRECT - select FB_CFB_IMAGEBLIT - select DRM_KMS_HELPER - select DRM_TTM - help - Say yes for an experimental 2D KMS framebuffer driver for the - Intel GMA500 ('Poulsbo') and other Intel IMG based graphics - devices. - -config DRM_PSB_MRST - bool "Intel GMA600 support (Experimental)" - depends on DRM_PSB - help - Say yes to include support for GMA600 (Intel Moorestown/Oaktrail) - platforms with LVDS ports. HDMI and MIPI are not currently - supported. - -config DRM_PSB_MFLD - bool "Intel Medfield support (Experimental)" - depends on DRM_PSB - help - Say yes to include support for Intel Medfield platforms with MIPI - interfaces. - -config DRM_PSB_CDV - bool "Intel Cedarview support (Experimental)" - depends on DRM_PSB - help - Say yes to include support for Intel Cedarview platforms diff --git a/drivers/staging/gma500/Makefile b/drivers/staging/gma500/Makefile deleted file mode 100644 index c729868..0000000 --- a/drivers/staging/gma500/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -# -# KMS driver for the GMA500 -# -ccflags-y += -Iinclude/drm - -psb_gfx-y += gem_glue.o \ - accel_2d.o \ - backlight.o \ - framebuffer.o \ - gem.o \ - gtt.o \ - intel_bios.o \ - intel_i2c.o \ - intel_opregion.o \ - mmu.o \ - power.o \ - psb_drv.o \ - psb_intel_display.o \ - psb_intel_lvds.o \ - psb_intel_modes.o \ - psb_intel_sdvo.o \ - psb_lid.o \ - psb_irq.o \ - psb_device.o \ - mid_bios.o - -psb_gfx-$(CONFIG_DRM_PSB_CDV) += cdv_device.o \ - cdv_intel_crt.o \ - cdv_intel_display.o \ - cdv_intel_hdmi.o \ - cdv_intel_lvds.o - -psb_gfx-$(CONFIG_DRM_PSB_MRST) += mrst_device.o \ - mrst_crtc.o \ - mrst_lvds.o \ - mrst_hdmi.o \ - mrst_hdmi_i2c.o - -psb_gfx-$(CONFIG_DRM_PSB_MFLD) += mdfld_device.o \ - mdfld_output.o \ - mdfld_pyr_cmd.o \ - mdfld_tmd_vid.o \ - mdfld_tpo_cmd.o \ - mdfld_tpo_vid.o \ - mdfld_dsi_pkg_sender.o \ - mdfld_dsi_dpi.o \ - mdfld_dsi_output.o \ - mdfld_dsi_dbi.o \ - mdfld_dsi_dbi_dpu.o \ - mdfld_intel_display.o - -obj-$(CONFIG_DRM_PSB) += psb_gfx.o diff --git a/drivers/staging/gma500/TODO b/drivers/staging/gma500/TODO deleted file mode 100644 index fc83615..0000000 --- a/drivers/staging/gma500/TODO +++ /dev/null @@ -1,15 +0,0 @@ -- Sort out the power management side. Not important for Poulsbo but - matters for Moorestown/Medfield -- Debug Oaktrail/Moorestown support (single pipe, no BIOS on mrst, - some other differences) -- Add 2D acceleration via console and DRM -- Add scrolling acceleration using the GTT to do remapping on the main - framebuffer. -- HDMI testing -- Oaktrail HDMI and other features -- Oaktrail MIPI -- Medfield needs a lot of further love - -As per kernel policy and the in the interest of the safety of various -kittens there is no support or plans to add hooks for the closed user space -stuff. diff --git a/drivers/staging/gma500/accel_2d.c b/drivers/staging/gma500/accel_2d.c deleted file mode 100644 index b8f78eb..0000000 --- a/drivers/staging/gma500/accel_2d.c +++ /dev/null @@ -1,414 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to - * develop this driver. - * - **************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "psb_drv.h" -#include "psb_reg.h" -#include "framebuffer.h" - -/** - * psb_spank - reset the 2D engine - * @dev_priv: our PSB DRM device - * - * Soft reset the graphics engine and then reload the necessary registers. - * We use this at initialisation time but it will become relevant for - * accelerated X later - */ -void psb_spank(struct drm_psb_private *dev_priv) -{ - PSB_WSGX32(_PSB_CS_RESET_BIF_RESET | _PSB_CS_RESET_DPM_RESET | - _PSB_CS_RESET_TA_RESET | _PSB_CS_RESET_USE_RESET | - _PSB_CS_RESET_ISP_RESET | _PSB_CS_RESET_TSP_RESET | - _PSB_CS_RESET_TWOD_RESET, PSB_CR_SOFT_RESET); - PSB_RSGX32(PSB_CR_SOFT_RESET); - - msleep(1); - - PSB_WSGX32(0, PSB_CR_SOFT_RESET); - wmb(); - PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_CB_CTRL_CLEAR_FAULT, - PSB_CR_BIF_CTRL); - wmb(); - (void) PSB_RSGX32(PSB_CR_BIF_CTRL); - - msleep(1); - PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_CB_CTRL_CLEAR_FAULT, - PSB_CR_BIF_CTRL); - (void) PSB_RSGX32(PSB_CR_BIF_CTRL); - PSB_WSGX32(dev_priv->gtt.gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); -} - -/** - * psb2_2d_wait_available - wait for FIFO room - * @dev_priv: our DRM device - * @size: size (in dwords) of the command we want to issue - * - * Wait until there is room to load the FIFO with our data. If the - * device is not responding then reset it - */ -static int psb_2d_wait_available(struct drm_psb_private *dev_priv, - unsigned size) -{ - uint32_t avail = PSB_RSGX32(PSB_CR_2D_SOCIF); - unsigned long t = jiffies + HZ; - - while (avail < size) { - avail = PSB_RSGX32(PSB_CR_2D_SOCIF); - if (time_after(jiffies, t)) { - psb_spank(dev_priv); - return -EIO; - } - } - return 0; -} - -/** - * psb_2d_submit - submit a 2D command - * @dev_priv: our DRM device - * @cmdbuf: command to issue - * @size: length (in dwords) - * - * Issue one or more 2D commands to the accelerator. This needs to be - * serialized later when we add the GEM interfaces for acceleration - */ -static int psbfb_2d_submit(struct drm_psb_private *dev_priv, uint32_t *cmdbuf, - unsigned size) -{ - int ret = 0; - int i; - unsigned submit_size; - unsigned long flags; - - spin_lock_irqsave(&dev_priv->lock_2d, flags); - while (size > 0) { - submit_size = (size < 0x60) ? size : 0x60; - size -= submit_size; - ret = psb_2d_wait_available(dev_priv, submit_size); - if (ret) - break; - - submit_size <<= 2; - - for (i = 0; i < submit_size; i += 4) - PSB_WSGX32(*cmdbuf++, PSB_SGX_2D_SLAVE_PORT + i); - - (void)PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT + i - 4); - } - spin_unlock_irqrestore(&dev_priv->lock_2d, flags); - return ret; -} - - -/** - * psb_accel_2d_copy_direction - compute blit order - * @xdir: X direction of move - * @ydir: Y direction of move - * - * Compute the correct order setings to ensure that an overlapping blit - * correctly copies all the pixels. - */ -static u32 psb_accel_2d_copy_direction(int xdir, int ydir) -{ - if (xdir < 0) - return (ydir < 0) ? PSB_2D_COPYORDER_BR2TL : - PSB_2D_COPYORDER_TR2BL; - else - return (ydir < 0) ? PSB_2D_COPYORDER_BL2TR : - PSB_2D_COPYORDER_TL2BR; -} - -/** - * psb_accel_2d_copy - accelerated 2D copy - * @dev_priv: our DRM device - * @src_offset in bytes - * @src_stride in bytes - * @src_format psb 2D format defines - * @dst_offset in bytes - * @dst_stride in bytes - * @dst_format psb 2D format defines - * @src_x offset in pixels - * @src_y offset in pixels - * @dst_x offset in pixels - * @dst_y offset in pixels - * @size_x of the copied area - * @size_y of the copied area - * - * Format and issue a 2D accelerated copy command. - */ -static int psb_accel_2d_copy(struct drm_psb_private *dev_priv, - uint32_t src_offset, uint32_t src_stride, - uint32_t src_format, uint32_t dst_offset, - uint32_t dst_stride, uint32_t dst_format, - uint16_t src_x, uint16_t src_y, - uint16_t dst_x, uint16_t dst_y, - uint16_t size_x, uint16_t size_y) -{ - uint32_t blit_cmd; - uint32_t buffer[10]; - uint32_t *buf; - uint32_t direction; - - buf = buffer; - - direction = - psb_accel_2d_copy_direction(src_x - dst_x, src_y - dst_y); - - if (direction == PSB_2D_COPYORDER_BR2TL || - direction == PSB_2D_COPYORDER_TR2BL) { - src_x += size_x - 1; - dst_x += size_x - 1; - } - if (direction == PSB_2D_COPYORDER_BR2TL || - direction == PSB_2D_COPYORDER_BL2TR) { - src_y += size_y - 1; - dst_y += size_y - 1; - } - - blit_cmd = - PSB_2D_BLIT_BH | - PSB_2D_ROT_NONE | - PSB_2D_DSTCK_DISABLE | - PSB_2D_SRCCK_DISABLE | - PSB_2D_USE_PAT | PSB_2D_ROP3_SRCCOPY | direction; - - *buf++ = PSB_2D_FENCE_BH; - *buf++ = - PSB_2D_DST_SURF_BH | dst_format | (dst_stride << - PSB_2D_DST_STRIDE_SHIFT); - *buf++ = dst_offset; - *buf++ = - PSB_2D_SRC_SURF_BH | src_format | (src_stride << - PSB_2D_SRC_STRIDE_SHIFT); - *buf++ = src_offset; - *buf++ = - PSB_2D_SRC_OFF_BH | (src_x << PSB_2D_SRCOFF_XSTART_SHIFT) | - (src_y << PSB_2D_SRCOFF_YSTART_SHIFT); - *buf++ = blit_cmd; - *buf++ = - (dst_x << PSB_2D_DST_XSTART_SHIFT) | (dst_y << - PSB_2D_DST_YSTART_SHIFT); - *buf++ = - (size_x << PSB_2D_DST_XSIZE_SHIFT) | (size_y << - PSB_2D_DST_YSIZE_SHIFT); - *buf++ = PSB_2D_FLUSH_BH; - - return psbfb_2d_submit(dev_priv, buffer, buf - buffer); -} - -/** - * psbfb_copyarea_accel - copyarea acceleration for /dev/fb - * @info: our framebuffer - * @a: copyarea parameters from the framebuffer core - * - * Perform a 2D copy via the accelerator - */ -static void psbfb_copyarea_accel(struct fb_info *info, - const struct fb_copyarea *a) -{ - struct psb_fbdev *fbdev = info->par; - struct psb_framebuffer *psbfb = &fbdev->pfb; - struct drm_device *dev = psbfb->base.dev; - struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb; - struct drm_psb_private *dev_priv = dev->dev_private; - uint32_t offset; - uint32_t stride; - uint32_t src_format; - uint32_t dst_format; - - if (!fb) - return; - - offset = psbfb->gtt->offset; - stride = fb->pitches[0]; - - switch (fb->depth) { - case 8: - src_format = PSB_2D_SRC_332RGB; - dst_format = PSB_2D_DST_332RGB; - break; - case 15: - src_format = PSB_2D_SRC_555RGB; - dst_format = PSB_2D_DST_555RGB; - break; - case 16: - src_format = PSB_2D_SRC_565RGB; - dst_format = PSB_2D_DST_565RGB; - break; - case 24: - case 32: - /* this is wrong but since we don't do blending its okay */ - src_format = PSB_2D_SRC_8888ARGB; - dst_format = PSB_2D_DST_8888ARGB; - break; - default: - /* software fallback */ - cfb_copyarea(info, a); - return; - } - - if (!gma_power_begin(dev, false)) { - cfb_copyarea(info, a); - return; - } - psb_accel_2d_copy(dev_priv, - offset, stride, src_format, - offset, stride, dst_format, - a->sx, a->sy, a->dx, a->dy, a->width, a->height); - gma_power_end(dev); -} - -/** - * psbfb_copyarea - 2D copy interface - * @info: our framebuffer - * @region: region to copy - * - * Copy an area of the framebuffer console either by the accelerator - * or directly using the cfb helpers according to the request - */ -void psbfb_copyarea(struct fb_info *info, - const struct fb_copyarea *region) -{ - if (unlikely(info->state != FBINFO_STATE_RUNNING)) - return; - - /* Avoid the 8 pixel erratum */ - if (region->width == 8 || region->height == 8 || - (info->flags & FBINFO_HWACCEL_DISABLED)) - return cfb_copyarea(info, region); - - psbfb_copyarea_accel(info, region); -} - -/** - * psbfb_sync - synchronize 2D - * @info: our framebuffer - * - * Wait for the 2D engine to quiesce so that we can do CPU - * access to the framebuffer again - */ -int psbfb_sync(struct fb_info *info) -{ - struct psb_fbdev *fbdev = info->par; - struct psb_framebuffer *psbfb = &fbdev->pfb; - struct drm_device *dev = psbfb->base.dev; - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long _end = jiffies + DRM_HZ; - int busy = 0; - unsigned long flags; - - spin_lock_irqsave(&dev_priv->lock_2d, flags); - /* - * First idle the 2D engine. - */ - - if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) && - ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0)) - goto out; - - do { - busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); - cpu_relax(); - } while (busy && !time_after_eq(jiffies, _end)); - - if (busy) - busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); - if (busy) - goto out; - - do { - busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & - _PSB_C2B_STATUS_BUSY) != 0); - cpu_relax(); - } while (busy && !time_after_eq(jiffies, _end)); - if (busy) - busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & - _PSB_C2B_STATUS_BUSY) != 0); - -out: - spin_unlock_irqrestore(&dev_priv->lock_2d, flags); - return (busy) ? -EBUSY : 0; -} - -int psb_accel_ioctl(struct drm_device *dev, void *data, struct drm_file *file) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_psb_2d_op *op = data; - u32 *op_ptr = &op->cmd[0]; - int i; - struct drm_gem_object *obj; - struct gtt_range *gtt; - int err = -EINVAL; - - if (!dev_priv->ops->accel_2d) - return -EOPNOTSUPP; - if (op->size > PSB_2D_OP_BUFLEN) - return -EINVAL; - - /* The GEM object being used. We need to support separate src/dst/etc - in the end but for now keep them all the same */ - obj = drm_gem_object_lookup(dev, file, op->src); - if (obj == NULL) - return -ENOENT; - gtt = container_of(obj, struct gtt_range, gem); - - if (psb_gtt_pin(gtt) < 0) - goto bad_2; - for (i = 0; i < op->size; i++, op_ptr++) { - u32 r = *op_ptr & 0xF0000000; - /* Fill in the GTT offsets for the command buffer */ - if (r == PSB_2D_SRC_SURF_BH || - r == PSB_2D_DST_SURF_BH || - r == PSB_2D_MASK_SURF_BH || - r == PSB_2D_PAT_SURF_BH) { - i++; - op_ptr++; - if (i == op->size) - goto bad; - if (*op_ptr) - goto bad; - *op_ptr = gtt->offset; - continue; - } - } - psbfb_2d_submit(dev_priv, op->cmd, op->size); - err = 0; -bad: - psb_gtt_unpin(gtt); -bad_2: - drm_gem_object_unreference(obj); - return err; -} diff --git a/drivers/staging/gma500/backlight.c b/drivers/staging/gma500/backlight.c deleted file mode 100644 index 2079395..0000000 --- a/drivers/staging/gma500/backlight.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * GMA500 Backlight Interface - * - * Copyright (c) 2009-2011, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: Eric Knopp - * - */ - -#include "psb_drv.h" -#include "psb_intel_reg.h" -#include "psb_intel_drv.h" -#include "intel_bios.h" -#include "power.h" - -int gma_backlight_init(struct drm_device *dev) -{ -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *dev_priv = dev->dev_private; - return dev_priv->ops->backlight_init(dev); -#else - return 0; -#endif -} - -void gma_backlight_exit(struct drm_device *dev) -{ -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *dev_priv = dev->dev_private; - if (dev_priv->backlight_device) { - dev_priv->backlight_device->props.brightness = 0; - backlight_update_status(dev_priv->backlight_device); - backlight_device_unregister(dev_priv->backlight_device); - } -#endif -} diff --git a/drivers/staging/gma500/cdv_device.c b/drivers/staging/gma500/cdv_device.c deleted file mode 100644 index 8ec10ca..0000000 --- a/drivers/staging/gma500/cdv_device.c +++ /dev/null @@ -1,350 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include "intel_bios.h" -#include "cdv_device.h" - -#define VGA_SR_INDEX 0x3c4 -#define VGA_SR_DATA 0x3c5 - -static void cdv_disable_vga(struct drm_device *dev) -{ - u8 sr1; - u32 vga_reg; - - vga_reg = VGACNTRL; - - outb(1, VGA_SR_INDEX); - sr1 = inb(VGA_SR_DATA); - outb(sr1 | 1<<5, VGA_SR_DATA); - udelay(300); - - REG_WRITE(vga_reg, VGA_DISP_DISABLE); - REG_READ(vga_reg); -} - -static int cdv_output_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - cdv_disable_vga(dev); - - cdv_intel_crt_init(dev, &dev_priv->mode_dev); - cdv_intel_lvds_init(dev, &dev_priv->mode_dev); - - /* These bits indicate HDMI not SDVO on CDV, but we don't yet support - the HDMI interface */ - if (REG_READ(SDVOB) & SDVO_DETECTED) - cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB); - if (REG_READ(SDVOC) & SDVO_DETECTED) - cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC); - return 0; -} - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - -/* - * Poulsbo Backlight Interfaces - */ - -#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ -#define BLC_PWM_FREQ_CALC_CONSTANT 32 -#define MHz 1000000 - -#define PSB_BLC_PWM_PRECISION_FACTOR 10 -#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE -#define PSB_BLC_MIN_PWM_REG_FREQ 0x2 - -#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) -#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) - -static int cdv_brightness; -static struct backlight_device *cdv_backlight_device; - -static int cdv_get_brightness(struct backlight_device *bd) -{ - /* return locally cached var instead of HW read (due to DPST etc.) */ - /* FIXME: ideally return actual value in case firmware fiddled with - it */ - return cdv_brightness; -} - - -static int cdv_backlight_setup(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long core_clock; - /* u32 bl_max_freq; */ - /* unsigned long value; */ - u16 bl_max_freq; - uint32_t value; - uint32_t blc_pwm_precision_factor; - - /* get bl_max_freq and pol from dev_priv*/ - if (!dev_priv->lvds_bl) { - dev_err(dev->dev, "Has no valid LVDS backlight info\n"); - return -ENOENT; - } - bl_max_freq = dev_priv->lvds_bl->freq; - blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; - - core_clock = dev_priv->core_freq; - - value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; - value *= blc_pwm_precision_factor; - value /= bl_max_freq; - value /= blc_pwm_precision_factor; - - if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || - value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) - return -ERANGE; - else { - /* FIXME */ - } - return 0; -} - -static int cdv_set_brightness(struct backlight_device *bd) -{ - int level = bd->props.brightness; - - /* Percentage 1-100% being valid */ - if (level < 1) - level = 1; - - /*cdv_intel_lvds_set_brightness(dev, level); FIXME */ - cdv_brightness = level; - return 0; -} - -static const struct backlight_ops cdv_ops = { - .get_brightness = cdv_get_brightness, - .update_status = cdv_set_brightness, -}; - -static int cdv_backlight_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - int ret; - struct backlight_properties props; - - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 100; - props.type = BACKLIGHT_PLATFORM; - - cdv_backlight_device = backlight_device_register("psb-bl", - NULL, (void *)dev, &cdv_ops, &props); - if (IS_ERR(cdv_backlight_device)) - return PTR_ERR(cdv_backlight_device); - - ret = cdv_backlight_setup(dev); - if (ret < 0) { - backlight_device_unregister(cdv_backlight_device); - cdv_backlight_device = NULL; - return ret; - } - cdv_backlight_device->props.brightness = 100; - cdv_backlight_device->props.max_brightness = 100; - backlight_update_status(cdv_backlight_device); - dev_priv->backlight_device = cdv_backlight_device; - return 0; -} - -#endif - -/* - * Provide the Cedarview specific chip logic and low level methods - * for power management - * - * FIXME: we need to implement the apm/ospm base management bits - * for this and the MID devices. - */ - -static inline u32 CDV_MSG_READ32(uint port, uint offset) -{ - int mcr = (0x10<<24) | (port << 16) | (offset << 8); - uint32_t ret_val = 0; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_read_config_dword(pci_root, 0xD4, &ret_val); - pci_dev_put(pci_root); - return ret_val; -} - -static inline void CDV_MSG_WRITE32(uint port, uint offset, u32 value) -{ - int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - pci_write_config_dword(pci_root, 0xD4, value); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_dev_put(pci_root); -} - -#define PSB_APM_CMD 0x0 -#define PSB_APM_STS 0x04 -#define PSB_PM_SSC 0x20 -#define PSB_PM_SSS 0x30 -#define PSB_PWRGT_GFX_MASK 0x3 -#define CDV_PWRGT_DISPLAY_CNTR 0x000fc00c -#define CDV_PWRGT_DISPLAY_STS 0x000fc00c - -static void cdv_init_pm(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 pwr_cnt; - int i; - - dev_priv->apm_base = CDV_MSG_READ32(PSB_PUNIT_PORT, - PSB_APMBA) & 0xFFFF; - dev_priv->ospm_base = CDV_MSG_READ32(PSB_PUNIT_PORT, - PSB_OSPMBA) & 0xFFFF; - - /* Force power on for now */ - pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD); - pwr_cnt &= ~PSB_PWRGT_GFX_MASK; - - outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD); - for (i = 0; i < 5; i++) { - u32 pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS); - if ((pwr_sts & PSB_PWRGT_GFX_MASK) == 0) - break; - udelay(10); - } - pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); - pwr_cnt &= ~CDV_PWRGT_DISPLAY_CNTR; - outl(pwr_cnt, dev_priv->ospm_base + PSB_PM_SSC); - for (i = 0; i < 5; i++) { - u32 pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); - if ((pwr_sts & CDV_PWRGT_DISPLAY_STS) == 0) - break; - udelay(10); - } -} - -/** - * cdv_save_display_registers - save registers lost on suspend - * @dev: our DRM device - * - * Save the state we need in order to be able to restore the interface - * upon resume from suspend - * - * FIXME: review - */ -static int cdv_save_display_registers(struct drm_device *dev) -{ - return 0; -} - -/** - * cdv_restore_display_registers - restore lost register state - * @dev: our DRM device - * - * Restore register state that was lost during suspend and resume. - * - * FIXME: review - */ -static int cdv_restore_display_registers(struct drm_device *dev) -{ - return 0; -} - -static int cdv_power_down(struct drm_device *dev) -{ - return 0; -} - -static int cdv_power_up(struct drm_device *dev) -{ - return 0; -} - -/* FIXME ? - shared with Poulsbo */ -static void cdv_get_core_freq(struct drm_device *dev) -{ - uint32_t clock; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - struct drm_psb_private *dev_priv = dev->dev_private; - - pci_write_config_dword(pci_root, 0xD0, 0xD0050300); - pci_read_config_dword(pci_root, 0xD4, &clock); - pci_dev_put(pci_root); - - switch (clock & 0x07) { - case 0: - dev_priv->core_freq = 100; - break; - case 1: - dev_priv->core_freq = 133; - break; - case 2: - dev_priv->core_freq = 150; - break; - case 3: - dev_priv->core_freq = 178; - break; - case 4: - dev_priv->core_freq = 200; - break; - case 5: - case 6: - case 7: - dev_priv->core_freq = 266; - default: - dev_priv->core_freq = 0; - } -} - -static int cdv_chip_setup(struct drm_device *dev) -{ - cdv_get_core_freq(dev); - gma_intel_opregion_init(dev); - psb_intel_init_bios(dev); - return 0; -} - -/* CDV is much like Poulsbo but has MID like SGX offsets and PM */ - -const struct psb_ops cdv_chip_ops = { - .name = "Cedartrail", - .accel_2d = 0, - .pipes = 2, - .sgx_offset = MRST_SGX_OFFSET, - .chip_setup = cdv_chip_setup, - - .crtc_helper = &cdv_intel_helper_funcs, - .crtc_funcs = &cdv_intel_crtc_funcs, - - .output_init = cdv_output_init, - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - .backlight_init = cdv_backlight_init, -#endif - - .init_pm = cdv_init_pm, - .save_regs = cdv_save_display_registers, - .restore_regs = cdv_restore_display_registers, - .power_down = cdv_power_down, - .power_up = cdv_power_up, -}; diff --git a/drivers/staging/gma500/cdv_device.h b/drivers/staging/gma500/cdv_device.h deleted file mode 100644 index 2a88b7b..0000000 --- a/drivers/staging/gma500/cdv_device.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -extern const struct drm_crtc_helper_funcs cdv_intel_helper_funcs; -extern const struct drm_crtc_funcs cdv_intel_crtc_funcs; -extern void cdv_intel_crt_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev); -extern void cdv_intel_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev); -extern void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, - int reg); -extern struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, - struct drm_crtc *crtc); - -extern inline void cdv_intel_wait_for_vblank(struct drm_device *dev) -{ - /* Wait for 20ms, i.e. one cycle at 50hz. */ - /* FIXME: msleep ?? */ - mdelay(20); -} - - diff --git a/drivers/staging/gma500/cdv_intel_crt.c b/drivers/staging/gma500/cdv_intel_crt.c deleted file mode 100644 index efda63b..0000000 --- a/drivers/staging/gma500/cdv_intel_crt.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright © 2006-2007 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Eric Anholt - */ - -#include -#include - -#include "intel_bios.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "power.h" -#include - - -static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - u32 temp, reg; - reg = ADPA; - - temp = REG_READ(reg); - temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); - temp &= ~ADPA_DAC_ENABLE; - - switch (mode) { - case DRM_MODE_DPMS_ON: - temp |= ADPA_DAC_ENABLE; - break; - case DRM_MODE_DPMS_STANDBY: - temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; - break; - case DRM_MODE_DPMS_SUSPEND: - temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; - break; - case DRM_MODE_DPMS_OFF: - temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; - break; - } - - REG_WRITE(reg, temp); -} - -static int cdv_intel_crt_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - int max_clock = 0; - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - /* The lowest clock for CDV is 20000KHz */ - if (mode->clock < 20000) - return MODE_CLOCK_LOW; - - /* The max clock for CDV is 355 instead of 400 */ - max_clock = 355000; - if (mode->clock > max_clock) - return MODE_CLOCK_HIGH; - - if (mode->hdisplay > 1680 || mode->vdisplay > 1050) - return MODE_PANEL; - - return MODE_OK; -} - -static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - - struct drm_device *dev = encoder->dev; - struct drm_crtc *crtc = encoder->crtc; - struct psb_intel_crtc *psb_intel_crtc = - to_psb_intel_crtc(crtc); - int dpll_md_reg; - u32 adpa, dpll_md; - u32 adpa_reg; - - if (psb_intel_crtc->pipe == 0) - dpll_md_reg = DPLL_A_MD; - else - dpll_md_reg = DPLL_B_MD; - - adpa_reg = ADPA; - - /* - * Disable separate mode multiplier used when cloning SDVO to CRT - * XXX this needs to be adjusted when we really are cloning - */ - { - dpll_md = REG_READ(dpll_md_reg); - REG_WRITE(dpll_md_reg, - dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); - } - - adpa = 0; - if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) - adpa |= ADPA_HSYNC_ACTIVE_HIGH; - if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) - adpa |= ADPA_VSYNC_ACTIVE_HIGH; - - if (psb_intel_crtc->pipe == 0) - adpa |= ADPA_PIPE_A_SELECT; - else - adpa |= ADPA_PIPE_B_SELECT; - - REG_WRITE(adpa_reg, adpa); -} - - -/** - * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. - * - * \return true if CRT is connected. - * \return false if CRT is disconnected. - */ -static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, - bool force) -{ - struct drm_device *dev = connector->dev; - u32 hotplug_en; - int i, tries = 0, ret = false; - u32 adpa_orig; - - /* disable the DAC when doing the hotplug detection */ - - adpa_orig = REG_READ(ADPA); - - REG_WRITE(ADPA, adpa_orig & ~(ADPA_DAC_ENABLE)); - - /* - * On a CDV thep, CRT detect sequence need to be done twice - * to get a reliable result. - */ - tries = 2; - - hotplug_en = REG_READ(PORT_HOTPLUG_EN); - hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); - hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; - - hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; - hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; - - for (i = 0; i < tries ; i++) { - unsigned long timeout; - /* turn on the FORCE_DETECT */ - REG_WRITE(PORT_HOTPLUG_EN, hotplug_en); - timeout = jiffies + msecs_to_jiffies(1000); - /* wait for FORCE_DETECT to go off */ - do { - if (!(REG_READ(PORT_HOTPLUG_EN) & - CRT_HOTPLUG_FORCE_DETECT)) - break; - msleep(1); - } while (time_after(timeout, jiffies)); - } - - if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != - CRT_HOTPLUG_MONITOR_NONE) - ret = true; - - /* Restore the saved ADPA */ - REG_WRITE(ADPA, adpa_orig); - return ret; -} - -static enum drm_connector_status cdv_intel_crt_detect( - struct drm_connector *connector, bool force) -{ - if (cdv_intel_crt_detect_hotplug(connector, force)) - return connector_status_connected; - else - return connector_status_disconnected; -} - -static void cdv_intel_crt_destroy(struct drm_connector *connector) -{ - struct psb_intel_output *intel_output = to_psb_intel_output(connector); - - psb_intel_i2c_destroy(intel_output->ddc_bus); - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(connector); -} - -static int cdv_intel_crt_get_modes(struct drm_connector *connector) -{ - struct psb_intel_output *intel_output = - to_psb_intel_output(connector); - return psb_intel_ddc_get_modes(intel_output); -} - -static int cdv_intel_crt_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t value) -{ - return 0; -} - -/* - * Routines for controlling stuff on the analog port - */ - -static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { - .dpms = cdv_intel_crt_dpms, - .mode_fixup = cdv_intel_crt_mode_fixup, - .prepare = psb_intel_encoder_prepare, - .commit = psb_intel_encoder_commit, - .mode_set = cdv_intel_crt_mode_set, -}; - -static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = cdv_intel_crt_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = cdv_intel_crt_destroy, - .set_property = cdv_intel_crt_set_property, -}; - -static const struct drm_connector_helper_funcs - cdv_intel_crt_connector_helper_funcs = { - .mode_valid = cdv_intel_crt_mode_valid, - .get_modes = cdv_intel_crt_get_modes, - .best_encoder = psb_intel_best_encoder, -}; - -static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - -static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = { - .destroy = cdv_intel_crt_enc_destroy, -}; - -void cdv_intel_crt_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev) -{ - - struct psb_intel_output *psb_intel_output; - struct drm_connector *connector; - struct drm_encoder *encoder; - - u32 i2c_reg; - - psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); - if (!psb_intel_output) - return; - - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - drm_connector_init(dev, connector, - &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); - - encoder = &psb_intel_output->enc; - drm_encoder_init(dev, encoder, - &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - - /* Set up the DDC bus. */ - i2c_reg = GPIOA; - /* Remove the following code for CDV */ - /* - if (dev_priv->crt_ddc_bus != 0) - i2c_reg = dev_priv->crt_ddc_bus; - }*/ - psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, - i2c_reg, "CRTDDC_A"); - if (!psb_intel_output->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " - "failed.\n"); - goto failed_ddc; - } - - psb_intel_output->type = INTEL_OUTPUT_ANALOG; - /* - psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); - psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); - */ - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - - drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs); - drm_connector_helper_add(connector, - &cdv_intel_crt_connector_helper_funcs); - - drm_sysfs_connector_add(connector); - - return; -failed_ddc: - drm_encoder_cleanup(&psb_intel_output->enc); - drm_connector_cleanup(&psb_intel_output->base); - kfree(psb_intel_output); - return; -} diff --git a/drivers/staging/gma500/cdv_intel_display.c b/drivers/staging/gma500/cdv_intel_display.c deleted file mode 100644 index c63a327..0000000 --- a/drivers/staging/gma500/cdv_intel_display.c +++ /dev/null @@ -1,1508 +0,0 @@ -/* - * Copyright © 2006-2011 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - */ - -#include -#include - -#include -#include "framebuffer.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "psb_intel_display.h" -#include "power.h" -#include "cdv_device.h" - - -struct cdv_intel_range_t { - int min, max; -}; - -struct cdv_intel_p2_t { - int dot_limit; - int p2_slow, p2_fast; -}; - -struct cdv_intel_clock_t { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -}; - -#define INTEL_P2_NUM 2 - -struct cdv_intel_limit_t { - struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1; - struct cdv_intel_p2_t p2; -}; - -#define CDV_LIMIT_SINGLE_LVDS_96 0 -#define CDV_LIMIT_SINGLE_LVDS_100 1 -#define CDV_LIMIT_DAC_HDMI_27 2 -#define CDV_LIMIT_DAC_HDMI_96 3 - -static const struct cdv_intel_limit_t cdv_intel_limits[] = { - { /* CDV_SIGNLE_LVDS_96MHz */ - .dot = {.min = 20000, .max = 115500}, - .vco = {.min = 1800000, .max = 3600000}, - .n = {.min = 2, .max = 6}, - .m = {.min = 60, .max = 160}, - .m1 = {.min = 0, .max = 0}, - .m2 = {.min = 58, .max = 158}, - .p = {.min = 28, .max = 140}, - .p1 = {.min = 2, .max = 10}, - .p2 = {.dot_limit = 200000, - .p2_slow = 14, .p2_fast = 14}, - }, - { /* CDV_SINGLE_LVDS_100MHz */ - .dot = {.min = 20000, .max = 115500}, - .vco = {.min = 1800000, .max = 3600000}, - .n = {.min = 2, .max = 6}, - .m = {.min = 60, .max = 160}, - .m1 = {.min = 0, .max = 0}, - .m2 = {.min = 58, .max = 158}, - .p = {.min = 28, .max = 140}, - .p1 = {.min = 2, .max = 10}, - /* The single-channel range is 25-112Mhz, and dual-channel - * is 80-224Mhz. Prefer single channel as much as possible. - */ - .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, - }, - { /* CDV_DAC_HDMI_27MHz */ - .dot = {.min = 20000, .max = 400000}, - .vco = {.min = 1809000, .max = 3564000}, - .n = {.min = 1, .max = 1}, - .m = {.min = 67, .max = 132}, - .m1 = {.min = 0, .max = 0}, - .m2 = {.min = 65, .max = 130}, - .p = {.min = 5, .max = 90}, - .p1 = {.min = 1, .max = 9}, - .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, - }, - { /* CDV_DAC_HDMI_96MHz */ - .dot = {.min = 20000, .max = 400000}, - .vco = {.min = 1800000, .max = 3600000}, - .n = {.min = 2, .max = 6}, - .m = {.min = 60, .max = 160}, - .m1 = {.min = 0, .max = 0}, - .m2 = {.min = 58, .max = 158}, - .p = {.min = 5, .max = 100}, - .p1 = {.min = 1, .max = 10}, - .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, - }, -}; - -#define _wait_for(COND, MS, W) ({ \ - unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ - int ret__ = 0; \ - while (!(COND)) { \ - if (time_after(jiffies, timeout__)) { \ - ret__ = -ETIMEDOUT; \ - break; \ - } \ - if (W && !in_dbg_master()) \ - msleep(W); \ - } \ - ret__; \ -}) - -#define wait_for(COND, MS) _wait_for(COND, MS, 1) - - -static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) -{ - int ret; - - ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); - if (ret) { - DRM_ERROR("timeout waiting for SB to idle before read\n"); - return ret; - } - - REG_WRITE(SB_ADDR, reg); - REG_WRITE(SB_PCKT, - SET_FIELD(SB_OPCODE_READ, SB_OPCODE) | - SET_FIELD(SB_DEST_DPLL, SB_DEST) | - SET_FIELD(0xf, SB_BYTE_ENABLE)); - - ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); - if (ret) { - DRM_ERROR("timeout waiting for SB to idle after read\n"); - return ret; - } - - *val = REG_READ(SB_DATA); - - return 0; -} - -static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) -{ - int ret; - static bool dpio_debug = true; - u32 temp; - - if (dpio_debug) { - if (cdv_sb_read(dev, reg, &temp) == 0) - DRM_DEBUG_KMS("0x%08x: 0x%08x (before)\n", reg, temp); - DRM_DEBUG_KMS("0x%08x: 0x%08x\n", reg, val); - } - - ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); - if (ret) { - DRM_ERROR("timeout waiting for SB to idle before write\n"); - return ret; - } - - REG_WRITE(SB_ADDR, reg); - REG_WRITE(SB_DATA, val); - REG_WRITE(SB_PCKT, - SET_FIELD(SB_OPCODE_WRITE, SB_OPCODE) | - SET_FIELD(SB_DEST_DPLL, SB_DEST) | - SET_FIELD(0xf, SB_BYTE_ENABLE)); - - ret = wait_for((REG_READ(SB_PCKT) & SB_BUSY) == 0, 1000); - if (ret) { - DRM_ERROR("timeout waiting for SB to idle after write\n"); - return ret; - } - - if (dpio_debug) { - if (cdv_sb_read(dev, reg, &temp) == 0) - DRM_DEBUG_KMS("0x%08x: 0x%08x (after)\n", reg, temp); - } - - return 0; -} - -/* Reset the DPIO configuration register. The BIOS does this at every - * mode set. - */ -static void cdv_sb_reset(struct drm_device *dev) -{ - - REG_WRITE(DPIO_CFG, 0); - REG_READ(DPIO_CFG); - REG_WRITE(DPIO_CFG, DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N); -} - -/* Unlike most Intel display engines, on Cedarview the DPLL registers - * are behind this sideband bus. They must be programmed while the - * DPLL reference clock is on in the DPLL control register, but before - * the DPLL is enabled in the DPLL control register. - */ -static int -cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, - struct cdv_intel_clock_t *clock) -{ - struct psb_intel_crtc *psb_crtc = - to_psb_intel_crtc(crtc); - int pipe = psb_crtc->pipe; - u32 m, n_vco, p; - int ret = 0; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - u32 ref_value; - - cdv_sb_reset(dev); - - if ((REG_READ(dpll_reg) & DPLL_SYNCLOCK_ENABLE) == 0) { - DRM_ERROR("Attempting to set DPLL with refclk disabled\n"); - return -EBUSY; - } - - /* Follow the BIOS and write the REF/SFR Register. Hardcoded value */ - ref_value = 0x68A701; - - cdv_sb_write(dev, SB_REF_SFR(pipe), ref_value); - - /* We don't know what the other fields of these regs are, so - * leave them in place. - */ - ret = cdv_sb_read(dev, SB_M(pipe), &m); - if (ret) - return ret; - m &= ~SB_M_DIVIDER_MASK; - m |= ((clock->m2) << SB_M_DIVIDER_SHIFT); - ret = cdv_sb_write(dev, SB_M(pipe), m); - if (ret) - return ret; - - ret = cdv_sb_read(dev, SB_N_VCO(pipe), &n_vco); - if (ret) - return ret; - - /* Follow the BIOS to program the N_DIVIDER REG */ - n_vco &= 0xFFFF; - n_vco |= 0x107; - n_vco &= ~(SB_N_VCO_SEL_MASK | - SB_N_DIVIDER_MASK | - SB_N_CB_TUNE_MASK); - - n_vco |= ((clock->n) << SB_N_DIVIDER_SHIFT); - - if (clock->vco < 2250000) { - n_vco |= (2 << SB_N_CB_TUNE_SHIFT); - n_vco |= (0 << SB_N_VCO_SEL_SHIFT); - } else if (clock->vco < 2750000) { - n_vco |= (1 << SB_N_CB_TUNE_SHIFT); - n_vco |= (1 << SB_N_VCO_SEL_SHIFT); - } else if (clock->vco < 3300000) { - n_vco |= (0 << SB_N_CB_TUNE_SHIFT); - n_vco |= (2 << SB_N_VCO_SEL_SHIFT); - } else { - n_vco |= (0 << SB_N_CB_TUNE_SHIFT); - n_vco |= (3 << SB_N_VCO_SEL_SHIFT); - } - - ret = cdv_sb_write(dev, SB_N_VCO(pipe), n_vco); - if (ret) - return ret; - - ret = cdv_sb_read(dev, SB_P(pipe), &p); - if (ret) - return ret; - p &= ~(SB_P2_DIVIDER_MASK | SB_P1_DIVIDER_MASK); - p |= SET_FIELD(clock->p1, SB_P1_DIVIDER); - switch (clock->p2) { - case 5: - p |= SET_FIELD(SB_P2_5, SB_P2_DIVIDER); - break; - case 10: - p |= SET_FIELD(SB_P2_10, SB_P2_DIVIDER); - break; - case 14: - p |= SET_FIELD(SB_P2_14, SB_P2_DIVIDER); - break; - case 7: - p |= SET_FIELD(SB_P2_7, SB_P2_DIVIDER); - break; - default: - DRM_ERROR("Bad P2 clock: %d\n", clock->p2); - return -EINVAL; - } - ret = cdv_sb_write(dev, SB_P(pipe), p); - if (ret) - return ret; - - /* always Program the Lane Register for the Pipe A*/ - if (pipe == 0) { - /* Program the Lane0/1 for HDMI B */ - u32 lane_reg, lane_value; - - lane_reg = PSB_LANE0; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE1; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - - /* Program the Lane2/3 for HDMI C */ - lane_reg = PSB_LANE2; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE3; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - } - - return 0; -} - -/* - * Returns whether any output on the specified pipe is of the specified type - */ -bool cdv_intel_pipe_has_type(struct drm_crtc *crtc, int type) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_connector *l_entry; - - list_for_each_entry(l_entry, &mode_config->connector_list, head) { - if (l_entry->encoder && l_entry->encoder->crtc == crtc) { - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(l_entry); - if (psb_intel_output->type == type) - return true; - } - } - return false; -} - -static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, - int refclk) -{ - const struct cdv_intel_limit_t *limit; - if (cdv_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - /* - * Now only single-channel LVDS is supported on CDV. If it is - * incorrect, please add the dual-channel LVDS. - */ - if (refclk == 96000) - limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; - else - limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; - } else { - if (refclk == 27000) - limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27]; - else - limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_96]; - } - return limit; -} - -/* m1 is reserved as 0 in CDV, n is a ring counter */ -static void cdv_intel_clock(struct drm_device *dev, - int refclk, struct cdv_intel_clock_t *clock) -{ - clock->m = clock->m2 + 2; - clock->p = clock->p1 * clock->p2; - clock->vco = (refclk * clock->m) / clock->n; - clock->dot = clock->vco / clock->p; -} - - -#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } -static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc, - const struct cdv_intel_limit_t *limit, - struct cdv_intel_clock_t *clock) -{ - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - INTELPllInvalid("p1 out of range\n"); - if (clock->p < limit->p.min || limit->p.max < clock->p) - INTELPllInvalid("p out of range\n"); - /* unnecessary to check the range of m(m1/M2)/n again */ - if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) - INTELPllInvalid("vco out of range\n"); - /* XXX: We may need to be checking "Dot clock" - * depending on the multiplier, connector, etc., - * rather than just a single range. - */ - if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) - INTELPllInvalid("dot out of range\n"); - - return true; -} - -static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target, - int refclk, - struct cdv_intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - struct cdv_intel_clock_t clock; - const struct cdv_intel_limit_t *limit = cdv_intel_limit(crtc, refclk); - int err = target; - - - if (cdv_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { - /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. - */ - if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) - clock.p2 = limit->p2.p2_fast; - else - clock.p2 = limit->p2.p2_slow; - } else { - if (target < limit->p2.dot_limit) - clock.p2 = limit->p2.p2_slow; - else - clock.p2 = limit->p2.p2_fast; - } - - memset(best_clock, 0, sizeof(*best_clock)); - clock.m1 = 0; - /* m1 is reserved as 0 in CDV, n is a ring counter. - So skip the m1 loop */ - for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) { - for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; - clock.m2++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; - clock.p1++) { - int this_err; - - cdv_intel_clock(dev, refclk, &clock); - - if (!cdv_intel_PLL_is_valid(crtc, - limit, &clock)) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - - return err != target; -} - -int cdv_intel_pipe_set_base(struct drm_crtc *crtc, - int x, int y, struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); - int pipe = psb_intel_crtc->pipe; - unsigned long start, offset; - int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - u32 dspcntr; - int ret = 0; - - if (!gma_power_begin(dev, true)) - return 0; - - /* no fb bound */ - if (!crtc->fb) { - dev_err(dev->dev, "No FB bound\n"); - goto psb_intel_pipe_cleaner; - } - - - /* We are displaying this buffer, make sure it is actually loaded - into the GTT */ - ret = psb_gtt_pin(psbfb->gtt); - if (ret < 0) - goto psb_intel_pipe_set_base_exit; - start = psbfb->gtt->offset; - offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - - REG_WRITE(dspstride, crtc->fb->pitches[0]); - - dspcntr = REG_READ(dspcntr_reg); - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - - switch (crtc->fb->bits_per_pixel) { - case 8: - dspcntr |= DISPPLANE_8BPP; - break; - case 16: - if (crtc->fb->depth == 15) - dspcntr |= DISPPLANE_15_16BPP; - else - dspcntr |= DISPPLANE_16BPP; - break; - case 24: - case 32: - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; - break; - default: - dev_err(dev->dev, "Unknown color depth\n"); - ret = -EINVAL; - goto psb_intel_pipe_set_base_exit; - } - REG_WRITE(dspcntr_reg, dspcntr); - - dev_dbg(dev->dev, - "Writing base %08lX %08lX %d %d\n", start, offset, x, y); - - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - -psb_intel_pipe_cleaner: - /* If there was a previous display we can now unpin it */ - if (old_fb) - psb_gtt_unpin(to_psb_fb(old_fb)->gtt); - -psb_intel_pipe_set_base_exit: - gma_power_end(dev); - return ret; -} - -/** - * Sets the power management mode of the pipe and plane. - * - * This code should probably grow support for turning the cursor off and back - * on appropriately at the same time as we're turning the pipe off/on. - */ -static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - u32 temp; - bool enabled; - - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable the DPLL */ - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) == 0) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } - - /* Jim Bish - switch plan and pipe per scott */ - /* Enable the plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, - temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - } - - udelay(150); - - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - - psb_intel_crtc_load_lut(crtc); - - /* Give the overlay scaler a chance to enable - * if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, true); TODO */ - break; - case DRM_MODE_DPMS_OFF: - /* Give the overlay scaler a chance to disable - * if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* Jim Bish - changed pipe/plane here as well. */ - - /* Wait for vblank for the disable to take effect */ - cdv_intel_wait_for_vblank(dev); - - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); - } - - /* Wait for vblank for the disable to take effect. */ - cdv_intel_wait_for_vblank(dev); - - udelay(150); - - /* Disable display plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, - temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); - } - - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - } - - /* Wait for the clocks to turn off. */ - udelay(150); - break; - } - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - /*Set FIFO Watermarks*/ - REG_WRITE(DSPARB, 0x3F3E); -} - -static void cdv_intel_crtc_prepare(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); -} - -static void cdv_intel_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -} - -void cdv_intel_encoder_prepare(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - /* lvds has its own version of prepare see cdv_intel_lvds_prepare */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); -} - -void cdv_intel_encoder_commit(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - /* lvds has its own version of commit see cdv_intel_lvds_commit */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); -} - -static bool cdv_intel_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - - -/** - * Return the pipe currently connected to the panel fitter, - * or -1 if the panel fitter is not present or not in use - */ -static int cdv_intel_panel_fitter_pipe(struct drm_device *dev) -{ - u32 pfit_control; - - pfit_control = REG_READ(PFIT_CONTROL); - - /* See if the panel fitter is in use */ - if ((pfit_control & PFIT_ENABLE) == 0) - return -1; - return (pfit_control >> 29) & 0x3; -} - -static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dpll_md_reg = (psb_intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int refclk; - struct cdv_intel_clock_t clock; - u32 dpll = 0, dspcntr, pipeconf; - bool ok, is_sdvo = false, is_dvo = false; - bool is_crt = false, is_lvds = false, is_tv = false; - bool is_hdmi = false; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_connector *connector; - - list_for_each_entry(connector, &mode_config->connector_list, head) { - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - if (!connector->encoder - || connector->encoder->crtc != crtc) - continue; - - switch (psb_intel_output->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - case INTEL_OUTPUT_SDVO: - is_sdvo = true; - break; - case INTEL_OUTPUT_DVO: - is_dvo = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; - case INTEL_OUTPUT_HDMI: - is_hdmi = true; - break; - } - } - - refclk = 96000; - - /* Hack selection about ref clk for CRT */ - /* Select 27MHz as the reference clk for HDMI */ - if (is_crt || is_hdmi) - refclk = 27000; - - drm_mode_debug_printmodeline(adjusted_mode); - - ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, - &clock); - if (!ok) { - dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); - return 0; - } - - dpll = DPLL_VGA_MODE_DIS; - if (is_tv) { - /* XXX: just matching BIOS for now */ -/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; - } - dpll |= PLL_REF_INPUT_DREFCLK; - - dpll |= DPLL_SYNCLOCK_ENABLE; - dpll |= DPLL_VGA_MODE_DIS; - if (is_lvds) - dpll |= DPLLB_MODE_LVDS; - else - dpll |= DPLLB_MODE_DAC_SERIAL; - /* dpll |= (2 << 11); */ - - /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); - - /* Set up the display plane register */ - dspcntr = DISPPLANE_GAMMA_ENABLE; - - if (pipe == 0) - dspcntr |= DISPPLANE_SEL_PIPE_A; - else - dspcntr |= DISPPLANE_SEL_PIPE_B; - - dspcntr |= DISPLAY_PLANE_ENABLE; - pipeconf |= PIPEACONF_ENABLE; - - REG_WRITE(dpll_reg, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); - REG_READ(dpll_reg); - - cdv_dpll_set_clock_cdv(dev, crtc, &clock); - - udelay(150); - - - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (is_lvds) { - u32 lvds = REG_READ(LVDS); - - lvds |= - LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | - LVDS_PIPEB_SELECT; - /* Set the B0-B3 data pairs corresponding to - * whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock.p2 == 7) - lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more - * thoroughly into how panels behave in the two modes. - */ - - REG_WRITE(LVDS, lvds); - REG_READ(LVDS); - } - - dpll |= DPLL_VCO_ENABLE; - - /* Disable the panel fitter if it was on our pipe */ - if (cdv_intel_panel_fitter_pipe(dev) == pipe) - REG_WRITE(PFIT_CONTROL, 0); - - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); - drm_mode_debug_printmodeline(mode); - - REG_WRITE(dpll_reg, - (REG_READ(dpll_reg) & ~DPLL_LOCK) | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); /* 42 usec w/o calibration, 110 with. rounded up. */ - - if (!(REG_READ(dpll_reg) & DPLL_LOCK)) { - dev_err(dev->dev, "Failed to get DPLL lock\n"); - return -EBUSY; - } - - { - int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - REG_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); - } - - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | - ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | - ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | - ((adjusted_mode->crtc_vsync_end - 1) << 16)); - /* pipesrc and dspsize control the size that is scaled from, - * which should always be the user's requested size. - */ - REG_WRITE(dspsize_reg, - ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - REG_WRITE(pipesrc_reg, - ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - - cdv_intel_wait_for_vblank(dev); - - REG_WRITE(dspcntr_reg, dspcntr); - - /* Flush the plane changes */ - { - struct drm_crtc_helper_funcs *crtc_funcs = - crtc->helper_private; - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - } - - cdv_intel_wait_for_vblank(dev); - - return 0; -} - -/** Loads the palette/gamma unit for the CRTC with the prepared values */ -void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int palreg = PALETTE_A; - int i; - - /* The clocks have to be on to load the palette. */ - if (!crtc->enabled) - return; - - switch (psb_intel_crtc->pipe) { - case 0: - break; - case 1: - palreg = PALETTE_B; - break; - case 2: - palreg = PALETTE_C; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return; - } - - if (gma_power_begin(dev, false)) { - for (i = 0; i < 256; i++) { - REG_WRITE(palreg + 4 * i, - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i])); - } - gma_power_end(dev); - } else { - for (i = 0; i < 256; i++) { - dev_priv->save_palette_a[i] = - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i]); - } - - } -} - -/** - * Save HW states of giving crtc - */ -static void cdv_intel_crtc_save(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_dbg(dev->dev, "No CRTC state found\n"); - return; - } - - crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); - crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); - crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); - crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); - crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); - crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); - crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); - crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); - crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); - crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); - crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); - crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); - crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); - - /*NOTE: DSPSIZE DSPPOS only for psb*/ - crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); - crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); - - crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); - - DRM_DEBUG("(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - crtc_state->saveDSPCNTR, - crtc_state->savePIPECONF, - crtc_state->savePIPESRC, - crtc_state->saveFP0, - crtc_state->saveFP1, - crtc_state->saveDPLL, - crtc_state->saveHTOTAL, - crtc_state->saveHBLANK, - crtc_state->saveHSYNC, - crtc_state->saveVTOTAL, - crtc_state->saveVBLANK, - crtc_state->saveVSYNC, - crtc_state->saveDSPSTRIDE, - crtc_state->saveDSPSIZE, - crtc_state->saveDSPPOS, - crtc_state->saveDSPBASE - ); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); -} - -/** - * Restore HW states of giving crtc - */ -static void cdv_intel_crtc_restore(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private * dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_dbg(dev->dev, "No crtc state\n"); - return; - } - - DRM_DEBUG( - "current:(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - REG_READ(pipeA ? DSPACNTR : DSPBCNTR), - REG_READ(pipeA ? PIPEACONF : PIPEBCONF), - REG_READ(pipeA ? PIPEASRC : PIPEBSRC), - REG_READ(pipeA ? FPA0 : FPB0), - REG_READ(pipeA ? FPA1 : FPB1), - REG_READ(pipeA ? DPLL_A : DPLL_B), - REG_READ(pipeA ? HTOTAL_A : HTOTAL_B), - REG_READ(pipeA ? HBLANK_A : HBLANK_B), - REG_READ(pipeA ? HSYNC_A : HSYNC_B), - REG_READ(pipeA ? VTOTAL_A : VTOTAL_B), - REG_READ(pipeA ? VBLANK_A : VBLANK_B), - REG_READ(pipeA ? VSYNC_A : VSYNC_B), - REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE), - REG_READ(pipeA ? DSPASIZE : DSPBSIZE), - REG_READ(pipeA ? DSPAPOS : DSPBPOS), - REG_READ(pipeA ? DSPABASE : DSPBBASE) - ); - - DRM_DEBUG( - "saved: (%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - crtc_state->saveDSPCNTR, - crtc_state->savePIPECONF, - crtc_state->savePIPESRC, - crtc_state->saveFP0, - crtc_state->saveFP1, - crtc_state->saveDPLL, - crtc_state->saveHTOTAL, - crtc_state->saveHBLANK, - crtc_state->saveHSYNC, - crtc_state->saveVTOTAL, - crtc_state->saveVBLANK, - crtc_state->saveVSYNC, - crtc_state->saveDSPSTRIDE, - crtc_state->saveDSPSIZE, - crtc_state->saveDSPPOS, - crtc_state->saveDSPBASE - ); - - - if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { - REG_WRITE(pipeA ? DPLL_A : DPLL_B, - crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); - REG_READ(pipeA ? DPLL_A : DPLL_B); - DRM_DEBUG("write dpll: %x\n", - REG_READ(pipeA ? DPLL_A : DPLL_B)); - udelay(150); - } - - REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); - REG_READ(pipeA ? FPA0 : FPB0); - - REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); - REG_READ(pipeA ? FPA1 : FPB1); - - REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); - REG_READ(pipeA ? DPLL_A : DPLL_B); - udelay(150); - - REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); - REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); - REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); - REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); - REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); - REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); - REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); - - REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); - REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); - - REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); - - cdv_intel_wait_for_vblank(dev); - - REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - - cdv_intel_wait_for_vblank(dev); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); -} - -static int cdv_intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; - uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; - uint32_t temp; - size_t addr = 0; - struct gtt_range *gt; - struct drm_gem_object *obj; - int ret; - - /* if we want to turn of the cursor ignore width and height */ - if (!handle) { - /* turn off the cursor */ - temp = CURSOR_MODE_DISABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, 0); - gma_power_end(dev); - } - - /* unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - - return 0; - } - - /* Currently we only support 64x64 cursors */ - if (width != 64 || height != 64) { - dev_dbg(dev->dev, "we currently only support 64x64 cursors\n"); - return -EINVAL; - } - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; - - if (obj->size < width * height * 4) { - dev_dbg(dev->dev, "buffer is to small\n"); - return -ENOMEM; - } - - gt = container_of(obj, struct gtt_range, gem); - - /* Pin the memory into the GTT */ - ret = psb_gtt_pin(gt); - if (ret) { - dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); - return ret; - } - - addr = gt->offset; /* Or resource.start ??? */ - - psb_intel_crtc->cursor_addr = addr; - - temp = 0; - /* set the pipe for the cursor */ - temp |= (pipe << 28); - temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, addr); - gma_power_end(dev); - } - - /* unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = obj; - } - return 0; -} - -static int cdv_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t temp = 0; - uint32_t adder; - - - if (x < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); - x = -x; - } - if (y < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); - y = -y; - } - - temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); - temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); - - adder = psb_intel_crtc->cursor_addr; - - if (gma_power_begin(dev, false)) { - REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); - REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder); - gma_power_end(dev); - } - return 0; -} - -static void cdv_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t start, uint32_t size) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int i; - int end = (start + size > 256) ? 256 : start + size; - - for (i = start; i < end; i++) { - psb_intel_crtc->lut_r[i] = red[i] >> 8; - psb_intel_crtc->lut_g[i] = green[i] >> 8; - psb_intel_crtc->lut_b[i] = blue[i] >> 8; - } - - cdv_intel_crtc_load_lut(crtc); -} - -static int cdv_crtc_set_config(struct drm_mode_set *set) -{ - int ret = 0; - struct drm_device *dev = set->crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!dev_priv->rpm_enabled) - return drm_crtc_helper_set_config(set); - - pm_runtime_forbid(&dev->pdev->dev); - - ret = drm_crtc_helper_set_config(set); - - pm_runtime_allow(&dev->pdev->dev); - - return ret; -} - -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ - -/* FIXME: why are we using this, should it be cdv_ in this tree ? */ - -static void i8xx_clock(int refclk, struct cdv_intel_clock_t *clock) -{ - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); - clock->p = clock->p1 * clock->p2; - clock->vco = refclk * clock->m / (clock->n + 2); - clock->dot = clock->vco / clock->p; -} - -/* Returns the clock of the currently programmed mode of the given pipe. */ -static int cdv_intel_crtc_clock_get(struct drm_device *dev, - struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - u32 dpll; - u32 fp; - struct cdv_intel_clock_t clock; - bool is_lvds; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (gma_power_begin(dev, false)) { - dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); - if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = REG_READ((pipe == 0) ? FPA0 : FPB0); - else - fp = REG_READ((pipe == 0) ? FPA1 : FPB1); - is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); - gma_power_end(dev); - } else { - dpll = (pipe == 0) ? - dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; - - if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = (pipe == 0) ? - dev_priv->saveFPA0 : - dev_priv->saveFPB0; - else - fp = (pipe == 0) ? - dev_priv->saveFPA1 : - dev_priv->saveFPB1; - - is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN); - } - - clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; - clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; - clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; - - if (is_lvds) { - clock.p1 = - ffs((dpll & - DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - if (clock.p1 == 0) { - clock.p1 = 4; - dev_err(dev->dev, "PLL %d\n", dpll); - } - clock.p2 = 14; - - if ((dpll & PLL_REF_INPUT_MASK) == - PLLB_REF_INPUT_SPREADSPECTRUMIN) { - /* XXX: might not be 66MHz */ - i8xx_clock(66000, &clock); - } else - i8xx_clock(48000, &clock); - } else { - if (dpll & PLL_P1_DIVIDE_BY_TWO) - clock.p1 = 2; - else { - clock.p1 = - ((dpll & - DPLL_FPA01_P1_POST_DIV_MASK_I830) >> - DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; - } - if (dpll & PLL_P2_DIVIDE_BY_4) - clock.p2 = 4; - else - clock.p2 = 2; - - i8xx_clock(48000, &clock); - } - - /* XXX: It would be nice to validate the clocks, but we can't reuse - * i830PllIsValid() because it relies on the xf86_config connector - * configuration being accurate, which it isn't necessarily. - */ - - return clock.dot; -} - -/** Returns the currently programmed mode of the given pipe. */ -struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, - struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - struct drm_display_mode *mode; - int htot; - int hsync; - int vtot; - int vsync; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (gma_power_begin(dev, false)) { - htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); - hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); - vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); - vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); - gma_power_end(dev); - } else { - htot = (pipe == 0) ? - dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; - hsync = (pipe == 0) ? - dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; - vtot = (pipe == 0) ? - dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; - vsync = (pipe == 0) ? - dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; - } - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) - return NULL; - - mode->clock = cdv_intel_crtc_clock_get(dev, crtc); - mode->hdisplay = (htot & 0xffff) + 1; - mode->htotal = ((htot & 0xffff0000) >> 16) + 1; - mode->hsync_start = (hsync & 0xffff) + 1; - mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; - mode->vdisplay = (vtot & 0xffff) + 1; - mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; - mode->vsync_start = (vsync & 0xffff) + 1; - mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - return mode; -} - -static void cdv_intel_crtc_destroy(struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - - kfree(psb_intel_crtc->crtc_state); - drm_crtc_cleanup(crtc); - kfree(psb_intel_crtc); -} - -const struct drm_crtc_helper_funcs cdv_intel_helper_funcs = { - .dpms = cdv_intel_crtc_dpms, - .mode_fixup = cdv_intel_crtc_mode_fixup, - .mode_set = cdv_intel_crtc_mode_set, - .mode_set_base = cdv_intel_pipe_set_base, - .prepare = cdv_intel_crtc_prepare, - .commit = cdv_intel_crtc_commit, -}; - -const struct drm_crtc_funcs cdv_intel_crtc_funcs = { - .save = cdv_intel_crtc_save, - .restore = cdv_intel_crtc_restore, - .cursor_set = cdv_intel_crtc_cursor_set, - .cursor_move = cdv_intel_crtc_cursor_move, - .gamma_set = cdv_intel_crtc_gamma_set, - .set_config = cdv_crtc_set_config, - .destroy = cdv_intel_crtc_destroy, -}; - -/* - * Set the default value of cursor control and base register - * to zero. This is a workaround for h/w defect on oaktrail - */ -void cdv_intel_cursor_init(struct drm_device *dev, int pipe) -{ - uint32_t control; - uint32_t base; - - switch (pipe) { - case 0: - control = CURACNTR; - base = CURABASE; - break; - case 1: - control = CURBCNTR; - base = CURBBASE; - break; - case 2: - control = CURCCNTR; - base = CURCBASE; - break; - default: - return; - } - - REG_WRITE(control, 0); - REG_WRITE(base, 0); -} - diff --git a/drivers/staging/gma500/cdv_intel_hdmi.c b/drivers/staging/gma500/cdv_intel_hdmi.c deleted file mode 100644 index cbca2b0..0000000 --- a/drivers/staging/gma500/cdv_intel_hdmi.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright © 2006-2011 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * - * FIXME: - * We should probably make this generic and share it with Medfield - */ - -#include -#include -#include -#include -#include "psb_intel_drv.h" -#include "psb_drv.h" -#include "psb_intel_reg.h" -#include - -/* hdmi control bits */ -#define HDMI_NULL_PACKETS_DURING_VSYNC (1 << 9) -#define HDMI_BORDER_ENABLE (1 << 7) -#define HDMI_AUDIO_ENABLE (1 << 6) -#define HDMI_VSYNC_ACTIVE_HIGH (1 << 4) -#define HDMI_HSYNC_ACTIVE_HIGH (1 << 3) -/* hdmi-b control bits */ -#define HDMIB_PIPE_B_SELECT (1 << 30) - - -struct mid_intel_hdmi_priv { - u32 hdmi_reg; - u32 save_HDMIB; - bool has_hdmi_sink; - bool has_hdmi_audio; - /* Should set this when detect hotplug */ - bool hdmi_device_connected; - struct mdfld_hdmi_i2c *i2c_bus; - struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */ - struct drm_device *dev; -}; - -static void cdv_hdmi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; - u32 hdmib; - struct drm_crtc *crtc = encoder->crtc; - struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); - - hdmib = (2 << 10); - - if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) - hdmib |= HDMI_VSYNC_ACTIVE_HIGH; - if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) - hdmib |= HDMI_HSYNC_ACTIVE_HIGH; - - if (intel_crtc->pipe == 1) - hdmib |= HDMIB_PIPE_B_SELECT; - - if (hdmi_priv->has_hdmi_audio) { - hdmib |= HDMI_AUDIO_ENABLE; - hdmib |= HDMI_NULL_PACKETS_DURING_VSYNC; - } - - REG_WRITE(hdmi_priv->hdmi_reg, hdmib); - REG_READ(hdmi_priv->hdmi_reg); -} - -static bool cdv_hdmi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static void cdv_hdmi_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; - u32 hdmib; - - hdmib = REG_READ(hdmi_priv->hdmi_reg); - - if (mode != DRM_MODE_DPMS_ON) - REG_WRITE(hdmi_priv->hdmi_reg, hdmib & ~HDMIB_PORT_EN); - else - REG_WRITE(hdmi_priv->hdmi_reg, hdmib | HDMIB_PORT_EN); - REG_READ(hdmi_priv->hdmi_reg); -} - -static void cdv_hdmi_save(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct psb_intel_output *output = to_psb_intel_output(connector); - struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; - - hdmi_priv->save_HDMIB = REG_READ(hdmi_priv->hdmi_reg); -} - -static void cdv_hdmi_restore(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct psb_intel_output *output = to_psb_intel_output(connector); - struct mid_intel_hdmi_priv *hdmi_priv = output->dev_priv; - - REG_WRITE(hdmi_priv->hdmi_reg, hdmi_priv->save_HDMIB); - REG_READ(hdmi_priv->hdmi_reg); -} - -static enum drm_connector_status cdv_hdmi_detect( - struct drm_connector *connector, bool force) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_output->dev_priv; - struct edid *edid = NULL; - enum drm_connector_status status = connector_status_disconnected; - - edid = drm_get_edid(&psb_intel_output->base, - psb_intel_output->hdmi_i2c_adapter); - - hdmi_priv->has_hdmi_sink = false; - hdmi_priv->has_hdmi_audio = false; - if (edid) { - if (edid->input & DRM_EDID_INPUT_DIGITAL) { - status = connector_status_connected; - hdmi_priv->has_hdmi_sink = - drm_detect_hdmi_monitor(edid); - hdmi_priv->has_hdmi_audio = - drm_detect_monitor_audio(edid); - } - - psb_intel_output->base.display_info.raw_edid = NULL; - kfree(edid); - } - return status; -} - -static int cdv_hdmi_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t value) -{ - struct drm_encoder *encoder = connector->encoder; - - if (!strcmp(property->name, "scaling mode") && encoder) { - struct psb_intel_crtc *crtc = to_psb_intel_crtc(encoder->crtc); - bool centre; - uint64_t curValue; - - if (!crtc) - return -1; - - switch (value) { - case DRM_MODE_SCALE_FULLSCREEN: - break; - case DRM_MODE_SCALE_NO_SCALE: - break; - case DRM_MODE_SCALE_ASPECT: - break; - default: - return -1; - } - - if (drm_connector_property_get_value(connector, - property, &curValue)) - return -1; - - if (curValue == value) - return 0; - - if (drm_connector_property_set_value(connector, - property, value)) - return -1; - - centre = (curValue == DRM_MODE_SCALE_NO_SCALE) || - (value == DRM_MODE_SCALE_NO_SCALE); - - if (crtc->saved_mode.hdisplay != 0 && - crtc->saved_mode.vdisplay != 0) { - if (centre) { - if (!drm_crtc_helper_set_mode(encoder->crtc, &crtc->saved_mode, - encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb)) - return -1; - } else { - struct drm_encoder_helper_funcs *helpers - = encoder->helper_private; - helpers->mode_set(encoder, &crtc->saved_mode, - &crtc->saved_adjusted_mode); - } - } - } - return 0; -} - -/* - * Return the list of HDMI DDC modes if available. - */ -static int cdv_hdmi_get_modes(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct edid *edid = NULL; - int ret = 0; - - edid = drm_get_edid(&psb_intel_output->base, - psb_intel_output->hdmi_i2c_adapter); - if (edid) { - drm_mode_connector_update_edid_property(&psb_intel_output-> - base, edid); - ret = drm_add_edid_modes(&psb_intel_output->base, edid); - kfree(edid); - } - return ret; -} - -static int cdv_hdmi_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - - if (mode->clock > 165000) - return MODE_CLOCK_HIGH; - if (mode->clock < 20000) - return MODE_CLOCK_HIGH; - - /* just in case */ - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - /* just in case */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - return MODE_NO_INTERLACE; - - /* - * FIXME: for now we limit the size to 1680x1050 on CDV, otherwise it - * will go beyond the stolen memory size allocated to the framebuffer - */ - if (mode->hdisplay > 1680) - return MODE_PANEL; - if (mode->vdisplay > 1050) - return MODE_PANEL; - return MODE_OK; -} - -static void cdv_hdmi_destroy(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - if (psb_intel_output->ddc_bus) - psb_intel_i2c_destroy(psb_intel_output->ddc_bus); - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(connector); -} - -static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = { - .dpms = cdv_hdmi_dpms, - .mode_fixup = cdv_hdmi_mode_fixup, - .prepare = psb_intel_encoder_prepare, - .mode_set = cdv_hdmi_mode_set, - .commit = psb_intel_encoder_commit, -}; - -static const struct drm_connector_helper_funcs - cdv_hdmi_connector_helper_funcs = { - .get_modes = cdv_hdmi_get_modes, - .mode_valid = cdv_hdmi_mode_valid, - .best_encoder = psb_intel_best_encoder, -}; - -static const struct drm_connector_funcs cdv_hdmi_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .save = cdv_hdmi_save, - .restore = cdv_hdmi_restore, - .detect = cdv_hdmi_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = cdv_hdmi_set_property, - .destroy = cdv_hdmi_destroy, -}; - -void cdv_hdmi_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev, int reg) -{ - struct psb_intel_output *psb_intel_output; - struct drm_connector *connector; - struct drm_encoder *encoder; - struct mid_intel_hdmi_priv *hdmi_priv; - int ddc_bus; - - psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + - sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL); - if (!psb_intel_output) - return; - - hdmi_priv = (struct mid_intel_hdmi_priv *)(psb_intel_output + 1); - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - drm_connector_init(dev, &psb_intel_output->base, - &cdv_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_DVID); - - drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, - DRM_MODE_ENCODER_TMDS); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - psb_intel_output->type = INTEL_OUTPUT_HDMI; - hdmi_priv->hdmi_reg = reg; - hdmi_priv->has_hdmi_sink = false; - psb_intel_output->dev_priv = hdmi_priv; - - drm_encoder_helper_add(encoder, &cdv_hdmi_helper_funcs); - drm_connector_helper_add(connector, - &cdv_hdmi_connector_helper_funcs); - connector->display_info.subpixel_order = SubPixelHorizontalRGB; - connector->interlace_allowed = false; - connector->doublescan_allowed = false; - - drm_connector_attach_property(connector, - dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); - - switch (reg) { - case SDVOB: - ddc_bus = GPIOE; - break; - case SDVOC: - ddc_bus = GPIOD; - break; - default: - DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); - goto failed_ddc; - break; - } - - psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, - ddc_bus, (reg == SDVOB) ? "HDMIB" : "HDMIC"); - - if (!psb_intel_output->ddc_bus) { - dev_err(dev->dev, "No ddc adapter available!\n"); - goto failed_ddc; - } - psb_intel_output->hdmi_i2c_adapter = - &(psb_intel_output->ddc_bus->adapter); - hdmi_priv->dev = dev; - drm_sysfs_connector_add(connector); - return; - -failed_ddc: - drm_encoder_cleanup(&psb_intel_output->enc); - drm_connector_cleanup(&psb_intel_output->base); - kfree(psb_intel_output); -} diff --git a/drivers/staging/gma500/cdv_intel_lvds.c b/drivers/staging/gma500/cdv_intel_lvds.c deleted file mode 100644 index 988b2d0..0000000 --- a/drivers/staging/gma500/cdv_intel_lvds.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * Copyright © 2006-2011 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - * Dave Airlie - * Jesse Barnes - */ - -#include -#include -#include - -#include "intel_bios.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "power.h" -#include -#include "cdv_device.h" - -/** - * LVDS I2C backlight control macros - */ -#define BRIGHTNESS_MAX_LEVEL 100 -#define BRIGHTNESS_MASK 0xFF -#define BLC_I2C_TYPE 0x01 -#define BLC_PWM_TYPT 0x02 - -#define BLC_POLARITY_NORMAL 0 -#define BLC_POLARITY_INVERSE 1 - -#define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE) -#define PSB_BLC_MIN_PWM_REG_FREQ (0x2) -#define PSB_BLC_PWM_PRECISION_FACTOR (10) -#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) -#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) - -struct cdv_intel_lvds_priv { - /** - * Saved LVDO output states - */ - uint32_t savePP_ON; - uint32_t savePP_OFF; - uint32_t saveLVDS; - uint32_t savePP_CONTROL; - uint32_t savePP_CYCLE; - uint32_t savePFIT_CONTROL; - uint32_t savePFIT_PGM_RATIOS; - uint32_t saveBLC_PWM_CTL; -}; - -/* - * Returns the maximum level of the backlight duty cycle field. - */ -static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 retval; - - if (gma_power_begin(dev, false)) { - retval = ((REG_READ(BLC_PWM_CTL) & - BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; - - gma_power_end(dev); - } else - retval = ((dev_priv->saveBLC_PWM_CTL & - BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; - - return retval; -} - -/* - * Set LVDS backlight level by I2C command - */ -static int cdv_lvds_i2c_set_brightness(struct drm_device *dev, - unsigned int level) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; - u8 out_buf[2]; - unsigned int blc_i2c_brightness; - - struct i2c_msg msgs[] = { - { - .addr = lvds_i2c_bus->slave_addr, - .flags = 0, - .len = 2, - .buf = out_buf, - } - }; - - blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level * - BRIGHTNESS_MASK / - BRIGHTNESS_MAX_LEVEL); - - if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) - blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness; - - out_buf[0] = dev_priv->lvds_bl->brightnesscmd; - out_buf[1] = (u8)blc_i2c_brightness; - - if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) - return 0; - - DRM_ERROR("I2C transfer error\n"); - return -1; -} - - -static int cdv_lvds_pwm_set_brightness(struct drm_device *dev, int level) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - u32 max_pwm_blc; - u32 blc_pwm_duty_cycle; - - max_pwm_blc = cdv_intel_lvds_get_max_backlight(dev); - - /*BLC_PWM_CTL Should be initiated while backlight device init*/ - BUG_ON((max_pwm_blc & PSB_BLC_MAX_PWM_REG_FREQ) == 0); - - blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL; - - if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) - blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle; - - blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; - REG_WRITE(BLC_PWM_CTL, - (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | - (blc_pwm_duty_cycle)); - - return 0; -} - -/* - * Set LVDS backlight level either by I2C or PWM - */ -void cdv_intel_lvds_set_brightness(struct drm_device *dev, int level) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!dev_priv->lvds_bl) { - DRM_ERROR("NO LVDS Backlight Info\n"); - return; - } - - if (dev_priv->lvds_bl->type == BLC_I2C_TYPE) - cdv_lvds_i2c_set_brightness(dev, level); - else - cdv_lvds_pwm_set_brightness(dev, level); -} - -/** - * Sets the backlight level. - * - * level backlight level, from 0 to cdv_intel_lvds_get_max_backlight(). - */ -static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 blc_pwm_ctl; - - if (gma_power_begin(dev, false)) { - blc_pwm_ctl = - REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; - REG_WRITE(BLC_PWM_CTL, - (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); - gma_power_end(dev); - } else { - blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & - ~BACKLIGHT_DUTY_CYCLE_MASK; - dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); - } -} - -/** - * Sets the power state for the panel. - */ -static void cdv_intel_lvds_set_power(struct drm_device *dev, - struct psb_intel_output *output, bool on) -{ - u32 pp_status; - - if (!gma_power_begin(dev, true)) - return; - - if (on) { - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | - POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); - - cdv_intel_lvds_set_backlight(dev, - output-> - mode_dev->backlight_duty_cycle); - } else { - cdv_intel_lvds_set_backlight(dev, 0); - - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & - ~POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while (pp_status & PP_ON); - } - gma_power_end(dev); -} - -static void cdv_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - if (mode == DRM_MODE_DPMS_ON) - cdv_intel_lvds_set_power(dev, output, true); - else - cdv_intel_lvds_set_power(dev, output, false); - /* XXX: We never power down the LVDS pairs. */ -} - -static void cdv_intel_lvds_save(struct drm_connector *connector) -{ -} - -static void cdv_intel_lvds_restore(struct drm_connector *connector) -{ -} - -int cdv_intel_lvds_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct drm_display_mode *fixed_mode = - psb_intel_output->mode_dev->panel_fixed_mode; - - /* just in case */ - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - /* just in case */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - return MODE_NO_INTERLACE; - - if (fixed_mode) { - if (mode->hdisplay > fixed_mode->hdisplay) - return MODE_PANEL; - if (mode->vdisplay > fixed_mode->vdisplay) - return MODE_PANEL; - } - return MODE_OK; -} - -bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct psb_intel_mode_device *mode_dev = - enc_to_psb_intel_output(encoder)->mode_dev; - struct drm_device *dev = encoder->dev; - struct drm_encoder *tmp_encoder; - struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; - - /* Should never happen!! */ - list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, - head) { - if (tmp_encoder != encoder - && tmp_encoder->crtc == encoder->crtc) { - printk(KERN_ERR "Can't enable LVDS and another " - "encoder on the same pipe\n"); - return false; - } - } - - /* - * If we have timings from the BIOS for the panel, put them in - * to the adjusted mode. The CRTC will be set up for this mode, - * with the panel scaling set up to source from the H/VDisplay - * of the original mode. - */ - if (panel_fixed_mode != NULL) { - adjusted_mode->hdisplay = panel_fixed_mode->hdisplay; - adjusted_mode->hsync_start = panel_fixed_mode->hsync_start; - adjusted_mode->hsync_end = panel_fixed_mode->hsync_end; - adjusted_mode->htotal = panel_fixed_mode->htotal; - adjusted_mode->vdisplay = panel_fixed_mode->vdisplay; - adjusted_mode->vsync_start = panel_fixed_mode->vsync_start; - adjusted_mode->vsync_end = panel_fixed_mode->vsync_end; - adjusted_mode->vtotal = panel_fixed_mode->vtotal; - adjusted_mode->clock = panel_fixed_mode->clock; - drm_mode_set_crtcinfo(adjusted_mode, - CRTC_INTERLACE_HALVE_V); - } - - /* - * XXX: It would be nice to support lower refresh rates on the - * panels to reduce power consumption, and perhaps match the - * user's requested refresh rate. - */ - - return true; -} - -static void cdv_intel_lvds_prepare(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_mode_device *mode_dev = output->mode_dev; - - if (!gma_power_begin(dev, true)) - return; - - mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); - mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); - - cdv_intel_lvds_set_power(dev, output, false); - - gma_power_end(dev); -} - -static void cdv_intel_lvds_commit(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_mode_device *mode_dev = output->mode_dev; - - if (mode_dev->backlight_duty_cycle == 0) - mode_dev->backlight_duty_cycle = - cdv_intel_lvds_get_max_backlight(dev); - - cdv_intel_lvds_set_power(dev, output, true); -} - -static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 pfit_control; - - /* - * The LVDS pin pair will already have been turned on in the - * cdv_intel_crtc_mode_set since it has a large impact on the DPLL - * settings. - */ - - /* - * Enable automatic panel scaling so that non-native modes fill the - * screen. Should be enabled before the pipe is enabled, according to - * register description and PRM. - */ - if (mode->hdisplay != adjusted_mode->hdisplay || - mode->vdisplay != adjusted_mode->vdisplay) - pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | - HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - else - pfit_control = 0; - - if (dev_priv->lvds_dither) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - - REG_WRITE(PFIT_CONTROL, pfit_control); -} - -/** - * Detect the LVDS connection. - * - * This always returns CONNECTOR_STATUS_CONNECTED. - * This connector should only have - * been set up if the LVDS was actually connected anyway. - */ -static enum drm_connector_status cdv_intel_lvds_detect( - struct drm_connector *connector, bool force) -{ - return connector_status_connected; -} - -/** - * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. - */ -static int cdv_intel_lvds_get_modes(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_mode_device *mode_dev = - psb_intel_output->mode_dev; - int ret; - - ret = psb_intel_ddc_get_modes(psb_intel_output); - - if (ret) - return ret; - - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - if (mode_dev->panel_fixed_mode != NULL) { - struct drm_display_mode *mode = - drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); - drm_mode_probed_add(connector, mode); - return 1; - } - - return 0; -} - -/** - * cdv_intel_lvds_destroy - unregister and free LVDS structures - * @connector: connector to free - * - * Unregister the DDC bus for this connector then free the driver private - * structure. - */ -void cdv_intel_lvds_destroy(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - if (psb_intel_output->ddc_bus) - psb_intel_i2c_destroy(psb_intel_output->ddc_bus); - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(connector); -} - -int cdv_intel_lvds_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t value) -{ - struct drm_encoder *encoder = connector->encoder; - - if (!strcmp(property->name, "scaling mode") && encoder) { - struct psb_intel_crtc *crtc = - to_psb_intel_crtc(encoder->crtc); - uint64_t curValue; - - if (!crtc) - return -1; - - switch (value) { - case DRM_MODE_SCALE_FULLSCREEN: - break; - case DRM_MODE_SCALE_NO_SCALE: - break; - case DRM_MODE_SCALE_ASPECT: - break; - default: - return -1; - } - - if (drm_connector_property_get_value(connector, - property, - &curValue)) - return -1; - - if (curValue == value) - return 0; - - if (drm_connector_property_set_value(connector, - property, - value)) - return -1; - - if (crtc->saved_mode.hdisplay != 0 && - crtc->saved_mode.vdisplay != 0) { - if (!drm_crtc_helper_set_mode(encoder->crtc, - &crtc->saved_mode, - encoder->crtc->x, - encoder->crtc->y, - encoder->crtc->fb)) - return -1; - } - } else if (!strcmp(property->name, "backlight") && encoder) { - if (drm_connector_property_set_value(connector, - property, - value)) - return -1; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *dev_priv = - encoder->dev->dev_private; - struct backlight_device *bd = - dev_priv->backlight_device; - bd->props.brightness = value; - backlight_update_status(bd); -#endif - } - } else if (!strcmp(property->name, "DPMS") && encoder) { - struct drm_encoder_helper_funcs *helpers = - encoder->helper_private; - helpers->dpms(encoder, value); - } - return 0; -} - -static const struct drm_encoder_helper_funcs - cdv_intel_lvds_helper_funcs = { - .dpms = cdv_intel_lvds_encoder_dpms, - .mode_fixup = cdv_intel_lvds_mode_fixup, - .prepare = cdv_intel_lvds_prepare, - .mode_set = cdv_intel_lvds_mode_set, - .commit = cdv_intel_lvds_commit, -}; - -static const struct drm_connector_helper_funcs - cdv_intel_lvds_connector_helper_funcs = { - .get_modes = cdv_intel_lvds_get_modes, - .mode_valid = cdv_intel_lvds_mode_valid, - .best_encoder = psb_intel_best_encoder, -}; - -static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .save = cdv_intel_lvds_save, - .restore = cdv_intel_lvds_restore, - .detect = cdv_intel_lvds_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = cdv_intel_lvds_set_property, - .destroy = cdv_intel_lvds_destroy, -}; - - -static void cdv_intel_lvds_enc_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - -const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = { - .destroy = cdv_intel_lvds_enc_destroy, -}; - -/** - * cdv_intel_lvds_init - setup LVDS connectors on this device - * @dev: drm device - * - * Create the connector, register the LVDS DDC bus, and try to figure out what - * modes we can display on the LVDS panel (if present). - */ -void cdv_intel_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev) -{ - struct psb_intel_output *psb_intel_output; - struct cdv_intel_lvds_priv *lvds_priv; - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_display_mode *scan; - struct drm_crtc *crtc; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 lvds; - int pipe; - - psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + - sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL); - if (!psb_intel_output) - return; - - lvds_priv = (struct cdv_intel_lvds_priv *)(psb_intel_output + 1); - - psb_intel_output->dev_priv = lvds_priv; - - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - - - drm_connector_init(dev, &psb_intel_output->base, - &cdv_intel_lvds_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - - drm_encoder_init(dev, &psb_intel_output->enc, - &cdv_intel_lvds_enc_funcs, - DRM_MODE_ENCODER_LVDS); - - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - psb_intel_output->type = INTEL_OUTPUT_LVDS; - - drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs); - drm_connector_helper_add(connector, - &cdv_intel_lvds_connector_helper_funcs); - connector->display_info.subpixel_order = SubPixelHorizontalRGB; - connector->interlace_allowed = false; - connector->doublescan_allowed = false; - - /*Attach connector properties*/ - drm_connector_attach_property(connector, - dev->mode_config.scaling_mode_property, - DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, - dev_priv->backlight_property, - BRIGHTNESS_MAX_LEVEL); - - /** - * Set up I2C bus - * FIXME: distroy i2c_bus when exit - */ - psb_intel_output->i2c_bus = psb_intel_i2c_create(dev, - GPIOB, - "LVDSBLC_B"); - if (!psb_intel_output->i2c_bus) { - dev_printk(KERN_ERR, - &dev->pdev->dev, "I2C bus registration failed.\n"); - goto failed_blc_i2c; - } - psb_intel_output->i2c_bus->slave_addr = 0x2C; - dev_priv->lvds_i2c_bus = psb_intel_output->i2c_bus; - - /* - * LVDS discovery: - * 1) check for EDID on DDC - * 2) check for VBT data - * 3) check to see if LVDS is already on - * if none of the above, no panel - * 4) make sure lid is open - * if closed, act like it's not there for now - */ - - /* Set up the DDC bus. */ - psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, - GPIOC, - "LVDSDDC_C"); - if (!psb_intel_output->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, - "DDC bus registration " "failed.\n"); - goto failed_ddc; - } - - /* - * Attempt to get the fixed panel mode from DDC. Assume that the - * preferred mode is the right one. - */ - psb_intel_ddc_get_modes(psb_intel_output); - list_for_each_entry(scan, &connector->probed_modes, head) { - if (scan->type & DRM_MODE_TYPE_PREFERRED) { - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, scan); - goto out; /* FIXME: check for quirks */ - } - } - - /* Failed to get EDID, what about VBT? do we need this?*/ - if (dev_priv->lfp_lvds_vbt_mode) { - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - if (mode_dev->panel_fixed_mode) { - mode_dev->panel_fixed_mode->type |= - DRM_MODE_TYPE_PREFERRED; - goto out; /* FIXME: check for quirks */ - } - } - /* - * If we didn't get EDID, try checking if the panel is already turned - * on. If so, assume that whatever is currently programmed is the - * correct mode. - */ - lvds = REG_READ(LVDS); - pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; - crtc = psb_intel_get_crtc_from_pipe(dev, pipe); - - if (crtc && (lvds & LVDS_PORT_EN)) { - mode_dev->panel_fixed_mode = - cdv_intel_crtc_mode_get(dev, crtc); - if (mode_dev->panel_fixed_mode) { - mode_dev->panel_fixed_mode->type |= - DRM_MODE_TYPE_PREFERRED; - goto out; /* FIXME: check for quirks */ - } - } - - /* If we still don't have a mode after all that, give up. */ - if (!mode_dev->panel_fixed_mode) { - DRM_DEBUG - ("Found no modes on the lvds, ignoring the LVDS\n"); - goto failed_find; - } - -out: - drm_sysfs_connector_add(connector); - return; - -failed_find: - printk(KERN_ERR "Failed find\n"); - if (psb_intel_output->ddc_bus) - psb_intel_i2c_destroy(psb_intel_output->ddc_bus); -failed_ddc: - printk(KERN_ERR "Failed DDC\n"); - if (psb_intel_output->i2c_bus) - psb_intel_i2c_destroy(psb_intel_output->i2c_bus); -failed_blc_i2c: - printk(KERN_ERR "Failed BLC\n"); - drm_encoder_cleanup(encoder); - drm_connector_cleanup(connector); - kfree(connector); -} diff --git a/drivers/staging/gma500/displays/hdmi.h b/drivers/staging/gma500/displays/hdmi.h deleted file mode 100644 index d58ba9b..0000000 --- a/drivers/staging/gma500/displays/hdmi.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe - */ - -#ifndef HDMI_H -#define HDMI_H - -extern void hdmi_init(struct drm_device *dev); - -#endif diff --git a/drivers/staging/gma500/displays/pyr_cmd.h b/drivers/staging/gma500/displays/pyr_cmd.h deleted file mode 100644 index 84bae5c..0000000 --- a/drivers/staging/gma500/displays/pyr_cmd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe - */ - -#ifndef PYR_CMD_H -#define PYR_CMD_H - -extern void pyr_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs); - -#endif - diff --git a/drivers/staging/gma500/displays/pyr_vid.h b/drivers/staging/gma500/displays/pyr_vid.h deleted file mode 100644 index ce98860..0000000 --- a/drivers/staging/gma500/displays/pyr_vid.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe -*/ - -#ifndef PYR_VID_H -#define PYR_VID_H - -extern void pyr_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs); -extern struct drm_display_mode *pyr_vid_get_config_mode(struct drm_device* dev); - -#endif diff --git a/drivers/staging/gma500/displays/tmd_cmd.h b/drivers/staging/gma500/displays/tmd_cmd.h deleted file mode 100644 index 641e85e..0000000 --- a/drivers/staging/gma500/displays/tmd_cmd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe - */ - -#ifndef TMD_CMD_H -#define TMD_CMD_H - -extern void tmd_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs); -extern struct drm_display_mode *tmd_cmd_get_config_mode(struct drm_device *dev); - -#endif diff --git a/drivers/staging/gma500/displays/tmd_vid.h b/drivers/staging/gma500/displays/tmd_vid.h deleted file mode 100644 index 7a5fa3b..0000000 --- a/drivers/staging/gma500/displays/tmd_vid.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe -*/ - -#ifndef TMD_VID_H -#define TMD_VID_H - -extern void tmd_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs); -extern struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev); - -#endif diff --git a/drivers/staging/gma500/displays/tpo_cmd.h b/drivers/staging/gma500/displays/tpo_cmd.h deleted file mode 100644 index 6105527..0000000 --- a/drivers/staging/gma500/displays/tpo_cmd.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe -*/ - -#ifndef TPO_CMD_H -#define TPO_CMD_H - -extern void tpo_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs); -/* extern struct drm_display_mode * */ -/* tpo_cmd_get_config_mode(struct drm_device *dev); */ - -#endif diff --git a/drivers/staging/gma500/displays/tpo_vid.h b/drivers/staging/gma500/displays/tpo_vid.h deleted file mode 100644 index c24f057..0000000 --- a/drivers/staging/gma500/displays/tpo_vid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe - */ - -#ifndef TPO_VID_H -#define TPO_VID_H - -extern void tpo_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs); - -#endif diff --git a/drivers/staging/gma500/framebuffer.c b/drivers/staging/gma500/framebuffer.c deleted file mode 100644 index b00761c..0000000 --- a/drivers/staging/gma500/framebuffer.c +++ /dev/null @@ -1,856 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "psb_drv.h" -#include "psb_intel_reg.h" -#include "psb_intel_drv.h" -#include "framebuffer.h" -#include "gtt.h" - -#include "mdfld_output.h" - -static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb); -static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle); - -static const struct drm_framebuffer_funcs psb_fb_funcs = { - .destroy = psb_user_framebuffer_destroy, - .create_handle = psb_user_framebuffer_create_handle, -}; - -#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) - -static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, - struct fb_info *info) -{ - struct psb_fbdev *fbdev = info->par; - struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb; - uint32_t v; - - if (!fb) - return -ENOMEM; - - if (regno > 255) - return 1; - - red = CMAP_TOHW(red, info->var.red.length); - blue = CMAP_TOHW(blue, info->var.blue.length); - green = CMAP_TOHW(green, info->var.green.length); - transp = CMAP_TOHW(transp, info->var.transp.length); - - v = (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset) | - (transp << info->var.transp.offset); - - if (regno < 16) { - switch (fb->bits_per_pixel) { - case 16: - ((uint32_t *) info->pseudo_palette)[regno] = v; - break; - case 24: - case 32: - ((uint32_t *) info->pseudo_palette)[regno] = v; - break; - } - } - - return 0; -} - -static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) -{ - struct psb_fbdev *fbdev = info->par; - struct psb_framebuffer *psbfb = &fbdev->pfb; - struct drm_device *dev = psbfb->base.dev; - - /* - * We have to poke our nose in here. The core fb code assumes - * panning is part of the hardware that can be invoked before - * the actual fb is mapped. In our case that isn't quite true. - */ - if (psbfb->gtt->npage) - psb_gtt_roll(dev, psbfb->gtt, var->yoffset); - return 0; -} - -void psbfb_suspend(struct drm_device *dev) -{ - struct drm_framebuffer *fb = 0; - struct psb_framebuffer *psbfb = to_psb_fb(fb); - - console_lock(); - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(fb, &dev->mode_config.fb_list, head) { - struct fb_info *info = psbfb->fbdev; - fb_set_suspend(info, 1); - drm_fb_helper_blank(FB_BLANK_POWERDOWN, info); - } - mutex_unlock(&dev->mode_config.mutex); - console_unlock(); -} - -void psbfb_resume(struct drm_device *dev) -{ - struct drm_framebuffer *fb = 0; - struct psb_framebuffer *psbfb = to_psb_fb(fb); - - console_lock(); - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(fb, &dev->mode_config.fb_list, head) { - struct fb_info *info = psbfb->fbdev; - fb_set_suspend(info, 0); - drm_fb_helper_blank(FB_BLANK_UNBLANK, info); - } - mutex_unlock(&dev->mode_config.mutex); - console_unlock(); - drm_helper_disable_unused_functions(dev); -} - -static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct psb_framebuffer *psbfb = vma->vm_private_data; - struct drm_device *dev = psbfb->base.dev; - struct drm_psb_private *dev_priv = dev->dev_private; - int page_num; - int i; - unsigned long address; - int ret; - unsigned long pfn; - /* FIXME: assumes fb at stolen base which may not be true */ - unsigned long phys_addr = (unsigned long)dev_priv->stolen_base; - - page_num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - address = (unsigned long)vmf->virtual_address; - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - for (i = 0; i < page_num; i++) { - pfn = (phys_addr >> PAGE_SHIFT); - - ret = vm_insert_mixed(vma, address, pfn); - if (unlikely((ret == -EBUSY) || (ret != 0 && i > 0))) - break; - else if (unlikely(ret != 0)) { - ret = (ret == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS; - return ret; - } - address += PAGE_SIZE; - phys_addr += PAGE_SIZE; - } - return VM_FAULT_NOPAGE; -} - -static void psbfb_vm_open(struct vm_area_struct *vma) -{ -} - -static void psbfb_vm_close(struct vm_area_struct *vma) -{ -} - -static struct vm_operations_struct psbfb_vm_ops = { - .fault = psbfb_vm_fault, - .open = psbfb_vm_open, - .close = psbfb_vm_close -}; - -static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma) -{ - struct psb_fbdev *fbdev = info->par; - struct psb_framebuffer *psbfb = &fbdev->pfb; - - if (vma->vm_pgoff != 0) - return -EINVAL; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - - if (!psbfb->addr_space) - psbfb->addr_space = vma->vm_file->f_mapping; - /* - * If this is a GEM object then info->screen_base is the virtual - * kernel remapping of the object. FIXME: Review if this is - * suitable for our mmap work - */ - vma->vm_ops = &psbfb_vm_ops; - vma->vm_private_data = (void *)psbfb; - vma->vm_flags |= VM_RESERVED | VM_IO | - VM_MIXEDMAP | VM_DONTEXPAND; - return 0; -} - -static int psbfb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - return -ENOTTY; -} - -static struct fb_ops psbfb_ops = { - .owner = THIS_MODULE, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_blank = drm_fb_helper_blank, - .fb_setcolreg = psbfb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = psbfb_copyarea, - .fb_imageblit = cfb_imageblit, - .fb_mmap = psbfb_mmap, - .fb_sync = psbfb_sync, - .fb_ioctl = psbfb_ioctl, -}; - -static struct fb_ops psbfb_roll_ops = { - .owner = THIS_MODULE, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_blank = drm_fb_helper_blank, - .fb_setcolreg = psbfb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, - .fb_pan_display = psbfb_pan, - .fb_mmap = psbfb_mmap, - .fb_sync = psbfb_sync, - .fb_ioctl = psbfb_ioctl, -}; - -static struct fb_ops psbfb_unaccel_ops = { - .owner = THIS_MODULE, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_blank = drm_fb_helper_blank, - .fb_setcolreg = psbfb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, - .fb_mmap = psbfb_mmap, - .fb_ioctl = psbfb_ioctl, -}; - -/** - * psb_framebuffer_init - initialize a framebuffer - * @dev: our DRM device - * @fb: framebuffer to set up - * @mode_cmd: mode description - * @gt: backing object - * - * Configure and fill in the boilerplate for our frame buffer. Return - * 0 on success or an error code if we fail. - */ -static int psb_framebuffer_init(struct drm_device *dev, - struct psb_framebuffer *fb, - struct drm_mode_fb_cmd2 *mode_cmd, - struct gtt_range *gt) -{ - u32 bpp, depth; - int ret; - - drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); - - if (mode_cmd->pitches[0] & 63) - return -EINVAL; - switch (bpp) { - case 8: - case 16: - case 24: - case 32: - break; - default: - return -EINVAL; - } - ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); - if (ret) { - dev_err(dev->dev, "framebuffer init failed: %d\n", ret); - return ret; - } - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); - fb->gtt = gt; - return 0; -} - -/** - * psb_framebuffer_create - create a framebuffer backed by gt - * @dev: our DRM device - * @mode_cmd: the description of the requested mode - * @gt: the backing object - * - * Create a framebuffer object backed by the gt, and fill in the - * boilerplate required - * - * TODO: review object references - */ - -static struct drm_framebuffer *psb_framebuffer_create - (struct drm_device *dev, - struct drm_mode_fb_cmd2 *mode_cmd, - struct gtt_range *gt) -{ - struct psb_framebuffer *fb; - int ret; - - fb = kzalloc(sizeof(*fb), GFP_KERNEL); - if (!fb) - return ERR_PTR(-ENOMEM); - - ret = psb_framebuffer_init(dev, fb, mode_cmd, gt); - if (ret) { - kfree(fb); - return ERR_PTR(ret); - } - return &fb->base; -} - -/** - * psbfb_alloc - allocate frame buffer memory - * @dev: the DRM device - * @aligned_size: space needed - * @force: fall back to GEM buffers if need be - * - * Allocate the frame buffer. In the usual case we get a GTT range that - * is stolen memory backed and life is simple. If there isn't sufficient - * stolen memory or the system has no stolen memory we allocate a range - * and back it with a GEM object. - * - * In this case the GEM object has no handle. - */ -static struct gtt_range *psbfb_alloc(struct drm_device *dev, - int aligned_size, int force) -{ - struct gtt_range *backing; - /* Begin by trying to use stolen memory backing */ - backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1); - if (backing) { - if (drm_gem_private_object_init(dev, - &backing->gem, aligned_size) == 0) - return backing; - psb_gtt_free_range(dev, backing); - } - if (!force) - return NULL; - - /* Next try using GEM host memory */ - backing = psb_gtt_alloc_range(dev, aligned_size, "fb(gem)", 0); - if (backing == NULL) - return NULL; - - /* Now back it with an object */ - if (drm_gem_object_init(dev, &backing->gem, aligned_size) != 0) { - psb_gtt_free_range(dev, backing); - return NULL; - } - return backing; -} - -/** - * psbfb_create - create a framebuffer - * @fbdev: the framebuffer device - * @sizes: specification of the layout - * - * Create a framebuffer to the specifications provided - */ -static int psbfb_create(struct psb_fbdev *fbdev, - struct drm_fb_helper_surface_size *sizes) -{ - struct drm_device *dev = fbdev->psb_fb_helper.dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct fb_info *info; - struct drm_framebuffer *fb; - struct psb_framebuffer *psbfb = &fbdev->pfb; - struct drm_mode_fb_cmd2 mode_cmd; - struct device *device = &dev->pdev->dev; - int size; - int ret; - struct gtt_range *backing; - int gtt_roll = 1; - u32 bpp, depth; - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - bpp = sizes->surface_bpp; - - /* No 24bit packed */ - if (bpp == 24) - bpp = 32; - - /* Acceleration via the GTT requires pitch to be 4096 byte aligned - (ie 1024 or 2048 pixels in normal use) */ - mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096); - depth = sizes->surface_depth; - - size = mode_cmd.pitches[0] * mode_cmd.height; - size = ALIGN(size, PAGE_SIZE); - - /* Allocate the framebuffer in the GTT with stolen page backing */ - backing = psbfb_alloc(dev, size, 0); - if (backing == NULL) { - /* - * We couldn't get the space we wanted, fall back to the - * display engine requirement instead. The HW requires - * the pitch to be 64 byte aligned - */ - - gtt_roll = 0; /* Don't use GTT accelerated scrolling */ - - mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64); - depth = sizes->surface_depth; - - size = mode_cmd.pitches[0] * mode_cmd.height; - size = ALIGN(size, PAGE_SIZE); - - /* Allocate the framebuffer in the GTT with stolen page - backing when there is room */ - backing = psbfb_alloc(dev, size, 1); - if (backing == NULL) - return -ENOMEM; - } - - mutex_lock(&dev->struct_mutex); - - info = framebuffer_alloc(0, device); - if (!info) { - ret = -ENOMEM; - goto out_err1; - } - info->par = fbdev; - - mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth); - - ret = psb_framebuffer_init(dev, psbfb, &mode_cmd, backing); - if (ret) - goto out_unref; - - fb = &psbfb->base; - psbfb->fbdev = info; - - fbdev->psb_fb_helper.fb = fb; - fbdev->psb_fb_helper.fbdev = info; - - strcpy(info->fix.id, "psbfb"); - - info->flags = FBINFO_DEFAULT; - if (gtt_roll) { /* GTT rolling seems best */ - info->fbops = &psbfb_roll_ops; - info->flags |= FBINFO_HWACCEL_YPAN; - } - else if (dev_priv->ops->accel_2d) /* 2D engine */ - info->fbops = &psbfb_ops; - else /* Software */ - info->fbops = &psbfb_unaccel_ops; - - ret = fb_alloc_cmap(&info->cmap, 256, 0); - if (ret) { - ret = -ENOMEM; - goto out_unref; - } - - info->fix.smem_start = dev->mode_config.fb_base; - info->fix.smem_len = size; - info->fix.ywrapstep = gtt_roll; - info->fix.ypanstep = gtt_roll; - - if (backing->stolen) { - /* Accessed stolen memory directly */ - info->screen_base = (char *)dev_priv->vram_addr + - backing->offset; - } else { - /* Pin the pages into the GTT and create a mapping to them */ - psb_gtt_pin(backing); - info->screen_base = vm_map_ram(backing->pages, backing->npage, - -1, PAGE_KERNEL); - if (info->screen_base == NULL) { - psb_gtt_unpin(backing); - ret = -ENOMEM; - goto out_unref; - } - psbfb->vm_map = 1; - } - info->screen_size = size; - - if (dev_priv->gtt.stolen_size) { - info->apertures = alloc_apertures(1); - if (!info->apertures) { - ret = -ENOMEM; - goto out_unref; - } - info->apertures->ranges[0].base = dev->mode_config.fb_base; - info->apertures->ranges[0].size = dev_priv->gtt.stolen_size; - } - - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); - drm_fb_helper_fill_var(info, &fbdev->psb_fb_helper, - sizes->fb_width, sizes->fb_height); - - info->fix.mmio_start = pci_resource_start(dev->pdev, 0); - info->fix.mmio_len = pci_resource_len(dev->pdev, 0); - - info->pixmap.size = 64 * 1024; - info->pixmap.buf_align = 8; - info->pixmap.access_align = 32; - info->pixmap.flags = FB_PIXMAP_SYSTEM; - info->pixmap.scan_align = 1; - - dev_info(dev->dev, "allocated %dx%d fb\n", - psbfb->base.width, psbfb->base.height); - - mutex_unlock(&dev->struct_mutex); - return 0; -out_unref: - if (backing->stolen) - psb_gtt_free_range(dev, backing); - else { - if (psbfb->vm_map) - vm_unmap_ram(info->screen_base, backing->npage); - drm_gem_object_unreference(&backing->gem); - } -out_err1: - mutex_unlock(&dev->struct_mutex); - psb_gtt_free_range(dev, backing); - return ret; -} - -/** - * psb_user_framebuffer_create - create framebuffer - * @dev: our DRM device - * @filp: client file - * @cmd: mode request - * - * Create a new framebuffer backed by a userspace GEM object - */ -static struct drm_framebuffer *psb_user_framebuffer_create - (struct drm_device *dev, struct drm_file *filp, - struct drm_mode_fb_cmd2 *cmd) -{ - struct gtt_range *r; - struct drm_gem_object *obj; - - /* - * Find the GEM object and thus the gtt range object that is - * to back this space - */ - obj = drm_gem_object_lookup(dev, filp, cmd->handles[0]); - if (obj == NULL) - return ERR_PTR(-ENOENT); - - /* Let the core code do all the work */ - r = container_of(obj, struct gtt_range, gem); - return psb_framebuffer_create(dev, cmd, r); -} - -static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, - u16 blue, int regno) -{ -} - -static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, int regno) -{ -} - -static int psbfb_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = psbfb_create(psb_fbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - -struct drm_fb_helper_funcs psb_fb_helper_funcs = { - .gamma_set = psbfb_gamma_set, - .gamma_get = psbfb_gamma_get, - .fb_probe = psbfb_probe, -}; - -int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) -{ - struct fb_info *info; - struct psb_framebuffer *psbfb = &fbdev->pfb; - - if (fbdev->psb_fb_helper.fbdev) { - info = fbdev->psb_fb_helper.fbdev; - - /* If this is our base framebuffer then kill any virtual map - for the framebuffer layer and unpin it */ - if (psbfb->vm_map) { - vm_unmap_ram(info->screen_base, psbfb->gtt->npage); - psb_gtt_unpin(psbfb->gtt); - } - unregister_framebuffer(info); - if (info->cmap.len) - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } - drm_fb_helper_fini(&fbdev->psb_fb_helper); - drm_framebuffer_cleanup(&psbfb->base); - - if (psbfb->gtt) - drm_gem_object_unreference(&psbfb->gtt->gem); - return 0; -} - -int psb_fbdev_init(struct drm_device *dev) -{ - struct psb_fbdev *fbdev; - struct drm_psb_private *dev_priv = dev->dev_private; - - fbdev = kzalloc(sizeof(struct psb_fbdev), GFP_KERNEL); - if (!fbdev) { - dev_err(dev->dev, "no memory\n"); - return -ENOMEM; - } - - dev_priv->fbdev = fbdev; - fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs; - - drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs, - INTELFB_CONN_LIMIT); - - drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); - drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); - return 0; -} - -void psb_fbdev_fini(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!dev_priv->fbdev) - return; - - psb_fbdev_destroy(dev, dev_priv->fbdev); - kfree(dev_priv->fbdev); - dev_priv->fbdev = NULL; -} - -static void psbfb_output_poll_changed(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = (struct psb_fbdev *)dev_priv->fbdev; - drm_fb_helper_hotplug_event(&fbdev->psb_fb_helper); -} - -/** - * psb_user_framebuffer_create_handle - add hamdle to a framebuffer - * @fb: framebuffer - * @file_priv: our DRM file - * @handle: returned handle - * - * Our framebuffer object is a GTT range which also contains a GEM - * object. We need to turn it into a handle for userspace. GEM will do - * the work for us - */ -static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - struct psb_framebuffer *psbfb = to_psb_fb(fb); - struct gtt_range *r = psbfb->gtt; - return drm_gem_handle_create(file_priv, &r->gem, handle); -} - -/** - * psb_user_framebuffer_destroy - destruct user created fb - * @fb: framebuffer - * - * User framebuffers are backed by GEM objects so all we have to do is - * clean up a bit and drop the reference, GEM will handle the fallout - */ -static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) -{ - struct psb_framebuffer *psbfb = to_psb_fb(fb); - struct gtt_range *r = psbfb->gtt; - struct drm_device *dev = fb->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = dev_priv->fbdev; - struct drm_crtc *crtc; - int reset = 0; - - /* Should never get stolen memory for a user fb */ - WARN_ON(r->stolen); - - /* Check if we are erroneously live */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - if (crtc->fb == fb) - reset = 1; - - if (reset) - /* - * Now force a sane response before we permit the DRM CRTC - * layer to do stupid things like blank the display. Instead - * we reset this framebuffer as if the user had forced a reset. - * We must do this before the cleanup so that the DRM layer - * doesn't get a chance to stick its oar in where it isn't - * wanted. - */ - drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); - - /* Let DRM do its clean up */ - drm_framebuffer_cleanup(fb); - /* We are no longer using the resource in GEM */ - drm_gem_object_unreference_unlocked(&r->gem); - kfree(fb); -} - -static const struct drm_mode_config_funcs psb_mode_funcs = { - .fb_create = psb_user_framebuffer_create, - .output_poll_changed = psbfb_output_poll_changed, -}; - -static int psb_create_backlight_property(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_property *backlight; - - if (dev_priv->backlight_property) - return 0; - - backlight = drm_property_create(dev, DRM_MODE_PROP_RANGE, - "backlight", 2); - backlight->values[0] = 0; - backlight->values[1] = 100; - - dev_priv->backlight_property = backlight; - - return 0; -} - -static void psb_setup_outputs(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_connector *connector; - - drm_mode_create_scaling_mode_property(dev); - psb_create_backlight_property(dev); - - dev_priv->ops->output_init(dev); - - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct drm_encoder *encoder = &psb_intel_output->enc; - int crtc_mask = 0, clone_mask = 0; - - /* valid crtcs */ - switch (psb_intel_output->type) { - case INTEL_OUTPUT_ANALOG: - crtc_mask = (1 << 0); - clone_mask = (1 << INTEL_OUTPUT_ANALOG); - break; - case INTEL_OUTPUT_SDVO: - crtc_mask = ((1 << 0) | (1 << 1)); - clone_mask = (1 << INTEL_OUTPUT_SDVO); - break; - case INTEL_OUTPUT_LVDS: - if (IS_MRST(dev)) - crtc_mask = (1 << 0); - else - crtc_mask = (1 << 1); - clone_mask = (1 << INTEL_OUTPUT_LVDS); - break; - case INTEL_OUTPUT_MIPI: - crtc_mask = (1 << 0); - clone_mask = (1 << INTEL_OUTPUT_MIPI); - break; - case INTEL_OUTPUT_MIPI2: - crtc_mask = (1 << 2); - clone_mask = (1 << INTEL_OUTPUT_MIPI2); - break; - case INTEL_OUTPUT_HDMI: - /* HDMI on crtc 1 for SoC devices and crtc 0 for - Cedarview. HDMI on Poulsbo is only via external - logic */ - if (IS_MFLD(dev) || IS_MRST(dev)) - crtc_mask = (1 << 1); - else - crtc_mask = (1 << 0); /* Cedarview */ - clone_mask = (1 << INTEL_OUTPUT_HDMI); - break; - } - encoder->possible_crtcs = crtc_mask; - encoder->possible_clones = - psb_intel_connector_clones(dev, clone_mask); - } -} - -void psb_modeset_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; - int i; - - drm_mode_config_init(dev); - - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - - dev->mode_config.funcs = (void *) &psb_mode_funcs; - - /* set memory base */ - /* MRST and PSB should use BAR 2*/ - pci_read_config_dword(dev->pdev, PSB_BSM, (u32 *) - &(dev->mode_config.fb_base)); - - /* num pipes is 2 for PSB but 1 for Mrst */ - for (i = 0; i < dev_priv->num_pipe; i++) - psb_intel_crtc_init(dev, i, mode_dev); - - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; - - psb_setup_outputs(dev); -} - -void psb_modeset_cleanup(struct drm_device *dev) -{ - mutex_lock(&dev->struct_mutex); - - drm_kms_helper_poll_fini(dev); - psb_fbdev_fini(dev); - drm_mode_config_cleanup(dev); - - mutex_unlock(&dev->struct_mutex); -} diff --git a/drivers/staging/gma500/framebuffer.h b/drivers/staging/gma500/framebuffer.h deleted file mode 100644 index d1b2289..0000000 --- a/drivers/staging/gma500/framebuffer.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2008-2011, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - * - */ - -#ifndef _FRAMEBUFFER_H_ -#define _FRAMEBUFFER_H_ - -#include -#include - -#include "psb_drv.h" - -struct psb_framebuffer { - struct drm_framebuffer base; - struct address_space *addr_space; - struct fb_info *fbdev; - struct gtt_range *gtt; - bool vm_map; /* True if we must undo a vm_map_ram */ -}; - -struct psb_fbdev { - struct drm_fb_helper psb_fb_helper; - struct psb_framebuffer pfb; -}; - -#define to_psb_fb(x) container_of(x, struct psb_framebuffer, base) - -extern int psb_intel_connector_clones(struct drm_device *dev, int type_mask); - -#endif - diff --git a/drivers/staging/gma500/gem.c b/drivers/staging/gma500/gem.c deleted file mode 100644 index f6433c0..0000000 --- a/drivers/staging/gma500/gem.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * psb GEM interface - * - * Copyright (c) 2011, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: Alan Cox - * - * TODO: - * - we need to work out if the MMU is relevant (eg for - * accelerated operations on a GEM object) - */ - -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" - -int psb_gem_init_object(struct drm_gem_object *obj) -{ - return -EINVAL; -} - -void psb_gem_free_object(struct drm_gem_object *obj) -{ - struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); - drm_gem_object_release_wrap(obj); - /* This must occur last as it frees up the memory of the GEM object */ - psb_gtt_free_range(obj->dev, gtt); -} - -int psb_gem_get_aperture(struct drm_device *dev, void *data, - struct drm_file *file) -{ - return -EINVAL; -} - -/** - * psb_gem_dumb_map_gtt - buffer mapping for dumb interface - * @file: our drm client file - * @dev: drm device - * @handle: GEM handle to the object (from dumb_create) - * - * Do the necessary setup to allow the mapping of the frame buffer - * into user memory. We don't have to do much here at the moment. - */ -int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset) -{ - int ret = 0; - struct drm_gem_object *obj; - - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - - mutex_lock(&dev->struct_mutex); - - /* GEM does all our handle to object mapping */ - obj = drm_gem_object_lookup(dev, file, handle); - if (obj == NULL) { - ret = -ENOENT; - goto unlock; - } - /* What validation is needed here ? */ - - /* Make it mmapable */ - if (!obj->map_list.map) { - ret = gem_create_mmap_offset(obj); - if (ret) - goto out; - } - /* GEM should really work out the hash offsets for us */ - *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; -out: - drm_gem_object_unreference(obj); -unlock: - mutex_unlock(&dev->struct_mutex); - return ret; -} - -/** - * psb_gem_create - create a mappable object - * @file: the DRM file of the client - * @dev: our device - * @size: the size requested - * @handlep: returned handle (opaque number) - * - * Create a GEM object, fill in the boilerplate and attach a handle to - * it so that userspace can speak about it. This does the core work - * for the various methods that do/will create GEM objects for things - */ -static int psb_gem_create(struct drm_file *file, - struct drm_device *dev, uint64_t size, uint32_t *handlep) -{ - struct gtt_range *r; - int ret; - u32 handle; - - size = roundup(size, PAGE_SIZE); - - /* Allocate our object - for now a direct gtt range which is not - stolen memory backed */ - r = psb_gtt_alloc_range(dev, size, "gem", 0); - if (r == NULL) { - dev_err(dev->dev, "no memory for %lld byte GEM object\n", size); - return -ENOSPC; - } - /* Initialize the extra goodies GEM needs to do all the hard work */ - if (drm_gem_object_init(dev, &r->gem, size) != 0) { - psb_gtt_free_range(dev, r); - /* GEM doesn't give an error code so use -ENOMEM */ - dev_err(dev->dev, "GEM init failed for %lld\n", size); - return -ENOMEM; - } - /* Give the object a handle so we can carry it more easily */ - ret = drm_gem_handle_create(file, &r->gem, &handle); - if (ret) { - dev_err(dev->dev, "GEM handle failed for %p, %lld\n", - &r->gem, size); - drm_gem_object_release(&r->gem); - psb_gtt_free_range(dev, r); - return ret; - } - /* We have the initial and handle reference but need only one now */ - drm_gem_object_unreference(&r->gem); - *handlep = handle; - return 0; -} - -/** - * psb_gem_dumb_create - create a dumb buffer - * @drm_file: our client file - * @dev: our device - * @args: the requested arguments copied from userspace - * - * Allocate a buffer suitable for use for a frame buffer of the - * form described by user space. Give userspace a handle by which - * to reference it. - */ -int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, - struct drm_mode_create_dumb *args) -{ - args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64); - args->size = args->pitch * args->height; - return psb_gem_create(file, dev, args->size, &args->handle); -} - -/** - * psb_gem_dumb_destroy - destroy a dumb buffer - * @file: client file - * @dev: our DRM device - * @handle: the object handle - * - * Destroy a handle that was created via psb_gem_dumb_create, at least - * we hope it was created that way. i915 seems to assume the caller - * does the checking but that might be worth review ! FIXME - */ -int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle) -{ - /* No special work needed, drop the reference and see what falls out */ - return drm_gem_handle_delete(file, handle); -} - -/** - * psb_gem_fault - pagefault handler for GEM objects - * @vma: the VMA of the GEM object - * @vmf: fault detail - * - * Invoked when a fault occurs on an mmap of a GEM managed area. GEM - * does most of the work for us including the actual map/unmap calls - * but we need to do the actual page work. - * - * This code eventually needs to handle faulting objects in and out - * of the GTT and repacking it when we run out of space. We can put - * that off for now and for our simple uses - * - * The VMA was set up by GEM. In doing so it also ensured that the - * vma->vm_private_data points to the GEM object that is backing this - * mapping. - */ -int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct drm_gem_object *obj; - struct gtt_range *r; - int ret; - unsigned long pfn; - pgoff_t page_offset; - struct drm_device *dev; - struct drm_psb_private *dev_priv; - - obj = vma->vm_private_data; /* GEM object */ - dev = obj->dev; - dev_priv = dev->dev_private; - - r = container_of(obj, struct gtt_range, gem); /* Get the gtt range */ - - /* Make sure we don't parallel update on a fault, nor move or remove - something from beneath our feet */ - mutex_lock(&dev->struct_mutex); - - /* For now the mmap pins the object and it stays pinned. As things - stand that will do us no harm */ - if (r->mmapping == 0) { - ret = psb_gtt_pin(r); - if (ret < 0) { - dev_err(dev->dev, "gma500: pin failed: %d\n", ret); - goto fail; - } - r->mmapping = 1; - } - - /* Page relative to the VMA start - we must calculate this ourselves - because vmf->pgoff is the fake GEM offset */ - page_offset = ((unsigned long) vmf->virtual_address - vma->vm_start) - >> PAGE_SHIFT; - - /* CPU view of the page, don't go via the GART for CPU writes */ - if (r->stolen) - pfn = (dev_priv->stolen_base + r->offset) >> PAGE_SHIFT; - else - pfn = page_to_pfn(r->pages[page_offset]); - ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); - -fail: - mutex_unlock(&dev->struct_mutex); - switch (ret) { - case 0: - case -ERESTARTSYS: - case -EINTR: - return VM_FAULT_NOPAGE; - case -ENOMEM: - return VM_FAULT_OOM; - default: - return VM_FAULT_SIGBUS; - } -} - -static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev, - int size, u32 *handle) -{ - struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1); - if (gtt == NULL) - return -ENOMEM; - if (drm_gem_private_object_init(dev, >t->gem, size) != 0) - goto free_gtt; - if (drm_gem_handle_create(file, >t->gem, handle) == 0) - return 0; -free_gtt: - psb_gtt_free_range(dev, gtt); - return -ENOMEM; -} - -/* - * GEM interfaces for our specific client - */ -int psb_gem_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) -{ - struct drm_psb_gem_create *args = data; - int ret; - if (args->flags & PSB_GEM_CREATE_STOLEN) { - ret = psb_gem_create_stolen(file, dev, args->size, - &args->handle); - if (ret == 0) - return 0; - /* Fall throguh */ - args->flags &= ~PSB_GEM_CREATE_STOLEN; - } - return psb_gem_create(file, dev, args->size, &args->handle); -} - -int psb_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) -{ - struct drm_psb_gem_mmap *args = data; - return dev->driver->dumb_map_offset(file, dev, - args->handle, &args->offset); -} - diff --git a/drivers/staging/gma500/gem_glue.c b/drivers/staging/gma500/gem_glue.c deleted file mode 100644 index daac121..0000000 --- a/drivers/staging/gma500/gem_glue.c +++ /dev/null @@ -1,89 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include - -void drm_gem_object_release_wrap(struct drm_gem_object *obj) -{ - /* Remove the list map if one is present */ - if (obj->map_list.map) { - struct drm_gem_mm *mm = obj->dev->mm_private; - struct drm_map_list *list = &obj->map_list; - drm_ht_remove_item(&mm->offset_hash, &list->hash); - drm_mm_put_block(list->file_offset_node); - kfree(list->map); - list->map = NULL; - } - drm_gem_object_release(obj); -} - -/** - * gem_create_mmap_offset - invent an mmap offset - * @obj: our object - * - * Standard implementation of offset generation for mmap as is - * duplicated in several drivers. This belongs in GEM. - */ -int gem_create_mmap_offset(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_local_map *map; - int ret; - - list = &obj->map_list; - list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); - if (list->map == NULL) - return -ENOMEM; - map = list->map; - map->type = _DRM_GEM; - map->size = obj->size; - map->handle = obj; - - list->file_offset_node = drm_mm_search_free(&mm->offset_manager, - obj->size / PAGE_SIZE, 0, 0); - if (!list->file_offset_node) { - dev_err(dev->dev, "failed to allocate offset for bo %d\n", - obj->name); - ret = -ENOSPC; - goto free_it; - } - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - obj->size / PAGE_SIZE, 0); - if (!list->file_offset_node) { - ret = -ENOMEM; - goto free_it; - } - list->hash.key = list->file_offset_node->start; - ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); - if (ret) { - dev_err(dev->dev, "failed to add to map hash\n"); - goto free_mm; - } - return 0; - -free_mm: - drm_mm_put_block(list->file_offset_node); -free_it: - kfree(list->map); - list->map = NULL; - return ret; -} diff --git a/drivers/staging/gma500/gem_glue.h b/drivers/staging/gma500/gem_glue.h deleted file mode 100644 index ce5ce30..0000000 --- a/drivers/staging/gma500/gem_glue.h +++ /dev/null @@ -1,2 +0,0 @@ -extern void drm_gem_object_release_wrap(struct drm_gem_object *obj); -extern int gem_create_mmap_offset(struct drm_gem_object *obj); diff --git a/drivers/staging/gma500/gtt.c b/drivers/staging/gma500/gtt.c deleted file mode 100644 index e770bd1..0000000 --- a/drivers/staging/gma500/gtt.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * Copyright (c) 2007, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: Thomas Hellstrom - * Alan Cox - */ - -#include -#include "psb_drv.h" - - -/* - * GTT resource allocator - manage page mappings in GTT space - */ - -/** - * psb_gtt_mask_pte - generate GTT pte entry - * @pfn: page number to encode - * @type: type of memory in the GTT - * - * Set the GTT entry for the appropriate memory type. - */ -static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) -{ - uint32_t mask = PSB_PTE_VALID; - - if (type & PSB_MMU_CACHED_MEMORY) - mask |= PSB_PTE_CACHED; - if (type & PSB_MMU_RO_MEMORY) - mask |= PSB_PTE_RO; - if (type & PSB_MMU_WO_MEMORY) - mask |= PSB_PTE_WO; - - return (pfn << PAGE_SHIFT) | mask; -} - -/** - * psb_gtt_entry - find the GTT entries for a gtt_range - * @dev: our DRM device - * @r: our GTT range - * - * Given a gtt_range object return the GTT offset of the page table - * entries for this gtt_range - */ -u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long offset; - - offset = r->resource.start - dev_priv->gtt_mem->start; - - return dev_priv->gtt_map + (offset >> PAGE_SHIFT); -} - -/** - * psb_gtt_insert - put an object into the GTT - * @dev: our DRM device - * @r: our GTT range - * - * Take our preallocated GTT range and insert the GEM object into - * the GTT. This is protected via the gtt mutex which the caller - * must hold. - */ -static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) -{ - u32 *gtt_slot, pte; - struct page **pages; - int i; - - if (r->pages == NULL) { - WARN_ON(1); - return -EINVAL; - } - - WARN_ON(r->stolen); /* refcount these maybe ? */ - - gtt_slot = psb_gtt_entry(dev, r); - pages = r->pages; - - /* Make sure changes are visible to the GPU */ - set_pages_array_uc(pages, r->npage); - - /* Write our page entries into the GTT itself */ - for (i = r->roll; i < r->npage; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); - iowrite32(pte, gtt_slot++); - } - for (i = 0; i < r->roll; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); - iowrite32(pte, gtt_slot++); - } - /* Make sure all the entries are set before we return */ - ioread32(gtt_slot - 1); - - return 0; -} - -/** - * psb_gtt_remove - remove an object from the GTT - * @dev: our DRM device - * @r: our GTT range - * - * Remove a preallocated GTT range from the GTT. Overwrite all the - * page table entries with the dummy page. This is protected via the gtt - * mutex which the caller must hold. - */ -static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 *gtt_slot, pte; - int i; - - WARN_ON(r->stolen); - - gtt_slot = psb_gtt_entry(dev, r); - pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0); - - for (i = 0; i < r->npage; i++) - iowrite32(pte, gtt_slot++); - ioread32(gtt_slot - 1); - set_pages_array_wb(r->pages, r->npage); -} - -/** - * psb_gtt_roll - set scrolling position - * @dev: our DRM device - * @r: the gtt mapping we are using - * @roll: roll offset - * - * Roll an existing pinned mapping by moving the pages through the GTT. - * This allows us to implement hardware scrolling on the consoles without - * a 2D engine - */ -void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) -{ - u32 *gtt_slot, pte; - int i; - - if (roll >= r->npage) { - WARN_ON(1); - return; - } - - r->roll = roll; - - /* Not currently in the GTT - no worry we will write the mapping at - the right position when it gets pinned */ - if (!r->stolen && !r->in_gart) - return; - - gtt_slot = psb_gtt_entry(dev, r); - - for (i = r->roll; i < r->npage; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); - iowrite32(pte, gtt_slot++); - } - for (i = 0; i < r->roll; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); - iowrite32(pte, gtt_slot++); - } - ioread32(gtt_slot - 1); -} - -/** - * psb_gtt_attach_pages - attach and pin GEM pages - * @gt: the gtt range - * - * Pin and build an in kernel list of the pages that back our GEM object. - * While we hold this the pages cannot be swapped out. This is protected - * via the gtt mutex which the caller must hold. - */ -static int psb_gtt_attach_pages(struct gtt_range *gt) -{ - struct inode *inode; - struct address_space *mapping; - int i; - struct page *p; - int pages = gt->gem.size / PAGE_SIZE; - - WARN_ON(gt->pages); - - /* This is the shared memory object that backs the GEM resource */ - inode = gt->gem.filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - - gt->pages = kmalloc(pages * sizeof(struct page *), GFP_KERNEL); - if (gt->pages == NULL) - return -ENOMEM; - gt->npage = pages; - - for (i = 0; i < pages; i++) { - /* FIXME: needs updating as per mail from Hugh Dickins */ - p = read_cache_page_gfp(mapping, i, - __GFP_COLD | GFP_KERNEL); - if (IS_ERR(p)) - goto err; - gt->pages[i] = p; - } - return 0; - -err: - while (i--) - page_cache_release(gt->pages[i]); - kfree(gt->pages); - gt->pages = NULL; - return PTR_ERR(p); -} - -/** - * psb_gtt_detach_pages - attach and pin GEM pages - * @gt: the gtt range - * - * Undo the effect of psb_gtt_attach_pages. At this point the pages - * must have been removed from the GTT as they could now be paged out - * and move bus address. This is protected via the gtt mutex which the - * caller must hold. - */ -static void psb_gtt_detach_pages(struct gtt_range *gt) -{ - int i; - for (i = 0; i < gt->npage; i++) { - /* FIXME: do we need to force dirty */ - set_page_dirty(gt->pages[i]); - page_cache_release(gt->pages[i]); - } - kfree(gt->pages); - gt->pages = NULL; -} - -/** - * psb_gtt_pin - pin pages into the GTT - * @gt: range to pin - * - * Pin a set of pages into the GTT. The pins are refcounted so that - * multiple pins need multiple unpins to undo. - * - * Non GEM backed objects treat this as a no-op as they are always GTT - * backed objects. - */ -int psb_gtt_pin(struct gtt_range *gt) -{ - int ret = 0; - struct drm_device *dev = gt->gem.dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - mutex_lock(&dev_priv->gtt_mutex); - - if (gt->in_gart == 0 && gt->stolen == 0) { - ret = psb_gtt_attach_pages(gt); - if (ret < 0) - goto out; - ret = psb_gtt_insert(dev, gt); - if (ret < 0) { - psb_gtt_detach_pages(gt); - goto out; - } - } - gt->in_gart++; -out: - mutex_unlock(&dev_priv->gtt_mutex); - return ret; -} - -/** - * psb_gtt_unpin - Drop a GTT pin requirement - * @gt: range to pin - * - * Undoes the effect of psb_gtt_pin. On the last drop the GEM object - * will be removed from the GTT which will also drop the page references - * and allow the VM to clean up or page stuff. - * - * Non GEM backed objects treat this as a no-op as they are always GTT - * backed objects. - */ -void psb_gtt_unpin(struct gtt_range *gt) -{ - struct drm_device *dev = gt->gem.dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - mutex_lock(&dev_priv->gtt_mutex); - - WARN_ON(!gt->in_gart); - - gt->in_gart--; - if (gt->in_gart == 0 && gt->stolen == 0) { - psb_gtt_remove(dev, gt); - psb_gtt_detach_pages(gt); - } - mutex_unlock(&dev_priv->gtt_mutex); -} - -/* - * GTT resource allocator - allocate and manage GTT address space - */ - -/** - * psb_gtt_alloc_range - allocate GTT address space - * @dev: Our DRM device - * @len: length (bytes) of address space required - * @name: resource name - * @backed: resource should be backed by stolen pages - * - * Ask the kernel core to find us a suitable range of addresses - * to use for a GTT mapping. - * - * Returns a gtt_range structure describing the object, or NULL on - * error. On successful return the resource is both allocated and marked - * as in use. - */ -struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, - const char *name, int backed) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct gtt_range *gt; - struct resource *r = dev_priv->gtt_mem; - int ret; - unsigned long start, end; - - if (backed) { - /* The start of the GTT is the stolen pages */ - start = r->start; - end = r->start + dev_priv->gtt.stolen_size - 1; - } else { - /* The rest we will use for GEM backed objects */ - start = r->start + dev_priv->gtt.stolen_size; - end = r->end; - } - - gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL); - if (gt == NULL) - return NULL; - gt->resource.name = name; - gt->stolen = backed; - gt->in_gart = backed; - gt->roll = 0; - /* Ensure this is set for non GEM objects */ - gt->gem.dev = dev; - ret = allocate_resource(dev_priv->gtt_mem, >->resource, - len, start, end, PAGE_SIZE, NULL, NULL); - if (ret == 0) { - gt->offset = gt->resource.start - r->start; - return gt; - } - kfree(gt); - return NULL; -} - -/** - * psb_gtt_free_range - release GTT address space - * @dev: our DRM device - * @gt: a mapping created with psb_gtt_alloc_range - * - * Release a resource that was allocated with psb_gtt_alloc_range. If the - * object has been pinned by mmap users we clean this up here currently. - */ -void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt) -{ - /* Undo the mmap pin if we are destroying the object */ - if (gt->mmapping) { - psb_gtt_unpin(gt); - gt->mmapping = 0; - } - WARN_ON(gt->in_gart && !gt->stolen); - release_resource(>->resource); - kfree(gt); -} - -void psb_gtt_alloc(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - init_rwsem(&dev_priv->gtt.sem); -} - -void psb_gtt_takedown(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - if (dev_priv->gtt_map) { - iounmap(dev_priv->gtt_map); - dev_priv->gtt_map = NULL; - } - if (dev_priv->gtt_initialized) { - pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, - dev_priv->gmch_ctrl); - PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); - (void) PSB_RVDC32(PSB_PGETBL_CTL); - } - if (dev_priv->vram_addr) - iounmap(dev_priv->gtt_map); -} - -int psb_gtt_init(struct drm_device *dev, int resume) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned gtt_pages; - unsigned long stolen_size, vram_stolen_size; - unsigned i, num_pages; - unsigned pfn_base; - uint32_t vram_pages; - uint32_t dvmt_mode = 0; - struct psb_gtt *pg; - - int ret = 0; - uint32_t pte; - - mutex_init(&dev_priv->gtt_mutex); - - psb_gtt_alloc(dev); - pg = &dev_priv->gtt; - - /* Enable the GTT */ - pci_read_config_word(dev->pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); - pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, - dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); - - dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); - PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); - (void) PSB_RVDC32(PSB_PGETBL_CTL); - - /* The root resource we allocate address space from */ - dev_priv->gtt_initialized = 1; - - pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; - - /* - * The video mmu has a hw bug when accessing 0x0D0000000. - * Make gatt start at 0x0e000,0000. This doesn't actually - * matter for us but may do if the video acceleration ever - * gets opened up. - */ - pg->mmu_gatt_start = 0xE0000000; - - pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE); - gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE) - >> PAGE_SHIFT; - /* Some CDV firmware doesn't report this currently. In which case the - system has 64 gtt pages */ - if (pg->gtt_start == 0 || gtt_pages == 0) { - dev_err(dev->dev, "GTT PCI BAR not initialized.\n"); - gtt_pages = 64; - pg->gtt_start = dev_priv->pge_ctl; - } - - pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE); - pg->gatt_pages = pci_resource_len(dev->pdev, PSB_GATT_RESOURCE) - >> PAGE_SHIFT; - dev_priv->gtt_mem = &dev->pdev->resource[PSB_GATT_RESOURCE]; - - if (pg->gatt_pages == 0 || pg->gatt_start == 0) { - static struct resource fudge; /* Preferably peppermint */ - /* This can occur on CDV SDV systems. Fudge it in this case. - We really don't care what imaginary space is being allocated - at this point */ - dev_err(dev->dev, "GATT PCI BAR not initialized.\n"); - pg->gatt_start = 0x40000000; - pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; - /* This is a little confusing but in fact the GTT is providing - a view from the GPU into memory and not vice versa. As such - this is really allocating space that is not the same as the - CPU address space on CDV */ - fudge.start = 0x40000000; - fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; - fudge.name = "fudge"; - fudge.flags = IORESOURCE_MEM; - dev_priv->gtt_mem = &fudge; - } - - pci_read_config_dword(dev->pdev, PSB_BSM, &dev_priv->stolen_base); - vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - - PAGE_SIZE; - - stolen_size = vram_stolen_size; - - printk(KERN_INFO "Stolen memory information\n"); - printk(KERN_INFO " base in RAM: 0x%x\n", dev_priv->stolen_base); - printk(KERN_INFO " size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n", - vram_stolen_size/1024); - dvmt_mode = (dev_priv->gmch_ctrl >> 4) & 0x7; - printk(KERN_INFO " the correct size should be: %dM(dvmt mode=%d)\n", - (dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode); - - if (resume && (gtt_pages != pg->gtt_pages) && - (stolen_size != pg->stolen_size)) { - dev_err(dev->dev, "GTT resume error.\n"); - ret = -EINVAL; - goto out_err; - } - - pg->gtt_pages = gtt_pages; - pg->stolen_size = stolen_size; - dev_priv->vram_stolen_size = vram_stolen_size; - - /* - * Map the GTT and the stolen memory area - */ - dev_priv->gtt_map = ioremap_nocache(pg->gtt_phys_start, - gtt_pages << PAGE_SHIFT); - if (!dev_priv->gtt_map) { - dev_err(dev->dev, "Failure to map gtt.\n"); - ret = -ENOMEM; - goto out_err; - } - - dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); - if (!dev_priv->vram_addr) { - dev_err(dev->dev, "Failure to map stolen base.\n"); - ret = -ENOMEM; - goto out_err; - } - - /* - * Insert vram stolen pages into the GTT - */ - - pfn_base = dev_priv->stolen_base >> PAGE_SHIFT; - vram_pages = num_pages = vram_stolen_size >> PAGE_SHIFT; - printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", - num_pages, pfn_base << PAGE_SHIFT, 0); - for (i = 0; i < num_pages; ++i) { - pte = psb_gtt_mask_pte(pfn_base + i, 0); - iowrite32(pte, dev_priv->gtt_map + i); - } - - /* - * Init rest of GTT to the scratch page to avoid accidents or scribbles - */ - - pfn_base = page_to_pfn(dev_priv->scratch_page); - pte = psb_gtt_mask_pte(pfn_base, 0); - for (; i < gtt_pages; ++i) - iowrite32(pte, dev_priv->gtt_map + i); - - (void) ioread32(dev_priv->gtt_map + i - 1); - return 0; - -out_err: - psb_gtt_takedown(dev); - return ret; -} diff --git a/drivers/staging/gma500/gtt.h b/drivers/staging/gma500/gtt.h deleted file mode 100644 index aa17423..0000000 --- a/drivers/staging/gma500/gtt.h +++ /dev/null @@ -1,64 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2008, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#ifndef _PSB_GTT_H_ -#define _PSB_GTT_H_ - -#include - -/* This wants cleaning up with respect to the psb_dev and un-needed stuff */ -struct psb_gtt { - uint32_t gatt_start; - uint32_t mmu_gatt_start; - uint32_t gtt_start; - uint32_t gtt_phys_start; - unsigned gtt_pages; - unsigned gatt_pages; - unsigned long stolen_size; - unsigned long vram_stolen_size; - struct rw_semaphore sem; -}; - -/* Exported functions */ -extern int psb_gtt_init(struct drm_device *dev, int resume); -extern void psb_gtt_takedown(struct drm_device *dev); - -/* Each gtt_range describes an allocation in the GTT area */ -struct gtt_range { - struct resource resource; /* Resource for our allocation */ - u32 offset; /* GTT offset of our object */ - struct drm_gem_object gem; /* GEM high level stuff */ - int in_gart; /* Currently in the GART (ref ct) */ - bool stolen; /* Backed from stolen RAM */ - bool mmapping; /* Is mmappable */ - struct page **pages; /* Backing pages if present */ - int npage; /* Number of backing pages */ - int roll; /* Roll applied to the GTT entries */ -}; - -extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, - const char *name, int backed); -extern void psb_gtt_kref_put(struct gtt_range *gt); -extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt); -extern int psb_gtt_pin(struct gtt_range *gt); -extern void psb_gtt_unpin(struct gtt_range *gt); -extern void psb_gtt_roll(struct drm_device *dev, - struct gtt_range *gt, int roll); - -#endif diff --git a/drivers/staging/gma500/intel_bios.c b/drivers/staging/gma500/intel_bios.c deleted file mode 100644 index 096757f..0000000 --- a/drivers/staging/gma500/intel_bios.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2006 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - * - */ -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "intel_bios.h" - - -static void *find_section(struct bdb_header *bdb, int section_id) -{ - u8 *base = (u8 *)bdb; - int index = 0; - u16 total, current_size; - u8 current_id; - - /* skip to first section */ - index += bdb->header_size; - total = bdb->bdb_size; - - /* walk the sections looking for section_id */ - while (index < total) { - current_id = *(base + index); - index++; - current_size = *((u16 *)(base + index)); - index += 2; - if (current_id == section_id) - return base + index; - index += current_size; - } - - return NULL; -} - -static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, - struct lvds_dvo_timing *dvo_timing) -{ - panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | - dvo_timing->hactive_lo; - panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + - ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); - panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + - dvo_timing->hsync_pulse_width; - panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + - ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); - - panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | - dvo_timing->vactive_lo; - panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + - dvo_timing->vsync_off; - panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + - dvo_timing->vsync_pulse_width; - panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + - ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); - panel_fixed_mode->clock = dvo_timing->clock * 10; - panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; - - /* Some VBTs have bogus h/vtotal values */ - if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) - panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; - if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) - panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; - - drm_mode_set_name(panel_fixed_mode); -} - -static void parse_backlight_data(struct drm_psb_private *dev_priv, - struct bdb_header *bdb) -{ - struct bdb_lvds_backlight *vbt_lvds_bl = NULL; - struct bdb_lvds_backlight *lvds_bl; - u8 p_type = 0; - void *bl_start = NULL; - struct bdb_lvds_options *lvds_opts - = find_section(bdb, BDB_LVDS_OPTIONS); - - dev_priv->lvds_bl = NULL; - - if (lvds_opts) - p_type = lvds_opts->panel_type; - else - return; - - bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT); - vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type; - - lvds_bl = kzalloc(sizeof(*vbt_lvds_bl), GFP_KERNEL); - if (!lvds_bl) { - dev_err(dev_priv->dev->dev, "out of memory for backlight data\n"); - return; - } - memcpy(lvds_bl, vbt_lvds_bl, sizeof(*vbt_lvds_bl)); - dev_priv->lvds_bl = lvds_bl; -} - -/* Try to find integrated panel data */ -static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, - struct bdb_header *bdb) -{ - struct bdb_lvds_options *lvds_options; - struct bdb_lvds_lfp_data *lvds_lfp_data; - struct bdb_lvds_lfp_data_entry *entry; - struct lvds_dvo_timing *dvo_timing; - struct drm_display_mode *panel_fixed_mode; - - /* Defaults if we can't find VBT info */ - dev_priv->lvds_dither = 0; - dev_priv->lvds_vbt = 0; - - lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); - if (!lvds_options) - return; - - dev_priv->lvds_dither = lvds_options->pixel_dither; - if (lvds_options->panel_type == 0xff) - return; - - lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); - if (!lvds_lfp_data) - return; - - - entry = &lvds_lfp_data->data[lvds_options->panel_type]; - dvo_timing = &entry->dvo_timing; - - panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), - GFP_KERNEL); - if (panel_fixed_mode == NULL) { - dev_err(dev_priv->dev->dev, "out of memory for fixed panel mode\n"); - return; - } - - dev_priv->lvds_vbt = 1; - fill_detail_timing_data(panel_fixed_mode, dvo_timing); - - if (panel_fixed_mode->htotal > 0 && panel_fixed_mode->vtotal > 0) { - dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; - drm_mode_debug_printmodeline(panel_fixed_mode); - } else { - dev_dbg(dev_priv->dev->dev, "ignoring invalid LVDS VBT\n"); - dev_priv->lvds_vbt = 0; - kfree(panel_fixed_mode); - } - return; -} - -/* Try to find sdvo panel data */ -static void parse_sdvo_panel_data(struct drm_psb_private *dev_priv, - struct bdb_header *bdb) -{ - struct bdb_sdvo_lvds_options *sdvo_lvds_options; - struct lvds_dvo_timing *dvo_timing; - struct drm_display_mode *panel_fixed_mode; - - dev_priv->sdvo_lvds_vbt_mode = NULL; - - sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); - if (!sdvo_lvds_options) - return; - - dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); - if (!dvo_timing) - return; - - panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); - - if (!panel_fixed_mode) - return; - - fill_detail_timing_data(panel_fixed_mode, - dvo_timing + sdvo_lvds_options->panel_type); - - dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; - - return; -} - -static void parse_general_features(struct drm_psb_private *dev_priv, - struct bdb_header *bdb) -{ - struct bdb_general_features *general; - - /* Set sensible defaults in case we can't find the general block */ - dev_priv->int_tv_support = 1; - dev_priv->int_crt_support = 1; - - general = find_section(bdb, BDB_GENERAL_FEATURES); - if (general) { - dev_priv->int_tv_support = general->int_tv_support; - dev_priv->int_crt_support = general->int_crt_support; - dev_priv->lvds_use_ssc = general->enable_ssc; - - if (dev_priv->lvds_use_ssc) { - dev_priv->lvds_ssc_freq - = general->ssc_freq ? 100 : 96; - } - } -} - -/** - * psb_intel_init_bios - initialize VBIOS settings & find VBT - * @dev: DRM device - * - * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers - * to appropriate values. - * - * VBT existence is a sanity check that is relied on by other i830_bios.c code. - * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may - * feed an updated VBT back through that, compared to what we'll fetch using - * this method of groping around in the BIOS data. - * - * Returns 0 on success, nonzero on failure. - */ -bool psb_intel_init_bios(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct pci_dev *pdev = dev->pdev; - struct vbt_header *vbt = NULL; - struct bdb_header *bdb; - u8 __iomem *bios; - size_t size; - int i; - - bios = pci_map_rom(pdev, &size); - if (!bios) - return -1; - - /* Scour memory looking for the VBT signature */ - for (i = 0; i + 4 < size; i++) { - if (!memcmp(bios + i, "$VBT", 4)) { - vbt = (struct vbt_header *)(bios + i); - break; - } - } - - if (!vbt) { - dev_err(dev->dev, "VBT signature missing\n"); - pci_unmap_rom(pdev, bios); - return -1; - } - - bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); - - /* Grab useful general definitions */ - parse_general_features(dev_priv, bdb); - parse_lfp_panel_data(dev_priv, bdb); - parse_sdvo_panel_data(dev_priv, bdb); - parse_backlight_data(dev_priv, bdb); - - pci_unmap_rom(pdev, bios); - - return 0; -} - -/** - * Destroy and free VBT data - */ -void psb_intel_destroy_bios(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_display_mode *sdvo_lvds_vbt_mode = - dev_priv->sdvo_lvds_vbt_mode; - struct drm_display_mode *lfp_lvds_vbt_mode = - dev_priv->lfp_lvds_vbt_mode; - struct bdb_lvds_backlight *lvds_bl = - dev_priv->lvds_bl; - - /*free sdvo panel mode*/ - if (sdvo_lvds_vbt_mode) { - dev_priv->sdvo_lvds_vbt_mode = NULL; - kfree(sdvo_lvds_vbt_mode); - } - - if (lfp_lvds_vbt_mode) { - dev_priv->lfp_lvds_vbt_mode = NULL; - kfree(lfp_lvds_vbt_mode); - } - - if (lvds_bl) { - dev_priv->lvds_bl = NULL; - kfree(lvds_bl); - } -} diff --git a/drivers/staging/gma500/intel_bios.h b/drivers/staging/gma500/intel_bios.h deleted file mode 100644 index 70f1bf0..0000000 --- a/drivers/staging/gma500/intel_bios.h +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (c) 2006 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - * - */ - -#ifndef _I830_BIOS_H_ -#define _I830_BIOS_H_ - -#include - -struct vbt_header { - u8 signature[20]; /**< Always starts with 'VBT$' */ - u16 version; /**< decimal */ - u16 header_size; /**< in bytes */ - u16 vbt_size; /**< in bytes */ - u8 vbt_checksum; - u8 reserved0; - u32 bdb_offset; /**< from beginning of VBT */ - u32 aim_offset[4]; /**< from beginning of VBT */ -} __attribute__((packed)); - - -struct bdb_header { - u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */ - u16 version; /**< decimal */ - u16 header_size; /**< in bytes */ - u16 bdb_size; /**< in bytes */ -}; - -/* strictly speaking, this is a "skip" block, but it has interesting info */ -struct vbios_data { - u8 type; /* 0 == desktop, 1 == mobile */ - u8 relstage; - u8 chipset; - u8 lvds_present:1; - u8 tv_present:1; - u8 rsvd2:6; /* finish byte */ - u8 rsvd3[4]; - u8 signon[155]; - u8 copyright[61]; - u16 code_segment; - u8 dos_boot_mode; - u8 bandwidth_percent; - u8 rsvd4; /* popup memory size */ - u8 resize_pci_bios; - u8 rsvd5; /* is crt already on ddc2 */ -} __attribute__((packed)); - -/* - * There are several types of BIOS data blocks (BDBs), each block has - * an ID and size in the first 3 bytes (ID in first, size in next 2). - * Known types are listed below. - */ -#define BDB_GENERAL_FEATURES 1 -#define BDB_GENERAL_DEFINITIONS 2 -#define BDB_OLD_TOGGLE_LIST 3 -#define BDB_MODE_SUPPORT_LIST 4 -#define BDB_GENERIC_MODE_TABLE 5 -#define BDB_EXT_MMIO_REGS 6 -#define BDB_SWF_IO 7 -#define BDB_SWF_MMIO 8 -#define BDB_DOT_CLOCK_TABLE 9 -#define BDB_MODE_REMOVAL_TABLE 10 -#define BDB_CHILD_DEVICE_TABLE 11 -#define BDB_DRIVER_FEATURES 12 -#define BDB_DRIVER_PERSISTENCE 13 -#define BDB_EXT_TABLE_PTRS 14 -#define BDB_DOT_CLOCK_OVERRIDE 15 -#define BDB_DISPLAY_SELECT 16 -/* 17 rsvd */ -#define BDB_DRIVER_ROTATION 18 -#define BDB_DISPLAY_REMOVE 19 -#define BDB_OEM_CUSTOM 20 -#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */ -#define BDB_SDVO_LVDS_OPTIONS 22 -#define BDB_SDVO_PANEL_DTDS 23 -#define BDB_SDVO_LVDS_PNP_IDS 24 -#define BDB_SDVO_LVDS_POWER_SEQ 25 -#define BDB_TV_OPTIONS 26 -#define BDB_LVDS_OPTIONS 40 -#define BDB_LVDS_LFP_DATA_PTRS 41 -#define BDB_LVDS_LFP_DATA 42 -#define BDB_LVDS_BACKLIGHT 43 -#define BDB_LVDS_POWER 44 -#define BDB_SKIP 254 /* VBIOS private block, ignore */ - -struct bdb_general_features { - /* bits 1 */ - u8 panel_fitting:2; - u8 flexaim:1; - u8 msg_enable:1; - u8 clear_screen:3; - u8 color_flip:1; - - /* bits 2 */ - u8 download_ext_vbt:1; - u8 enable_ssc:1; - u8 ssc_freq:1; - u8 enable_lfp_on_override:1; - u8 disable_ssc_ddt:1; - u8 rsvd8:3; /* finish byte */ - - /* bits 3 */ - u8 disable_smooth_vision:1; - u8 single_dvi:1; - u8 rsvd9:6; /* finish byte */ - - /* bits 4 */ - u8 legacy_monitor_detect; - - /* bits 5 */ - u8 int_crt_support:1; - u8 int_tv_support:1; - u8 rsvd11:6; /* finish byte */ -} __attribute__((packed)); - -struct bdb_general_definitions { - /* DDC GPIO */ - u8 crt_ddc_gmbus_pin; - - /* DPMS bits */ - u8 dpms_acpi:1; - u8 skip_boot_crt_detect:1; - u8 dpms_aim:1; - u8 rsvd1:5; /* finish byte */ - - /* boot device bits */ - u8 boot_display[2]; - u8 child_dev_size; - - /* device info */ - u8 tv_or_lvds_info[33]; - u8 dev1[33]; - u8 dev2[33]; - u8 dev3[33]; - u8 dev4[33]; - /* may be another device block here on some platforms */ -}; - -struct bdb_lvds_options { - u8 panel_type; - u8 rsvd1; - /* LVDS capabilities, stored in a dword */ - u8 pfit_mode:2; - u8 pfit_text_mode_enhanced:1; - u8 pfit_gfx_mode_enhanced:1; - u8 pfit_ratio_auto:1; - u8 pixel_dither:1; - u8 lvds_edid:1; - u8 rsvd2:1; - u8 rsvd4; -} __attribute__((packed)); - -struct bdb_lvds_backlight { - u8 type:2; - u8 pol:1; - u8 gpio:3; - u8 gmbus:2; - u16 freq; - u8 minbrightness; - u8 i2caddr; - u8 brightnesscmd; - /*FIXME: more...*/ -} __attribute__((packed)); - -/* LFP pointer table contains entries to the struct below */ -struct bdb_lvds_lfp_data_ptr { - u16 fp_timing_offset; /* offsets are from start of bdb */ - u8 fp_table_size; - u16 dvo_timing_offset; - u8 dvo_table_size; - u16 panel_pnp_id_offset; - u8 pnp_table_size; -} __attribute__((packed)); - -struct bdb_lvds_lfp_data_ptrs { - u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */ - struct bdb_lvds_lfp_data_ptr ptr[16]; -} __attribute__((packed)); - -/* LFP data has 3 blocks per entry */ -struct lvds_fp_timing { - u16 x_res; - u16 y_res; - u32 lvds_reg; - u32 lvds_reg_val; - u32 pp_on_reg; - u32 pp_on_reg_val; - u32 pp_off_reg; - u32 pp_off_reg_val; - u32 pp_cycle_reg; - u32 pp_cycle_reg_val; - u32 pfit_reg; - u32 pfit_reg_val; - u16 terminator; -} __attribute__((packed)); - -struct lvds_dvo_timing { - u16 clock; /**< In 10khz */ - u8 hactive_lo; - u8 hblank_lo; - u8 hblank_hi:4; - u8 hactive_hi:4; - u8 vactive_lo; - u8 vblank_lo; - u8 vblank_hi:4; - u8 vactive_hi:4; - u8 hsync_off_lo; - u8 hsync_pulse_width; - u8 vsync_pulse_width:4; - u8 vsync_off:4; - u8 rsvd0:6; - u8 hsync_off_hi:2; - u8 h_image; - u8 v_image; - u8 max_hv; - u8 h_border; - u8 v_border; - u8 rsvd1:3; - u8 digital:2; - u8 vsync_positive:1; - u8 hsync_positive:1; - u8 rsvd2:1; -} __attribute__((packed)); - -struct lvds_pnp_id { - u16 mfg_name; - u16 product_code; - u32 serial; - u8 mfg_week; - u8 mfg_year; -} __attribute__((packed)); - -struct bdb_lvds_lfp_data_entry { - struct lvds_fp_timing fp_timing; - struct lvds_dvo_timing dvo_timing; - struct lvds_pnp_id pnp_id; -} __attribute__((packed)); - -struct bdb_lvds_lfp_data { - struct bdb_lvds_lfp_data_entry data[16]; -} __attribute__((packed)); - -struct aimdb_header { - char signature[16]; - char oem_device[20]; - u16 aimdb_version; - u16 aimdb_header_size; - u16 aimdb_size; -} __attribute__((packed)); - -struct aimdb_block { - u8 aimdb_id; - u16 aimdb_size; -} __attribute__((packed)); - -struct vch_panel_data { - u16 fp_timing_offset; - u8 fp_timing_size; - u16 dvo_timing_offset; - u8 dvo_timing_size; - u16 text_fitting_offset; - u8 text_fitting_size; - u16 graphics_fitting_offset; - u8 graphics_fitting_size; -} __attribute__((packed)); - -struct vch_bdb_22 { - struct aimdb_block aimdb_block; - struct vch_panel_data panels[16]; -} __attribute__((packed)); - -struct bdb_sdvo_lvds_options { - u8 panel_backlight; - u8 h40_set_panel_type; - u8 panel_type; - u8 ssc_clk_freq; - u16 als_low_trip; - u16 als_high_trip; - u8 sclalarcoeff_tab_row_num; - u8 sclalarcoeff_tab_row_size; - u8 coefficient[8]; - u8 panel_misc_bits_1; - u8 panel_misc_bits_2; - u8 panel_misc_bits_3; - u8 panel_misc_bits_4; -} __attribute__((packed)); - - -extern bool psb_intel_init_bios(struct drm_device *dev); -extern void psb_intel_destroy_bios(struct drm_device *dev); - -/* - * Driver<->VBIOS interaction occurs through scratch bits in - * GR18 & SWF*. - */ - -/* GR18 bits are set on display switch and hotkey events */ -#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */ -#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */ -#define GR18_HK_NONE (0x0<<3) -#define GR18_HK_LFP_STRETCH (0x1<<3) -#define GR18_HK_TOGGLE_DISP (0x2<<3) -#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */ -#define GR18_HK_POPUP_DISABLED (0x6<<3) -#define GR18_HK_POPUP_ENABLED (0x7<<3) -#define GR18_HK_PFIT (0x8<<3) -#define GR18_HK_APM_CHANGE (0xa<<3) -#define GR18_HK_MULTIPLE (0xc<<3) -#define GR18_USER_INT_EN (1<<2) -#define GR18_A0000_FLUSH_EN (1<<1) -#define GR18_SMM_EN (1<<0) - -/* Set by driver, cleared by VBIOS */ -#define SWF00_YRES_SHIFT 16 -#define SWF00_XRES_SHIFT 0 -#define SWF00_RES_MASK 0xffff - -/* Set by VBIOS at boot time and driver at runtime */ -#define SWF01_TV2_FORMAT_SHIFT 8 -#define SWF01_TV1_FORMAT_SHIFT 0 -#define SWF01_TV_FORMAT_MASK 0xffff - -#define SWF10_VBIOS_BLC_I2C_EN (1<<29) -#define SWF10_GTT_OVERRIDE_EN (1<<28) -#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */ -#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24) -#define SWF10_OLD_TOGGLE 0x0 -#define SWF10_TOGGLE_LIST_1 0x1 -#define SWF10_TOGGLE_LIST_2 0x2 -#define SWF10_TOGGLE_LIST_3 0x3 -#define SWF10_TOGGLE_LIST_4 0x4 -#define SWF10_PANNING_EN (1<<23) -#define SWF10_DRIVER_LOADED (1<<22) -#define SWF10_EXTENDED_DESKTOP (1<<21) -#define SWF10_EXCLUSIVE_MODE (1<<20) -#define SWF10_OVERLAY_EN (1<<19) -#define SWF10_PLANEB_HOLDOFF (1<<18) -#define SWF10_PLANEA_HOLDOFF (1<<17) -#define SWF10_VGA_HOLDOFF (1<<16) -#define SWF10_ACTIVE_DISP_MASK 0xffff -#define SWF10_PIPEB_LFP2 (1<<15) -#define SWF10_PIPEB_EFP2 (1<<14) -#define SWF10_PIPEB_TV2 (1<<13) -#define SWF10_PIPEB_CRT2 (1<<12) -#define SWF10_PIPEB_LFP (1<<11) -#define SWF10_PIPEB_EFP (1<<10) -#define SWF10_PIPEB_TV (1<<9) -#define SWF10_PIPEB_CRT (1<<8) -#define SWF10_PIPEA_LFP2 (1<<7) -#define SWF10_PIPEA_EFP2 (1<<6) -#define SWF10_PIPEA_TV2 (1<<5) -#define SWF10_PIPEA_CRT2 (1<<4) -#define SWF10_PIPEA_LFP (1<<3) -#define SWF10_PIPEA_EFP (1<<2) -#define SWF10_PIPEA_TV (1<<1) -#define SWF10_PIPEA_CRT (1<<0) - -#define SWF11_MEMORY_SIZE_SHIFT 16 -#define SWF11_SV_TEST_EN (1<<15) -#define SWF11_IS_AGP (1<<14) -#define SWF11_DISPLAY_HOLDOFF (1<<13) -#define SWF11_DPMS_REDUCED (1<<12) -#define SWF11_IS_VBE_MODE (1<<11) -#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */ -#define SWF11_DPMS_MASK 0x07 -#define SWF11_DPMS_OFF (1<<2) -#define SWF11_DPMS_SUSPEND (1<<1) -#define SWF11_DPMS_STANDBY (1<<0) -#define SWF11_DPMS_ON 0 - -#define SWF14_GFX_PFIT_EN (1<<31) -#define SWF14_TEXT_PFIT_EN (1<<30) -#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */ -#define SWF14_POPUP_EN (1<<28) -#define SWF14_DISPLAY_HOLDOFF (1<<27) -#define SWF14_DISP_DETECT_EN (1<<26) -#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */ -#define SWF14_DRIVER_STATUS (1<<24) -#define SWF14_OS_TYPE_WIN9X (1<<23) -#define SWF14_OS_TYPE_WINNT (1<<22) -/* 21:19 rsvd */ -#define SWF14_PM_TYPE_MASK 0x00070000 -#define SWF14_PM_ACPI_VIDEO (0x4 << 16) -#define SWF14_PM_ACPI (0x3 << 16) -#define SWF14_PM_APM_12 (0x2 << 16) -#define SWF14_PM_APM_11 (0x1 << 16) -#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */ - /* if GR18 indicates a display switch */ -#define SWF14_DS_PIPEB_LFP2_EN (1<<15) -#define SWF14_DS_PIPEB_EFP2_EN (1<<14) -#define SWF14_DS_PIPEB_TV2_EN (1<<13) -#define SWF14_DS_PIPEB_CRT2_EN (1<<12) -#define SWF14_DS_PIPEB_LFP_EN (1<<11) -#define SWF14_DS_PIPEB_EFP_EN (1<<10) -#define SWF14_DS_PIPEB_TV_EN (1<<9) -#define SWF14_DS_PIPEB_CRT_EN (1<<8) -#define SWF14_DS_PIPEA_LFP2_EN (1<<7) -#define SWF14_DS_PIPEA_EFP2_EN (1<<6) -#define SWF14_DS_PIPEA_TV2_EN (1<<5) -#define SWF14_DS_PIPEA_CRT2_EN (1<<4) -#define SWF14_DS_PIPEA_LFP_EN (1<<3) -#define SWF14_DS_PIPEA_EFP_EN (1<<2) -#define SWF14_DS_PIPEA_TV_EN (1<<1) -#define SWF14_DS_PIPEA_CRT_EN (1<<0) - /* if GR18 indicates a panel fitting request */ -#define SWF14_PFIT_EN (1<<0) /* 0 means disable */ - /* if GR18 indicates an APM change request */ -#define SWF14_APM_HIBERNATE 0x4 -#define SWF14_APM_SUSPEND 0x3 -#define SWF14_APM_STANDBY 0x1 -#define SWF14_APM_RESTORE 0x0 - -#endif /* _I830_BIOS_H_ */ diff --git a/drivers/staging/gma500/intel_i2c.c b/drivers/staging/gma500/intel_i2c.c deleted file mode 100644 index 51cbf65..0000000 --- a/drivers/staging/gma500/intel_i2c.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright © 2006-2007 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - */ - -#include -#include -#include - -#include "psb_drv.h" -#include "psb_intel_reg.h" - -/* - * Intel GPIO access functions - */ - -#define I2C_RISEFALL_TIME 20 - -static int get_clock(void *data) -{ - struct psb_intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - u32 val; - - val = REG_READ(chan->reg); - return (val & GPIO_CLOCK_VAL_IN) != 0; -} - -static int get_data(void *data) -{ - struct psb_intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - u32 val; - - val = REG_READ(chan->reg); - return (val & GPIO_DATA_VAL_IN) != 0; -} - -static void set_clock(void *data, int state_high) -{ - struct psb_intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - u32 reserved = 0, clock_bits; - - /* On most chips, these bits must be preserved in software. */ - reserved = - REG_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | - GPIO_CLOCK_PULLUP_DISABLE); - - if (state_high) - clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; - else - clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | - GPIO_CLOCK_VAL_MASK; - REG_WRITE(chan->reg, reserved | clock_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ -} - -static void set_data(void *data, int state_high) -{ - struct psb_intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - u32 reserved = 0, data_bits; - - /* On most chips, these bits must be preserved in software. */ - reserved = - REG_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | - GPIO_CLOCK_PULLUP_DISABLE); - - if (state_high) - data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; - else - data_bits = - GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | - GPIO_DATA_VAL_MASK; - - REG_WRITE(chan->reg, reserved | data_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ -} - -/** - * psb_intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg - * @dev: DRM device - * @output: driver specific output device - * @reg: GPIO reg to use - * @name: name for this bus - * - * Creates and registers a new i2c bus with the Linux i2c layer, for use - * in output probing and control (e.g. DDC or SDVO control functions). - * - * Possible values for @reg include: - * %GPIOA - * %GPIOB - * %GPIOC - * %GPIOD - * %GPIOE - * %GPIOF - * %GPIOG - * %GPIOH - * see PRM for details on how these different busses are used. - */ -struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, - const u32 reg, const char *name) -{ - struct psb_intel_i2c_chan *chan; - - chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL); - if (!chan) - goto out_free; - - chan->drm_dev = dev; - chan->reg = reg; - snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); - chan->adapter.owner = THIS_MODULE; - chan->adapter.algo_data = &chan->algo; - chan->adapter.dev.parent = &dev->pdev->dev; - chan->algo.setsda = set_data; - chan->algo.setscl = set_clock; - chan->algo.getsda = get_data; - chan->algo.getscl = get_clock; - chan->algo.udelay = 20; - chan->algo.timeout = usecs_to_jiffies(2200); - chan->algo.data = chan; - - i2c_set_adapdata(&chan->adapter, chan); - - if (i2c_bit_add_bus(&chan->adapter)) - goto out_free; - - /* JJJ: raise SCL and SDA? */ - set_data(chan, 1); - set_clock(chan, 1); - udelay(20); - - return chan; - -out_free: - kfree(chan); - return NULL; -} - -/** - * psb_intel_i2c_destroy - unregister and free i2c bus resources - * @output: channel to free - * - * Unregister the adapter from the i2c layer, then free the structure. - */ -void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan) -{ - if (!chan) - return; - - i2c_del_adapter(&chan->adapter); - kfree(chan); -} diff --git a/drivers/staging/gma500/intel_opregion.c b/drivers/staging/gma500/intel_opregion.c deleted file mode 100644 index d946bc1..0000000 --- a/drivers/staging/gma500/intel_opregion.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * FIXME: resolve with the i915 version - */ - -#include "psb_drv.h" - -struct opregion_header { - u8 signature[16]; - u32 size; - u32 opregion_ver; - u8 bios_ver[32]; - u8 vbios_ver[16]; - u8 driver_ver[16]; - u32 mboxes; - u8 reserved[164]; -} __packed; - -struct opregion_apci { - /*FIXME: add it later*/ -} __packed; - -struct opregion_swsci { - /*FIXME: add it later*/ -} __packed; - -struct opregion_acpi { - /*FIXME: add it later*/ -} __packed; - -int gma_intel_opregion_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 opregion_phy; - void *base; - u32 *lid_state; - - dev_priv->lid_state = NULL; - - pci_read_config_dword(dev->pdev, 0xfc, &opregion_phy); - if (opregion_phy == 0) - return -ENOTSUPP; - - base = ioremap(opregion_phy, 8*1024); - if (!base) - return -ENOMEM; - - lid_state = base + 0x01ac; - - dev_priv->lid_state = lid_state; - dev_priv->lid_last_state = readl(lid_state); - return 0; -} - -int gma_intel_opregion_exit(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - if (dev_priv->lid_state) - iounmap(dev_priv->lid_state); - return 0; -} diff --git a/drivers/staging/gma500/mdfld_device.c b/drivers/staging/gma500/mdfld_device.c deleted file mode 100644 index f47aeb7..0000000 --- a/drivers/staging/gma500/mdfld_device.c +++ /dev/null @@ -1,714 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include "psb_drm.h" -#include "psb_drv.h" -#include "mdfld_output.h" -#include "mdfld_dsi_output.h" -#include "mid_bios.h" - -/* - * Provide the Medfield specific backlight management - */ - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - -static int mdfld_brightness; -struct backlight_device *mdfld_backlight_device; - -static int mfld_set_brightness(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(mdfld_backlight_device); - struct drm_psb_private *dev_priv = dev->dev_private; - int level = bd->props.brightness; - - /* Percentage 1-100% being valid */ - if (level < 1) - level = 1; - - if (gma_power_begin(dev, 0)) { - /* Calculate and set the brightness value */ - u32 adjusted_level; - - /* Adjust the backlight level with the percent in - * dev_priv->blc_adj2; - */ - adjusted_level = level * dev_priv->blc_adj2; - adjusted_level = adjusted_level / 100; -#if 0 -#ifndef CONFIG_MDFLD_DSI_DPU - if(!(dev_priv->dsr_fb_update & MDFLD_DSR_MIPI_CONTROL) && - (dev_priv->dbi_panel_on || dev_priv->dbi_panel_on2)){ - mdfld_dsi_dbi_exit_dsr(dev,MDFLD_DSR_MIPI_CONTROL, 0, 0); - dev_dbg(dev->dev, "Out of DSR before set brightness to %d.\n",adjusted_level); - } -#endif - mdfld_dsi_brightness_control(dev, 0, adjusted_level); - - if ((dev_priv->dbi_panel_on2) || (dev_priv->dpi_panel_on2)) - mdfld_dsi_brightness_control(dev, 2, adjusted_level); -#endif - gma_power_end(dev); - } - mdfld_brightness = level; - return 0; -} - -int psb_get_brightness(struct backlight_device *bd) -{ - /* return locally cached var instead of HW read (due to DPST etc.) */ - /* FIXME: ideally return actual value in case firmware fiddled with - it */ - return mdfld_brightness; -} - -static const struct backlight_ops mfld_ops = { - .get_brightness = psb_get_brightness, - .update_status = mfld_set_brightness, -}; - -static int mdfld_backlight_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct backlight_properties props; - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 100; - props.type = BACKLIGHT_PLATFORM; - - mdfld_backlight_device = backlight_device_register("mfld-bl", - NULL, (void *)dev, &mfld_ops, &props); - - if (IS_ERR(mdfld_backlight_device)) - return PTR_ERR(mdfld_backlight_device); - - dev_priv->blc_adj1 = 100; - dev_priv->blc_adj2 = 100; - mdfld_backlight_device->props.brightness = 100; - mdfld_backlight_device->props.max_brightness = 100; - backlight_update_status(mdfld_backlight_device); - dev_priv->backlight_device = mdfld_backlight_device; - return 0; -} - -#endif - -/* - * Provide the Medfield specific chip logic and low level methods for - * power management. - */ - -static void mdfld_init_pm(struct drm_device *dev) -{ - /* No work needed here yet */ -} - -/** - * mdfld_save_display_registers - save registers for pipe - * @dev: our device - * @pipe: pipe to save - * - * Save the pipe state of the device before we power it off. Keep everything - * we need to put it back again - */ -static int mdfld_save_display_registers(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - int i; - - /* register */ - u32 dpll_reg = MRST_DPLL_A; - u32 fp_reg = MRST_FPA0; - u32 pipeconf_reg = PIPEACONF; - u32 htot_reg = HTOTAL_A; - u32 hblank_reg = HBLANK_A; - u32 hsync_reg = HSYNC_A; - u32 vtot_reg = VTOTAL_A; - u32 vblank_reg = VBLANK_A; - u32 vsync_reg = VSYNC_A; - u32 pipesrc_reg = PIPEASRC; - u32 dspstride_reg = DSPASTRIDE; - u32 dsplinoff_reg = DSPALINOFF; - u32 dsptileoff_reg = DSPATILEOFF; - u32 dspsize_reg = DSPASIZE; - u32 dsppos_reg = DSPAPOS; - u32 dspsurf_reg = DSPASURF; - u32 mipi_reg = MIPI; - u32 dspcntr_reg = DSPACNTR; - u32 dspstatus_reg = PIPEASTAT; - u32 palette_reg = PALETTE_A; - - /* pointer to values */ - u32 *dpll_val = &dev_priv->saveDPLL_A; - u32 *fp_val = &dev_priv->saveFPA0; - u32 *pipeconf_val = &dev_priv->savePIPEACONF; - u32 *htot_val = &dev_priv->saveHTOTAL_A; - u32 *hblank_val = &dev_priv->saveHBLANK_A; - u32 *hsync_val = &dev_priv->saveHSYNC_A; - u32 *vtot_val = &dev_priv->saveVTOTAL_A; - u32 *vblank_val = &dev_priv->saveVBLANK_A; - u32 *vsync_val = &dev_priv->saveVSYNC_A; - u32 *pipesrc_val = &dev_priv->savePIPEASRC; - u32 *dspstride_val = &dev_priv->saveDSPASTRIDE; - u32 *dsplinoff_val = &dev_priv->saveDSPALINOFF; - u32 *dsptileoff_val = &dev_priv->saveDSPATILEOFF; - u32 *dspsize_val = &dev_priv->saveDSPASIZE; - u32 *dsppos_val = &dev_priv->saveDSPAPOS; - u32 *dspsurf_val = &dev_priv->saveDSPASURF; - u32 *mipi_val = &dev_priv->saveMIPI; - u32 *dspcntr_val = &dev_priv->saveDSPACNTR; - u32 *dspstatus_val = &dev_priv->saveDSPASTATUS; - u32 *palette_val = dev_priv->save_palette_a; - - switch (pipe) { - case 0: - break; - case 1: - /* register */ - dpll_reg = MDFLD_DPLL_B; - fp_reg = MDFLD_DPLL_DIV0; - pipeconf_reg = PIPEBCONF; - htot_reg = HTOTAL_B; - hblank_reg = HBLANK_B; - hsync_reg = HSYNC_B; - vtot_reg = VTOTAL_B; - vblank_reg = VBLANK_B; - vsync_reg = VSYNC_B; - pipesrc_reg = PIPEBSRC; - dspstride_reg = DSPBSTRIDE; - dsplinoff_reg = DSPBLINOFF; - dsptileoff_reg = DSPBTILEOFF; - dspsize_reg = DSPBSIZE; - dsppos_reg = DSPBPOS; - dspsurf_reg = DSPBSURF; - dspcntr_reg = DSPBCNTR; - dspstatus_reg = PIPEBSTAT; - palette_reg = PALETTE_B; - - /* values */ - dpll_val = &dev_priv->saveDPLL_B; - fp_val = &dev_priv->saveFPB0; - pipeconf_val = &dev_priv->savePIPEBCONF; - htot_val = &dev_priv->saveHTOTAL_B; - hblank_val = &dev_priv->saveHBLANK_B; - hsync_val = &dev_priv->saveHSYNC_B; - vtot_val = &dev_priv->saveVTOTAL_B; - vblank_val = &dev_priv->saveVBLANK_B; - vsync_val = &dev_priv->saveVSYNC_B; - pipesrc_val = &dev_priv->savePIPEBSRC; - dspstride_val = &dev_priv->saveDSPBSTRIDE; - dsplinoff_val = &dev_priv->saveDSPBLINOFF; - dsptileoff_val = &dev_priv->saveDSPBTILEOFF; - dspsize_val = &dev_priv->saveDSPBSIZE; - dsppos_val = &dev_priv->saveDSPBPOS; - dspsurf_val = &dev_priv->saveDSPBSURF; - dspcntr_val = &dev_priv->saveDSPBCNTR; - dspstatus_val = &dev_priv->saveDSPBSTATUS; - palette_val = dev_priv->save_palette_b; - break; - case 2: - /* register */ - pipeconf_reg = PIPECCONF; - htot_reg = HTOTAL_C; - hblank_reg = HBLANK_C; - hsync_reg = HSYNC_C; - vtot_reg = VTOTAL_C; - vblank_reg = VBLANK_C; - vsync_reg = VSYNC_C; - pipesrc_reg = PIPECSRC; - dspstride_reg = DSPCSTRIDE; - dsplinoff_reg = DSPCLINOFF; - dsptileoff_reg = DSPCTILEOFF; - dspsize_reg = DSPCSIZE; - dsppos_reg = DSPCPOS; - dspsurf_reg = DSPCSURF; - mipi_reg = MIPI_C; - dspcntr_reg = DSPCCNTR; - dspstatus_reg = PIPECSTAT; - palette_reg = PALETTE_C; - - /* pointer to values */ - pipeconf_val = &dev_priv->savePIPECCONF; - htot_val = &dev_priv->saveHTOTAL_C; - hblank_val = &dev_priv->saveHBLANK_C; - hsync_val = &dev_priv->saveHSYNC_C; - vtot_val = &dev_priv->saveVTOTAL_C; - vblank_val = &dev_priv->saveVBLANK_C; - vsync_val = &dev_priv->saveVSYNC_C; - pipesrc_val = &dev_priv->savePIPECSRC; - dspstride_val = &dev_priv->saveDSPCSTRIDE; - dsplinoff_val = &dev_priv->saveDSPCLINOFF; - dsptileoff_val = &dev_priv->saveDSPCTILEOFF; - dspsize_val = &dev_priv->saveDSPCSIZE; - dsppos_val = &dev_priv->saveDSPCPOS; - dspsurf_val = &dev_priv->saveDSPCSURF; - mipi_val = &dev_priv->saveMIPI_C; - dspcntr_val = &dev_priv->saveDSPCCNTR; - dspstatus_val = &dev_priv->saveDSPCSTATUS; - palette_val = dev_priv->save_palette_c; - break; - default: - DRM_ERROR("%s, invalid pipe number.\n", __func__); - return -EINVAL; - } - - /* Pipe & plane A info */ - *dpll_val = PSB_RVDC32(dpll_reg); - *fp_val = PSB_RVDC32(fp_reg); - *pipeconf_val = PSB_RVDC32(pipeconf_reg); - *htot_val = PSB_RVDC32(htot_reg); - *hblank_val = PSB_RVDC32(hblank_reg); - *hsync_val = PSB_RVDC32(hsync_reg); - *vtot_val = PSB_RVDC32(vtot_reg); - *vblank_val = PSB_RVDC32(vblank_reg); - *vsync_val = PSB_RVDC32(vsync_reg); - *pipesrc_val = PSB_RVDC32(pipesrc_reg); - *dspstride_val = PSB_RVDC32(dspstride_reg); - *dsplinoff_val = PSB_RVDC32(dsplinoff_reg); - *dsptileoff_val = PSB_RVDC32(dsptileoff_reg); - *dspsize_val = PSB_RVDC32(dspsize_reg); - *dsppos_val = PSB_RVDC32(dsppos_reg); - *dspsurf_val = PSB_RVDC32(dspsurf_reg); - *dspcntr_val = PSB_RVDC32(dspcntr_reg); - *dspstatus_val = PSB_RVDC32(dspstatus_reg); - - /*save palette (gamma) */ - for (i = 0; i < 256; i++) - palette_val[i] = PSB_RVDC32(palette_reg + (i<<2)); - - if (pipe == 1) { - dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); - dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); - dev_priv->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL); - dev_priv->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL); - return 0; - } - *mipi_val = PSB_RVDC32(mipi_reg); - return 0; -} - -/** - * mdfld_save_cursor_overlay_registers - save cursor overlay info - * @dev: our device - * - * Save the cursor and overlay register state - */ -static int mdfld_save_cursor_overlay_registers(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - /* Save cursor regs */ - dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); - dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); - dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); - - dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); - dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); - dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); - - dev_priv->saveDSPCCURSOR_CTRL = PSB_RVDC32(CURCCNTR); - dev_priv->saveDSPCCURSOR_BASE = PSB_RVDC32(CURCBASE); - dev_priv->saveDSPCCURSOR_POS = PSB_RVDC32(CURCPOS); - - /* HW overlay */ - dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); - dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); - dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); - dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); - dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); - dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); - dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); - - dev_priv->saveOV_OVADD_C = PSB_RVDC32(OV_OVADD + OV_C_OFFSET); - dev_priv->saveOV_OGAMC0_C = PSB_RVDC32(OV_OGAMC0 + OV_C_OFFSET); - dev_priv->saveOV_OGAMC1_C = PSB_RVDC32(OV_OGAMC1 + OV_C_OFFSET); - dev_priv->saveOV_OGAMC2_C = PSB_RVDC32(OV_OGAMC2 + OV_C_OFFSET); - dev_priv->saveOV_OGAMC3_C = PSB_RVDC32(OV_OGAMC3 + OV_C_OFFSET); - dev_priv->saveOV_OGAMC4_C = PSB_RVDC32(OV_OGAMC4 + OV_C_OFFSET); - dev_priv->saveOV_OGAMC5_C = PSB_RVDC32(OV_OGAMC5 + OV_C_OFFSET); - - return 0; -} -/* - * mdfld_restore_display_registers - restore the state of a pipe - * @dev: our device - * @pipe: the pipe to restore - * - * Restore the state of a pipe to that which was saved by the register save - * functions. - */ -static int mdfld_restore_display_registers(struct drm_device *dev, int pipe) -{ - /* To get panel out of ULPS mode */ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dsi_config *dsi_config = NULL; - u32 i = 0; - u32 dpll = 0; - u32 timeout = 0; - u32 reg_offset = 0; - - /* register */ - u32 dpll_reg = MRST_DPLL_A; - u32 fp_reg = MRST_FPA0; - u32 pipeconf_reg = PIPEACONF; - u32 htot_reg = HTOTAL_A; - u32 hblank_reg = HBLANK_A; - u32 hsync_reg = HSYNC_A; - u32 vtot_reg = VTOTAL_A; - u32 vblank_reg = VBLANK_A; - u32 vsync_reg = VSYNC_A; - u32 pipesrc_reg = PIPEASRC; - u32 dspstride_reg = DSPASTRIDE; - u32 dsplinoff_reg = DSPALINOFF; - u32 dsptileoff_reg = DSPATILEOFF; - u32 dspsize_reg = DSPASIZE; - u32 dsppos_reg = DSPAPOS; - u32 dspsurf_reg = DSPASURF; - u32 dspstatus_reg = PIPEASTAT; - u32 mipi_reg = MIPI; - u32 dspcntr_reg = DSPACNTR; - u32 palette_reg = PALETTE_A; - - /* values */ - u32 dpll_val = dev_priv->saveDPLL_A & ~DPLL_VCO_ENABLE; - u32 fp_val = dev_priv->saveFPA0; - u32 pipeconf_val = dev_priv->savePIPEACONF; - u32 htot_val = dev_priv->saveHTOTAL_A; - u32 hblank_val = dev_priv->saveHBLANK_A; - u32 hsync_val = dev_priv->saveHSYNC_A; - u32 vtot_val = dev_priv->saveVTOTAL_A; - u32 vblank_val = dev_priv->saveVBLANK_A; - u32 vsync_val = dev_priv->saveVSYNC_A; - u32 pipesrc_val = dev_priv->savePIPEASRC; - u32 dspstride_val = dev_priv->saveDSPASTRIDE; - u32 dsplinoff_val = dev_priv->saveDSPALINOFF; - u32 dsptileoff_val = dev_priv->saveDSPATILEOFF; - u32 dspsize_val = dev_priv->saveDSPASIZE; - u32 dsppos_val = dev_priv->saveDSPAPOS; - u32 dspsurf_val = dev_priv->saveDSPASURF; - u32 dspstatus_val = dev_priv->saveDSPASTATUS; - u32 mipi_val = dev_priv->saveMIPI; - u32 dspcntr_val = dev_priv->saveDSPACNTR; - u32 *palette_val = dev_priv->save_palette_a; - - switch (pipe) { - case 0: - dsi_config = dev_priv->dsi_configs[0]; - break; - case 1: - /* register */ - dpll_reg = MDFLD_DPLL_B; - fp_reg = MDFLD_DPLL_DIV0; - pipeconf_reg = PIPEBCONF; - htot_reg = HTOTAL_B; - hblank_reg = HBLANK_B; - hsync_reg = HSYNC_B; - vtot_reg = VTOTAL_B; - vblank_reg = VBLANK_B; - vsync_reg = VSYNC_B; - pipesrc_reg = PIPEBSRC; - dspstride_reg = DSPBSTRIDE; - dsplinoff_reg = DSPBLINOFF; - dsptileoff_reg = DSPBTILEOFF; - dspsize_reg = DSPBSIZE; - dsppos_reg = DSPBPOS; - dspsurf_reg = DSPBSURF; - dspcntr_reg = DSPBCNTR; - palette_reg = PALETTE_B; - dspstatus_reg = PIPEBSTAT; - - /* values */ - dpll_val = dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE; - fp_val = dev_priv->saveFPB0; - pipeconf_val = dev_priv->savePIPEBCONF; - htot_val = dev_priv->saveHTOTAL_B; - hblank_val = dev_priv->saveHBLANK_B; - hsync_val = dev_priv->saveHSYNC_B; - vtot_val = dev_priv->saveVTOTAL_B; - vblank_val = dev_priv->saveVBLANK_B; - vsync_val = dev_priv->saveVSYNC_B; - pipesrc_val = dev_priv->savePIPEBSRC; - dspstride_val = dev_priv->saveDSPBSTRIDE; - dsplinoff_val = dev_priv->saveDSPBLINOFF; - dsptileoff_val = dev_priv->saveDSPBTILEOFF; - dspsize_val = dev_priv->saveDSPBSIZE; - dsppos_val = dev_priv->saveDSPBPOS; - dspsurf_val = dev_priv->saveDSPBSURF; - dspcntr_val = dev_priv->saveDSPBCNTR; - dspstatus_val = dev_priv->saveDSPBSTATUS; - palette_val = dev_priv->save_palette_b; - break; - case 2: - reg_offset = MIPIC_REG_OFFSET; - - /* register */ - pipeconf_reg = PIPECCONF; - htot_reg = HTOTAL_C; - hblank_reg = HBLANK_C; - hsync_reg = HSYNC_C; - vtot_reg = VTOTAL_C; - vblank_reg = VBLANK_C; - vsync_reg = VSYNC_C; - pipesrc_reg = PIPECSRC; - dspstride_reg = DSPCSTRIDE; - dsplinoff_reg = DSPCLINOFF; - dsptileoff_reg = DSPCTILEOFF; - dspsize_reg = DSPCSIZE; - dsppos_reg = DSPCPOS; - dspsurf_reg = DSPCSURF; - mipi_reg = MIPI_C; - dspcntr_reg = DSPCCNTR; - palette_reg = PALETTE_C; - dspstatus_reg = PIPECSTAT; - - /* values */ - pipeconf_val = dev_priv->savePIPECCONF; - htot_val = dev_priv->saveHTOTAL_C; - hblank_val = dev_priv->saveHBLANK_C; - hsync_val = dev_priv->saveHSYNC_C; - vtot_val = dev_priv->saveVTOTAL_C; - vblank_val = dev_priv->saveVBLANK_C; - vsync_val = dev_priv->saveVSYNC_C; - pipesrc_val = dev_priv->savePIPECSRC; - dspstride_val = dev_priv->saveDSPCSTRIDE; - dsplinoff_val = dev_priv->saveDSPCLINOFF; - dsptileoff_val = dev_priv->saveDSPCTILEOFF; - dspsize_val = dev_priv->saveDSPCSIZE; - dsppos_val = dev_priv->saveDSPCPOS; - dspsurf_val = dev_priv->saveDSPCSURF; - dspstatus_val = dev_priv->saveDSPCSTATUS; - mipi_val = dev_priv->saveMIPI_C; - dspcntr_val = dev_priv->saveDSPCCNTR; - palette_val = dev_priv->save_palette_c; - - dsi_config = dev_priv->dsi_configs[1]; - break; - default: - DRM_ERROR("%s, invalid pipe number.\n", __func__); - return -EINVAL; - } - - /* Make sure VGA plane is off. it initializes to on after reset!*/ - PSB_WVDC32(0x80000000, VGACNTRL); - if (pipe == 1) { - PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg); - PSB_RVDC32(dpll_reg); - - PSB_WVDC32(fp_val, fp_reg); - } else { - dpll = PSB_RVDC32(dpll_reg); - - if (!(dpll & DPLL_VCO_ENABLE)) { - - /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ - if (dpll & MDFLD_PWR_GATE_EN) { - dpll &= ~MDFLD_PWR_GATE_EN; - PSB_WVDC32(dpll, dpll_reg); - udelay(500); /* FIXME: 1 ? */ - } - - PSB_WVDC32(fp_val, fp_reg); - PSB_WVDC32(dpll_val, dpll_reg); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - - dpll_val |= DPLL_VCO_ENABLE; - PSB_WVDC32(dpll_val, dpll_reg); - PSB_RVDC32(dpll_reg); - - /* wait for DSI PLL to lock */ - while ((timeout < 20000) && !(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { - udelay(150); - timeout++; - } - - if (timeout == 20000) { - DRM_ERROR("%s, can't lock DSIPLL.\n", - __func__); - return -EINVAL; - } - } - } - /* Restore mode */ - PSB_WVDC32(htot_val, htot_reg); - PSB_WVDC32(hblank_val, hblank_reg); - PSB_WVDC32(hsync_val, hsync_reg); - PSB_WVDC32(vtot_val, vtot_reg); - PSB_WVDC32(vblank_val, vblank_reg); - PSB_WVDC32(vsync_val, vsync_reg); - PSB_WVDC32(pipesrc_val, pipesrc_reg); - PSB_WVDC32(dspstatus_val, dspstatus_reg); - - /* Set up the plane */ - PSB_WVDC32(dspstride_val, dspstride_reg); - PSB_WVDC32(dsplinoff_val, dsplinoff_reg); - PSB_WVDC32(dsptileoff_val, dsptileoff_reg); - PSB_WVDC32(dspsize_val, dspsize_reg); - PSB_WVDC32(dsppos_val, dsppos_reg); - PSB_WVDC32(dspsurf_val, dspsurf_reg); - - if (pipe == 1) { - PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); - PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); - PSB_WVDC32(dev_priv->saveHDMIPHYMISCCTL, HDMIPHYMISCCTL); - PSB_WVDC32(dev_priv->saveHDMIB_CONTROL, HDMIB_CONTROL); - - } else { - /* Set up pipe related registers */ - PSB_WVDC32(mipi_val, mipi_reg); - /* Setup MIPI adapter + MIPI IP registers */ - mdfld_dsi_controller_init(dsi_config, pipe); - msleep(20); - } - /* Enable the plane */ - PSB_WVDC32(dspcntr_val, dspcntr_reg); - msleep(20); - /* Enable the pipe */ - PSB_WVDC32(pipeconf_val, pipeconf_reg); - - for (i = 0; i < 256; i++) - PSB_WVDC32(palette_val[i], palette_reg + (i<<2)); - if (pipe == 1) - return 0; - if (!mdfld_panel_dpi(dev)) - mdfld_enable_te(dev, pipe); - return 0; -} - -/** - * mdfld_restore_cursor_overlay_registers - restore cursor - * @dev: our device - * - * Restore the cursor and overlay state that was saved earlier - */ -static int mdfld_restore_cursor_overlay_registers(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - /* Enable Cursor A */ - PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); - PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); - PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); - - PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); - PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); - PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); - - PSB_WVDC32(dev_priv->saveDSPCCURSOR_CTRL, CURCCNTR); - PSB_WVDC32(dev_priv->saveDSPCCURSOR_POS, CURCPOS); - PSB_WVDC32(dev_priv->saveDSPCCURSOR_BASE, CURCBASE); - - /* Restore HW overlay */ - PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); - PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); - PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); - PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); - PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); - PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); - PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); - - PSB_WVDC32(dev_priv->saveOV_OVADD_C, OV_OVADD + OV_C_OFFSET); - PSB_WVDC32(dev_priv->saveOV_OGAMC0_C, OV_OGAMC0 + OV_C_OFFSET); - PSB_WVDC32(dev_priv->saveOV_OGAMC1_C, OV_OGAMC1 + OV_C_OFFSET); - PSB_WVDC32(dev_priv->saveOV_OGAMC2_C, OV_OGAMC2 + OV_C_OFFSET); - PSB_WVDC32(dev_priv->saveOV_OGAMC3_C, OV_OGAMC3 + OV_C_OFFSET); - PSB_WVDC32(dev_priv->saveOV_OGAMC4_C, OV_OGAMC4 + OV_C_OFFSET); - PSB_WVDC32(dev_priv->saveOV_OGAMC5_C, OV_OGAMC5 + OV_C_OFFSET); - - return 0; -} - -/** - * mdfld_save_display_registers - save registers lost on suspend - * @dev: our DRM device - * - * Save the state we need in order to be able to restore the interface - * upon resume from suspend - */ -static int mdfld_save_registers(struct drm_device *dev) -{ - /* FIXME: We need to shut down panels here if using them - and once the right bits are merged */ - mdfld_save_cursor_overlay_registers(dev); - mdfld_save_display_registers(dev, 0); - mdfld_save_display_registers(dev, 0); - mdfld_save_display_registers(dev, 2); - mdfld_save_display_registers(dev, 1); - mdfld_disable_crtc(dev, 0); - mdfld_disable_crtc(dev, 2); - mdfld_disable_crtc(dev, 1); - return 0; -} - -/** - * mdfld_restore_display_registers - restore lost register state - * @dev: our DRM device - * - * Restore register state that was lost during suspend and resume. - */ -static int mdfld_restore_registers(struct drm_device *dev) -{ - mdfld_restore_display_registers(dev, 1); - mdfld_restore_display_registers(dev, 0); - mdfld_restore_display_registers(dev, 2); - mdfld_restore_cursor_overlay_registers(dev); - return 0; -} - -static int mdfld_power_down(struct drm_device *dev) -{ - /* FIXME */ - return 0; -} - -static int mdfld_power_up(struct drm_device *dev) -{ - /* FIXME */ - return 0; -} - -const struct psb_ops mdfld_chip_ops = { - .name = "Medfield", - .accel_2d = 0, - .pipes = 3, - .crtcs = 2, - .sgx_offset = MRST_SGX_OFFSET, - - .chip_setup = mid_chip_setup, - - .crtc_helper = &mdfld_helper_funcs, - .crtc_funcs = &mdfld_intel_crtc_funcs, - - .output_init = mdfld_output_init, - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - .backlight_init = mdfld_backlight_init, -#endif - - .init_pm = mdfld_init_pm, - .save_regs = mdfld_save_registers, - .restore_regs = mdfld_restore_registers, - .power_down = mdfld_power_down, - .power_up = mdfld_power_up, -}; - diff --git a/drivers/staging/gma500/mdfld_dsi_dbi.c b/drivers/staging/gma500/mdfld_dsi_dbi.c deleted file mode 100644 index fd211f3..0000000 --- a/drivers/staging/gma500/mdfld_dsi_dbi.c +++ /dev/null @@ -1,761 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dbi_dpu.h" -#include "mdfld_dsi_pkg_sender.h" - -#include "power.h" -#include - -int enable_gfx_rtpm; - -extern struct drm_device *gpDrmDevice; -extern int gfxrtdelay; -int enter_dsr; -struct mdfld_dsi_dbi_output *gdbi_output; -extern bool gbgfxsuspended; -extern int enable_gfx_rtpm; -extern int gfxrtdelay; - -#define MDFLD_DSR_MAX_IDLE_COUNT 2 - -/* - * set refreshing area - */ -int mdfld_dsi_dbi_update_area(struct mdfld_dsi_dbi_output *dbi_output, - u16 x1, u16 y1, u16 x2, u16 y2) -{ - struct mdfld_dsi_pkg_sender *sender = - mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); - u8 param[4]; - u8 cmd; - int err; - - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - - /* Set column */ - cmd = DCS_SET_COLUMN_ADDRESS; - param[0] = x1 >> 8; - param[1] = x1; - param[2] = x2 >> 8; - param[3] = x2; - - err = mdfld_dsi_send_dcs(sender, - cmd, - param, - 4, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd); - goto err_out; - } - - /* Set page */ - cmd = DCS_SET_PAGE_ADDRESS; - param[0] = y1 >> 8; - param[1] = y1; - param[2] = y2 >> 8; - param[3] = y2; - - err = mdfld_dsi_send_dcs(sender, - cmd, - param, - 4, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd); - goto err_out; - } - - /*update screen*/ - err = mdfld_dsi_send_dcs(sender, - write_mem_start, - NULL, - 0, - CMD_DATA_SRC_PIPE, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(sender->dev->dev, "DCS 0x%x sent failed\n", cmd); - goto err_out; - } - mdfld_dsi_cmds_kick_out(sender); -err_out: - return err; -} - -/* - * set panel's power state - */ -int mdfld_dsi_dbi_update_power(struct mdfld_dsi_dbi_output *dbi_output, - int mode) -{ - struct drm_device *dev = dbi_output->dev; - struct mdfld_dsi_pkg_sender *sender = - mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); - u8 param = 0; - u32 err = 0; - - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - - if (mode == DRM_MODE_DPMS_ON) { - /* Exit sleep mode */ - err = mdfld_dsi_send_dcs(sender, - DCS_EXIT_SLEEP_MODE, - NULL, - 0, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - DCS_EXIT_SLEEP_MODE); - goto power_err; - } - - /* Set display on */ - err = mdfld_dsi_send_dcs(sender, - DCS_SET_DISPLAY_ON, - NULL, - 0, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - DCS_SET_DISPLAY_ON); - goto power_err; - } - - /* set tear effect on */ - err = mdfld_dsi_send_dcs(sender, - DCS_SET_TEAR_ON, - ¶m, - 1, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - set_tear_on); - goto power_err; - } - - /** - * FIXME: remove this later - */ - err = mdfld_dsi_send_dcs(sender, - DCS_WRITE_MEM_START, - NULL, - 0, - CMD_DATA_SRC_PIPE, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - DCS_WRITE_MEM_START); - goto power_err; - } - } else { - /* Set tear effect off */ - err = mdfld_dsi_send_dcs(sender, - DCS_SET_TEAR_OFF, - NULL, - 0, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - DCS_SET_TEAR_OFF); - goto power_err; - } - - /* Turn display off */ - err = mdfld_dsi_send_dcs(sender, - DCS_SET_DISPLAY_OFF, - NULL, - 0, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - DCS_SET_DISPLAY_OFF); - goto power_err; - } - - /* Now enter sleep mode */ - err = mdfld_dsi_send_dcs(sender, - DCS_ENTER_SLEEP_MODE, - NULL, - 0, - CMD_DATA_SRC_SYSTEM_MEM, - MDFLD_DSI_QUEUE_PACKAGE); - if (err) { - dev_err(dev->dev, "DCS 0x%x sent failed\n", - DCS_ENTER_SLEEP_MODE); - goto power_err; - } - } - mdfld_dsi_cmds_kick_out(sender); -power_err: - return err; -} - -/* - * send a generic DCS command with a parameter list - */ -int mdfld_dsi_dbi_send_dcs(struct mdfld_dsi_dbi_output *dbi_output, - u8 dcs, u8 *param, u32 num, u8 data_src) -{ - struct mdfld_dsi_pkg_sender *sender = - mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); - int ret; - - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - - ret = mdfld_dsi_send_dcs(sender, - dcs, - param, - num, - data_src, - MDFLD_DSI_SEND_PACKAGE); - - return ret; -} - -/* - * Enter DSR - */ -void mdfld_dsi_dbi_enter_dsr(struct mdfld_dsi_dbi_output *dbi_output, int pipe) -{ - u32 reg_val; - struct drm_device *dev = dbi_output->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = dbi_output->base.base.crtc; - struct psb_intel_crtc *psb_crtc = (crtc) ? - to_psb_intel_crtc(crtc) : NULL; - u32 dpll_reg = MRST_DPLL_A; - u32 pipeconf_reg = PIPEACONF; - u32 dspcntr_reg = DSPACNTR; - - if (!dbi_output) - return; - - /* FIXME check if can go */ - dev_priv->is_in_idle = true; - - gdbi_output = dbi_output; - if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || - (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING)) - return; - - if (pipe == 2) { - dpll_reg = MRST_DPLL_A; - pipeconf_reg = PIPECCONF; - dspcntr_reg = DSPCCNTR; - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - /* Disable te interrupts */ - mdfld_disable_te(dev, pipe); - - /* Disable plane */ - reg_val = REG_READ(dspcntr_reg); - if (!(reg_val & DISPLAY_PLANE_ENABLE)) { - REG_WRITE(dspcntr_reg, reg_val & ~DISPLAY_PLANE_ENABLE); - REG_READ(dspcntr_reg); - } - - /* Disable pipe */ - reg_val = REG_READ(pipeconf_reg); - if (!(reg_val & DISPLAY_PLANE_ENABLE)) { - reg_val &= ~DISPLAY_PLANE_ENABLE; - reg_val |= (PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF); - REG_WRITE(pipeconf_reg, reg_val); - REG_READ(pipeconf_reg); - mdfldWaitForPipeDisable(dev, pipe); - } - - /* Disable DPLL */ - reg_val = REG_READ(dpll_reg); - if (!(reg_val & DPLL_VCO_ENABLE)) { - reg_val &= ~DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, reg_val); - REG_READ(dpll_reg); - udelay(500); - } - - gma_power_end(dev); - dbi_output->mode_flags |= MODE_SETTING_IN_DSR; - if (pipe == 2) { - enter_dsr = 1; - /* pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay); */ - } -} - -static void mdfld_dbi_output_exit_dsr(struct mdfld_dsi_dbi_output *dbi_output, - int pipe) -{ - struct drm_device *dev = dbi_output->dev; - struct drm_crtc *crtc = dbi_output->base.base.crtc; - struct psb_intel_crtc *psb_crtc = (crtc) ? - to_psb_intel_crtc(crtc) : NULL; - u32 reg_val; - u32 dpll_reg = MRST_DPLL_A; - u32 pipeconf_reg = PIPEACONF; - u32 dspcntr_reg = DSPACNTR; - u32 reg_offset = 0; - - /*if mode setting on-going, back off*/ - if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || - (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING)) - return; - - if (pipe == 2) { - dpll_reg = MRST_DPLL_A; - pipeconf_reg = PIPECCONF; - dspcntr_reg = DSPCCNTR; - reg_offset = MIPIC_REG_OFFSET; - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - /* Enable DPLL */ - reg_val = REG_READ(dpll_reg); - if (!(reg_val & DPLL_VCO_ENABLE)) { - if (reg_val & MDFLD_PWR_GATE_EN) { - reg_val &= ~MDFLD_PWR_GATE_EN; - REG_WRITE(dpll_reg, reg_val); - REG_READ(dpll_reg); - udelay(500); - } - - reg_val |= DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, reg_val); - REG_READ(dpll_reg); - udelay(500); - - /* Add timeout */ - while (!(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) - cpu_relax(); - } - - /* Enable pipe */ - reg_val = REG_READ(pipeconf_reg); - if (!(reg_val & PIPEACONF_ENABLE)) { - reg_val |= PIPEACONF_ENABLE; - REG_WRITE(pipeconf_reg, reg_val); - REG_READ(pipeconf_reg); - udelay(500); - mdfldWaitForPipeEnable(dev, pipe); - } - - /* Enable plane */ - reg_val = REG_READ(dspcntr_reg); - if (!(reg_val & DISPLAY_PLANE_ENABLE)) { - reg_val |= DISPLAY_PLANE_ENABLE; - REG_WRITE(dspcntr_reg, reg_val); - REG_READ(dspcntr_reg); - udelay(500); - } - - /* Enable TE interrupt on this pipe */ - mdfld_enable_te(dev, pipe); - gma_power_end(dev); - - /*clean IN_DSR flag*/ - dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR; -} - -/* - * Exit from DSR - */ -void mdfld_dsi_dbi_exit_dsr(struct drm_device *dev, u32 update_src) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; - struct mdfld_dsi_dbi_output **dbi_output; - int i; - int pipe; - - /* FIXME can go ? */ - dev_priv->is_in_idle = false; - dbi_output = dsr_info->dbi_outputs; - -#ifdef CONFIG_PM_RUNTIME - if (!enable_gfx_rtpm) { -/* pm_runtime_allow(&gpDrmDevice->pdev->dev); */ -/* schedule_delayed_work(&rtpm_work, 30 * 1000);*/ /* FIXME: HZ ? */ - } -#endif - - /* For each output, exit dsr */ - for (i = 0; i < dsr_info->dbi_output_num; i++) { - /* If panel has been turned off, skip */ - if (!dbi_output[i] || !dbi_output[i]->dbi_panel_on) - continue; - pipe = dbi_output[i]->channel_num ? 2 : 0; - enter_dsr = 0; - mdfld_dbi_output_exit_dsr(dbi_output[i], pipe); - } - dev_priv->dsr_fb_update |= update_src; -} - -static bool mdfld_dbi_is_in_dsr(struct drm_device *dev) -{ - if (REG_READ(MRST_DPLL_A) & DPLL_VCO_ENABLE) - return false; - if ((REG_READ(PIPEACONF) & PIPEACONF_ENABLE) || - (REG_READ(PIPECCONF) & PIPEACONF_ENABLE)) - return false; - if ((REG_READ(DSPACNTR) & DISPLAY_PLANE_ENABLE) || - (REG_READ(DSPCCNTR) & DISPLAY_PLANE_ENABLE)) - return false; - - return true; -} - -/* Periodically update dbi panel */ -void mdfld_dbi_update_panel(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; - struct mdfld_dsi_dbi_output **dbi_outputs; - struct mdfld_dsi_dbi_output *dbi_output; - int i; - int can_enter_dsr = 0; - u32 damage_mask; - - dbi_outputs = dsr_info->dbi_outputs; - dbi_output = pipe ? dbi_outputs[1] : dbi_outputs[0]; - - if (!dbi_output) - return; - - if (pipe == 0) - damage_mask = dev_priv->dsr_fb_update & MDFLD_DSR_DAMAGE_MASK_0; - else if (pipe == 2) - damage_mask = dev_priv->dsr_fb_update & MDFLD_DSR_DAMAGE_MASK_2; - else - return; - - /* If FB is damaged and panel is on update on-panel FB */ - if (damage_mask && dbi_output->dbi_panel_on) { - dbi_output->dsr_fb_update_done = false; - - if (dbi_output->p_funcs->update_fb) - dbi_output->p_funcs->update_fb(dbi_output, pipe); - - if (dev_priv->dsr_enable && dbi_output->dsr_fb_update_done) - dev_priv->dsr_fb_update &= ~damage_mask; - - /*clean IN_DSR flag*/ - dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR; - - dbi_output->dsr_idle_count = 0; - } else { - dbi_output->dsr_idle_count++; - } - - switch (dsr_info->dbi_output_num) { - case 1: - if (dbi_output->dsr_idle_count > MDFLD_DSR_MAX_IDLE_COUNT) - can_enter_dsr = 1; - break; - case 2: - if (dbi_outputs[0]->dsr_idle_count > MDFLD_DSR_MAX_IDLE_COUNT - && dbi_outputs[1]->dsr_idle_count > MDFLD_DSR_MAX_IDLE_COUNT) - can_enter_dsr = 1; - break; - default: - DRM_ERROR("Wrong DBI output number\n"); - } - - /* Try to enter DSR */ - if (can_enter_dsr) { - for (i = 0; i < dsr_info->dbi_output_num; i++) { - if (!mdfld_dbi_is_in_dsr(dev) && dbi_outputs[i] && - !(dbi_outputs[i]->mode_flags & MODE_SETTING_ON_GOING)) { - mdfld_dsi_dbi_enter_dsr(dbi_outputs[i], - dbi_outputs[i]->channel_num ? 2 : 0); -#if 0 - enter_dsr = 1; - pr_err("%s: enter_dsr = 1\n", __func__); -#endif - } - } - /*schedule rpm suspend after gfxrtdelay*/ -#ifdef CONFIG_GFX_RTPM - if (!dev_priv->rpm_enabled - || !enter_dsr - /* || (REG_READ(HDMIB_CONTROL) & HDMIB_PORT_EN) */ - || pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay)) - dev_warn(dev->dev, - "Runtime PM schedule suspend failed, rpm %d\n", - dev_priv->rpm_enabled); -#endif - } -} - -int mdfld_dbi_dsr_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; - - if (!dsr_info || IS_ERR(dsr_info)) { - dsr_info = kzalloc(sizeof(struct mdfld_dbi_dsr_info), - GFP_KERNEL); - if (!dsr_info) { - dev_err(dev->dev, "No memory\n"); - return -ENOMEM; - } - dev_priv->dbi_dsr_info = dsr_info; - } - return 0; -} - -void mdfld_dbi_dsr_exit(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dsr_info *dsr_info = dev_priv->dbi_dsr_info; - - if (dsr_info) { - kfree(dsr_info); - dev_priv->dbi_dsr_info = NULL; - } -} - -void mdfld_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config, - int pipe) -{ - struct drm_device *dev = dsi_config->dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int lane_count = dsi_config->lane_count; - u32 val = 0; - - dev_dbg(dev->dev, "Init DBI interface on pipe %d...\n", pipe); - - /* Un-ready device */ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); - - /* Init dsi adapter before kicking off */ - REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); - - /* TODO: figure out how to setup these registers */ - REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); - REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), - 0x000a0014); - REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400); - REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000001); - REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000); - - /* Enable all interrupts */ - REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); - /* Max value: 20 clock cycles of txclkesc */ - REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f); - /* Min 21 txclkesc, max: ffffh */ - REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff); - /* Min: 7d0 max: 4e20 */ - REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0); - - /* Set up func_prg */ - val |= lane_count; - val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET); - val |= DSI_DBI_COLOR_FORMAT_OPTION2; - REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); - - REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff); - REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff); - - /* De-assert dbi_stall when half of DBI FIFO is empty */ - /* REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000000); */ - - REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); - REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); - REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); -} - -#if 0 -/*DBI encoder helper funcs*/ -static const struct drm_encoder_helper_funcs mdfld_dsi_dbi_helper_funcs = { - .dpms = mdfld_dsi_dbi_dpms, - .mode_fixup = mdfld_dsi_dbi_mode_fixup, - .prepare = mdfld_dsi_dbi_prepare, - .mode_set = mdfld_dsi_dbi_mode_set, - .commit = mdfld_dsi_dbi_commit, -}; - -/*DBI encoder funcs*/ -static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -#endif - -/* - * Init DSI DBI encoder. - * Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector - * return pointer of newly allocated DBI encoder, NULL on error - */ -struct mdfld_dsi_encoder *mdfld_dsi_dbi_init(struct drm_device *dev, - struct mdfld_dsi_connector *dsi_connector, - struct panel_funcs *p_funcs) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dsi_dbi_output *dbi_output = NULL; - struct mdfld_dsi_config *dsi_config; - struct drm_connector *connector = NULL; - struct drm_encoder *encoder = NULL; - struct drm_display_mode *fixed_mode = NULL; - struct psb_gtt *pg = dev_priv ? (&dev_priv->gtt) : NULL; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv ? (dev_priv->dbi_dpu_info) : NULL; - struct mdfld_dbi_dsr_info *dsr_info = dev_priv ? (dev_priv->dbi_dsr_info) : NULL; - u32 data = 0; - int pipe; - int ret; - - if (!pg || !dsi_connector || !p_funcs) { - WARN_ON(1); - return NULL; - } - - dsi_config = mdfld_dsi_get_config(dsi_connector); - pipe = dsi_connector->pipe; - - /*panel hard-reset*/ - if (p_funcs->reset) { - ret = p_funcs->reset(pipe); - if (ret) { - DRM_ERROR("Panel %d hard-reset failed\n", pipe); - return NULL; - } - } - /* Panel drvIC init */ - if (p_funcs->drv_ic_init) - p_funcs->drv_ic_init(dsi_config, pipe); - - /* Panel power mode detect */ - ret = mdfld_dsi_get_power_mode(dsi_config, - &data, - MDFLD_DSI_HS_TRANSMISSION); - if (ret) { - DRM_ERROR("Panel %d get power mode failed\n", pipe); - dsi_connector->status = connector_status_disconnected; - } else { - DRM_INFO("pipe %d power mode 0x%x\n", pipe, data); - dsi_connector->status = connector_status_connected; - } - - /*TODO: get panel info from DDB*/ - - dbi_output = kzalloc(sizeof(struct mdfld_dsi_dbi_output), GFP_KERNEL); - if (!dbi_output) { - dev_err(dev->dev, "No memory\n"); - return NULL; - } - - if (dsi_connector->pipe == 0) { - dbi_output->channel_num = 0; - dev_priv->dbi_output = dbi_output; - } else if (dsi_connector->pipe == 2) { - dbi_output->channel_num = 1; - dev_priv->dbi_output2 = dbi_output; - } else { - dev_err(dev->dev, "only support 2 DSI outputs\n"); - goto out_err1; - } - - dbi_output->dev = dev; - dbi_output->p_funcs = p_funcs; - fixed_mode = dsi_config->fixed_mode; - dbi_output->panel_fixed_mode = fixed_mode; - - /* Create drm encoder object */ - connector = &dsi_connector->base.base; - encoder = &dbi_output->base.base; - /* Review this if we ever get MIPI-HDMI bridges or similar */ - drm_encoder_init(dev, - encoder, - p_funcs->encoder_funcs, - DRM_MODE_ENCODER_LVDS); - drm_encoder_helper_add(encoder, p_funcs->encoder_helper_funcs); - - /* Attach to given connector */ - drm_mode_connector_attach_encoder(connector, encoder); - - /* Set possible CRTCs and clones */ - if (dsi_connector->pipe) { - encoder->possible_crtcs = (1 << 2); - encoder->possible_clones = (1 << 1); - } else { - encoder->possible_crtcs = (1 << 0); - encoder->possible_clones = (1 << 0); - } - - dev_priv->dsr_fb_update = 0; - dev_priv->dsr_enable = false; - dev_priv->exit_idle = mdfld_dsi_dbi_exit_dsr; - - dbi_output->first_boot = true; - dbi_output->mode_flags = MODE_SETTING_IN_ENCODER; - - /* Add this output to dpu_info if in DPU mode */ - if (dpu_info && dsi_connector->status == connector_status_connected) { - if (dsi_connector->pipe == 0) - dpu_info->dbi_outputs[0] = dbi_output; - else - dpu_info->dbi_outputs[1] = dbi_output; - - dpu_info->dbi_output_num++; - } else if (dsi_connector->status == connector_status_connected) { - /* Add this output to dsr_info if not */ - if (dsi_connector->pipe == 0) - dsr_info->dbi_outputs[0] = dbi_output; - else - dsr_info->dbi_outputs[1] = dbi_output; - - dsr_info->dbi_output_num++; - } - return &dbi_output->base; -out_err1: - kfree(dbi_output); - return NULL; -} diff --git a/drivers/staging/gma500/mdfld_dsi_dbi.h b/drivers/staging/gma500/mdfld_dsi_dbi.h deleted file mode 100644 index f0fa986..0000000 --- a/drivers/staging/gma500/mdfld_dsi_dbi.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#ifndef __MDFLD_DSI_DBI_H__ -#define __MDFLD_DSI_DBI_H__ - -#include -#include -#include -#include -#include - -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "power.h" - -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" - -/* - * DBI encoder which inherits from mdfld_dsi_encoder - */ -struct mdfld_dsi_dbi_output { - struct mdfld_dsi_encoder base; - struct drm_display_mode *panel_fixed_mode; - u8 last_cmd; - u8 lane_count; - u8 channel_num; - struct drm_device *dev; - - /* Backlight operations */ - - /* DSR timer */ - u32 dsr_idle_count; - bool dsr_fb_update_done; - - /* Mode setting flags */ - u32 mode_flags; - - /* Panel status */ - bool dbi_panel_on; - bool first_boot; - struct panel_funcs *p_funcs; - - /* DPU */ - u32 *dbi_cb_addr; - u32 dbi_cb_phy; - spinlock_t cb_lock; - u32 cb_write; -}; - -#define MDFLD_DSI_DBI_OUTPUT(dsi_encoder) \ - container_of(dsi_encoder, struct mdfld_dsi_dbi_output, base) - -struct mdfld_dbi_dsr_info { - int dbi_output_num; - struct mdfld_dsi_dbi_output *dbi_outputs[2]; - - u32 dsr_idle_count; -}; - -#define DBI_CB_TIMEOUT_COUNT 0xffff - -/* Offsets */ -#define CMD_MEM_ADDR_OFFSET 0 - -#define CMD_DATA_SRC_SYSTEM_MEM 0 -#define CMD_DATA_SRC_PIPE 1 - -static inline int mdfld_dsi_dbi_fifo_ready(struct mdfld_dsi_dbi_output *dbi_output) -{ - struct drm_device *dev = dbi_output->dev; - u32 retry = DBI_CB_TIMEOUT_COUNT; - int reg_offset = (dbi_output->channel_num == 1) ? MIPIC_REG_OFFSET : 0; - int ret = 0; - - /* Query the dbi fifo status*/ - while (retry--) { - if (REG_READ(MIPIA_GEN_FIFO_STAT_REG + reg_offset) & (1 << 27)) - break; - } - - if (!retry) { - DRM_ERROR("Timeout waiting for DBI FIFO empty\n"); - ret = -EAGAIN; - } - return ret; -} - -static inline int mdfld_dsi_dbi_cmd_sent(struct mdfld_dsi_dbi_output *dbi_output) -{ - struct drm_device *dev = dbi_output->dev; - u32 retry = DBI_CB_TIMEOUT_COUNT; - int reg_offset = (dbi_output->channel_num == 1) ? MIPIC_REG_OFFSET : 0; - int ret = 0; - - /* Query the command execution status */ - while (retry--) - if (!(REG_READ(MIPIA_CMD_ADD_REG + reg_offset) & (1 << 0))) - break; - - if (!retry) { - DRM_ERROR("Timeout waiting for DBI command status\n"); - ret = -EAGAIN; - } - - return ret; -} - -static inline int mdfld_dsi_dbi_cb_ready(struct mdfld_dsi_dbi_output *dbi_output) -{ - int ret = 0; - - /* Query the command execution status*/ - ret = mdfld_dsi_dbi_cmd_sent(dbi_output); - if (ret) { - DRM_ERROR("Peripheral is busy\n"); - ret = -EAGAIN; - } - /* Query the dbi fifo status*/ - ret = mdfld_dsi_dbi_fifo_ready(dbi_output); - if (ret) { - DRM_ERROR("DBI FIFO is not empty\n"); - ret = -EAGAIN; - } - return ret; -} - -extern void mdfld_dsi_dbi_output_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev, int pipe); -extern void mdfld_dsi_dbi_exit_dsr(struct drm_device *dev, u32 update_src); -extern void mdfld_dsi_dbi_enter_dsr(struct mdfld_dsi_dbi_output *dbi_output, - int pipe); -extern int mdfld_dbi_dsr_init(struct drm_device *dev); -extern void mdfld_dbi_dsr_exit(struct drm_device *dev); -extern struct mdfld_dsi_encoder *mdfld_dsi_dbi_init(struct drm_device *dev, - struct mdfld_dsi_connector *dsi_connector, - struct panel_funcs *p_funcs); -extern int mdfld_dsi_dbi_send_dcs(struct mdfld_dsi_dbi_output *dbi_output, - u8 dcs, u8 *param, u32 num, u8 data_src); -extern int mdfld_dsi_dbi_update_area(struct mdfld_dsi_dbi_output *dbi_output, - u16 x1, u16 y1, u16 x2, u16 y2); -extern int mdfld_dsi_dbi_update_power(struct mdfld_dsi_dbi_output *dbi_output, - int mode); -extern void mdfld_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config, - int pipe); - -#endif /*__MDFLD_DSI_DBI_H__*/ diff --git a/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c b/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c deleted file mode 100644 index a4e2ff4..0000000 --- a/drivers/staging/gma500/mdfld_dsi_dbi_dpu.c +++ /dev/null @@ -1,778 +0,0 @@ -/* - * Copyright © 2010-2011 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Jim Liu - * Jackie Li - */ - -#include "mdfld_dsi_dbi_dpu.h" -#include "mdfld_dsi_dbi.h" - -/* - * NOTE: all mdlfd_x_damage funcs should be called by holding dpu_update_lock - */ - -static int mdfld_cursor_damage(struct mdfld_dbi_dpu_info *dpu_info, - mdfld_plane_t plane, - struct psb_drm_dpu_rect *damaged_rect) -{ - int x, y; - int new_x, new_y; - struct psb_drm_dpu_rect *rect; - struct psb_drm_dpu_rect *pipe_rect; - int cursor_size; - struct mdfld_cursor_info *cursor; - mdfld_plane_t fb_plane; - - if (plane == MDFLD_CURSORA) { - cursor = &dpu_info->cursors[0]; - x = dpu_info->cursors[0].x; - y = dpu_info->cursors[0].y; - cursor_size = dpu_info->cursors[0].size; - pipe_rect = &dpu_info->damage_pipea; - fb_plane = MDFLD_PLANEA; - } else { - cursor = &dpu_info->cursors[1]; - x = dpu_info->cursors[1].x; - y = dpu_info->cursors[1].y; - cursor_size = dpu_info->cursors[1].size; - pipe_rect = &dpu_info->damage_pipec; - fb_plane = MDFLD_PLANEC; - } - new_x = damaged_rect->x; - new_y = damaged_rect->y; - - if (x == new_x && y == new_y) - return 0; - - rect = &dpu_info->damaged_rects[plane]; - /* Move to right */ - if (new_x >= x) { - if (new_y > y) { - rect->x = x; - rect->y = y; - rect->width = (new_x + cursor_size) - x; - rect->height = (new_y + cursor_size) - y; - goto cursor_out; - } else { - rect->x = x; - rect->y = new_y; - rect->width = (new_x + cursor_size) - x; - rect->height = (y - new_y); - goto cursor_out; - } - } else { - if (new_y > y) { - rect->x = new_x; - rect->y = y; - rect->width = (x + cursor_size) - new_x; - rect->height = new_y - y; - goto cursor_out; - } else { - rect->x = new_x; - rect->y = new_y; - rect->width = (x + cursor_size) - new_x; - rect->height = (y + cursor_size) - new_y; - } - } -cursor_out: - if (new_x < 0) - cursor->x = 0; - else if (new_x > 864) - cursor->x = 864; - else - cursor->x = new_x; - - if (new_y < 0) - cursor->y = 0; - else if (new_y > 480) - cursor->y = 480; - else - cursor->y = new_y; - - /* - * FIXME: this is a workaround for cursor plane update, - * remove it later! - */ - rect->x = 0; - rect->y = 0; - rect->width = 864; - rect->height = 480; - - mdfld_check_boundary(dpu_info, rect); - mdfld_dpu_region_extent(pipe_rect, rect); - - /* Update pending status of dpu_info */ - dpu_info->pending |= (1 << plane); - /* Update fb panel as well */ - dpu_info->pending |= (1 << fb_plane); - return 0; -} - -static int mdfld_fb_damage(struct mdfld_dbi_dpu_info *dpu_info, - mdfld_plane_t plane, - struct psb_drm_dpu_rect *damaged_rect) -{ - struct psb_drm_dpu_rect *rect; - - if (plane == MDFLD_PLANEA) - rect = &dpu_info->damage_pipea; - else - rect = &dpu_info->damage_pipec; - - mdfld_check_boundary(dpu_info, damaged_rect); - - /* Add fb damage area to this pipe */ - mdfld_dpu_region_extent(rect, damaged_rect); - - /* Update pending status of dpu_info */ - dpu_info->pending |= (1 << plane); - return 0; -} - -/* Do nothing here, right now */ -static int mdfld_overlay_damage(struct mdfld_dbi_dpu_info *dpu_info, - mdfld_plane_t plane, - struct psb_drm_dpu_rect *damaged_rect) -{ - return 0; -} - -int mdfld_dbi_dpu_report_damage(struct drm_device *dev, - mdfld_plane_t plane, - struct psb_drm_dpu_rect *rect) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - int ret = 0; - - /* DPU not in use, no damage reporting needed */ - if (dpu_info == NULL) - return 0; - - spin_lock(&dpu_info->dpu_update_lock); - - switch (plane) { - case MDFLD_PLANEA: - case MDFLD_PLANEC: - mdfld_fb_damage(dpu_info, plane, rect); - break; - case MDFLD_CURSORA: - case MDFLD_CURSORC: - mdfld_cursor_damage(dpu_info, plane, rect); - break; - case MDFLD_OVERLAYA: - case MDFLD_OVERLAYC: - mdfld_overlay_damage(dpu_info, plane, rect); - break; - default: - DRM_ERROR("Invalid plane type %d\n", plane); - ret = -EINVAL; - } - spin_unlock(&dpu_info->dpu_update_lock); - return ret; -} - -int mdfld_dbi_dpu_report_fullscreen_damage(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv; - struct mdfld_dbi_dpu_info *dpu_info; - struct mdfld_dsi_config *dsi_config; - struct psb_drm_dpu_rect rect; - int i; - - if (!dev) { - DRM_ERROR("Invalid parameter\n"); - return -EINVAL; - } - - dev_priv = dev->dev_private; - dpu_info = dev_priv->dbi_dpu_info; - - /* This is fine - we may be in non DPU mode */ - if (!dpu_info) - return -EINVAL; - - for (i = 0; i < dpu_info->dbi_output_num; i++) { - dsi_config = dev_priv->dsi_configs[i]; - if (dsi_config) { - rect.x = rect.y = 0; - rect.width = dsi_config->fixed_mode->hdisplay; - rect.height = dsi_config->fixed_mode->vdisplay; - mdfld_dbi_dpu_report_damage(dev, - i ? (MDFLD_PLANEC) : (MDFLD_PLANEA), - &rect); - } - } - /* Exit DSR state */ - mdfld_dpu_exit_dsr(dev); - return 0; -} - -int mdfld_dsi_dbi_dsr_off(struct drm_device *dev, - struct psb_drm_dpu_rect *rect) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - - mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEA, rect); - - /* If dual display mode */ - if (dpu_info->dbi_output_num == 2) - mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEC, rect); - - /* Force dsi to exit DSR mode */ - mdfld_dpu_exit_dsr(dev); - return 0; -} - -static void mdfld_dpu_cursor_plane_flush(struct mdfld_dbi_dpu_info *dpu_info, - mdfld_plane_t plane) -{ - struct drm_device *dev = dpu_info->dev; - u32 curpos_reg = CURAPOS; - u32 curbase_reg = CURABASE; - u32 curcntr_reg = CURACNTR; - struct mdfld_cursor_info *cursor = &dpu_info->cursors[0]; - - if (plane == MDFLD_CURSORC) { - curpos_reg = CURCPOS; - curbase_reg = CURCBASE; - curcntr_reg = CURCCNTR; - cursor = &dpu_info->cursors[1]; - } - - REG_WRITE(curcntr_reg, REG_READ(curcntr_reg)); - REG_WRITE(curpos_reg, - (((cursor->x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) | - ((cursor->y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT))); - REG_WRITE(curbase_reg, REG_READ(curbase_reg)); -} - -static void mdfld_dpu_fb_plane_flush(struct mdfld_dbi_dpu_info *dpu_info, - mdfld_plane_t plane) -{ - u32 pipesrc_reg = PIPEASRC; - u32 dspsize_reg = DSPASIZE; - u32 dspoff_reg = DSPALINOFF; - u32 dspsurf_reg = DSPASURF; - u32 dspstride_reg = DSPASTRIDE; - u32 stride; - struct psb_drm_dpu_rect *rect = &dpu_info->damage_pipea; - struct drm_device *dev = dpu_info->dev; - - if (plane == MDFLD_PLANEC) { - pipesrc_reg = PIPECSRC; - dspsize_reg = DSPCSIZE; - dspoff_reg = DSPCLINOFF; - dspsurf_reg = DSPCSURF; - dspstride_reg = DSPCSTRIDE; - rect = &dpu_info->damage_pipec; - } - - stride = REG_READ(dspstride_reg); - /* FIXME: should I do the pipe src update here? */ - REG_WRITE(pipesrc_reg, ((rect->width - 1) << 16) | (rect->height - 1)); - /* Flush plane */ - REG_WRITE(dspsize_reg, ((rect->height - 1) << 16) | (rect->width - 1)); - REG_WRITE(dspoff_reg, ((rect->x * 4) + (rect->y * stride))); - REG_WRITE(dspsurf_reg, REG_READ(dspsurf_reg)); - - /* - * TODO: wait for flip finished and restore the pipesrc reg, - * or cursor will be show at a wrong position - */ -} - -static void mdfld_dpu_overlay_plane_flush(struct mdfld_dbi_dpu_info *dpu_info, - mdfld_plane_t plane) -{ -} - -/* - * TODO: we are still in dbi normal mode now, we will try to use partial - * mode later. - */ -static int mdfld_dbi_prepare_cb(struct mdfld_dsi_dbi_output *dbi_output, - struct mdfld_dbi_dpu_info *dpu_info, int pipe) -{ - u8 *cb_addr = (u8 *)dbi_output->dbi_cb_addr; - u32 *index; - struct psb_drm_dpu_rect *rect = pipe ? - (&dpu_info->damage_pipec) : (&dpu_info->damage_pipea); - - /* FIXME: lock command buffer, this may lead to a deadlock, - as we already hold the dpu_update_lock */ - if (!spin_trylock(&dbi_output->cb_lock)) { - DRM_ERROR("lock command buffer failed, try again\n"); - return -EAGAIN; - } - - index = &dbi_output->cb_write; - - if (*index) { - DRM_ERROR("DBI command buffer unclean\n"); - return -EAGAIN; - } - - /* Column address */ - *(cb_addr + ((*index)++)) = set_column_address; - *(cb_addr + ((*index)++)) = rect->x >> 8; - *(cb_addr + ((*index)++)) = rect->x; - *(cb_addr + ((*index)++)) = (rect->x + rect->width - 1) >> 8; - *(cb_addr + ((*index)++)) = (rect->x + rect->width - 1); - - *index = 8; - - /* Page address */ - *(cb_addr + ((*index)++)) = set_page_addr; - *(cb_addr + ((*index)++)) = rect->y >> 8; - *(cb_addr + ((*index)++)) = rect->y; - *(cb_addr + ((*index)++)) = (rect->y + rect->height - 1) >> 8; - *(cb_addr + ((*index)++)) = (rect->y + rect->height - 1); - - *index = 16; - - /*write memory*/ - *(cb_addr + ((*index)++)) = write_mem_start; - - return 0; -} - -static int mdfld_dbi_flush_cb(struct mdfld_dsi_dbi_output *dbi_output, int pipe) -{ - u32 cmd_phy = dbi_output->dbi_cb_phy; - u32 *index = &dbi_output->cb_write; - int reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - struct drm_device *dev = dbi_output->dev; - - if (*index == 0 || !dbi_output) - return 0; - - REG_WRITE((MIPIA_CMD_LEN_REG + reg_offset), 0x010505); - REG_WRITE((MIPIA_CMD_ADD_REG + reg_offset), cmd_phy | 3); - - *index = 0; - - /* FIXME: unlock command buffer */ - spin_unlock(&dbi_output->cb_lock); - return 0; -} - -static int mdfld_dpu_update_pipe(struct mdfld_dsi_dbi_output *dbi_output, - struct mdfld_dbi_dpu_info *dpu_info, int pipe) -{ - struct drm_device *dev = dbi_output->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - mdfld_plane_t cursor_plane = MDFLD_CURSORA; - mdfld_plane_t fb_plane = MDFLD_PLANEA; - mdfld_plane_t overlay_plane = MDFLD_OVERLAYA; - int ret = 0; - u32 plane_mask = MDFLD_PIPEA_PLANE_MASK; - - /* Damaged rects on this pipe */ - if (pipe) { - cursor_plane = MDFLD_CURSORC; - fb_plane = MDFLD_PLANEC; - overlay_plane = MDFLD_OVERLAYC; - plane_mask = MDFLD_PIPEC_PLANE_MASK; - } - - /*update cursor which assigned to @pipe*/ - if (dpu_info->pending & (1 << cursor_plane)) - mdfld_dpu_cursor_plane_flush(dpu_info, cursor_plane); - - /*update fb which assigned to @pipe*/ - if (dpu_info->pending & (1 << fb_plane)) - mdfld_dpu_fb_plane_flush(dpu_info, fb_plane); - - /* TODO: update overlay */ - if (dpu_info->pending & (1 << overlay_plane)) - mdfld_dpu_overlay_plane_flush(dpu_info, overlay_plane); - - /* Flush damage area to panel fb */ - if (dpu_info->pending & plane_mask) { - ret = mdfld_dbi_prepare_cb(dbi_output, dpu_info, pipe); - /* - * TODO: remove b_dsr_enable later, - * added it so that text console could boot smoothly - */ - /* Clean pending flags on this pipe */ - if (!ret && dev_priv->dsr_enable) { - dpu_info->pending &= ~plane_mask; - /* Reset overlay pipe damage rect */ - mdfld_dpu_init_damage(dpu_info, pipe); - } - } - return ret; -} - -static int mdfld_dpu_update_fb(struct drm_device *dev) -{ - struct drm_crtc *crtc; - struct psb_intel_crtc *psb_crtc; - struct mdfld_dsi_dbi_output **dbi_output; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - bool pipe_updated[2]; - unsigned long irq_flags; - u32 dpll_reg = MRST_DPLL_A; - u32 dspcntr_reg = DSPACNTR; - u32 pipeconf_reg = PIPEACONF; - u32 dsplinoff_reg = DSPALINOFF; - u32 dspsurf_reg = DSPASURF; - u32 mipi_state_reg = MIPIA_INTR_STAT_REG; - u32 reg_offset = 0; - int pipe; - int i; - int ret; - - dbi_output = dpu_info->dbi_outputs; - pipe_updated[0] = pipe_updated[1] = false; - - if (!gma_power_begin(dev, true)) - return -EAGAIN; - - /* Try to prevent any new damage reports */ - if (!spin_trylock_irqsave(&dpu_info->dpu_update_lock, irq_flags)) - return -EAGAIN; - - for (i = 0; i < dpu_info->dbi_output_num; i++) { - crtc = dbi_output[i]->base.base.crtc; - psb_crtc = (crtc) ? to_psb_intel_crtc(crtc) : NULL; - - pipe = dbi_output[i]->channel_num ? 2 : 0; - - if (pipe == 2) { - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - dsplinoff_reg = DSPCLINOFF; - dspsurf_reg = DSPCSURF; - reg_offset = MIPIC_REG_OFFSET; - } - - if (!(REG_READ((MIPIA_GEN_FIFO_STAT_REG + reg_offset)) - & (1 << 27)) || - !(REG_READ(dpll_reg) & DPLL_VCO_ENABLE) || - !(REG_READ(dspcntr_reg) & DISPLAY_PLANE_ENABLE) || - !(REG_READ(pipeconf_reg) & DISPLAY_PLANE_ENABLE)) { - dev_err(dev->dev, - "DBI FIFO is busy, DSI %d state %x\n", - pipe, - REG_READ(mipi_state_reg + reg_offset)); - continue; - } - - /* - * If DBI output is in a exclusive state then the pipe - * change won't be updated - */ - if (dbi_output[i]->dbi_panel_on && - !(dbi_output[i]->mode_flags & MODE_SETTING_ON_GOING) && - !(psb_crtc && - psb_crtc->mode_flags & MODE_SETTING_ON_GOING) && - !(dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR)) { - ret = mdfld_dpu_update_pipe(dbi_output[i], - dpu_info, dbi_output[i]->channel_num ? 2 : 0); - if (!ret) - pipe_updated[i] = true; - } - } - - for (i = 0; i < dpu_info->dbi_output_num; i++) - if (pipe_updated[i]) - mdfld_dbi_flush_cb(dbi_output[i], - dbi_output[i]->channel_num ? 2 : 0); - - spin_unlock_irqrestore(&dpu_info->dpu_update_lock, irq_flags); - gma_power_end(dev); - return 0; -} - -static int __mdfld_dbi_exit_dsr(struct mdfld_dsi_dbi_output *dbi_output, - int pipe) -{ - struct drm_device *dev = dbi_output->dev; - struct drm_crtc *crtc = dbi_output->base.base.crtc; - struct psb_intel_crtc *psb_crtc = (crtc) ? to_psb_intel_crtc(crtc) - : NULL; - u32 reg_val; - u32 dpll_reg = MRST_DPLL_A; - u32 pipeconf_reg = PIPEACONF; - u32 dspcntr_reg = DSPACNTR; - u32 dspbase_reg = DSPABASE; - u32 dspsurf_reg = DSPASURF; - u32 reg_offset = 0; - - if (!dbi_output) - return 0; - - /* If mode setting on-going, back off */ - if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || - (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING)) - return -EAGAIN; - - if (pipe == 2) { - dpll_reg = MRST_DPLL_A; - pipeconf_reg = PIPECCONF; - dspcntr_reg = DSPCCNTR; - dspbase_reg = MDFLD_DSPCBASE; - dspsurf_reg = DSPCSURF; - - reg_offset = MIPIC_REG_OFFSET; - } - - if (!gma_power_begin(dev, true)) - return -EAGAIN; - - /* Enable DPLL */ - reg_val = REG_READ(dpll_reg); - if (!(reg_val & DPLL_VCO_ENABLE)) { - - if (reg_val & MDFLD_PWR_GATE_EN) { - reg_val &= ~MDFLD_PWR_GATE_EN; - REG_WRITE(dpll_reg, reg_val); - REG_READ(dpll_reg); - udelay(500); - } - - reg_val |= DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, reg_val); - REG_READ(dpll_reg); - udelay(500); - - /* FIXME: add timeout */ - while (!(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) - cpu_relax(); - } - - /* Enable pipe */ - reg_val = REG_READ(pipeconf_reg); - if (!(reg_val & PIPEACONF_ENABLE)) { - reg_val |= PIPEACONF_ENABLE; - REG_WRITE(pipeconf_reg, reg_val); - REG_READ(pipeconf_reg); - udelay(500); - mdfldWaitForPipeEnable(dev, pipe); - } - - /* Enable plane */ - reg_val = REG_READ(dspcntr_reg); - if (!(reg_val & DISPLAY_PLANE_ENABLE)) { - reg_val |= DISPLAY_PLANE_ENABLE; - REG_WRITE(dspcntr_reg, reg_val); - REG_READ(dspcntr_reg); - udelay(500); - } - - gma_power_end(dev); - - /* Clean IN_DSR flag */ - dbi_output->mode_flags &= ~MODE_SETTING_IN_DSR; - - return 0; -} - -int mdfld_dpu_exit_dsr(struct drm_device *dev) -{ - struct mdfld_dsi_dbi_output **dbi_output; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - int i; - int pipe; - - dbi_output = dpu_info->dbi_outputs; - - for (i = 0; i < dpu_info->dbi_output_num; i++) { - /* If this output is not in DSR mode, don't call exit dsr */ - if (dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR) - __mdfld_dbi_exit_dsr(dbi_output[i], - dbi_output[i]->channel_num ? 2 : 0); - } - - /* Enable TE interrupt */ - for (i = 0; i < dpu_info->dbi_output_num; i++) { - /* If this output is not in DSR mode, don't call exit dsr */ - pipe = dbi_output[i]->channel_num ? 2 : 0; - if (dbi_output[i]->dbi_panel_on && pipe) { - mdfld_disable_te(dev, 0); - mdfld_enable_te(dev, 2); - } else if (dbi_output[i]->dbi_panel_on && !pipe) { - mdfld_disable_te(dev, 2); - mdfld_enable_te(dev, 0); - } - } - return 0; -} - -static int mdfld_dpu_enter_dsr(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - struct mdfld_dsi_dbi_output **dbi_output; - int i; - - dbi_output = dpu_info->dbi_outputs; - - for (i = 0; i < dpu_info->dbi_output_num; i++) { - /* If output is off or already in DSR state, don't re-enter */ - if (dbi_output[i]->dbi_panel_on && - !(dbi_output[i]->mode_flags & MODE_SETTING_IN_DSR)) { - mdfld_dsi_dbi_enter_dsr(dbi_output[i], - dbi_output[i]->channel_num ? 2 : 0); - } - } - - return 0; -} - -static void mdfld_dbi_dpu_timer_func(unsigned long data) -{ - struct drm_device *dev = (struct drm_device *)data; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - struct timer_list *dpu_timer = &dpu_info->dpu_timer; - unsigned long flags; - - if (dpu_info->pending) { - dpu_info->idle_count = 0; - /* Update panel fb with damaged area */ - mdfld_dpu_update_fb(dev); - } else { - dpu_info->idle_count++; - } - - if (dpu_info->idle_count >= MDFLD_MAX_IDLE_COUNT) { - mdfld_dpu_enter_dsr(dev); - /* Stop timer by return */ - return; - } - - spin_lock_irqsave(&dpu_info->dpu_timer_lock, flags); - if (!timer_pending(dpu_timer)) { - dpu_timer->expires = jiffies + MDFLD_DSR_DELAY; - add_timer(dpu_timer); - } - spin_unlock_irqrestore(&dpu_info->dpu_timer_lock, flags); -} - -void mdfld_dpu_update_panel(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - - if (dpu_info->pending) { - dpu_info->idle_count = 0; - - /*update panel fb with damaged area*/ - mdfld_dpu_update_fb(dev); - } else { - dpu_info->idle_count++; - } - - if (dpu_info->idle_count >= MDFLD_MAX_IDLE_COUNT) { - /*enter dsr*/ - mdfld_dpu_enter_dsr(dev); - } -} - -static int mdfld_dbi_dpu_timer_init(struct drm_device *dev, - struct mdfld_dbi_dpu_info *dpu_info) -{ - struct timer_list *dpu_timer = &dpu_info->dpu_timer; - unsigned long flags; - - spin_lock_init(&dpu_info->dpu_timer_lock); - spin_lock_irqsave(&dpu_info->dpu_timer_lock, flags); - - init_timer(dpu_timer); - - dpu_timer->data = (unsigned long)dev; - dpu_timer->function = mdfld_dbi_dpu_timer_func; - dpu_timer->expires = jiffies + MDFLD_DSR_DELAY; - - spin_unlock_irqrestore(&dpu_info->dpu_timer_lock, flags); - - return 0; -} - -void mdfld_dbi_dpu_timer_start(struct mdfld_dbi_dpu_info *dpu_info) -{ - struct timer_list *dpu_timer = &dpu_info->dpu_timer; - unsigned long flags; - - spin_lock_irqsave(&dpu_info->dpu_timer_lock, flags); - if (!timer_pending(dpu_timer)) { - dpu_timer->expires = jiffies + MDFLD_DSR_DELAY; - add_timer(dpu_timer); - } - spin_unlock_irqrestore(&dpu_info->dpu_timer_lock, flags); -} - -int mdfld_dbi_dpu_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - - if (!dpu_info || IS_ERR(dpu_info)) { - dpu_info = kzalloc(sizeof(struct mdfld_dbi_dpu_info), - GFP_KERNEL); - if (!dpu_info) { - DRM_ERROR("No memory\n"); - return -ENOMEM; - } - dev_priv->dbi_dpu_info = dpu_info; - } - - dpu_info->dev = dev; - - dpu_info->cursors[0].size = MDFLD_CURSOR_SIZE; - dpu_info->cursors[1].size = MDFLD_CURSOR_SIZE; - - /*init dpu_update_lock*/ - spin_lock_init(&dpu_info->dpu_update_lock); - - /*init dpu refresh timer*/ - mdfld_dbi_dpu_timer_init(dev, dpu_info); - - /*init pipe damage area*/ - mdfld_dpu_init_damage(dpu_info, 0); - mdfld_dpu_init_damage(dpu_info, 2); - - return 0; -} - -void mdfld_dbi_dpu_exit(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - - if (!dpu_info) - return; - - del_timer_sync(&dpu_info->dpu_timer); - kfree(dpu_info); - dev_priv->dbi_dpu_info = NULL; -} - - diff --git a/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h b/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h deleted file mode 100644 index 42367ed..0000000 --- a/drivers/staging/gma500/mdfld_dsi_dbi_dpu.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#ifndef __MDFLD_DSI_DBI_DPU_H__ -#define __MDFLD_DSI_DBI_DPU_H__ - -#include "mdfld_dsi_dbi.h" - -typedef enum { - MDFLD_PLANEA, - MDFLD_PLANEC, - MDFLD_CURSORA, - MDFLD_CURSORC, - MDFLD_OVERLAYA, - MDFLD_OVERLAYC, - MDFLD_PLANE_NUM, -} mdfld_plane_t; - -#define MDFLD_PIPEA_PLANE_MASK 0x15 -#define MDFLD_PIPEC_PLANE_MASK 0x2A - -struct mdfld_cursor_info { - int x, y; - int size; -}; - -#define MDFLD_CURSOR_SIZE 64 - -/* - * enter DSR mode if screen has no update for 2 frames. - */ -#define MDFLD_MAX_IDLE_COUNT 2 - -struct mdfld_dbi_dpu_info { - struct drm_device *dev; - /* Lock */ - spinlock_t dpu_update_lock; - - /* Cursor postion */ - struct mdfld_cursor_info cursors[2]; - - /* Damaged area for each plane */ - struct psb_drm_dpu_rect damaged_rects[MDFLD_PLANE_NUM]; - - /* Final damaged area */ - struct psb_drm_dpu_rect damage_pipea; - struct psb_drm_dpu_rect damage_pipec; - - /* Pending */ - u32 pending; - - /* DPU timer */ - struct timer_list dpu_timer; - spinlock_t dpu_timer_lock; - - /* DPU idle count */ - u32 idle_count; - - /* DSI outputs */ - struct mdfld_dsi_dbi_output *dbi_outputs[2]; - int dbi_output_num; -}; - -static inline int mdfld_dpu_region_extent(struct psb_drm_dpu_rect *origin, - struct psb_drm_dpu_rect *rect) -{ - int x1, y1, x2, y2; - - x1 = origin->x + origin->width; - y1 = origin->y + origin->height; - - x2 = rect->x + rect->width; - y2 = rect->y + rect->height; - - origin->x = min(origin->x, rect->x); - origin->y = min(origin->y, rect->y); - origin->width = max(x1, x2) - origin->x; - origin->height = max(y1, y2) - origin->y; - - return 0; -} - -static inline void mdfld_check_boundary(struct mdfld_dbi_dpu_info *dpu_info, - struct psb_drm_dpu_rect *rect) -{ - if (rect->x < 0) - rect->x = 0; - if (rect->y < 0) - rect->y = 0; - - if (rect->x + rect->width > 864) - rect->width = 864 - rect->x; - if (rect->y + rect->height > 480) - rect->height = 480 - rect->height; - - if (!rect->width) - rect->width = 1; - if (!rect->height) - rect->height = 1; -} - -static inline void mdfld_dpu_init_damage(struct mdfld_dbi_dpu_info *dpu_info, - int pipe) -{ - struct psb_drm_dpu_rect *rect; - - if (pipe == 0) - rect = &dpu_info->damage_pipea; - else - rect = &dpu_info->damage_pipec; - - rect->x = 864; - rect->y = 480; - rect->width = -864; - rect->height = -480; -} - -extern int mdfld_dsi_dbi_dsr_off(struct drm_device *dev, - struct psb_drm_dpu_rect *rect); -extern int mdfld_dbi_dpu_report_damage(struct drm_device *dev, - mdfld_plane_t plane, - struct psb_drm_dpu_rect *rect); -extern int mdfld_dbi_dpu_report_fullscreen_damage(struct drm_device *dev); -extern int mdfld_dpu_exit_dsr(struct drm_device *dev); -extern void mdfld_dbi_dpu_timer_start(struct mdfld_dbi_dpu_info *dpu_info); -extern int mdfld_dbi_dpu_init(struct drm_device *dev); -extern void mdfld_dbi_dpu_exit(struct drm_device *dev); -extern void mdfld_dpu_update_panel(struct drm_device *dev); - -#endif /*__MDFLD_DSI_DBI_DPU_H__*/ diff --git a/drivers/staging/gma500/mdfld_dsi_dpi.c b/drivers/staging/gma500/mdfld_dsi_dpi.c deleted file mode 100644 index e685f12..0000000 --- a/drivers/staging/gma500/mdfld_dsi_dpi.c +++ /dev/null @@ -1,805 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#include "mdfld_dsi_dpi.h" -#include "mdfld_output.h" -#include "mdfld_dsi_pkg_sender.h" - - -static void mdfld_wait_for_HS_DATA_FIFO(struct drm_device *dev, u32 pipe) -{ - u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; - int timeout = 0; - - if (pipe == 2) - gen_fifo_stat_reg += MIPIC_REG_OFFSET; - - udelay(500); - - /* This will time out after approximately 2+ seconds */ - while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_DATA_FULL)) { - udelay(100); - timeout++; - } - - if (timeout == 20000) - dev_warn(dev->dev, "MIPI: HS Data FIFO was never cleared!\n"); -} - -static void mdfld_wait_for_HS_CTRL_FIFO(struct drm_device *dev, u32 pipe) -{ - u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; - int timeout = 0; - - if (pipe == 2) - gen_fifo_stat_reg += MIPIC_REG_OFFSET; - - udelay(500); - - /* This will time out after approximately 2+ seconds */ - while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_CTRL_FULL)) { - udelay(100); - timeout++; - } - if (timeout == 20000) - dev_warn(dev->dev, "MIPI: HS CMD FIFO was never cleared!\n"); -} - -static void mdfld_wait_for_DPI_CTRL_FIFO(struct drm_device *dev, u32 pipe) -{ - u32 gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; - int timeout = 0; - - if (pipe == 2) - gen_fifo_stat_reg += MIPIC_REG_OFFSET; - - udelay(500); - - /* This will time out after approximately 2+ seconds */ - while ((timeout < 20000) && ((REG_READ(gen_fifo_stat_reg) & DPI_FIFO_EMPTY) - != DPI_FIFO_EMPTY)) { - udelay(100); - timeout++; - } - - if (timeout == 20000) - dev_warn(dev->dev, "MIPI: DPI FIFO was never cleared!\n"); -} - -static void mdfld_wait_for_SPL_PKG_SENT(struct drm_device *dev, u32 pipe) -{ - u32 intr_stat_reg = MIPIA_INTR_STAT_REG; - int timeout = 0; - - if (pipe == 2) - intr_stat_reg += MIPIC_REG_OFFSET; - - udelay(500); - - /* This will time out after approximately 2+ seconds */ - while ((timeout < 20000) && (!(REG_READ(intr_stat_reg) & DSI_INTR_STATE_SPL_PKG_SENT))) { - udelay(100); - timeout++; - } - - if (timeout == 20000) - dev_warn(dev->dev, "MIPI: SPL_PKT_SENT_INTERRUPT was not sent successfully!\n"); -} - - -/* ************************************************************************* *\ - * FUNCTION: mdfld_dsi_tpo_ic_init - * - * DESCRIPTION: This function is called only by mrst_dsi_mode_set and - * restore_display_registers. since this function does not - * acquire the mutex, it is important that the calling function - * does! -\* ************************************************************************* */ -void mdfld_dsi_tpo_ic_init(struct mdfld_dsi_config *dsi_config, u32 pipe) -{ - struct drm_device *dev = dsi_config->dev; - u32 dcsChannelNumber = dsi_config->channel_num; - u32 gen_data_reg = MIPIA_HS_GEN_DATA_REG; - u32 gen_ctrl_reg = MIPIA_HS_GEN_CTRL_REG; - u32 gen_ctrl_val = GEN_LONG_WRITE; - - if (pipe == 2) { - gen_data_reg = HS_GEN_DATA_REG + MIPIC_REG_OFFSET; - gen_ctrl_reg = HS_GEN_CTRL_REG + MIPIC_REG_OFFSET; - } - - gen_ctrl_val |= dcsChannelNumber << DCS_CHANNEL_NUMBER_POS; - - /* Flip page order */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00008036); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS)); - - /* 0xF0 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x005a5af0); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); - - /* Write protection key */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x005a5af1); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); - - /* 0xFC */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x005a5afc); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); - - /* 0xB7 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x770000b7); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00000044); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x05 << WORD_COUNTS_POS)); - - /* 0xB6 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x000a0ab6); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); - - /* 0xF2 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x081010f2); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x4a070708); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x000000c5); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); - - /* 0xF8 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x024003f8); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x01030a04); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x0e020220); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00000004); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x0d << WORD_COUNTS_POS)); - - /* 0xE2 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x398fc3e2); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x0000916f); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x06 << WORD_COUNTS_POS)); - - /* 0xB0 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x000000b0); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS)); - - /* 0xF4 */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x240242f4); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x78ee2002); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x2a071050); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x507fee10); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x10300710); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x14 << WORD_COUNTS_POS)); - - /* 0xBA */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x19fe07ba); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x101c0a31); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00000010); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); - - /* 0xBB */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x28ff07bb); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x24280a31); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00000034); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); - - /* 0xFB */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x535d05fb); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x1b1a2130); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x221e180e); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x131d2120); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x535d0508); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x1c1a2131); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x231f160d); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x111b2220); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x535c2008); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x1f1d2433); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x2c251a10); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x2c34372d); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00000023); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS)); - - /* 0xFA */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x525c0bfa); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x1c1c232f); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x2623190e); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x18212625); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x545d0d0e); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x1e1d2333); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x26231a10); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x1a222725); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x545d280f); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x21202635); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x31292013); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x31393d33); - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x00000029); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS)); - - /* Set DM */ - mdfld_wait_for_HS_DATA_FIFO(dev, pipe); - REG_WRITE(gen_data_reg, 0x000100f7); - mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); - REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); -} - -static u16 mdfld_dsi_dpi_to_byte_clock_count(int pixel_clock_count, - int num_lane, int bpp) -{ - return (u16)((pixel_clock_count * bpp) / (num_lane * 8)); -} - -/* - * Calculate the dpi time basing on a given drm mode @mode - * return 0 on success. - * FIXME: I was using proposed mode value for calculation, may need to - * use crtc mode values later - */ -int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode, - struct mdfld_dsi_dpi_timing *dpi_timing, - int num_lane, int bpp) -{ - int pclk_hsync, pclk_hfp, pclk_hbp, pclk_hactive; - int pclk_vsync, pclk_vfp, pclk_vbp, pclk_vactive; - - if(!mode || !dpi_timing) { - DRM_ERROR("Invalid parameter\n"); - return -EINVAL; - } - - pclk_hactive = mode->hdisplay; - pclk_hfp = mode->hsync_start - mode->hdisplay; - pclk_hsync = mode->hsync_end - mode->hsync_start; - pclk_hbp = mode->htotal - mode->hsync_end; - - pclk_vactive = mode->vdisplay; - pclk_vfp = mode->vsync_start - mode->vdisplay; - pclk_vsync = mode->vsync_end - mode->vsync_start; - pclk_vbp = mode->vtotal - mode->vsync_end; - - /* - * byte clock counts were calculated by following formula - * bclock_count = pclk_count * bpp / num_lane / 8 - */ - dpi_timing->hsync_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hsync, num_lane, bpp); - dpi_timing->hbp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hbp, num_lane, bpp); - dpi_timing->hfp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hfp, num_lane, bpp); - dpi_timing->hactive_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_hactive, num_lane, bpp); - dpi_timing->vsync_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vsync, num_lane, bpp); - dpi_timing->vbp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vbp, num_lane, bpp); - dpi_timing->vfp_count = mdfld_dsi_dpi_to_byte_clock_count(pclk_vfp, num_lane, bpp); - - return 0; -} - -void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe) -{ - struct drm_device *dev = dsi_config->dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int lane_count = dsi_config->lane_count; - struct mdfld_dsi_dpi_timing dpi_timing; - struct drm_display_mode *mode = dsi_config->mode; - u32 val = 0; - - /*un-ready device*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); - - /*init dsi adapter before kicking off*/ - REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); - - /*enable all interrupts*/ - REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); - - - /*set up func_prg*/ - val |= lane_count; - val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET; - - switch(dsi_config->bpp) { - case 16: - val |= DSI_DPI_COLOR_FORMAT_RGB565; - break; - case 18: - val |= DSI_DPI_COLOR_FORMAT_RGB666; - break; - case 24: - val |= DSI_DPI_COLOR_FORMAT_RGB888; - break; - default: - DRM_ERROR("unsupported color format, bpp = %d\n", dsi_config->bpp); - } - REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); - - REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), - (mode->vtotal * mode->htotal * dsi_config->bpp / (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK); - REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff & DSI_LP_RX_TIMEOUT_MASK); - - /*max value: 20 clock cycles of txclkesc*/ - REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK); - - /*min 21 txclkesc, max: ffffh*/ - REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0xffff & DSI_RESET_TIMER_MASK); - - REG_WRITE((MIPIA_DPI_RESOLUTION_REG + reg_offset), mode->vdisplay << 16 | mode->hdisplay); - - /*set DPI timing registers*/ - mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, dsi_config->lane_count, dsi_config->bpp); - - REG_WRITE((MIPIA_HSYNC_COUNT_REG + reg_offset), dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_HBP_COUNT_REG + reg_offset), dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_HFP_COUNT_REG + reg_offset), dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_HACTIVE_COUNT_REG + reg_offset), dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_VSYNC_COUNT_REG + reg_offset), dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_VBP_COUNT_REG + reg_offset), dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_VFP_COUNT_REG + reg_offset), dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); - - REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); - - /*min: 7d0 max: 4e20*/ - REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x000007d0); - - /*set up video mode*/ - val = 0; - val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE; - REG_WRITE((MIPIA_VIDEO_MODE_FORMAT_REG + reg_offset), val); - - REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); - - REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); - - /*TODO: figure out how to setup these registers*/ - REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); - - REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14); - /*set device ready*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); -} - -void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, int pipe) -{ - struct drm_device *dev = output->dev; - u32 reg_offset = 0; - - if(output->panel_on) - return; - - if(pipe) - reg_offset = MIPIC_REG_OFFSET; - - /* clear special packet sent bit */ - if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { - REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); - } - - /*send turn on package*/ - REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_TURN_ON); - - /*wait for SPL_PKG_SENT interrupt*/ - mdfld_wait_for_SPL_PKG_SENT(dev, pipe); - - if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { - REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); - } - - output->panel_on = 1; - - /* FIXME the following is disabled to WA the X slow start issue for TMD panel */ - /* if(pipe == 2) */ - /* dev_priv->dpi_panel_on2 = true; */ - /* else if (pipe == 0) */ - /* dev_priv->dpi_panel_on = true; */ -} - -static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output, int pipe) -{ - struct drm_device *dev = output->dev; - u32 reg_offset = 0; - - /*if output is on, or mode setting didn't happen, ignore this*/ - if((!output->panel_on) || output->first_boot) { - output->first_boot = 0; - return; - } - - if(pipe) - reg_offset = MIPIC_REG_OFFSET; - - /* Wait for dpi fifo to empty */ - mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe); - - /* Clear the special packet interrupt bit if set */ - if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { - REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); - } - - if(REG_READ(MIPIA_DPI_CONTROL_REG + reg_offset) == DSI_DPI_CTRL_HS_SHUTDOWN) { - dev_warn(dev->dev, "try to send the same package again, abort!"); - goto shutdown_out; - } - - REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_SHUTDOWN); - -shutdown_out: - output->panel_on = 0; - output->first_boot = 0; - - /* FIXME the following is disabled to WA the X slow start issue for TMD panel */ - /* if(pipe == 2) */ - /* dev_priv->dpi_panel_on2 = false; */ - /* else if (pipe == 0) */ - /* dev_priv->dpi_panel_on = false; */ - /* #ifdef CONFIG_PM_RUNTIME*/ - /* if (drm_psb_ospm && !enable_gfx_rtpm) { */ - /* pm_runtime_allow(&gpDrmDevice->pdev->dev); */ - /* schedule_delayed_work(&dev_priv->rtpm_work, 30 * 1000); */ - /* } */ - /*if (enable_gfx_rtpm) */ - /* pm_schedule_suspend(&dev->pdev->dev, gfxrtdelay); */ - /* #endif */ -} - -void mdfld_dsi_dpi_set_power(struct drm_encoder *encoder, bool on) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dpi_output *dpi_output = MDFLD_DSI_DPI_OUTPUT(dsi_encoder); - struct mdfld_dsi_config *dsi_config = mdfld_dsi_encoder_get_config(dsi_encoder); - int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder); - struct drm_device *dev = dsi_config->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 mipi_reg = MIPI; - u32 pipeconf_reg = PIPEACONF; - - if(pipe) { - mipi_reg = MIPI_C; - pipeconf_reg = PIPECCONF; - } - - /* Start up display island if it was shutdown */ - if (!gma_power_begin(dev, true)) - return; - - if(on) { - if (mdfld_get_panel_type(dev, pipe) == TMD_VID){ - mdfld_dsi_dpi_turn_on(dpi_output, pipe); - } else { - /* Enable mipi port */ - REG_WRITE(mipi_reg, (REG_READ(mipi_reg) | (1 << 31))); - REG_READ(mipi_reg); - - mdfld_dsi_dpi_turn_on(dpi_output, pipe); - mdfld_dsi_tpo_ic_init(dsi_config, pipe); - } - - if(pipe == 2) { - dev_priv->dpi_panel_on2 = true; - } - else { - dev_priv->dpi_panel_on = true; - } - - } else { - if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { - mdfld_dsi_dpi_shut_down(dpi_output, pipe); - } else { - mdfld_dsi_dpi_shut_down(dpi_output, pipe); - /* Disable mipi port */ - REG_WRITE(mipi_reg, (REG_READ(mipi_reg) & ~(1<<31))); - REG_READ(mipi_reg); - } - - if(pipe == 2) - dev_priv->dpi_panel_on2 = false; - else - dev_priv->dpi_panel_on = false; - } - gma_power_end(dev); -} - -void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode) -{ - dev_dbg(encoder->dev->dev, "DPMS %s\n", - (mode == DRM_MODE_DPMS_ON ? "on":"off")); - - if (mode == DRM_MODE_DPMS_ON) - mdfld_dsi_dpi_set_power(encoder, true); - else { - mdfld_dsi_dpi_set_power(encoder, false); -#if 0 /* FIXME */ -#ifdef CONFIG_PM_RUNTIME - if (enable_gfx_rtpm) - pm_schedule_suspend(&gpDrmDevice->pdev->dev, gfxrtdelay); -#endif -#endif - } -} - -bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_config *dsi_config = mdfld_dsi_encoder_get_config(dsi_encoder); - struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; - - if(fixed_mode) { - adjusted_mode->hdisplay = fixed_mode->hdisplay; - adjusted_mode->hsync_start = fixed_mode->hsync_start; - adjusted_mode->hsync_end = fixed_mode->hsync_end; - adjusted_mode->htotal = fixed_mode->htotal; - adjusted_mode->vdisplay = fixed_mode->vdisplay; - adjusted_mode->vsync_start = fixed_mode->vsync_start; - adjusted_mode->vsync_end = fixed_mode->vsync_end; - adjusted_mode->vtotal = fixed_mode->vtotal; - adjusted_mode->clock = fixed_mode->clock; - drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); - } - - return true; -} - -void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder) -{ - mdfld_dsi_dpi_set_power(encoder, false); -} - -void mdfld_dsi_dpi_commit(struct drm_encoder *encoder) -{ - mdfld_dsi_dpi_set_power(encoder, true); -} - -void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dpi_output *dpi_output = MDFLD_DSI_DPI_OUTPUT(dsi_encoder); - struct mdfld_dsi_config *dsi_config = mdfld_dsi_encoder_get_config(dsi_encoder); - struct drm_device *dev = dsi_config->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder); - - u32 pipeconf_reg = PIPEACONF; - u32 dspcntr_reg = DSPACNTR; - u32 mipi_reg = MIPI; - u32 reg_offset = 0; - - u32 pipeconf = dev_priv->pipeconf; - u32 dspcntr = dev_priv->dspcntr; - u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; - - dev_dbg(dev->dev, "set mode %dx%d on pipe %d\n", - mode->hdisplay, mode->vdisplay, pipe); - - if(pipe) { - pipeconf_reg = PIPECCONF; - dspcntr_reg = DSPCCNTR; - mipi_reg = MIPI_C; - reg_offset = MIPIC_REG_OFFSET; - } else { - mipi |= 2; - } - - if (!gma_power_begin(dev, true)) - return; - - /* Set up mipi port FIXME: do at init time */ - REG_WRITE(mipi_reg, mipi); - REG_READ(mipi_reg); - - /* Set up DSI controller DPI interface */ - mdfld_dsi_dpi_controller_init(dsi_config, pipe); - - if (mdfld_get_panel_type(dev, pipe) != TMD_VID) { - /* Turn on DPI interface */ - mdfld_dsi_dpi_turn_on(dpi_output, pipe); - } - - /* Set up pipe */ - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - - /* Set up display plane */ - REG_WRITE(dspcntr_reg, dspcntr); - REG_READ(dspcntr_reg); - - msleep(20); /* FIXME: this should wait for vblank */ - - dev_dbg(dev->dev, "State %x, power %d\n", - REG_READ(MIPIA_INTR_STAT_REG + reg_offset), - dpi_output->panel_on); - - if (mdfld_get_panel_type(dev, pipe) != TMD_VID) { - /* Init driver ic */ - mdfld_dsi_tpo_ic_init(dsi_config, pipe); - /* Init backlight */ - mdfld_dsi_brightness_init(dsi_config, pipe); - } - gma_power_end(dev); -} - - -/* - * Init DSI DPI encoder. - * Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector - * return pointer of newly allocated DPI encoder, NULL on error - */ -struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev, - struct mdfld_dsi_connector *dsi_connector, - struct panel_funcs *p_funcs) -{ - struct mdfld_dsi_dpi_output *dpi_output = NULL; - struct mdfld_dsi_config *dsi_config; - struct drm_connector *connector = NULL; - struct drm_encoder *encoder = NULL; - struct drm_display_mode *fixed_mode = NULL; - int pipe; - u32 data; - int ret; - - if (!dsi_connector || !p_funcs) { - WARN_ON(1); - return NULL; - } - - dsi_config = mdfld_dsi_get_config(dsi_connector); - pipe = dsi_connector->pipe; - - /* Panel hard-reset */ - if (p_funcs->reset) { - ret = p_funcs->reset(pipe); - if (ret) { - DRM_ERROR("Panel %d hard-reset failed\n", pipe); - return NULL; - } - } - - /* Panel drvIC init */ - if (p_funcs->drv_ic_init) - p_funcs->drv_ic_init(dsi_config, pipe); - - /* Panel power mode detect */ - ret = mdfld_dsi_get_power_mode(dsi_config, - &data, - MDFLD_DSI_LP_TRANSMISSION); - if (ret) { - DRM_ERROR("Panel %d get power mode failed\n", pipe); - dsi_connector->status = connector_status_disconnected; - } else { - DRM_INFO("pipe %d power mode 0x%x\n", pipe, data); - dsi_connector->status = connector_status_connected; - } - - dpi_output = kzalloc(sizeof(struct mdfld_dsi_dpi_output), GFP_KERNEL); - if(!dpi_output) { - dev_err(dev->dev, "No memory for dsi_dpi_output\n"); - return NULL; - } - - if(dsi_connector->pipe) - dpi_output->panel_on = 0; - else - dpi_output->panel_on = 0; - - dpi_output->dev = dev; - dpi_output->p_funcs = p_funcs; - dpi_output->first_boot = 1; - - /* Get fixed mode */ - dsi_config = mdfld_dsi_get_config(dsi_connector); - fixed_mode = dsi_config->fixed_mode; - - /* Create drm encoder object */ - connector = &dsi_connector->base.base; - encoder = &dpi_output->base.base; - /* - * On existing hardware this will be a panel of some form, - * if future devices also have HDMI bridges this will need - * revisiting - */ - drm_encoder_init(dev, - encoder, - p_funcs->encoder_funcs, - DRM_MODE_ENCODER_LVDS); - drm_encoder_helper_add(encoder, - p_funcs->encoder_helper_funcs); - - /* Attach to given connector */ - drm_mode_connector_attach_encoder(connector, encoder); - - /* Set possible crtcs and clones */ - if(dsi_connector->pipe) { - encoder->possible_crtcs = (1 << 2); - encoder->possible_clones = (1 << 1); - } else { - encoder->possible_crtcs = (1 << 0); - encoder->possible_clones = (1 << 0); - } - return &dpi_output->base; -} - diff --git a/drivers/staging/gma500/mdfld_dsi_dpi.h b/drivers/staging/gma500/mdfld_dsi_dpi.h deleted file mode 100644 index ed92d45..0000000 --- a/drivers/staging/gma500/mdfld_dsi_dpi.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#ifndef __MDFLD_DSI_DPI_H__ -#define __MDFLD_DSI_DPI_H__ - -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" - -struct mdfld_dsi_dpi_timing { - u16 hsync_count; - u16 hbp_count; - u16 hfp_count; - u16 hactive_count; - u16 vsync_count; - u16 vbp_count; - u16 vfp_count; -}; - -struct mdfld_dsi_dpi_output { - struct mdfld_dsi_encoder base; - struct drm_device *dev; - - int panel_on; - int first_boot; - - struct panel_funcs *p_funcs; -}; - -#define MDFLD_DSI_DPI_OUTPUT(dsi_encoder) \ - container_of(dsi_encoder, struct mdfld_dsi_dpi_output, base) - -extern int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode, - struct mdfld_dsi_dpi_timing *dpi_timing, - int num_lane, int bpp); -extern struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev, - struct mdfld_dsi_connector *dsi_connector, - struct panel_funcs *p_funcs); - -/* Medfield DPI helper functions */ -extern void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode); -extern bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -extern void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder); -extern void mdfld_dsi_dpi_commit(struct drm_encoder *encoder); -extern void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -extern void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, - int pipe); -extern void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *si_config, - int pipe); -#endif /*__MDFLD_DSI_DPI_H__*/ diff --git a/drivers/staging/gma500/mdfld_dsi_output.c b/drivers/staging/gma500/mdfld_dsi_output.c deleted file mode 100644 index 3f979db..0000000 --- a/drivers/staging/gma500/mdfld_dsi_output.c +++ /dev/null @@ -1,1014 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#include "mdfld_dsi_output.h" -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_output.h" -#include -#include "mdfld_dsi_pkg_sender.h" -#include -#include - -#define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100 - -static int CABC_control = 1; -static int LABC_control = 1; - -module_param (CABC_control, int, 0644); -module_param (LABC_control, int, 0644); - -/** - * make these MCS command global - * we don't need 'movl' everytime we send them. - * FIXME: these datas were provided by OEM, we should get them from GCT. - **/ -static u32 mdfld_dbi_mcs_hysteresis[] = { - 0x42000f57, 0x8c006400, 0xff00bf00, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0x38000aff, 0x82005000, 0xff00ab00, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0x000000ff, -}; - -static u32 mdfld_dbi_mcs_display_profile[] = { - 0x50281450, 0x0000c882, 0x00000000, 0x00000000, - 0x00000000, -}; - -static u32 mdfld_dbi_mcs_kbbc_profile[] = { - 0x00ffcc60, 0x00000000, 0x00000000, 0x00000000, -}; - -static u32 mdfld_dbi_mcs_gamma_profile[] = { - 0x81111158, 0x88888888, 0x88888888, -}; - -/* - * write hysteresis values. - */ -static void mdfld_dsi_write_hysteresis (struct mdfld_dsi_config *dsi_config, - int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); - - if(!sender) { - WARN_ON(1); - return; - } - mdfld_dsi_send_mcs_long_hs(sender, - mdfld_dbi_mcs_hysteresis, - 17, - MDFLD_DSI_SEND_PACKAGE); -} - -/* - * write display profile values. - */ -static void mdfld_dsi_write_display_profile(struct mdfld_dsi_config *dsi_config, int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); - - if(!sender) { - WARN_ON(1); - return; - } - mdfld_dsi_send_mcs_long_hs(sender, - mdfld_dbi_mcs_display_profile, - 5, - MDFLD_DSI_SEND_PACKAGE); -} - -/* - * write KBBC profile values. - */ -static void mdfld_dsi_write_kbbc_profile (struct mdfld_dsi_config * dsi_config, int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); - - if(!sender) { - WARN_ON(1); - return; - } - mdfld_dsi_send_mcs_long_hs(sender, - mdfld_dbi_mcs_kbbc_profile, - 4, - MDFLD_DSI_SEND_PACKAGE); -} - -/* - * write gamma setting. - */ -static void mdfld_dsi_write_gamma_setting (struct mdfld_dsi_config *dsi_config, int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); - - if(!sender) { - WARN_ON(1); - return; - } - mdfld_dsi_send_mcs_long_hs(sender, - mdfld_dbi_mcs_gamma_profile, - 3, - MDFLD_DSI_SEND_PACKAGE); -} - -/* - * Check and see if the generic control or data buffer is empty and ready. - */ -void mdfld_dsi_gen_fifo_ready (struct drm_device *dev, u32 gen_fifo_stat_reg, u32 fifo_stat) -{ - u32 GEN_BF_time_out_count = 0; - - /* Check MIPI Adatper command registers */ - for (GEN_BF_time_out_count = 0; GEN_BF_time_out_count < GEN_FB_TIME_OUT; GEN_BF_time_out_count++) - { - if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat) - break; - udelay (100); - } - - if (GEN_BF_time_out_count == GEN_FB_TIME_OUT) - dev_err(dev->dev, - "mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x. \n", - gen_fifo_stat_reg); -} - -/* - * Manage the DSI MIPI keyboard and display brightness. - * FIXME: this is exported to OSPM code. should work out an specific - * display interface to OSPM. - */ -void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = mdfld_dsi_get_pkg_sender(dsi_config); - struct drm_device *dev = sender->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 gen_ctrl_val; - - if(!sender) { - WARN_ON(1); - return; - } - /* Set default display backlight value to 85% (0xd8)*/ - mdfld_dsi_send_mcs_short_hs(sender, - write_display_brightness, - 0xd8, - 1, - MDFLD_DSI_SEND_PACKAGE); - - /* Set minimum brightness setting of CABC function to 20% (0x33)*/ - mdfld_dsi_send_mcs_short_hs(sender, - write_cabc_min_bright, - 0x33, - 1, - MDFLD_DSI_SEND_PACKAGE); - - mdfld_dsi_write_hysteresis(dsi_config, pipe); - mdfld_dsi_write_display_profile (dsi_config, pipe); - mdfld_dsi_write_kbbc_profile (dsi_config, pipe); - mdfld_dsi_write_gamma_setting (dsi_config, pipe); - - /* Enable backlight or/and LABC */ - gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON| BACKLIGHT_ON; - if (LABC_control == 1 || CABC_control == 1) - gen_ctrl_val |= DISPLAY_DIMMING_ON| DISPLAY_BRIGHTNESS_AUTO | GAMMA_AUTO; - - if (LABC_control == 1) - gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON; - - dev_priv->mipi_ctrl_display = gen_ctrl_val; - - mdfld_dsi_send_mcs_short_hs(sender, - write_ctrl_display, - (u8)gen_ctrl_val, - 1, - MDFLD_DSI_SEND_PACKAGE); - - if (CABC_control == 0) - return; - mdfld_dsi_send_mcs_short_hs(sender, - write_ctrl_cabc, - UI_IMAGE, - 1, - MDFLD_DSI_SEND_PACKAGE); -} - -/* - * Manage the mipi display brightness. - * TODO: refine this interface later - */ -void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level) -{ - struct mdfld_dsi_pkg_sender *sender; - struct drm_psb_private *dev_priv; - struct mdfld_dsi_config *dsi_config; - u32 gen_ctrl_val; - int p_type; - - if (!dev || (pipe != 0 && pipe != 2)) { - dev_err(dev->dev, "Invalid parameter\n"); - return; - } - - p_type = mdfld_get_panel_type(dev, 0); - - dev_priv = dev->dev_private; - - if(pipe) - dsi_config = dev_priv->dsi_configs[1]; - else - dsi_config = dev_priv->dsi_configs[0]; - - sender = mdfld_dsi_get_pkg_sender(dsi_config); - - if(!sender) { - WARN_ON(1); - return; - } - - gen_ctrl_val = ((level * 0xff) / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff; - - dev_dbg(dev->dev, - "pipe = %d, gen_ctrl_val = %d. \n", pipe, gen_ctrl_val); - - if(p_type == TMD_VID || p_type == TMD_CMD){ - /* Set display backlight value */ - mdfld_dsi_send_mcs_short_hs(sender, - tmd_write_display_brightness, - (u8)gen_ctrl_val, - 1, - MDFLD_DSI_SEND_PACKAGE); - } else { - /* Set display backlight value */ - mdfld_dsi_send_mcs_short_hs(sender, - write_display_brightness, - (u8)gen_ctrl_val, - 1, - MDFLD_DSI_SEND_PACKAGE); - - - /* Enable backlight control */ - if (level == 0) - gen_ctrl_val = 0; - else - gen_ctrl_val = dev_priv->mipi_ctrl_display; - - mdfld_dsi_send_mcs_short_hs(sender, - write_ctrl_display, - (u8)gen_ctrl_val, - 1, - MDFLD_DSI_SEND_PACKAGE); - } -} - -/* - * shut down DSI controller - */ -void mdfld_dsi_controller_shutdown(struct mdfld_dsi_config * dsi_config, int pipe) -{ - struct drm_device * dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int retry = 100; - - if (!dsi_config) { - WARN_ON(1); - return; - } - - dev = dsi_config->dev; - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - if(!(REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & DSI_DEVICE_READY)) - goto shutdown_out; - - /* Send shut down package, clean packet send bit first */ - if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { - REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), - (REG_READ(MIPIA_INTR_STAT_REG + reg_offset) | DSI_INTR_STATE_SPL_PKG_SENT)); - } - - /*send shut down package in HS*/ - REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_SHUTDOWN); - - - /* - * make sure shut down is sent. - * FIXME: add max retry counter - */ - while(!(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)) { - retry--; - - if(!retry) { - dev_err(dev->dev, "timeout\n"); - break; - } - } - - /*sleep 1 ms to ensure shutdown finished*/ - msleep(100); - - /*un-ready device*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), - (REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & ~DSI_DEVICE_READY)); - -shutdown_out: - gma_power_end(dev); -} - -void mdfld_dsi_controller_startup(struct mdfld_dsi_config * dsi_config, int pipe) -{ - struct drm_device * dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int retry = 100; - - - if (!dsi_config) { - WARN_ON(1); - return; - } - - dev = dsi_config->dev; - dev_dbg(dev->dev, "starting up DSI controller on pipe %d...\n", pipe); - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - if((REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) & DSI_DEVICE_READY)) - goto startup_out; - - /*if config DPI, turn on DPI interface*/ - if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) { - if(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT) { - REG_WRITE((MIPIA_INTR_STAT_REG + reg_offset), DSI_INTR_STATE_SPL_PKG_SENT); - } - - REG_WRITE((MIPIA_DPI_CONTROL_REG + reg_offset), DSI_DPI_CTRL_HS_TURN_ON); - - /* - * make sure shut down is sent. - * FIXME: add max retry counter - */ - while(!(REG_READ(MIPIA_INTR_STAT_REG + reg_offset) & DSI_INTR_STATE_SPL_PKG_SENT)) { - retry--; - if(!retry) { - dev_err(dev->dev, "timeout\n"); - break; - } - } - - msleep(100); - } - - /*set device ready*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), - (REG_READ(MIPIA_DEVICE_READY_REG + reg_offset) | DSI_DEVICE_READY)); - -startup_out: - gma_power_end(dev); -} - - -static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config, - u8 dcs, - u32 *data, - u8 transmission) -{ - struct mdfld_dsi_pkg_sender *sender - = mdfld_dsi_get_pkg_sender(dsi_config); - - if (!sender || !data) { - DRM_ERROR("Invalid parameter\n"); - return -EINVAL; - } - - if (transmission == MDFLD_DSI_HS_TRANSMISSION) - return mdfld_dsi_read_mcs_hs(sender, dcs, data, 1); - else if (transmission == MDFLD_DSI_LP_TRANSMISSION) - return mdfld_dsi_read_mcs_lp(sender, dcs, data, 1); - else - return -EINVAL; -} - -int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, - u32 *mode, - u8 transmission) -{ - if (!dsi_config || !mode) { - DRM_ERROR("Invalid parameter\n"); - return -EINVAL; - } - - return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, transmission); -} - -int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config, - u32 *result, - u8 transmission) -{ - if (!dsi_config || !result) { - DRM_ERROR("Invalid parameter\n"); - return -EINVAL; - } - - return mdfld_dsi_get_panel_status(dsi_config, 0x0f, result, - transmission); -} - -/* - * NOTE: this function was used by OSPM. - * TODO: will be removed later, should work out display interfaces for OSPM - */ -void mdfld_dsi_controller_init(struct mdfld_dsi_config * dsi_config, int pipe) -{ - if(!dsi_config || ((pipe != 0) && (pipe != 2))) { - WARN_ON(1); - return; - } - - if(dsi_config->type) - mdfld_dsi_dpi_controller_init(dsi_config, pipe); - else - mdfld_dsi_controller_dbi_init(dsi_config, pipe); -} - -static void mdfld_dsi_connector_save(struct drm_connector * connector) -{ -} - -static void mdfld_dsi_connector_restore(struct drm_connector * connector) -{ -} - -static enum drm_connector_status mdfld_dsi_connector_detect(struct drm_connector * connector, bool force) -{ - struct psb_intel_output *psb_output - = to_psb_intel_output(connector); - struct mdfld_dsi_connector *dsi_connector - = MDFLD_DSI_CONNECTOR(psb_output); - return dsi_connector->status; -} - -static int mdfld_dsi_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t value) -{ - struct drm_encoder *encoder = connector->encoder; - - if (!strcmp(property->name, "scaling mode") && encoder) { - struct psb_intel_crtc * psb_crtc = to_psb_intel_crtc(encoder->crtc); - bool bTransitionFromToCentered; - uint64_t curValue; - - if (!psb_crtc) - goto set_prop_error; - - switch (value) { - case DRM_MODE_SCALE_FULLSCREEN: - break; - case DRM_MODE_SCALE_NO_SCALE: - break; - case DRM_MODE_SCALE_ASPECT: - break; - default: - goto set_prop_error; - } - - if (drm_connector_property_get_value(connector, property, &curValue)) - goto set_prop_error; - - if (curValue == value) - goto set_prop_done; - - if (drm_connector_property_set_value(connector, property, value)) - goto set_prop_error; - - bTransitionFromToCentered = (curValue == DRM_MODE_SCALE_NO_SCALE) || - (value == DRM_MODE_SCALE_NO_SCALE); - - if (psb_crtc->saved_mode.hdisplay != 0 && - psb_crtc->saved_mode.vdisplay != 0) { - if (bTransitionFromToCentered) { - if (!drm_crtc_helper_set_mode(encoder->crtc, &psb_crtc->saved_mode, - encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb)) - goto set_prop_error; - } else { - struct drm_encoder_helper_funcs *pEncHFuncs = encoder->helper_private; - pEncHFuncs->mode_set(encoder, &psb_crtc->saved_mode, - &psb_crtc->saved_adjusted_mode); - } - } -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - } else if (!strcmp(property->name, "backlight") && encoder) { - struct drm_psb_private *dev_priv = encoder->dev->dev_private; - struct backlight_device *psb_bd = dev_priv->backlight_device; - dev_dbg(encoder->dev->dev, "backlight level = %d\n", (int)value); - if (drm_connector_property_set_value(connector, property, value)) - goto set_prop_error; - else { - dev_dbg(encoder->dev->dev, - "set brightness to %d", (int)value); - if (psb_bd) { - psb_bd->props.brightness = value; - backlight_update_status(psb_bd); - } - } -#endif - } -set_prop_done: - return 0; -set_prop_error: - return -1; -} - -static void mdfld_dsi_connector_destroy(struct drm_connector *connector) -{ - struct psb_intel_output * psb_output = to_psb_intel_output(connector); - struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); - struct mdfld_dsi_pkg_sender * sender; - - if(!dsi_connector) - return; - - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - - sender = dsi_connector->pkg_sender; - - mdfld_dsi_pkg_sender_destroy(sender); - - kfree(dsi_connector); -} - -static int mdfld_dsi_connector_get_modes(struct drm_connector * connector) -{ - struct psb_intel_output * psb_output = to_psb_intel_output(connector); - struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); - struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector); - struct drm_display_mode * fixed_mode = dsi_config->fixed_mode; - struct drm_display_mode * dup_mode = NULL; - struct drm_device * dev = connector->dev; - - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - - if(fixed_mode) { - dev_dbg(dev->dev, "fixed_mode %dx%d\n", - fixed_mode->hdisplay, fixed_mode->vdisplay); - - dup_mode = drm_mode_duplicate(dev, fixed_mode); - drm_mode_probed_add(connector, dup_mode); - return 1; - } - dev_err(dev->dev, "Didn't get any modes!\n"); - return 0; -} - -static int mdfld_dsi_connector_mode_valid(struct drm_connector * connector, struct drm_display_mode * mode) -{ - struct psb_intel_output * psb_output = to_psb_intel_output(connector); - struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); - struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector); - struct drm_display_mode * fixed_mode = dsi_config->fixed_mode; - - dev_dbg(connector->dev->dev, "mode %p, fixed mode %p\n", - mode, fixed_mode); - - if(mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - if(mode->flags & DRM_MODE_FLAG_INTERLACE) - return MODE_NO_INTERLACE; - - /** - * FIXME: current DC has no fitting unit, reject any mode setting request - * will figure out a way to do up-scaling(pannel fitting) later. - **/ - if(fixed_mode) { - if(mode->hdisplay != fixed_mode->hdisplay) - return MODE_PANEL; - - if(mode->vdisplay != fixed_mode->vdisplay) - return MODE_PANEL; - } - dev_dbg(connector->dev->dev, "mode ok\n"); - - return MODE_OK; -} - -static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode) -{ -#ifdef CONFIG_PM_RUNTIME - struct drm_device * dev = connector->dev; - struct drm_psb_private * dev_priv = dev->dev_private; - bool panel_on, panel_on2; -#endif - /* First, execute DPMS */ - drm_helper_connector_dpms(connector, mode); - -#ifdef CONFIG_PM_RUNTIME - if(mdfld_panel_dpi(dev)) { - /* DPI panel */ - panel_on = dev_priv->dpi_panel_on; - panel_on2 = dev_priv->dpi_panel_on2; - } else { - /* DBI panel */ - panel_on = dev_priv->dbi_panel_on; - panel_on2 = dev_priv->dbi_panel_on2; - } - - /* Then check all display panels + monitors status */ - /* Make sure that the Display (B) sub-system status isn't i3 when - * R/W the DC register, otherwise "Fabric error" issue would occur - * during S0i3 state. */ - if(!panel_on && !panel_on2 && !(REG_READ(HDMIB_CONTROL) - & HDMIB_PORT_EN)) { - /* Request rpm idle */ - if(dev_priv->rpm_enabled) - pm_request_idle(&dev->pdev->dev); - } - /* - * if rpm wasn't enabled yet, try to allow it - * FIXME: won't enable rpm for DPI since DPI - * CRTC setting is a little messy now. - * Enable it later! - */ -#if 0 - if(!dev_priv->rpm_enabled && !mdfld_panel_dpi(dev)) - ospm_runtime_pm_allow(dev); -#endif -#endif -} - -static struct drm_encoder *mdfld_dsi_connector_best_encoder( - struct drm_connector *connector) -{ - struct psb_intel_output * psb_output = to_psb_intel_output(connector); - struct mdfld_dsi_connector * dsi_connector = MDFLD_DSI_CONNECTOR(psb_output); - struct mdfld_dsi_config * dsi_config = mdfld_dsi_get_config(dsi_connector); - struct mdfld_dsi_encoder * encoder = NULL; - - if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) - encoder = dsi_config->encoders[MDFLD_DSI_ENCODER_DBI]; - else if (dsi_config->type == MDFLD_DSI_ENCODER_DPI) - encoder = dsi_config->encoders[MDFLD_DSI_ENCODER_DPI]; - - dev_dbg(connector->dev->dev, "get encoder %p\n", encoder); - - if(!encoder) { - dev_err(connector->dev->dev, - "Invalid encoder for type %d\n", dsi_config->type); - return NULL; - } - dsi_config->encoder = encoder; - return &encoder->base; -} - -/* DSI connector funcs */ -static const struct drm_connector_funcs mdfld_dsi_connector_funcs = { - .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms, - .save = mdfld_dsi_connector_save, - .restore = mdfld_dsi_connector_restore, - .detect = mdfld_dsi_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = mdfld_dsi_connector_set_property, - .destroy = mdfld_dsi_connector_destroy, -}; - -/* DSI connector helper funcs */ -static const struct drm_connector_helper_funcs mdfld_dsi_connector_helper_funcs = { - .get_modes = mdfld_dsi_connector_get_modes, - .mode_valid = mdfld_dsi_connector_mode_valid, - .best_encoder = mdfld_dsi_connector_best_encoder, -}; - -static int mdfld_dsi_get_default_config(struct drm_device * dev, - struct mdfld_dsi_config * config, int pipe) -{ - if(!dev || !config) { - WARN_ON(1); - return -EINVAL; - } - - config->bpp = 24; - config->type = mdfld_panel_dpi(dev); - config->lane_count = 2; - config->channel_num = 0; - /*NOTE: video mode is ignored when type is MDFLD_DSI_ENCODER_DBI*/ - if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { - config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE; - } else { - config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE; - } - - return 0; -} - -/* - * Returns the panel fixed mode from configuration. - */ -struct drm_display_mode * -mdfld_dsi_get_configuration_mode(struct mdfld_dsi_config * dsi_config, int pipe) -{ - struct drm_device *dev = dsi_config->dev; - struct drm_display_mode *mode; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; - bool use_gct = false; - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) { - dev_err(dev->dev, "Out of memory for mode\n"); - return NULL; - } - if (use_gct) { - dev_dbg(dev->dev, "gct find MIPI panel.\n"); - - mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; - mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; - mode->hsync_start = mode->hdisplay + \ - ((ti->hsync_offset_hi << 8) | \ - ti->hsync_offset_lo); - mode->hsync_end = mode->hsync_start + \ - ((ti->hsync_pulse_width_hi << 8) | \ - ti->hsync_pulse_width_lo); - mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ - ti->hblank_lo); - mode->vsync_start = \ - mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ - ti->vsync_offset_lo); - mode->vsync_end = \ - mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ - ti->vsync_pulse_width_lo); - mode->vtotal = mode->vdisplay + \ - ((ti->vblank_hi << 8) | ti->vblank_lo); - mode->clock = ti->pixel_clock * 10; - } else { - if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) { - if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { - mode->hdisplay = 480; - mode->vdisplay = 854; - mode->hsync_start = 487; - mode->hsync_end = 490; - mode->htotal = 499; - mode->vsync_start = 861; - mode->vsync_end = 865; - mode->vtotal = 873; - mode->clock = 33264; - } else { - mode->hdisplay = 864; - mode->vdisplay = 480; - mode->hsync_start = 873; - mode->hsync_end = 876; - mode->htotal = 887; - mode->vsync_start = 487; - mode->vsync_end = 490; - mode->vtotal = 499; - mode->clock = 33264; - } - } else if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) { - mode->hdisplay = 864; - mode->vdisplay = 480; - mode->hsync_start = 872; - mode->hsync_end = 876; - mode->htotal = 884; - mode->vsync_start = 482; - mode->vsync_end = 494; - mode->vtotal = 486; - mode->clock = 25777; - - } - } - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - mode->type |= DRM_MODE_TYPE_PREFERRED; - - return mode; -} - -int mdfld_dsi_panel_reset(int pipe) -{ - unsigned gpio; - int ret = 0; - - switch (pipe) { - case 0: - gpio = 128; - break; - case 2: - gpio = 34; - break; - default: - DRM_ERROR("Invalid output\n"); - return -EINVAL; - } - - ret = gpio_request(gpio, "gfx"); - if (ret) { - DRM_ERROR("gpio_rqueset failed\n"); - return ret; - } - - ret = gpio_direction_output(gpio, 1); - if (ret) { - DRM_ERROR("gpio_direction_output failed\n"); - goto gpio_error; - } - - gpio_get_value(128); - -gpio_error: - if (gpio_is_valid(gpio)) - gpio_free(gpio); - - return ret; -} - -/* - * MIPI output init - * @dev drm device - * @pipe pipe number. 0 or 2 - * @config - * - * Do the initialization of a MIPI output, including create DRM mode objects - * initialization of DSI output on @pipe - */ -void mdfld_dsi_output_init(struct drm_device *dev, - int pipe, - struct mdfld_dsi_config *config, - struct panel_funcs* p_cmd_funcs, - struct panel_funcs* p_vid_funcs) -{ - struct mdfld_dsi_config * dsi_config; - struct mdfld_dsi_connector * dsi_connector; - struct psb_intel_output * psb_output; - struct drm_connector * connector; - struct mdfld_dsi_encoder * encoder; - struct drm_psb_private * dev_priv = dev->dev_private; - struct panel_info dsi_panel_info; - u32 width_mm, height_mm; - - dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe); - - if(!dev || ((pipe != 0) && (pipe != 2))) { - WARN_ON(1); - return; - } - - /*create a new connetor*/ - dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL); - if(!dsi_connector) { - DRM_ERROR("No memory"); - return; - } - - dsi_connector->pipe = pipe; - - /*set DSI config*/ - if(config) { - dsi_config = config; - } else { - dsi_config = kzalloc(sizeof(struct mdfld_dsi_config), GFP_KERNEL); - if(!dsi_config) { - dev_err(dev->dev, - "cannot allocate memory for DSI config\n"); - goto dsi_init_err0; - } - - mdfld_dsi_get_default_config(dev, dsi_config, pipe); - } - - dsi_connector->private = dsi_config; - - dsi_config->changed = 1; - dsi_config->dev = dev; - - /* Init fixed mode basing on DSI config type */ - if(dsi_config->type == MDFLD_DSI_ENCODER_DBI) { - dsi_config->fixed_mode = p_cmd_funcs->get_config_mode(dev); - if(p_cmd_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) - goto dsi_init_err0; - } else if(dsi_config->type == MDFLD_DSI_ENCODER_DPI) { - dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev); - if(p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) - goto dsi_init_err0; - } - - width_mm = dsi_panel_info.width_mm; - height_mm = dsi_panel_info.height_mm; - - dsi_config->mode = dsi_config->fixed_mode; - dsi_config->connector = dsi_connector; - - if(!dsi_config->fixed_mode) { - dev_err(dev->dev, "No pannel fixed mode was found\n"); - goto dsi_init_err0; - } - - if(pipe && dev_priv->dsi_configs[0]) { - dsi_config->dvr_ic_inited = 0; - dev_priv->dsi_configs[1] = dsi_config; - } else if(pipe == 0) { - dsi_config->dvr_ic_inited = 1; - dev_priv->dsi_configs[0] = dsi_config; - } else { - dev_err(dev->dev, "Trying to init MIPI1 before MIPI0\n"); - goto dsi_init_err0; - } - - /*init drm connector object*/ - psb_output = &dsi_connector->base; - - psb_output->type = (pipe == 0) ? INTEL_OUTPUT_MIPI : INTEL_OUTPUT_MIPI2; - - connector = &psb_output->base; - /* Revisit type if MIPI/HDMI bridges ever appear on Medfield */ - drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs); - - connector->display_info.subpixel_order = SubPixelHorizontalRGB; - connector->display_info.width_mm = width_mm; - connector->display_info.height_mm = height_mm; - connector->interlace_allowed = false; - connector->doublescan_allowed = false; - - /* Attach properties */ - drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, dev_priv->backlight_property, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); - - /* Init DSI package sender on this output */ - if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) { - DRM_ERROR("Package Sender initialization failed on pipe %d\n", pipe); - goto dsi_init_err0; - } - - /* Init DBI & DPI encoders */ - if (p_cmd_funcs) { - encoder = mdfld_dsi_dbi_init(dev, dsi_connector, p_cmd_funcs); - if(!encoder) { - dev_err(dev->dev, "Create DBI encoder failed\n"); - goto dsi_init_err1; - } - encoder->private = dsi_config; - dsi_config->encoders[MDFLD_DSI_ENCODER_DBI] = encoder; - } - - if(p_vid_funcs) { - encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs); - if(!encoder) { - dev_err(dev->dev, "Create DPI encoder failed\n"); - goto dsi_init_err1; - } - encoder->private = dsi_config; - dsi_config->encoders[MDFLD_DSI_ENCODER_DPI] = encoder; - } - - drm_sysfs_connector_add(connector); - return; - - /*TODO: add code to destroy outputs on error*/ -dsi_init_err1: - /*destroy sender*/ - mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender); - - drm_connector_cleanup(connector); - kfree(dsi_config->fixed_mode); - kfree(dsi_config); -dsi_init_err0: - kfree(dsi_connector); -} diff --git a/drivers/staging/gma500/mdfld_dsi_output.h b/drivers/staging/gma500/mdfld_dsi_output.h deleted file mode 100644 index 4699267..0000000 --- a/drivers/staging/gma500/mdfld_dsi_output.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#ifndef __MDFLD_DSI_OUTPUT_H__ -#define __MDFLD_DSI_OUTPUT_H__ - -#include -#include -#include -#include -#include - -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "power.h" -#include "mdfld_output.h" - -#include - - -static inline struct mdfld_dsi_config * - mdfld_dsi_get_config(struct mdfld_dsi_connector *connector) -{ - if (!connector) - return NULL; - return (struct mdfld_dsi_config *)connector->private; -} - -static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config) -{ - struct mdfld_dsi_connector *dsi_connector; - - if (!config) - return NULL; - - dsi_connector = config->connector; - - if (!dsi_connector) - return NULL; - - return dsi_connector->pkg_sender; -} - -static inline struct mdfld_dsi_config * - mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder) -{ - if (!encoder) - return NULL; - return (struct mdfld_dsi_config *)encoder->private; -} - -static inline struct mdfld_dsi_connector * - mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder) -{ - struct mdfld_dsi_config *config; - - if (!encoder) - return NULL; - - config = mdfld_dsi_encoder_get_config(encoder); - if (!config) - return NULL; - - return config->connector; -} - -static inline void *mdfld_dsi_encoder_get_pkg_sender( - struct mdfld_dsi_encoder *encoder) -{ - struct mdfld_dsi_config *dsi_config; - - dsi_config = mdfld_dsi_encoder_get_config(encoder); - if (!dsi_config) - return NULL; - - return mdfld_dsi_get_pkg_sender(dsi_config); -} - -static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder) -{ - struct mdfld_dsi_connector *connector; - - if (!encoder) - return -1; - - connector = mdfld_dsi_encoder_get_connector(encoder); - if (!connector) - return -1; - - return connector->pipe; -} - -extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, - u32 gen_fifo_stat_reg, u32 fifo_stat); -extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, - int pipe); -extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, - int level); -extern void mdfld_dsi_output_init(struct drm_device *dev, int pipe, - struct mdfld_dsi_config *config, - struct panel_funcs *p_cmd_funcs, - struct panel_funcs *p_vid_funcs); -extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, - int pipe); -extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, - u32 *mode, - u8 transmission); -extern int mdfld_dsi_get_diagnostic_result(struct mdfld_dsi_config *dsi_config, - u32 *result, - u8 transmission); -extern int mdfld_dsi_panel_reset(int pipe); - -#endif /*__MDFLD_DSI_OUTPUT_H__*/ diff --git a/drivers/staging/gma500/mdfld_dsi_pkg_sender.c b/drivers/staging/gma500/mdfld_dsi_pkg_sender.c deleted file mode 100644 index 9b96a5c..0000000 --- a/drivers/staging/gma500/mdfld_dsi_pkg_sender.c +++ /dev/null @@ -1,1484 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Jackie Li - */ - -#include - -#include "mdfld_dsi_output.h" -#include "mdfld_dsi_pkg_sender.h" -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" - -#define MDFLD_DSI_DBI_FIFO_TIMEOUT 100 -#define MDFLD_DSI_MAX_RETURN_PACKET_SIZE 512 -#define MDFLD_DSI_READ_MAX_COUNT 5000 - -static const char * const dsi_errors[] = { - "RX SOT Error", - "RX SOT Sync Error", - "RX EOT Sync Error", - "RX Escape Mode Entry Error", - "RX LP TX Sync Error", - "RX HS Receive Timeout Error", - "RX False Control Error", - "RX ECC Single Bit Error", - "RX ECC Multibit Error", - "RX Checksum Error", - "RX DSI Data Type Not Recognised", - "RX DSI VC ID Invalid", - "TX False Control Error", - "TX ECC Single Bit Error", - "TX ECC Multibit Error", - "TX Checksum Error", - "TX DSI Data Type Not Recognised", - "TX DSI VC ID invalid", - "High Contention", - "Low contention", - "DPI FIFO Under run", - "HS TX Timeout", - "LP RX Timeout", - "Turn Around ACK Timeout", - "ACK With No Error", - "RX Invalid TX Length", - "RX Prot Violation", - "HS Generic Write FIFO Full", - "LP Generic Write FIFO Full", - "Generic Read Data Avail", - "Special Packet Sent", - "Tearing Effect", -}; - -static int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender, - u32 mask) -{ - struct drm_device *dev = sender->dev; - u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg; - int retry = 0xffff; - - while (retry--) { - if ((mask & REG_READ(gen_fifo_stat_reg)) == mask) - return 0; - udelay(100); - } - dev_err(dev->dev, "fifo is NOT empty 0x%08x\n", - REG_READ(gen_fifo_stat_reg)); - return -EIO; -} - -static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender) -{ - return wait_for_gen_fifo_empty(sender, (1 << 2) | (1 << 10) | (1 << 18) - | (1 << 26) | (1 << 27) | (1 << 28)); -} - -static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender) -{ - return wait_for_gen_fifo_empty(sender, (1 << 10) | (1 << 26)); -} - -static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender) -{ - return wait_for_gen_fifo_empty(sender, (1 << 2) | (1 << 18)); -} - -static int wait_for_dbi_fifo_empty(struct mdfld_dsi_pkg_sender *sender) -{ - return wait_for_gen_fifo_empty(sender, (1 << 27)); -} - -static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask) -{ - u32 intr_stat_reg = sender->mipi_intr_stat_reg; - struct drm_device *dev = sender->dev; - - switch (mask) { - case (1 << 0): - case (1 << 1): - case (1 << 2): - case (1 << 3): - case (1 << 4): - case (1 << 5): - case (1 << 6): - case (1 << 7): - case (1 << 8): - case (1 << 9): - case (1 << 10): - case (1 << 11): - case (1 << 12): - case (1 << 13): - break; - case (1 << 14): - /*wait for all fifo empty*/ - /*wait_for_all_fifos_empty(sender)*/; - break; - case (1 << 15): - break; - case (1 << 16): - break; - case (1 << 17): - break; - case (1 << 18): - case (1 << 19): - /*wait for contention recovery time*/ - /*mdelay(10);*/ - /*wait for all fifo empty*/ - if (0) - wait_for_all_fifos_empty(sender); - break; - case (1 << 20): - break; - case (1 << 21): - /*wait for all fifo empty*/ - /*wait_for_all_fifos_empty(sender);*/ - break; - case (1 << 22): - break; - case (1 << 23): - case (1 << 24): - case (1 << 25): - case (1 << 26): - case (1 << 27): - /* HS Gen fifo full */ - REG_WRITE(intr_stat_reg, mask); - wait_for_hs_fifos_empty(sender); - break; - case (1 << 28): - /* LP Gen fifo full\n */ - REG_WRITE(intr_stat_reg, mask); - wait_for_lp_fifos_empty(sender); - break; - case (1 << 29): - case (1 << 30): - case (1 << 31): - break; - } - - if (mask & REG_READ(intr_stat_reg)) - dev_warn(dev->dev, "Cannot clean interrupt 0x%08x\n", mask); - - return 0; -} - -static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender) -{ - struct drm_device *dev = sender->dev; - u32 intr_stat_reg = sender->mipi_intr_stat_reg; - u32 mask; - u32 intr_stat; - int i; - int err = 0; - - intr_stat = REG_READ(intr_stat_reg); - - for (i = 0; i < 32; i++) { - mask = (0x00000001UL) << i; - if (intr_stat & mask) { - dev_dbg(dev->dev, "[DSI]: %s\n", dsi_errors[i]); - err = handle_dsi_error(sender, mask); - if (err) - dev_err(dev->dev, "Cannot handle error\n"); - } - } - return err; -} - -static inline int dbi_cmd_sent(struct mdfld_dsi_pkg_sender *sender) -{ - struct drm_device *dev = sender->dev; - u32 retry = 0xffff; - u32 dbi_cmd_addr_reg = sender->mipi_cmd_addr_reg; - - /* Query the command execution status */ - while (retry--) { - if (!(REG_READ(dbi_cmd_addr_reg) & (1 << 0))) - break; - } - - if (!retry) { - dev_err(dev->dev, "Timeout waiting for DBI Command status\n"); - return -EAGAIN; - } - return 0; -} - -/* - * NOTE: this interface is abandoned expect for write_mem_start DCS - * other DCS are sent via generic pkg interfaces - */ -static int send_dcs_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - struct drm_device *dev = sender->dev; - struct mdfld_dsi_dcs_pkg *dcs_pkg = &pkg->pkg.dcs_pkg; - u32 dbi_cmd_len_reg = sender->mipi_cmd_len_reg; - u32 dbi_cmd_addr_reg = sender->mipi_cmd_addr_reg; - u32 cb_phy = sender->dbi_cb_phy; - u32 index = 0; - u8 *cb = (u8 *)sender->dbi_cb_addr; - int i; - int ret; - - if (!sender->dbi_pkg_support) { - dev_err(dev->dev, "Trying to send DCS on a non DBI output, abort!\n"); - return -ENOTSUPP; - } - - /*wait for DBI fifo empty*/ - wait_for_dbi_fifo_empty(sender); - - *(cb + (index++)) = dcs_pkg->cmd; - if (dcs_pkg->param_num) { - for (i = 0; i < dcs_pkg->param_num; i++) - *(cb + (index++)) = *(dcs_pkg->param + i); - } - - REG_WRITE(dbi_cmd_len_reg, (1 + dcs_pkg->param_num)); - REG_WRITE(dbi_cmd_addr_reg, - (cb_phy << CMD_MEM_ADDR_OFFSET) - | (1 << 0) - | ((dcs_pkg->data_src == CMD_DATA_SRC_PIPE) ? (1 << 1) : 0)); - - ret = dbi_cmd_sent(sender); - if (ret) { - dev_err(dev->dev, "command 0x%x not complete\n", dcs_pkg->cmd); - return -EAGAIN; - } - return 0; -} - -static int __send_short_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - struct drm_device *dev = sender->dev; - u32 hs_gen_ctrl_reg = sender->mipi_hs_gen_ctrl_reg; - u32 lp_gen_ctrl_reg = sender->mipi_lp_gen_ctrl_reg; - u32 gen_ctrl_val = 0; - struct mdfld_dsi_gen_short_pkg *short_pkg = &pkg->pkg.short_pkg; - - gen_ctrl_val |= short_pkg->cmd << MCS_COMMANDS_POS; - gen_ctrl_val |= 0 << DCS_CHANNEL_NUMBER_POS; - gen_ctrl_val |= pkg->pkg_type; - gen_ctrl_val |= short_pkg->param << MCS_PARAMETER_POS; - - if (pkg->transmission_type == MDFLD_DSI_HS_TRANSMISSION) { - /* wait for hs fifo empty */ - /* wait_for_hs_fifos_empty(sender); */ - /* Send pkg */ - REG_WRITE(hs_gen_ctrl_reg, gen_ctrl_val); - } else if (pkg->transmission_type == MDFLD_DSI_LP_TRANSMISSION) { - /* wait_for_lp_fifos_empty(sender); */ - /* Send pkg*/ - REG_WRITE(lp_gen_ctrl_reg, gen_ctrl_val); - } else { - dev_err(dev->dev, "Unknown transmission type %d\n", - pkg->transmission_type); - return -EINVAL; - } - - return 0; -} - -static int __send_long_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - struct drm_device *dev = sender->dev; - u32 hs_gen_ctrl_reg = sender->mipi_hs_gen_ctrl_reg; - u32 hs_gen_data_reg = sender->mipi_hs_gen_data_reg; - u32 lp_gen_ctrl_reg = sender->mipi_lp_gen_ctrl_reg; - u32 lp_gen_data_reg = sender->mipi_lp_gen_data_reg; - u32 gen_ctrl_val = 0; - u32 *dp; - int i; - struct mdfld_dsi_gen_long_pkg *long_pkg = &pkg->pkg.long_pkg; - - dp = long_pkg->data; - - /* - * Set up word count for long pkg - * FIXME: double check word count field. - * currently, using the byte counts of the payload as the word count. - * ------------------------------------------------------------ - * | DI | WC | ECC| PAYLOAD |CHECKSUM| - * ------------------------------------------------------------ - */ - gen_ctrl_val |= (long_pkg->len << 2) << WORD_COUNTS_POS; - gen_ctrl_val |= 0 << DCS_CHANNEL_NUMBER_POS; - gen_ctrl_val |= pkg->pkg_type; - - if (pkg->transmission_type == MDFLD_DSI_HS_TRANSMISSION) { - /* Wait for hs ctrl and data fifos to be empty */ - /* wait_for_hs_fifos_empty(sender); */ - for (i = 0; i < long_pkg->len; i++) - REG_WRITE(hs_gen_data_reg, *(dp + i)); - REG_WRITE(hs_gen_ctrl_reg, gen_ctrl_val); - } else if (pkg->transmission_type == MDFLD_DSI_LP_TRANSMISSION) { - /* wait_for_lp_fifos_empty(sender); */ - for (i = 0; i < long_pkg->len; i++) - REG_WRITE(lp_gen_data_reg, *(dp + i)); - REG_WRITE(lp_gen_ctrl_reg, gen_ctrl_val); - } else { - dev_err(dev->dev, "Unknown transmission type %d\n", - pkg->transmission_type); - return -EINVAL; - } - - return 0; - -} - -static int send_mcs_short_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - return __send_short_pkg(sender, pkg); -} - -static int send_mcs_long_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - return __send_long_pkg(sender, pkg); -} - -static int send_gen_short_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - return __send_short_pkg(sender, pkg); -} - -static int send_gen_long_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - return __send_long_pkg(sender, pkg); -} - -static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - u8 cmd; - u8 *data; - - switch (pkg->pkg_type) { - case MDFLD_DSI_PKG_DCS: - cmd = pkg->pkg.dcs_pkg.cmd; - break; - case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0: - case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1: - cmd = pkg->pkg.short_pkg.cmd; - break; - case MDFLD_DSI_PKG_MCS_LONG_WRITE: - data = (u8 *)pkg->pkg.long_pkg.data; - cmd = *data; - break; - default: - return 0; - } - - /* This prevents other package sending while doing msleep */ - sender->status = MDFLD_DSI_PKG_SENDER_BUSY; - - /* Check panel mode v.s. sending command */ - if ((sender->panel_mode & MDFLD_DSI_PANEL_MODE_SLEEP) && - cmd != exit_sleep_mode) { - dev_err(sender->dev->dev, - "sending 0x%x when panel sleep in\n", cmd); - sender->status = MDFLD_DSI_PKG_SENDER_FREE; - return -EINVAL; - } - - /* Wait for 120 milliseconds in case exit_sleep_mode just be sent */ - if (cmd == DCS_ENTER_SLEEP_MODE) { - /*TODO: replace it with msleep later*/ - mdelay(120); - } - return 0; -} - -static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - u8 cmd; - u8 *data; - - switch (pkg->pkg_type) { - case MDFLD_DSI_PKG_DCS: - cmd = pkg->pkg.dcs_pkg.cmd; - break; - case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0: - case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1: - cmd = pkg->pkg.short_pkg.cmd; - break; - case MDFLD_DSI_PKG_MCS_LONG_WRITE: - data = (u8 *)pkg->pkg.long_pkg.data; - cmd = *data; - break; - default: - return 0; - } - - /* Update panel status */ - if (cmd == DCS_ENTER_SLEEP_MODE) { - sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP; - /*TODO: replace it with msleep later*/ - mdelay(120); - } else if (cmd == DCS_EXIT_SLEEP_MODE) { - sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP; - /*TODO: replace it with msleep later*/ - mdelay(120); - } else if (unlikely(cmd == DCS_SOFT_RESET)) { - /*TODO: replace it with msleep later*/ - mdelay(5); - } - sender->status = MDFLD_DSI_PKG_SENDER_FREE; - return 0; - -} - -static int do_send_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - int ret; - - if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) { - dev_err(sender->dev->dev, "sender is busy\n"); - return -EAGAIN; - } - - ret = send_pkg_prepare(sender, pkg); - if (ret) { - dev_err(sender->dev->dev, "send_pkg_prepare error\n"); - return ret; - } - - switch (pkg->pkg_type) { - case MDFLD_DSI_PKG_DCS: - ret = send_dcs_pkg(sender, pkg); - break; - case MDFLD_DSI_PKG_GEN_SHORT_WRITE_0: - case MDFLD_DSI_PKG_GEN_SHORT_WRITE_1: - case MDFLD_DSI_PKG_GEN_SHORT_WRITE_2: - case MDFLD_DSI_PKG_GEN_READ_0: - case MDFLD_DSI_PKG_GEN_READ_1: - case MDFLD_DSI_PKG_GEN_READ_2: - ret = send_gen_short_pkg(sender, pkg); - break; - case MDFLD_DSI_PKG_GEN_LONG_WRITE: - ret = send_gen_long_pkg(sender, pkg); - break; - case MDFLD_DSI_PKG_MCS_SHORT_WRITE_0: - case MDFLD_DSI_PKG_MCS_SHORT_WRITE_1: - case MDFLD_DSI_PKG_MCS_READ: - ret = send_mcs_short_pkg(sender, pkg); - break; - case MDFLD_DSI_PKG_MCS_LONG_WRITE: - ret = send_mcs_long_pkg(sender, pkg); - break; - default: - dev_err(sender->dev->dev, "Invalid pkg type 0x%x\n", - pkg->pkg_type); - ret = -EINVAL; - } - send_pkg_done(sender, pkg); - return ret; -} - -static int send_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - int err ; - - /* Handle DSI error */ - err = dsi_error_handler(sender); - if (err) { - dev_err(sender->dev->dev, "Error handling failed\n"); - err = -EAGAIN; - goto send_pkg_err; - } - - /* Send pkg */ - err = do_send_pkg(sender, pkg); - if (err) { - dev_err(sender->dev->dev, "sent pkg failed\n"); - err = -EAGAIN; - goto send_pkg_err; - } - - /* FIXME: should I query complete and fifo empty here? */ -send_pkg_err: - return err; -} - -static struct mdfld_dsi_pkg *pkg_sender_get_pkg_locked( - struct mdfld_dsi_pkg_sender *sender) -{ - struct mdfld_dsi_pkg *pkg; - - if (list_empty(&sender->free_list)) { - dev_err(sender->dev->dev, "No free pkg left\n"); - return NULL; - } - pkg = list_first_entry(&sender->free_list, struct mdfld_dsi_pkg, entry); - /* Detach from free list */ - list_del_init(&pkg->entry); - return pkg; -} - -static void pkg_sender_put_pkg_locked(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg) -{ - memset(pkg, 0, sizeof(struct mdfld_dsi_pkg)); - INIT_LIST_HEAD(&pkg->entry); - list_add_tail(&pkg->entry, &sender->free_list); -} - -static int mdfld_dbi_cb_init(struct mdfld_dsi_pkg_sender *sender, - struct psb_gtt *pg, int pipe) -{ - unsigned long phys; - void *virt_addr = NULL; - - switch (pipe) { - case 0: - /* FIXME: Doesn't this collide with stolen space ? */ - phys = pg->gtt_phys_start - 0x1000; - break; - case 2: - phys = pg->gtt_phys_start - 0x800; - break; - default: - dev_err(sender->dev->dev, "Unsupported channel %d\n", pipe); - return -EINVAL; - } - - virt_addr = ioremap_nocache(phys, 0x800); - if (!virt_addr) { - dev_err(sender->dev->dev, "Map DBI command buffer error\n"); - return -ENOMEM; - } - sender->dbi_cb_phy = phys; - sender->dbi_cb_addr = virt_addr; - return 0; -} - -static void mdfld_dbi_cb_destroy(struct mdfld_dsi_pkg_sender *sender) -{ - if (sender && sender->dbi_cb_addr) - iounmap(sender->dbi_cb_addr); -} - -static void pkg_sender_queue_pkg(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg, - int delay) -{ - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - - if (!delay) { - send_pkg(sender, pkg); - pkg_sender_put_pkg_locked(sender, pkg); - } else { - /* Queue it */ - list_add_tail(&pkg->entry, &sender->pkg_list); - } - spin_unlock_irqrestore(&sender->lock, flags); -} - -static void process_pkg_list(struct mdfld_dsi_pkg_sender *sender) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - - while (!list_empty(&sender->pkg_list)) { - pkg = list_first_entry(&sender->pkg_list, - struct mdfld_dsi_pkg, entry); - send_pkg(sender, pkg); - list_del_init(&pkg->entry); - pkg_sender_put_pkg_locked(sender, pkg); - } - - spin_unlock_irqrestore(&sender->lock, flags); -} - -static int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, - u32 *data, u32 len, u8 transmission, int delay) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - pkg = pkg_sender_get_pkg_locked(sender); - spin_unlock_irqrestore(&sender->lock, flags); - - if (!pkg) { - dev_err(sender->dev->dev, "No memory\n"); - return -ENOMEM; - } - pkg->pkg_type = MDFLD_DSI_PKG_MCS_LONG_WRITE; - pkg->transmission_type = transmission; - pkg->pkg.long_pkg.data = data; - pkg->pkg.long_pkg.len = len; - INIT_LIST_HEAD(&pkg->entry); - - pkg_sender_queue_pkg(sender, pkg, delay); - return 0; -} - -static int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u8 param, u8 param_num, - u8 transmission, - int delay) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - pkg = pkg_sender_get_pkg_locked(sender); - spin_unlock_irqrestore(&sender->lock, flags); - - if (!pkg) { - dev_err(sender->dev->dev, "No memory\n"); - return -ENOMEM; - } - - if (param_num) { - pkg->pkg_type = MDFLD_DSI_PKG_MCS_SHORT_WRITE_1; - pkg->pkg.short_pkg.param = param; - } else { - pkg->pkg_type = MDFLD_DSI_PKG_MCS_SHORT_WRITE_0; - pkg->pkg.short_pkg.param = 0; - } - pkg->transmission_type = transmission; - pkg->pkg.short_pkg.cmd = cmd; - INIT_LIST_HEAD(&pkg->entry); - - pkg_sender_queue_pkg(sender, pkg, delay); - return 0; -} - -static int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, - u8 transmission, - int delay) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - pkg = pkg_sender_get_pkg_locked(sender); - spin_unlock_irqrestore(&sender->lock, flags); - - if (!pkg) { - dev_err(sender->dev->dev, "No pkg memory\n"); - return -ENOMEM; - } - - switch (param_num) { - case 0: - pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_0; - pkg->pkg.short_pkg.cmd = 0; - pkg->pkg.short_pkg.param = 0; - break; - case 1: - pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_1; - pkg->pkg.short_pkg.cmd = param0; - pkg->pkg.short_pkg.param = 0; - break; - case 2: - pkg->pkg_type = MDFLD_DSI_PKG_GEN_SHORT_WRITE_2; - pkg->pkg.short_pkg.cmd = param0; - pkg->pkg.short_pkg.param = param1; - break; - } - - pkg->transmission_type = transmission; - INIT_LIST_HEAD(&pkg->entry); - - pkg_sender_queue_pkg(sender, pkg, delay); - return 0; -} - -static int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, - u32 *data, u32 len, u8 transmission, int delay) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - pkg = pkg_sender_get_pkg_locked(sender); - spin_unlock_irqrestore(&sender->lock, flags); - - if (!pkg) { - dev_err(sender->dev->dev, "No pkg memory\n"); - return -ENOMEM; - } - - pkg->pkg_type = MDFLD_DSI_PKG_GEN_LONG_WRITE; - pkg->transmission_type = transmission; - pkg->pkg.long_pkg.data = data; - pkg->pkg.long_pkg.len = len; - - INIT_LIST_HEAD(&pkg->entry); - - pkg_sender_queue_pkg(sender, pkg, delay); - - return 0; -} - -static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, - struct mdfld_dsi_pkg *pkg, - u32 *data, - u16 len) -{ - unsigned long flags; - struct drm_device *dev = sender->dev; - int i; - u32 gen_data_reg; - int retry = MDFLD_DSI_READ_MAX_COUNT; - u8 transmission = pkg->transmission_type; - - /* - * do reading. - * 0) send out generic read request - * 1) polling read data avail interrupt - * 2) read data - */ - spin_lock_irqsave(&sender->lock, flags); - - REG_WRITE(sender->mipi_intr_stat_reg, 1 << 29); - - if ((REG_READ(sender->mipi_intr_stat_reg) & (1 << 29))) - DRM_ERROR("Can NOT clean read data valid interrupt\n"); - - /*send out read request*/ - send_pkg(sender, pkg); - - pkg_sender_put_pkg_locked(sender, pkg); - - /*polling read data avail interrupt*/ - while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & (1 << 29))) { - udelay(100); - retry--; - } - - if (!retry) { - spin_unlock_irqrestore(&sender->lock, flags); - return -ETIMEDOUT; - } - - REG_WRITE(sender->mipi_intr_stat_reg, (1 << 29)); - - /*read data*/ - if (transmission == MDFLD_DSI_HS_TRANSMISSION) - gen_data_reg = sender->mipi_hs_gen_data_reg; - else if (transmission == MDFLD_DSI_LP_TRANSMISSION) - gen_data_reg = sender->mipi_lp_gen_data_reg; - else { - DRM_ERROR("Unknown transmission"); - spin_unlock_irqrestore(&sender->lock, flags); - return -EINVAL; - } - - for (i=0; ilock, flags); - - return 0; -} - -static int mdfld_dsi_read_gen(struct mdfld_dsi_pkg_sender *sender, - u8 param0, - u8 param1, - u8 param_num, - u32 *data, - u16 len, - u8 transmission) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - - pkg = pkg_sender_get_pkg_locked(sender); - - spin_unlock_irqrestore(&sender->lock,flags); - - if (!pkg) { - dev_err(sender->dev->dev, "No pkg memory\n"); - return -ENOMEM; - } - - switch (param_num) { - case 0: - pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_0; - pkg->pkg.short_pkg.cmd = 0; - pkg->pkg.short_pkg.param = 0; - break; - case 1: - pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_1; - pkg->pkg.short_pkg.cmd = param0; - pkg->pkg.short_pkg.param = 0; - break; - case 2: - pkg->pkg_type = MDFLD_DSI_PKG_GEN_READ_2; - pkg->pkg.short_pkg.cmd = param0; - pkg->pkg.short_pkg.param = param1; - break; - } - - pkg->transmission_type = transmission; - - INIT_LIST_HEAD(&pkg->entry); - - return __read_panel_data(sender, pkg, data, len); -} - -static int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, - u32 *data, - u16 len, - u8 transmission) -{ - struct mdfld_dsi_pkg *pkg; - unsigned long flags; - - spin_lock_irqsave(&sender->lock, flags); - - pkg = pkg_sender_get_pkg_locked(sender); - - spin_unlock_irqrestore(&sender->lock, flags); - - if (!pkg) { - dev_err(sender->dev->dev, "No pkg memory\n"); - return -ENOMEM; - } - - pkg->pkg_type = MDFLD_DSI_PKG_MCS_READ; - pkg->pkg.short_pkg.cmd = cmd; - pkg->pkg.short_pkg.param = 0; - - pkg->transmission_type = transmission; - - INIT_LIST_HEAD(&pkg->entry); - - return __read_panel_data(sender, pkg, data, len); -} - -void dsi_controller_dbi_init(struct mdfld_dsi_config * dsi_config, int pipe) -{ - struct drm_device * dev = dsi_config->dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int lane_count = dsi_config->lane_count; - u32 val = 0; - - /*un-ready device*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); - - /*init dsi adapter before kicking off*/ - REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); - - /*TODO: figure out how to setup these registers*/ - REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); - REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), 0x000a0014); - REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400); - REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000001); - REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000); - - /*enable all interrupts*/ - REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); - /*max value: 20 clock cycles of txclkesc*/ - REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f); - /*min 21 txclkesc, max: ffffh*/ - REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff); - /*min: 7d0 max: 4e20*/ - REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0); - - /*set up max return packet size*/ - REG_WRITE((MIPIA_MAX_RETURN_PACK_SIZE_REG + reg_offset), - MDFLD_DSI_MAX_RETURN_PACKET_SIZE); - - /*set up func_prg*/ - val |= lane_count; - val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET); - val |= DSI_DBI_COLOR_FORMAT_OPTION2; - REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); - - REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff); - REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff); - - REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); - REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); - REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); -} - -void dsi_controller_dpi_init(struct mdfld_dsi_config * dsi_config, int pipe) -{ - struct drm_device * dev = dsi_config->dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int lane_count = dsi_config->lane_count; - struct mdfld_dsi_dpi_timing dpi_timing; - struct drm_display_mode * mode = dsi_config->mode; - u32 val = 0; - - /*un-ready device*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); - - /*init dsi adapter before kicking off*/ - REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); - - /*enable all interrupts*/ - REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); - - /*set up func_prg*/ - val |= lane_count; - val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET; - - switch(dsi_config->bpp) { - case 16: - val |= DSI_DPI_COLOR_FORMAT_RGB565; - break; - case 18: - val |= DSI_DPI_COLOR_FORMAT_RGB666; - break; - case 24: - val |= DSI_DPI_COLOR_FORMAT_RGB888; - break; - default: - DRM_ERROR("unsupported color format, bpp = %d\n", dsi_config->bpp); - } - - REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); - - REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), - (mode->vtotal * mode->htotal * dsi_config->bpp / (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK); - REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff & DSI_LP_RX_TIMEOUT_MASK); - - /*max value: 20 clock cycles of txclkesc*/ - REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK); - - /*min 21 txclkesc, max: ffffh*/ - REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0xffff & DSI_RESET_TIMER_MASK); - - REG_WRITE((MIPIA_DPI_RESOLUTION_REG + reg_offset), mode->vdisplay << 16 | mode->hdisplay); - - /*set DPI timing registers*/ - mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, dsi_config->lane_count, dsi_config->bpp); - - REG_WRITE((MIPIA_HSYNC_COUNT_REG + reg_offset), dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_HBP_COUNT_REG + reg_offset), dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_HFP_COUNT_REG + reg_offset), dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_HACTIVE_COUNT_REG + reg_offset), dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_VSYNC_COUNT_REG + reg_offset), dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_VBP_COUNT_REG + reg_offset), dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); - REG_WRITE((MIPIA_VFP_COUNT_REG + reg_offset), dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); - - REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); - - /*min: 7d0 max: 4e20*/ - REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x000007d0); - - /*set up video mode*/ - val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE; - REG_WRITE((MIPIA_VIDEO_MODE_FORMAT_REG + reg_offset), val); - - REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000000); - - REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); - - /*TODO: figure out how to setup these registers*/ - REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c3408); - - REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), (0xa << 16) | 0x14); - - /*set device ready*/ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); -} - -static void dsi_controller_init(struct mdfld_dsi_config * dsi_config, int pipe) -{ - if (!dsi_config || ((pipe != 0) && (pipe != 2))) { - DRM_ERROR("Invalid parameters\n"); - return; - } - - if (dsi_config->type == MDFLD_DSI_ENCODER_DPI) - dsi_controller_dpi_init(dsi_config, pipe); - else if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) - dsi_controller_dbi_init(dsi_config, pipe); - else - DRM_ERROR("Bad DSI encoder type\n"); -} - -void mdfld_dsi_cmds_kick_out(struct mdfld_dsi_pkg_sender *sender) -{ - process_pkg_list(sender); -} - -int mdfld_dsi_send_dcs(struct mdfld_dsi_pkg_sender *sender, - u8 dcs, u8 *param, u32 param_num, u8 data_src, - int delay) -{ - struct mdfld_dsi_pkg *pkg; - u32 cb_phy = sender->dbi_cb_phy; - struct drm_device *dev = sender->dev; - u32 index = 0; - u8 *cb = (u8 *)sender->dbi_cb_addr; - unsigned long flags; - int retry; - u8 *dst = NULL; - u32 len; - - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - - if (!sender->dbi_pkg_support) { - dev_err(dev->dev, "No DBI pkg sending on this sender\n"); - return -ENOTSUPP; - } - - if (param_num > MDFLD_MAX_DCS_PARAM) { - dev_err(dev->dev, "Sender only supports up to %d DCS params\n", - MDFLD_MAX_DCS_PARAM); - return -EINVAL; - } - - /* - * If dcs is write_mem_start, send it directly using DSI adapter - * interface - */ - if (dcs == DCS_WRITE_MEM_START) { - if (!spin_trylock(&sender->lock)) - return -EAGAIN; - - /* - * query whether DBI FIFO is empty, - * if not wait it becoming empty - */ - retry = MDFLD_DSI_DBI_FIFO_TIMEOUT; - while (retry && - !(REG_READ(sender->mipi_gen_fifo_stat_reg) & (1 << 27))) { - udelay(500); - retry--; - } - - /* If DBI FIFO timeout, drop this frame */ - if (!retry) { - spin_unlock(&sender->lock); - return 0; - } - - *(cb + (index++)) = write_mem_start; - - REG_WRITE(sender->mipi_cmd_len_reg, 1); - REG_WRITE(sender->mipi_cmd_addr_reg, - cb_phy | (1 << 0) | (1 << 1)); - - retry = MDFLD_DSI_DBI_FIFO_TIMEOUT; - while (retry && - (REG_READ(sender->mipi_cmd_addr_reg) & (1 << 0))) { - udelay(1); - retry--; - } - - spin_unlock(&sender->lock); - return 0; - } - - /* Get a free pkg */ - spin_lock_irqsave(&sender->lock, flags); - pkg = pkg_sender_get_pkg_locked(sender); - spin_unlock_irqrestore(&sender->lock, flags); - - if (!pkg) { - dev_err(dev->dev, "No packages memory\n"); - return -ENOMEM; - } - - dst = pkg->pkg.dcs_pkg.param; - memcpy(dst, param, param_num); - - pkg->pkg_type = MDFLD_DSI_PKG_DCS; - pkg->transmission_type = MDFLD_DSI_DCS; - pkg->pkg.dcs_pkg.cmd = dcs; - pkg->pkg.dcs_pkg.param_num = param_num; - pkg->pkg.dcs_pkg.data_src = data_src; - - INIT_LIST_HEAD(&pkg->entry); - - if (param_num == 0) - return mdfld_dsi_send_mcs_short_hs(sender, dcs, 0, 0, delay); - else if (param_num == 1) - return mdfld_dsi_send_mcs_short_hs(sender, dcs, - param[0], 1, delay); - else if (param_num > 1) { - len = (param_num + 1) / 4; - if ((param_num + 1) % 4) - len++; - return mdfld_dsi_send_mcs_long_hs(sender, - (u32 *)&pkg->pkg.dcs_pkg, len, delay); - } - return 0; -} - -int mdfld_dsi_send_mcs_short_hs(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u8 param, u8 param_num, int delay) -{ - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_mcs_short(sender, cmd, param, param_num, - MDFLD_DSI_HS_TRANSMISSION, delay); -} - -int mdfld_dsi_send_mcs_short_lp(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u8 param, u8 param_num, int delay) -{ - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_mcs_short(sender, cmd, param, param_num, - MDFLD_DSI_LP_TRANSMISSION, delay); -} - -int mdfld_dsi_send_mcs_long_hs(struct mdfld_dsi_pkg_sender *sender, - u32 *data, - u32 len, - int delay) -{ - if (!sender || !data || !len) { - DRM_ERROR("Invalid parameters\n"); - return -EINVAL; - } - return mdfld_dsi_send_mcs_long(sender, data, len, - MDFLD_DSI_HS_TRANSMISSION, delay); -} - -int mdfld_dsi_send_mcs_long_lp(struct mdfld_dsi_pkg_sender *sender, - u32 *data, - u32 len, - int delay) -{ - if (!sender || !data || !len) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_mcs_long(sender, data, len, - MDFLD_DSI_LP_TRANSMISSION, delay); -} - -int mdfld_dsi_send_gen_short_hs(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, int delay) -{ - if (!sender) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_gen_short(sender, param0, param1, param_num, - MDFLD_DSI_HS_TRANSMISSION, delay); -} - -int mdfld_dsi_send_gen_short_lp(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, int delay) -{ - if (!sender || param_num < 0 || param_num > 2) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_gen_short(sender, param0, param1, param_num, - MDFLD_DSI_LP_TRANSMISSION, delay); -} - -int mdfld_dsi_send_gen_long_hs(struct mdfld_dsi_pkg_sender *sender, - u32 *data, - u32 len, - int delay) -{ - if (!sender || !data || !len) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_gen_long(sender, data, len, - MDFLD_DSI_HS_TRANSMISSION, delay); -} - -int mdfld_dsi_send_gen_long_lp(struct mdfld_dsi_pkg_sender *sender, - u32 *data, - u32 len, - int delay) -{ - if (!sender || !data || !len) { - WARN_ON(1); - return -EINVAL; - } - return mdfld_dsi_send_gen_long(sender, data, len, - MDFLD_DSI_LP_TRANSMISSION, delay); -} - -int mdfld_dsi_read_gen_hs(struct mdfld_dsi_pkg_sender *sender, - u8 param0, - u8 param1, - u8 param_num, - u32 *data, - u16 len) -{ - if (!sender || !data || param_num < 0 || param_num > 2 - || !data || !len) { - DRM_ERROR("Invalid parameters\n"); - return -EINVAL; - } - - return mdfld_dsi_read_gen(sender, param0, param1, param_num, - data, len, MDFLD_DSI_HS_TRANSMISSION); - -} - -int mdfld_dsi_read_gen_lp(struct mdfld_dsi_pkg_sender *sender, - u8 param0, - u8 param1, - u8 param_num, - u32 *data, - u16 len) -{ - if (!sender || !data || param_num < 0 || param_num > 2 - || !data || !len) { - DRM_ERROR("Invalid parameters\n"); - return -EINVAL; - } - - return mdfld_dsi_read_gen(sender, param0, param1, param_num, - data, len, MDFLD_DSI_LP_TRANSMISSION); -} - -int mdfld_dsi_read_mcs_hs(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, - u32 *data, - u16 len) -{ - if (!sender || !data || !len) { - DRM_ERROR("Invalid parameters\n"); - return -EINVAL; - } - - return mdfld_dsi_read_mcs(sender, cmd, data, len, - MDFLD_DSI_HS_TRANSMISSION); -} - -int mdfld_dsi_read_mcs_lp(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, - u32 *data, - u16 len) -{ - if (!sender || !data || !len) { - WARN_ON(1); - return -EINVAL; - } - - return mdfld_dsi_read_mcs(sender, cmd, data, len, - MDFLD_DSI_LP_TRANSMISSION); -} - -int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, - int pipe) -{ - int ret; - struct mdfld_dsi_pkg_sender *pkg_sender; - struct mdfld_dsi_config *dsi_config = - mdfld_dsi_get_config(dsi_connector); - struct drm_device *dev = dsi_config->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_gtt *pg = &dev_priv->gtt; - int i; - struct mdfld_dsi_pkg *pkg, *tmp; - u32 mipi_val = 0; - - if (!dsi_connector) { - WARN_ON(1); - return -EINVAL; - } - - pkg_sender = dsi_connector->pkg_sender; - - if (!pkg_sender || IS_ERR(pkg_sender)) { - pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender), - GFP_KERNEL); - if (!pkg_sender) { - dev_err(dev->dev, "Create DSI pkg sender failed\n"); - return -ENOMEM; - } - - dsi_connector->pkg_sender = (void *)pkg_sender; - } - - pkg_sender->dev = dev; - pkg_sender->dsi_connector = dsi_connector; - pkg_sender->pipe = pipe; - pkg_sender->pkg_num = 0; - pkg_sender->panel_mode = 0; - pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE; - - /* Init dbi command buffer*/ - - if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) { - pkg_sender->dbi_pkg_support = 1; - ret = mdfld_dbi_cb_init(pkg_sender, pg, pipe); - if (ret) { - dev_err(dev->dev, "DBI command buffer map failed\n"); - goto mapping_err; - } - } - - /* Init regs */ - if (pipe == 0) { - pkg_sender->dpll_reg = MRST_DPLL_A; - pkg_sender->dspcntr_reg = DSPACNTR; - pkg_sender->pipeconf_reg = PIPEACONF; - pkg_sender->dsplinoff_reg = DSPALINOFF; - pkg_sender->dspsurf_reg = DSPASURF; - pkg_sender->pipestat_reg = PIPEASTAT; - - pkg_sender->mipi_intr_stat_reg = MIPIA_INTR_STAT_REG; - pkg_sender->mipi_lp_gen_data_reg = MIPIA_LP_GEN_DATA_REG; - pkg_sender->mipi_hs_gen_data_reg = MIPIA_HS_GEN_DATA_REG; - pkg_sender->mipi_lp_gen_ctrl_reg = MIPIA_LP_GEN_CTRL_REG; - pkg_sender->mipi_hs_gen_ctrl_reg = MIPIA_HS_GEN_CTRL_REG; - pkg_sender->mipi_gen_fifo_stat_reg = MIPIA_GEN_FIFO_STAT_REG; - pkg_sender->mipi_data_addr_reg = MIPIA_DATA_ADD_REG; - pkg_sender->mipi_data_len_reg = MIPIA_DATA_LEN_REG; - pkg_sender->mipi_cmd_addr_reg = MIPIA_CMD_ADD_REG; - pkg_sender->mipi_cmd_len_reg = MIPIA_CMD_LEN_REG; - } else if (pipe == 2) { - pkg_sender->dpll_reg = MRST_DPLL_A; - pkg_sender->dspcntr_reg = DSPCCNTR; - pkg_sender->pipeconf_reg = PIPECCONF; - pkg_sender->dsplinoff_reg = DSPCLINOFF; - pkg_sender->dspsurf_reg = DSPCSURF; - pkg_sender->pipestat_reg = PIPECSTAT; - - pkg_sender->mipi_intr_stat_reg = - MIPIA_INTR_STAT_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_lp_gen_data_reg = - MIPIA_LP_GEN_DATA_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_hs_gen_data_reg = - MIPIA_HS_GEN_DATA_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_lp_gen_ctrl_reg = - MIPIA_LP_GEN_CTRL_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_hs_gen_ctrl_reg = - MIPIA_HS_GEN_CTRL_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_gen_fifo_stat_reg = - MIPIA_GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_data_addr_reg = - MIPIA_DATA_ADD_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_data_len_reg = - MIPIA_DATA_LEN_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_cmd_addr_reg = - MIPIA_CMD_ADD_REG + MIPIC_REG_OFFSET; - pkg_sender->mipi_cmd_len_reg = - MIPIA_CMD_LEN_REG + MIPIC_REG_OFFSET; - } - - /* Init pkg list */ - INIT_LIST_HEAD(&pkg_sender->pkg_list); - INIT_LIST_HEAD(&pkg_sender->free_list); - - spin_lock_init(&pkg_sender->lock); - - /* Allocate free pkg pool */ - for (i = 0; i < MDFLD_MAX_PKG_NUM; i++) { - pkg = kzalloc(sizeof(struct mdfld_dsi_pkg), GFP_KERNEL); - if (!pkg) { - dev_err(dev->dev, "Out of memory allocating pkg pool"); - ret = -ENOMEM; - goto pkg_alloc_err; - } - INIT_LIST_HEAD(&pkg->entry); - list_add_tail(&pkg->entry, &pkg_sender->free_list); - } - - /* - * For video mode, don't enable DPI timing output here, - * will init the DPI timing output during mode setting. - */ - if (dsi_config->type == MDFLD_DSI_ENCODER_DPI) - mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; - else if (dsi_config->type == MDFLD_DSI_ENCODER_DBI) - mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX - | TE_TRIGGER_GPIO_PIN; - else - DRM_ERROR("Bad DSI encoder type\n"); - - if (pipe == 0) { - mipi_val |= 0x2; - REG_WRITE(MIPI, mipi_val); - REG_READ(MIPI); - } else if (pipe == 2) { - REG_WRITE(MIPI_C, mipi_val); - REG_READ(MIPI_C); - } - - /*do dsi controller init*/ - dsi_controller_init(dsi_config, pipe); - - return 0; - -pkg_alloc_err: - list_for_each_entry_safe(pkg, tmp, &pkg_sender->free_list, entry) { - list_del(&pkg->entry); - kfree(pkg); - } - - /* Free mapped command buffer */ - mdfld_dbi_cb_destroy(pkg_sender); -mapping_err: - kfree(pkg_sender); - dsi_connector->pkg_sender = NULL; - return ret; -} - -void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender) -{ - struct mdfld_dsi_pkg *pkg, *tmp; - - if (!sender || IS_ERR(sender)) - return; - - /* Free pkg pool */ - list_for_each_entry_safe(pkg, tmp, &sender->free_list, entry) { - list_del(&pkg->entry); - kfree(pkg); - } - /* Free pkg list */ - list_for_each_entry_safe(pkg, tmp, &sender->pkg_list, entry) { - list_del(&pkg->entry); - kfree(pkg); - } - mdfld_dbi_cb_destroy(sender); /* free mapped command buffer */ - kfree(sender); -} diff --git a/drivers/staging/gma500/mdfld_dsi_pkg_sender.h b/drivers/staging/gma500/mdfld_dsi_pkg_sender.h deleted file mode 100644 index f24abc7..0000000 --- a/drivers/staging/gma500/mdfld_dsi_pkg_sender.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Jackie Li - */ -#ifndef __MDFLD_DSI_PKG_SENDER_H__ -#define __MDFLD_DSI_PKG_SENDER_H__ - -#include - -#define MDFLD_MAX_DCS_PARAM 8 -#define MDFLD_MAX_PKG_NUM 2048 - -enum { - MDFLD_DSI_PKG_DCS, - MDFLD_DSI_PKG_GEN_SHORT_WRITE_0 = 0x03, - MDFLD_DSI_PKG_GEN_SHORT_WRITE_1 = 0x13, - MDFLD_DSI_PKG_GEN_SHORT_WRITE_2 = 0x23, - MDFLD_DSI_PKG_GEN_READ_0 = 0x04, - MDFLD_DSI_PKG_GEN_READ_1 = 0x14, - MDFLD_DSI_PKG_GEN_READ_2 = 0x24, - MDFLD_DSI_PKG_GEN_LONG_WRITE = 0x29, - MDFLD_DSI_PKG_MCS_SHORT_WRITE_0 = 0x05, - MDFLD_DSI_PKG_MCS_SHORT_WRITE_1 = 0x15, - MDFLD_DSI_PKG_MCS_READ = 0x06, - MDFLD_DSI_PKG_MCS_LONG_WRITE = 0x39, -}; - -enum { - MDFLD_DSI_LP_TRANSMISSION, - MDFLD_DSI_HS_TRANSMISSION, - MDFLD_DSI_DCS, -}; - -enum { - MDFLD_DSI_PANEL_MODE_SLEEP = 0x1, -}; - -enum { - MDFLD_DSI_PKG_SENDER_FREE = 0x0, - MDFLD_DSI_PKG_SENDER_BUSY = 0x1, -}; - -enum { - MDFLD_DSI_SEND_PACKAGE, - MDFLD_DSI_QUEUE_PACKAGE, -}; - -struct mdfld_dsi_gen_short_pkg { - u8 cmd; - u8 param; -}; - -struct mdfld_dsi_gen_long_pkg { - u32 *data; - u32 len; -}; - -struct mdfld_dsi_dcs_pkg { - u8 cmd; - u8 param[MDFLD_MAX_DCS_PARAM]; - u32 param_num; - u8 data_src; -}; - -struct mdfld_dsi_pkg { - u8 pkg_type; - u8 transmission_type; - - union { - struct mdfld_dsi_gen_short_pkg short_pkg; - struct mdfld_dsi_gen_long_pkg long_pkg; - struct mdfld_dsi_dcs_pkg dcs_pkg; - } pkg; - - struct list_head entry; -}; - -struct mdfld_dsi_pkg_sender { - struct drm_device *dev; - struct mdfld_dsi_connector *dsi_connector; - u32 status; - - u32 panel_mode; - - int pipe; - - spinlock_t lock; - struct list_head pkg_list; - struct list_head free_list; - - u32 pkg_num; - - int dbi_pkg_support; - - u32 dbi_cb_phy; - void *dbi_cb_addr; - - /* Registers */ - u32 dpll_reg; - u32 dspcntr_reg; - u32 pipeconf_reg; - u32 pipestat_reg; - u32 dsplinoff_reg; - u32 dspsurf_reg; - - u32 mipi_intr_stat_reg; - u32 mipi_lp_gen_data_reg; - u32 mipi_hs_gen_data_reg; - u32 mipi_lp_gen_ctrl_reg; - u32 mipi_hs_gen_ctrl_reg; - u32 mipi_gen_fifo_stat_reg; - u32 mipi_data_addr_reg; - u32 mipi_data_len_reg; - u32 mipi_cmd_addr_reg; - u32 mipi_cmd_len_reg; -}; - -/* DCS definitions */ -#define DCS_SOFT_RESET 0x01 -#define DCS_ENTER_SLEEP_MODE 0x10 -#define DCS_EXIT_SLEEP_MODE 0x11 -#define DCS_SET_DISPLAY_OFF 0x28 -#define DCS_SET_DISPLAY_ON 0x29 -#define DCS_SET_COLUMN_ADDRESS 0x2a -#define DCS_SET_PAGE_ADDRESS 0x2b -#define DCS_WRITE_MEM_START 0x2c -#define DCS_SET_TEAR_OFF 0x34 -#define DCS_SET_TEAR_ON 0x35 - -extern int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, - int pipe); -extern void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender); -extern int mdfld_dsi_send_dcs(struct mdfld_dsi_pkg_sender *sender, u8 dcs, - u8 *param, u32 param_num, u8 data_src, int delay); -extern int mdfld_dsi_send_mcs_short_hs(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u8 param, u8 param_num, int delay); -extern int mdfld_dsi_send_mcs_short_lp(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u8 param, u8 param_num, int delay); -extern int mdfld_dsi_send_mcs_long_hs(struct mdfld_dsi_pkg_sender *sender, - u32 *data, u32 len, int delay); -extern int mdfld_dsi_send_mcs_long_lp(struct mdfld_dsi_pkg_sender *sender, - u32 *data, u32 len, int delay); -extern int mdfld_dsi_send_gen_short_hs(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, int delay); -extern int mdfld_dsi_send_gen_short_lp(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, int delay); -extern int mdfld_dsi_send_gen_long_hs(struct mdfld_dsi_pkg_sender *sender, - u32 *data, u32 len, int delay); -extern int mdfld_dsi_send_gen_long_lp(struct mdfld_dsi_pkg_sender *sender, - u32 *data, u32 len, int delay); - -extern int mdfld_dsi_read_gen_hs(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, u32 *data, u16 len); -extern int mdfld_dsi_read_gen_lp(struct mdfld_dsi_pkg_sender *sender, - u8 param0, u8 param1, u8 param_num, u32 *data, u16 len); -extern int mdfld_dsi_read_mcs_hs(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u32 *data, u16 len); -extern int mdfld_dsi_read_mcs_lp(struct mdfld_dsi_pkg_sender *sender, - u8 cmd, u32 *data, u16 len); - -extern void mdfld_dsi_cmds_kick_out(struct mdfld_dsi_pkg_sender *sender); - -#endif /* __MDFLD_DSI_PKG_SENDER_H__ */ diff --git a/drivers/staging/gma500/mdfld_intel_display.c b/drivers/staging/gma500/mdfld_intel_display.c deleted file mode 100644 index 0b37b7b..0000000 --- a/drivers/staging/gma500/mdfld_intel_display.c +++ /dev/null @@ -1,1404 +0,0 @@ -/* - * Copyright © 2006-2011 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Eric Anholt - */ - -#include "framebuffer.h" -#include "psb_intel_display.h" -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_dsi_dbi_dpu.h" - -#include - -#ifdef MIN -#undef MIN -#endif - -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) - -/* Hardcoded currently */ -static int ksel = KSEL_CRYSTAL_19; - -extern void mdfld_save_display(struct drm_device *dev); -extern bool gbgfxsuspended; - -struct psb_intel_range_t { - int min, max; -}; - -struct mdfld_limit_t { - struct psb_intel_range_t dot, m, p1; -}; - -struct mdfld_intel_clock_t { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -}; - - - -#define COUNT_MAX 0x10000000 - -void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe) -{ - int count, temp; - u32 pipeconf_reg = PIPEACONF; - - switch (pipe) { - case 0: - break; - case 1: - pipeconf_reg = PIPEBCONF; - break; - case 2: - pipeconf_reg = PIPECCONF; - break; - default: - DRM_ERROR("Illegal Pipe Number. \n"); - return; - } - - /* FIXME JLIU7_PO */ - psb_intel_wait_for_vblank(dev); - return; - - /* Wait for for the pipe disable to take effect. */ - for (count = 0; count < COUNT_MAX; count++) { - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_PIPE_STATE) == 0) - break; - } -} - -void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe) -{ - int count, temp; - u32 pipeconf_reg = PIPEACONF; - - switch (pipe) { - case 0: - break; - case 1: - pipeconf_reg = PIPEBCONF; - break; - case 2: - pipeconf_reg = PIPECCONF; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return; - } - - /* FIXME JLIU7_PO */ - psb_intel_wait_for_vblank(dev); - return; - - /* Wait for for the pipe enable to take effect. */ - for (count = 0; count < COUNT_MAX; count++) { - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_PIPE_STATE) == 1) - break; - } -} - - -static int mdfld_intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t control = CURACNTR; - uint32_t base = CURABASE; - uint32_t temp; - size_t addr = 0; - struct gtt_range *gt; - struct drm_gem_object *obj; - int ret; - - switch (pipe) { - case 0: - break; - case 1: - control = CURBCNTR; - base = CURBBASE; - break; - case 2: - control = CURCCNTR; - base = CURCBASE; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number. \n"); - return -EINVAL; - } - -#if 1 /* FIXME_JLIU7 can't enalbe cursorB/C HW issue. need to remove after HW fix */ - if (pipe != 0) - return 0; -#endif - /* if we want to turn of the cursor ignore width and height */ - if (!handle) { - dev_dbg(dev->dev, "cursor off\n"); - /* turn off the cursor */ - temp = 0; - temp |= CURSOR_MODE_DISABLE; - - if (gma_power_begin(dev, true)) { - REG_WRITE(control, temp); - REG_WRITE(base, 0); - gma_power_end(dev); - } - /* Unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - return 0; - } - - /* Currently we only support 64x64 cursors */ - if (width != 64 || height != 64) { - DRM_ERROR("we currently only support 64x64 cursors\n"); - return -EINVAL; - } - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; - - if (obj->size < width * height * 4) { - dev_dbg(dev->dev, "buffer is to small\n"); - return -ENOMEM; - } - - gt = container_of(obj, struct gtt_range, gem); - - /* Pin the memory into the GTT */ - ret = psb_gtt_pin(gt); - if (ret) { - dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); - return ret; - } - - - addr = gt->offset; /* Or resource.start ??? */ - - psb_intel_crtc->cursor_addr = addr; - - temp = 0; - /* set the pipe for the cursor */ - temp |= (pipe << 28); - temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; - - if (gma_power_begin(dev, true)) { - REG_WRITE(control, temp); - REG_WRITE(base, addr); - gma_power_end(dev); - } - /* unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = obj; - } - return 0; -} - -static int mdfld_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private * dev_priv = (struct drm_psb_private *)dev->dev_private; - struct mdfld_dbi_dpu_info *dpu_info = dev_priv->dbi_dpu_info; - struct psb_drm_dpu_rect rect; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t pos = CURAPOS; - uint32_t base = CURABASE; - uint32_t temp = 0; - uint32_t addr; - - switch (pipe) { - case 0: - if (dpu_info) { - rect.x = x; - rect.y = y; - - mdfld_dbi_dpu_report_damage(dev, MDFLD_CURSORA, &rect); - mdfld_dpu_exit_dsr(dev); - } else if (!(dev_priv->dsr_fb_update & MDFLD_DSR_CURSOR_0)) - mdfld_dsi_dbi_exit_dsr(dev, MDFLD_DSR_CURSOR_0); - break; - case 1: - pos = CURBPOS; - base = CURBBASE; - break; - case 2: - if (dpu_info) { - mdfld_dbi_dpu_report_damage(dev, MDFLD_CURSORC, &rect); - mdfld_dpu_exit_dsr(dev); - } else if (!(dev_priv->dsr_fb_update & MDFLD_DSR_CURSOR_2)) - mdfld_dsi_dbi_exit_dsr(dev, MDFLD_DSR_CURSOR_2); - pos = CURCPOS; - base = CURCBASE; - break; - default: - DRM_ERROR("Illegal Pipe Number. \n"); - return -EINVAL; - } - -#if 1 /* FIXME_JLIU7 can't enable cursorB/C HW issue. need to remove after HW fix */ - if (pipe != 0) - return 0; -#endif - if (x < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); - x = -x; - } - if (y < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); - y = -y; - } - - temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); - temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); - - addr = psb_intel_crtc->cursor_addr; - - if (gma_power_begin(dev, true)) { - REG_WRITE(pos, temp); - REG_WRITE(base, addr); - gma_power_end(dev); - } - - return 0; -} - -const struct drm_crtc_funcs mdfld_intel_crtc_funcs = { - .cursor_set = mdfld_intel_crtc_cursor_set, - .cursor_move = mdfld_intel_crtc_cursor_move, - .gamma_set = psb_intel_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, - .destroy = psb_intel_crtc_destroy, -}; - -static struct drm_device globle_dev; - -void mdfld__intel_plane_set_alpha(int enable) -{ - struct drm_device *dev = &globle_dev; - int dspcntr_reg = DSPACNTR; - u32 dspcntr; - - dspcntr = REG_READ(dspcntr_reg); - - if (enable) { - dspcntr &= ~DISPPLANE_32BPP_NO_ALPHA; - dspcntr |= DISPPLANE_32BPP; - } else { - dspcntr &= ~DISPPLANE_32BPP; - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; - } - - REG_WRITE(dspcntr_reg, dspcntr); -} - -int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); - int pipe = psb_intel_crtc->pipe; - unsigned long start, offset; - int dsplinoff = DSPALINOFF; - int dspsurf = DSPASURF; - int dspstride = DSPASTRIDE; - int dspcntr_reg = DSPACNTR; - u32 dspcntr; - int ret = 0; - - memcpy(&globle_dev, dev, sizeof(struct drm_device)); - - if (!gma_power_begin(dev, true)) - return 0; - - /* no fb bound */ - if (!crtc->fb) { - dev_err(dev->dev, "No FB bound\n"); - goto psb_intel_pipe_cleaner; - } - - switch (pipe) { - case 0: - dsplinoff = DSPALINOFF; - break; - case 1: - dsplinoff = DSPBLINOFF; - dspsurf = DSPBSURF; - dspstride = DSPBSTRIDE; - dspcntr_reg = DSPBCNTR; - break; - case 2: - dsplinoff = DSPCLINOFF; - dspsurf = DSPCSURF; - dspstride = DSPCSTRIDE; - dspcntr_reg = DSPCCNTR; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return -EINVAL; - } - - ret = psb_gtt_pin(psbfb->gtt); - if (ret < 0) - goto psb_intel_pipe_set_base_exit; - - start = psbfb->gtt->offset; - offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - - REG_WRITE(dspstride, crtc->fb->pitches[0]); - dspcntr = REG_READ(dspcntr_reg); - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - - switch (crtc->fb->bits_per_pixel) { - case 8: - dspcntr |= DISPPLANE_8BPP; - break; - case 16: - if (crtc->fb->depth == 15) - dspcntr |= DISPPLANE_15_16BPP; - else - dspcntr |= DISPPLANE_16BPP; - break; - case 24: - case 32: - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; - break; - default: - dev_err(dev->dev, "Unknown color depth\n"); - ret = -EINVAL; - goto psb_intel_pipe_set_base_exit; - } - REG_WRITE(dspcntr_reg, dspcntr); - - dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n", - start, offset, x, y); - - REG_WRITE(dsplinoff, offset); - REG_READ(dsplinoff); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - -psb_intel_pipe_cleaner: - /* If there was a previous display we can now unpin it */ - if (old_fb) - psb_gtt_unpin(to_psb_fb(old_fb)->gtt); - -psb_intel_pipe_set_base_exit: - gma_power_end(dev); - return ret; -} - -/** - * Disable the pipe, plane and pll. - * - */ -void mdfld_disable_crtc (struct drm_device *dev, int pipe) -{ - int dpll_reg = MRST_DPLL_A; - int dspcntr_reg = DSPACNTR; - int dspbase_reg = MRST_DSPABASE; - int pipeconf_reg = PIPEACONF; - u32 gen_fifo_stat_reg = GEN_FIFO_STAT_REG; - u32 temp; - - switch (pipe) { - case 0: - break; - case 1: - dpll_reg = MDFLD_DPLL_B; - dspcntr_reg = DSPBCNTR; - dspbase_reg = DSPBSURF; - pipeconf_reg = PIPEBCONF; - break; - case 2: - dpll_reg = MRST_DPLL_A; - dspcntr_reg = DSPCCNTR; - dspbase_reg = MDFLD_DSPCBASE; - pipeconf_reg = PIPECCONF; - gen_fifo_stat_reg = GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number. \n"); - return; - } - - if (pipe != 1) - mdfld_dsi_gen_fifo_ready (dev, gen_fifo_stat_reg, HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); - - /* Disable display plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, - temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); - } - - /* FIXME_JLIU7 MDFLD_PO revisit */ - /* Wait for vblank for the disable to take effect */ -/* MDFLD_PO_JLIU7 psb_intel_wait_for_vblank(dev); */ - - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - temp &= ~PIPEACONF_ENABLE; - temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; - REG_WRITE(pipeconf_reg, temp); - REG_READ(pipeconf_reg); - - /* Wait for for the pipe disable to take effect. */ - mdfldWaitForPipeDisable(dev, pipe); - } - - temp = REG_READ(dpll_reg); - if (temp & DPLL_VCO_ENABLE) { - if (((pipe != 1) && !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) - || (pipe == 1)){ - temp &= ~(DPLL_VCO_ENABLE); - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to turn off. */ - /* FIXME_MDFLD PO may need more delay */ - udelay(500); - - if (!(temp & MDFLD_PWR_GATE_EN)) { - /* gating power of DPLL */ - REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(5000); - } - } - } - -} - -/** - * Sets the power management mode of the pipe and plane. - * - * This code should probably grow support for turning the cursor off and back - * on appropriately at the same time as we're turning the pipe off/on. - */ -static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = MRST_DPLL_A; - int dspcntr_reg = DSPACNTR; - int dspbase_reg = MRST_DSPABASE; - int pipeconf_reg = PIPEACONF; - u32 pipestat_reg = PIPEASTAT; - u32 gen_fifo_stat_reg = GEN_FIFO_STAT_REG; - u32 pipeconf = dev_priv->pipeconf; - u32 dspcntr = dev_priv->dspcntr; - u32 mipi_enable_reg = MIPIA_DEVICE_READY_REG; - u32 temp; - bool enabled; - int timeout = 0; - - if (!gma_power_begin(dev, true)) - return; - - /* Ignore if system is already in DSR and in suspended state. */ - if(/*gbgfxsuspended */0 && dev_priv->dispstatus == false && mode == 3){ - if(dev_priv->rpm_enabled && pipe == 1){ - // dev_priv->is_mipi_on = false; - pm_request_idle(&dev->pdev->dev); - } - return; - }else if(mode == 0) { - //do not need to set gbdispstatus=true in crtc. - //this will be set in encoder such as mdfld_dsi_dbi_dpms - //gbdispstatus = true; - } - -/* FIXME_JLIU7 MDFLD_PO replaced w/ the following function */ -/* mdfld_dbi_dpms (struct drm_device *dev, int pipe, bool enabled) */ - - switch (pipe) { - case 0: - break; - case 1: - dpll_reg = DPLL_B; - dspcntr_reg = DSPBCNTR; - dspbase_reg = MRST_DSPBBASE; - pipeconf_reg = PIPEBCONF; - pipeconf = dev_priv->pipeconf1; - dspcntr = dev_priv->dspcntr1; - dpll_reg = MDFLD_DPLL_B; - break; - case 2: - dpll_reg = MRST_DPLL_A; - dspcntr_reg = DSPCCNTR; - dspbase_reg = MDFLD_DSPCBASE; - pipeconf_reg = PIPECCONF; - pipestat_reg = PIPECSTAT; - pipeconf = dev_priv->pipeconf2; - dspcntr = dev_priv->dspcntr2; - gen_fifo_stat_reg = GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET; - mipi_enable_reg = MIPIA_DEVICE_READY_REG + MIPIC_REG_OFFSET; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return; - } - - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable the DPLL */ - temp = REG_READ(dpll_reg); - - if ((temp & DPLL_VCO_ENABLE) == 0) { - /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ - if (temp & MDFLD_PWR_GATE_EN) { - temp &= ~MDFLD_PWR_GATE_EN; - REG_WRITE(dpll_reg, temp); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - } - - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - - /** - * wait for DSI PLL to lock - * NOTE: only need to poll status of pipe 0 and pipe 1, - * since both MIPI pipes share the same PLL. - */ - while ((pipe != 2) && (timeout < 20000) && !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { - udelay(150); - timeout ++; - } - } - - /* Enable the plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, - temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - } - - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) { - REG_WRITE(pipeconf_reg, pipeconf); - - /* Wait for for the pipe enable to take effect. */ - mdfldWaitForPipeEnable(dev, pipe); - } - - /*workaround for sighting 3741701 Random X blank display*/ - /*perform w/a in video mode only on pipe A or C*/ - if ((pipe == 0 || pipe == 2) && - (mdfld_panel_dpi(dev) == true)) { - REG_WRITE(pipestat_reg, REG_READ(pipestat_reg)); - msleep(100); - if(PIPE_VBLANK_STATUS & REG_READ(pipestat_reg)) { - printk(KERN_ALERT "OK"); - } else { - printk(KERN_ALERT "STUCK!!!!"); - /*shutdown controller*/ - temp = REG_READ(dspcntr_reg); - REG_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - /*mdfld_dsi_dpi_shut_down(dev, pipe);*/ - REG_WRITE(0xb048, 1); - msleep(100); - temp = REG_READ(pipeconf_reg); - temp &= ~PIPEACONF_ENABLE; - REG_WRITE(pipeconf_reg, temp); - msleep(100); /*wait for pipe disable*/ - /*printk(KERN_ALERT "70008 is %x\n", REG_READ(0x70008)); - printk(KERN_ALERT "b074 is %x\n", REG_READ(0xb074));*/ - REG_WRITE(mipi_enable_reg, 0); - msleep(100); - printk(KERN_ALERT "70008 is %x\n", REG_READ(0x70008)); - printk(KERN_ALERT "b074 is %x\n", REG_READ(0xb074)); - REG_WRITE(0xb004, REG_READ(0xb004)); - /* try to bring the controller back up again*/ - REG_WRITE(mipi_enable_reg, 1); - temp = REG_READ(dspcntr_reg); - REG_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - /*mdfld_dsi_dpi_turn_on(dev, pipe);*/ - REG_WRITE(0xb048, 2); - msleep(100); - temp = REG_READ(pipeconf_reg); - temp |= PIPEACONF_ENABLE; - REG_WRITE(pipeconf_reg, temp); - } - } - - psb_intel_crtc_load_lut(crtc); - - /* Give the overlay scaler a chance to enable - if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, true); TODO */ - - break; - case DRM_MODE_DPMS_OFF: - /* Give the overlay scaler a chance to disable - * if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ - if (pipe != 1) - mdfld_dsi_gen_fifo_ready (dev, gen_fifo_stat_reg, HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* Disable display plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, - temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); - } - - /* FIXME_JLIU7 MDFLD_PO revisit */ - /* Wait for vblank for the disable to take effect */ -// MDFLD_PO_JLIU7 psb_intel_wait_for_vblank(dev); - - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - temp &= ~PIPEACONF_ENABLE; - temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; - REG_WRITE(pipeconf_reg, temp); -// REG_WRITE(pipeconf_reg, 0); - REG_READ(pipeconf_reg); - - /* Wait for for the pipe disable to take effect. */ - mdfldWaitForPipeDisable(dev, pipe); - } - - temp = REG_READ(dpll_reg); - if (temp & DPLL_VCO_ENABLE) { - if (((pipe != 1) && !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) - || (pipe == 1)){ - temp &= ~(DPLL_VCO_ENABLE); - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to turn off. */ - /* FIXME_MDFLD PO may need more delay */ - udelay(500); -#if 0 /* MDFLD_PO_JLIU7 */ - if (!(temp & MDFLD_PWR_GATE_EN)) { - /* gating power of DPLL */ - REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(5000); - } -#endif /* MDFLD_PO_JLIU7 */ - } - } - break; - } - - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - -#if 0 /* JB: Add vblank support later */ - if (enabled) - dev_priv->vblank_pipe |= (1 << pipe); - else - dev_priv->vblank_pipe &= ~(1 << pipe); -#endif - - gma_power_end(dev); -} - - -#define MDFLD_LIMT_DPLL_19 0 -#define MDFLD_LIMT_DPLL_25 1 -#define MDFLD_LIMT_DPLL_83 2 -#define MDFLD_LIMT_DPLL_100 3 -#define MDFLD_LIMT_DSIPLL_19 4 -#define MDFLD_LIMT_DSIPLL_25 5 -#define MDFLD_LIMT_DSIPLL_83 6 -#define MDFLD_LIMT_DSIPLL_100 7 - -#define MDFLD_DOT_MIN 19750 /* FIXME_MDFLD JLIU7 need to find out min & max for MDFLD */ -#define MDFLD_DOT_MAX 120000 -#define MDFLD_DPLL_M_MIN_19 113 -#define MDFLD_DPLL_M_MAX_19 155 -#define MDFLD_DPLL_P1_MIN_19 2 -#define MDFLD_DPLL_P1_MAX_19 10 -#define MDFLD_DPLL_M_MIN_25 101 -#define MDFLD_DPLL_M_MAX_25 130 -#define MDFLD_DPLL_P1_MIN_25 2 -#define MDFLD_DPLL_P1_MAX_25 10 -#define MDFLD_DPLL_M_MIN_83 64 -#define MDFLD_DPLL_M_MAX_83 64 -#define MDFLD_DPLL_P1_MIN_83 2 -#define MDFLD_DPLL_P1_MAX_83 2 -#define MDFLD_DPLL_M_MIN_100 64 -#define MDFLD_DPLL_M_MAX_100 64 -#define MDFLD_DPLL_P1_MIN_100 2 -#define MDFLD_DPLL_P1_MAX_100 2 -#define MDFLD_DSIPLL_M_MIN_19 131 -#define MDFLD_DSIPLL_M_MAX_19 175 -#define MDFLD_DSIPLL_P1_MIN_19 3 -#define MDFLD_DSIPLL_P1_MAX_19 8 -#define MDFLD_DSIPLL_M_MIN_25 97 -#define MDFLD_DSIPLL_M_MAX_25 140 -#define MDFLD_DSIPLL_P1_MIN_25 3 -#define MDFLD_DSIPLL_P1_MAX_25 9 -#define MDFLD_DSIPLL_M_MIN_83 33 -#define MDFLD_DSIPLL_M_MAX_83 92 -#define MDFLD_DSIPLL_P1_MIN_83 2 -#define MDFLD_DSIPLL_P1_MAX_83 3 -#define MDFLD_DSIPLL_M_MIN_100 97 -#define MDFLD_DSIPLL_M_MAX_100 140 -#define MDFLD_DSIPLL_P1_MIN_100 3 -#define MDFLD_DSIPLL_P1_MAX_100 9 - -static const struct mdfld_limit_t mdfld_limits[] = { - { /* MDFLD_LIMT_DPLL_19 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DPLL_M_MIN_19, .max = MDFLD_DPLL_M_MAX_19}, - .p1 = {.min = MDFLD_DPLL_P1_MIN_19, .max = MDFLD_DPLL_P1_MAX_19}, - }, - { /* MDFLD_LIMT_DPLL_25 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DPLL_M_MIN_25, .max = MDFLD_DPLL_M_MAX_25}, - .p1 = {.min = MDFLD_DPLL_P1_MIN_25, .max = MDFLD_DPLL_P1_MAX_25}, - }, - { /* MDFLD_LIMT_DPLL_83 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DPLL_M_MIN_83, .max = MDFLD_DPLL_M_MAX_83}, - .p1 = {.min = MDFLD_DPLL_P1_MIN_83, .max = MDFLD_DPLL_P1_MAX_83}, - }, - { /* MDFLD_LIMT_DPLL_100 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DPLL_M_MIN_100, .max = MDFLD_DPLL_M_MAX_100}, - .p1 = {.min = MDFLD_DPLL_P1_MIN_100, .max = MDFLD_DPLL_P1_MAX_100}, - }, - { /* MDFLD_LIMT_DSIPLL_19 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DSIPLL_M_MIN_19, .max = MDFLD_DSIPLL_M_MAX_19}, - .p1 = {.min = MDFLD_DSIPLL_P1_MIN_19, .max = MDFLD_DSIPLL_P1_MAX_19}, - }, - { /* MDFLD_LIMT_DSIPLL_25 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DSIPLL_M_MIN_25, .max = MDFLD_DSIPLL_M_MAX_25}, - .p1 = {.min = MDFLD_DSIPLL_P1_MIN_25, .max = MDFLD_DSIPLL_P1_MAX_25}, - }, - { /* MDFLD_LIMT_DSIPLL_83 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DSIPLL_M_MIN_83, .max = MDFLD_DSIPLL_M_MAX_83}, - .p1 = {.min = MDFLD_DSIPLL_P1_MIN_83, .max = MDFLD_DSIPLL_P1_MAX_83}, - }, - { /* MDFLD_LIMT_DSIPLL_100 */ - .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, - .m = {.min = MDFLD_DSIPLL_M_MIN_100, .max = MDFLD_DSIPLL_M_MAX_100}, - .p1 = {.min = MDFLD_DSIPLL_P1_MIN_100, .max = MDFLD_DSIPLL_P1_MAX_100}, - }, -}; - -#define MDFLD_M_MIN 21 -#define MDFLD_M_MAX 180 -static const u32 mdfld_m_converts[] = { -/* M configuration table from 9-bit LFSR table */ - 224, 368, 440, 220, 366, 439, 219, 365, 182, 347, /* 21 - 30 */ - 173, 342, 171, 85, 298, 149, 74, 37, 18, 265, /* 31 - 40 */ - 388, 194, 353, 432, 216, 108, 310, 155, 333, 166, /* 41 - 50 */ - 83, 41, 276, 138, 325, 162, 337, 168, 340, 170, /* 51 - 60 */ - 341, 426, 469, 234, 373, 442, 221, 110, 311, 411, /* 61 - 70 */ - 461, 486, 243, 377, 188, 350, 175, 343, 427, 213, /* 71 - 80 */ - 106, 53, 282, 397, 354, 227, 113, 56, 284, 142, /* 81 - 90 */ - 71, 35, 273, 136, 324, 418, 465, 488, 500, 506, /* 91 - 100 */ - 253, 126, 63, 287, 399, 455, 483, 241, 376, 444, /* 101 - 110 */ - 478, 495, 503, 251, 381, 446, 479, 239, 375, 443, /* 111 - 120 */ - 477, 238, 119, 315, 157, 78, 295, 147, 329, 420, /* 121 - 130 */ - 210, 105, 308, 154, 77, 38, 275, 137, 68, 290, /* 131 - 140 */ - 145, 328, 164, 82, 297, 404, 458, 485, 498, 249, /* 141 - 150 */ - 380, 190, 351, 431, 471, 235, 117, 314, 413, 206, /* 151 - 160 */ - 103, 51, 25, 12, 262, 387, 193, 96, 48, 280, /* 161 - 170 */ - 396, 198, 99, 305, 152, 76, 294, 403, 457, 228, /* 171 - 180 */ -}; - -static const struct mdfld_limit_t *mdfld_limit(struct drm_crtc *crtc) -{ - const struct mdfld_limit_t *limit = NULL; - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI) - || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI2)) { - if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) - limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_19]; - else if (ksel == KSEL_BYPASS_25) - limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_25]; - else if ((ksel == KSEL_BYPASS_83_100) && (dev_priv->core_freq == 166)) - limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_83]; - else if ((ksel == KSEL_BYPASS_83_100) && - (dev_priv->core_freq == 100 || dev_priv->core_freq == 200)) - limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_100]; - } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { - if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) - limit = &mdfld_limits[MDFLD_LIMT_DPLL_19]; - else if (ksel == KSEL_BYPASS_25) - limit = &mdfld_limits[MDFLD_LIMT_DPLL_25]; - else if ((ksel == KSEL_BYPASS_83_100) && (dev_priv->core_freq == 166)) - limit = &mdfld_limits[MDFLD_LIMT_DPLL_83]; - else if ((ksel == KSEL_BYPASS_83_100) && - (dev_priv->core_freq == 100 || dev_priv->core_freq == 200)) - limit = &mdfld_limits[MDFLD_LIMT_DPLL_100]; - } else { - limit = NULL; - dev_err(dev->dev, "mdfld_limit Wrong display type.\n"); - } - - return limit; -} - -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ -static void mdfld_clock(int refclk, struct mdfld_intel_clock_t *clock) -{ - clock->dot = (refclk * clock->m) / clock->p1; -} - -/** - * Returns a set of divisors for the desired target clock with the given refclk, - * or FALSE. Divisor values are the actual divisors for - */ -static bool -mdfldFindBestPLL(struct drm_crtc *crtc, int target, int refclk, - struct mdfld_intel_clock_t *best_clock) -{ - struct mdfld_intel_clock_t clock; - const struct mdfld_limit_t *limit = mdfld_limit(crtc); - int err = target; - - memset(best_clock, 0, sizeof(*best_clock)); - - for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { - for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; - clock.p1++) { - int this_err; - - mdfld_clock(refclk, &clock); - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - return err != target; -} - -/** - * Return the pipe currently connected to the panel fitter, - * or -1 if the panel fitter is not present or not in use - */ -static int mdfld_panel_fitter_pipe(struct drm_device *dev) -{ - u32 pfit_control; - - pfit_control = REG_READ(PFIT_CONTROL); - - /* See if the panel fitter is in use */ - if ((pfit_control & PFIT_ENABLE) == 0) - return -1; - return (pfit_control >> 29) & 3; -} - -static int mdfld_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct drm_psb_private *dev_priv = dev->dev_private; - int pipe = psb_intel_crtc->pipe; - int fp_reg = MRST_FPA0; - int dpll_reg = MRST_DPLL_A; - int dspcntr_reg = DSPACNTR; - int pipeconf_reg = PIPEACONF; - int htot_reg = HTOTAL_A; - int hblank_reg = HBLANK_A; - int hsync_reg = HSYNC_A; - int vtot_reg = VTOTAL_A; - int vblank_reg = VBLANK_A; - int vsync_reg = VSYNC_A; - int dspsize_reg = DSPASIZE; - int dsppos_reg = DSPAPOS; - int pipesrc_reg = PIPEASRC; - u32 *pipeconf = &dev_priv->pipeconf; - u32 *dspcntr = &dev_priv->dspcntr; - int refclk = 0; - int clk_n = 0, clk_p2 = 0, clk_byte = 1, clk = 0, m_conv = 0, clk_tmp = 0; - struct mdfld_intel_clock_t clock; - bool ok; - u32 dpll = 0, fp = 0; - bool is_crt = false, is_lvds = false, is_tv = false; - bool is_mipi = false, is_mipi2 = false, is_hdmi = false; - struct drm_mode_config *mode_config = &dev->mode_config; - struct psb_intel_output *psb_intel_output = NULL; - uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; - struct drm_encoder *encoder; - struct drm_connector *connector; - int timeout = 0; - - dev_dbg(dev->dev, "pipe = 0x%x \n", pipe); - - switch (pipe) { - case 0: - break; - case 1: - fp_reg = FPB0; - dpll_reg = DPLL_B; - dspcntr_reg = DSPBCNTR; - pipeconf_reg = PIPEBCONF; - htot_reg = HTOTAL_B; - hblank_reg = HBLANK_B; - hsync_reg = HSYNC_B; - vtot_reg = VTOTAL_B; - vblank_reg = VBLANK_B; - vsync_reg = VSYNC_B; - dspsize_reg = DSPBSIZE; - dsppos_reg = DSPBPOS; - pipesrc_reg = PIPEBSRC; - pipeconf = &dev_priv->pipeconf1; - dspcntr = &dev_priv->dspcntr1; - fp_reg = MDFLD_DPLL_DIV0; - dpll_reg = MDFLD_DPLL_B; - break; - case 2: - dpll_reg = MRST_DPLL_A; - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - htot_reg = HTOTAL_C; - hblank_reg = HBLANK_C; - hsync_reg = HSYNC_C; - vtot_reg = VTOTAL_C; - vblank_reg = VBLANK_C; - vsync_reg = VSYNC_C; - dspsize_reg = DSPCSIZE; - dsppos_reg = DSPCPOS; - pipesrc_reg = PIPECSRC; - pipeconf = &dev_priv->pipeconf2; - dspcntr = &dev_priv->dspcntr2; - break; - default: - DRM_ERROR("Illegal Pipe Number. \n"); - return 0; - } - - dev_dbg(dev->dev, "adjusted_hdisplay = %d\n", - adjusted_mode->hdisplay); - dev_dbg(dev->dev, "adjusted_vdisplay = %d\n", - adjusted_mode->vdisplay); - dev_dbg(dev->dev, "adjusted_hsync_start = %d\n", - adjusted_mode->hsync_start); - dev_dbg(dev->dev, "adjusted_hsync_end = %d\n", - adjusted_mode->hsync_end); - dev_dbg(dev->dev, "adjusted_htotal = %d\n", - adjusted_mode->htotal); - dev_dbg(dev->dev, "adjusted_vsync_start = %d\n", - adjusted_mode->vsync_start); - dev_dbg(dev->dev, "adjusted_vsync_end = %d\n", - adjusted_mode->vsync_end); - dev_dbg(dev->dev, "adjusted_vtotal = %d\n", - adjusted_mode->vtotal); - dev_dbg(dev->dev, "adjusted_clock = %d\n", - adjusted_mode->clock); - dev_dbg(dev->dev, "hdisplay = %d\n", - mode->hdisplay); - dev_dbg(dev->dev, "vdisplay = %d\n", - mode->vdisplay); - - if (!gma_power_begin(dev, true)) - return 0; - - memcpy(&psb_intel_crtc->saved_mode, mode, sizeof(struct drm_display_mode)); - memcpy(&psb_intel_crtc->saved_adjusted_mode, adjusted_mode, sizeof(struct drm_display_mode)); - - list_for_each_entry(connector, &mode_config->connector_list, head) { - - encoder = connector->encoder; - - if(!encoder) - continue; - - if (encoder->crtc != crtc) - continue; - - psb_intel_output = to_psb_intel_output(connector); - - dev_dbg(dev->dev, "output->type = 0x%x \n", psb_intel_output->type); - - switch (psb_intel_output->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; - case INTEL_OUTPUT_MIPI: - is_mipi = true; - break; - case INTEL_OUTPUT_MIPI2: - is_mipi2 = true; - break; - case INTEL_OUTPUT_HDMI: - is_hdmi = true; - break; - } - } - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* Disable the panel fitter if it was on our pipe */ - if (mdfld_panel_fitter_pipe(dev) == pipe) - REG_WRITE(PFIT_CONTROL, 0); - - /* pipesrc and dspsize control the size that is scaled from, - * which should always be the user's requested size. - */ - if (pipe == 1) { - /* FIXME: To make HDMI display with 864x480 (TPO), 480x864 (PYR) or 480x854 (TMD), set the sprite - * width/height and souce image size registers with the adjusted mode for pipe B. */ - - /* The defined sprite rectangle must always be completely contained within the displayable - * area of the screen image (frame buffer). */ - REG_WRITE(dspsize_reg, ((MIN(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16) - | (MIN(mode->crtc_hdisplay, adjusted_mode->crtc_hdisplay) - 1)); - /* Set the CRTC with encoder mode. */ - REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) - | (mode->crtc_vdisplay - 1)); - } else { - REG_WRITE(dspsize_reg, ((mode->crtc_vdisplay - 1) << 16) | (mode->crtc_hdisplay - 1)); - REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); - } - - REG_WRITE(dsppos_reg, 0); - - if (psb_intel_output) - drm_connector_property_get_value(&psb_intel_output->base, - dev->mode_config.scaling_mode_property, &scalingType); - - if (scalingType == DRM_MODE_SCALE_NO_SCALE) { - /* - * Medfield doesn't have register support for centering so - * we need to mess with the h/vblank and h/vsync start and - * ends to get central - */ - int offsetX = 0, offsetY = 0; - - offsetX = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; - offsetY = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; - - REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - offsetX - 1) | - ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - offsetX - 1) | - ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - offsetY - 1) | - ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - offsetY - 1) | - ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); - } else { - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | - ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | - ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | - ((adjusted_mode->crtc_vsync_end - 1) << 16)); - } - - /* Flush the plane changes */ - { - struct drm_crtc_helper_funcs *crtc_funcs = - crtc->helper_private; - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - } - - /* setup pipeconf */ - *pipeconf = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */ - - /* Set up the display plane register */ - *dspcntr = REG_READ(dspcntr_reg); - *dspcntr |= pipe << DISPPLANE_SEL_PIPE_POS; - *dspcntr |= DISPLAY_PLANE_ENABLE; -/* MDFLD_PO_JLIU7 dspcntr |= DISPPLANE_BOTTOM; */ -/* MDFLD_PO_JLIU7 dspcntr |= DISPPLANE_GAMMA_ENABLE; */ - - if (is_mipi2) - { - goto mrst_crtc_mode_set_exit; - } -/* FIXME JLIU7 Add MDFLD HDMI supports */ -/* FIXME_MDFLD JLIU7 DSIPLL clock *= 8? */ -/* FIXME_MDFLD JLIU7 need to revist for dual MIPI supports */ - clk = adjusted_mode->clock; - - if (is_hdmi) { - if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) - { - refclk = 19200; - - if (is_mipi || is_mipi2) - { - clk_n = 1, clk_p2 = 8; - } else if (is_hdmi) { - clk_n = 1, clk_p2 = 10; - } - } else if (ksel == KSEL_BYPASS_25) { - refclk = 25000; - - if (is_mipi || is_mipi2) - { - clk_n = 1, clk_p2 = 8; - } else if (is_hdmi) { - clk_n = 1, clk_p2 = 10; - } - } else if ((ksel == KSEL_BYPASS_83_100) && (dev_priv->core_freq == 166)) { - refclk = 83000; - - if (is_mipi || is_mipi2) - { - clk_n = 4, clk_p2 = 8; - } else if (is_hdmi) { - clk_n = 4, clk_p2 = 10; - } - } else if ((ksel == KSEL_BYPASS_83_100) && - (dev_priv->core_freq == 100 || dev_priv->core_freq == 200)) { - refclk = 100000; - if (is_mipi || is_mipi2) - { - clk_n = 4, clk_p2 = 8; - } else if (is_hdmi) { - clk_n = 4, clk_p2 = 10; - } - } - - if (is_mipi) - clk_byte = dev_priv->bpp / 8; - else if (is_mipi2) - clk_byte = dev_priv->bpp2 / 8; - - clk_tmp = clk * clk_n * clk_p2 * clk_byte; - - dev_dbg(dev->dev, "clk = %d, clk_n = %d, clk_p2 = %d. \n", clk, clk_n, clk_p2); - dev_dbg(dev->dev, "adjusted_mode->clock = %d, clk_tmp = %d. \n", adjusted_mode->clock, clk_tmp); - - ok = mdfldFindBestPLL(crtc, clk_tmp, refclk, &clock); - - if (!ok) { - dev_err(dev->dev, - "mdfldFindBestPLL fail in mdfld_crtc_mode_set. \n"); - } else { - m_conv = mdfld_m_converts[(clock.m - MDFLD_M_MIN)]; - - dev_dbg(dev->dev, "dot clock = %d," - "m = %d, p1 = %d, m_conv = %d. \n", clock.dot, clock.m, - clock.p1, m_conv); - } - - dpll = REG_READ(dpll_reg); - - if (dpll & DPLL_VCO_ENABLE) { - dpll &= ~DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - - /* FIXME jliu7 check the DPLL lock bit PIPEACONF[29] */ - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - - /* reset M1, N1 & P1 */ - REG_WRITE(fp_reg, 0); - dpll &= ~MDFLD_P1_MASK; - REG_WRITE(dpll_reg, dpll); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - } - - /* When ungating power of DPLL, needs to wait 0.5us before enable the VCO */ - if (dpll & MDFLD_PWR_GATE_EN) { - dpll &= ~MDFLD_PWR_GATE_EN; - REG_WRITE(dpll_reg, dpll); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - } - - dpll = 0; - -#if 0 /* FIXME revisit later */ - if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19) || (ksel == KSEL_BYPASS_25)) { - dpll &= ~MDFLD_INPUT_REF_SEL; - } else if (ksel == KSEL_BYPASS_83_100) { - dpll |= MDFLD_INPUT_REF_SEL; - } -#endif /* FIXME revisit later */ - - if (is_hdmi) - dpll |= MDFLD_VCO_SEL; - - fp = (clk_n / 2) << 16; - fp |= m_conv; - - /* compute bitmask from p1 value */ - dpll |= (1 << (clock.p1 - 2)) << 17; - -#if 0 /* 1080p30 & 720p */ - dpll = 0x00050000; - fp = 0x000001be; -#endif -#if 0 /* 480p */ - dpll = 0x02010000; - fp = 0x000000d2; -#endif - } else { -#if 0 /*DBI_TPO_480x864*/ - dpll = 0x00020000; - fp = 0x00000156; -#endif /* DBI_TPO_480x864 */ /* get from spec. */ - - dpll = 0x00800000; - fp = 0x000000c1; -} - - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - /* FIXME_MDFLD PO - change 500 to 1 after PO */ - udelay(500); - - dpll |= DPLL_VCO_ENABLE; - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - - /* wait for DSI PLL to lock */ - while ((timeout < 20000) && !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { - udelay(150); - timeout ++; - } - - if (is_mipi) - goto mrst_crtc_mode_set_exit; - - dev_dbg(dev->dev, "is_mipi = 0x%x \n", is_mipi); - - REG_WRITE(pipeconf_reg, *pipeconf); - REG_READ(pipeconf_reg); - - /* Wait for for the pipe enable to take effect. */ -//FIXME_JLIU7 HDMI mrstWaitForPipeEnable(dev); - - REG_WRITE(dspcntr_reg, *dspcntr); - psb_intel_wait_for_vblank(dev); - -mrst_crtc_mode_set_exit: - - gma_power_end(dev); - - return 0; -} - -static void mdfld_crtc_prepare(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); -} - -static void mdfld_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -} - -static bool mdfld_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -const struct drm_crtc_helper_funcs mdfld_helper_funcs = { - .dpms = mdfld_crtc_dpms, - .mode_fixup = mdfld_crtc_mode_fixup, - .mode_set = mdfld_crtc_mode_set, - .mode_set_base = mdfld__intel_pipe_set_base, - .prepare = mdfld_crtc_prepare, - .commit = mdfld_crtc_commit, -}; diff --git a/drivers/staging/gma500/mdfld_msic.h b/drivers/staging/gma500/mdfld_msic.h deleted file mode 100644 index a7ad6547..0000000 --- a/drivers/staging/gma500/mdfld_msic.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Jim Liu - */ - -#define MSIC_PCI_DEVICE_ID 0x831 - -int msic_regsiter_driver(void); -int msic_unregister_driver(void); -extern void hpd_notify_um(void); diff --git a/drivers/staging/gma500/mdfld_output.c b/drivers/staging/gma500/mdfld_output.c deleted file mode 100644 index eabf53d..0000000 --- a/drivers/staging/gma500/mdfld_output.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe -*/ - -#include -#include -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" -#include "mdfld_dsi_dbi_dpu.h" - -#include "displays/tpo_cmd.h" -#include "displays/tpo_vid.h" -#include "displays/tmd_cmd.h" -#include "displays/tmd_vid.h" -#include "displays/pyr_cmd.h" -#include "displays/pyr_vid.h" -/* #include "displays/hdmi.h" */ - -static int mdfld_dual_mipi; -static int mdfld_hdmi; -static int mdfld_dpu; - -module_param(mdfld_dual_mipi, int, 0600); -MODULE_PARM_DESC(mdfld_dual_mipi, "Enable dual MIPI configuration"); -module_param(mdfld_hdmi, int, 0600); -MODULE_PARM_DESC(mdfld_hdmi, "Enable Medfield HDMI"); -module_param(mdfld_dpu, int, 0600); -MODULE_PARM_DESC(mdfld_dpu, "Enable Medfield DPU"); - -/* For now a single type per device is all we cope with */ -int mdfld_get_panel_type(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - return dev_priv->panel_id; -} - -int mdfld_panel_dpi(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - switch (dev_priv->panel_id) { - case TMD_VID: - case TPO_VID: - case PYR_VID: - return true; - case TMD_CMD: - case TPO_CMD: - case PYR_CMD: - default: - return false; - } -} - -static int init_panel(struct drm_device *dev, int mipi_pipe, int p_type) -{ - struct panel_funcs *p_cmd_funcs; - struct panel_funcs *p_vid_funcs; - - /* Oh boy ... FIXME */ - p_cmd_funcs = kzalloc(sizeof(struct panel_funcs), GFP_KERNEL); - if (p_cmd_funcs == NULL) - return -ENODEV; - p_vid_funcs = kzalloc(sizeof(struct panel_funcs), GFP_KERNEL); - if (p_vid_funcs == NULL) { - kfree(p_cmd_funcs); - return -ENODEV; - } - - switch (p_type) { - case TPO_CMD: - tpo_cmd_init(dev, p_cmd_funcs); - mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, NULL); - break; - case TPO_VID: - tpo_vid_init(dev, p_vid_funcs); - mdfld_dsi_output_init(dev, mipi_pipe, NULL, NULL, p_vid_funcs); - break; - case TMD_CMD: - /*tmd_cmd_init(dev, p_cmd_funcs); */ - mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, NULL); - break; - case TMD_VID: - tmd_vid_init(dev, p_vid_funcs); - mdfld_dsi_output_init(dev, mipi_pipe, NULL, NULL, p_vid_funcs); - break; - case PYR_CMD: - pyr_cmd_init(dev, p_cmd_funcs); - mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, NULL); - break; - case PYR_VID: - mdfld_dsi_output_init(dev, mipi_pipe, NULL, NULL, p_vid_funcs); - break; - case TPO: /* TPO panel supports both cmd & vid interfaces */ - tpo_cmd_init(dev, p_cmd_funcs); - tpo_vid_init(dev, p_vid_funcs); - mdfld_dsi_output_init(dev, mipi_pipe, NULL, p_cmd_funcs, - p_vid_funcs); - break; - case TMD: - break; - case PYR: - break; -#if 0 - case HDMI: - dev_dbg(dev->dev, "Initializing HDMI"); - mdfld_hdmi_init(dev, &dev_priv->mode_dev); - break; -#endif - default: - dev_err(dev->dev, "Unsupported interface %d", p_type); - return -ENODEV; - } - return 0; -} - -int mdfld_output_init(struct drm_device *dev) -{ - int type; - - /* MIPI panel 1 */ - type = mdfld_get_panel_type(dev, 0); - dev_info(dev->dev, "panel 1: type is %d\n", type); - init_panel(dev, 0, type); - - if (mdfld_dual_mipi) { - /* MIPI panel 2 */ - type = mdfld_get_panel_type(dev, 2); - dev_info(dev->dev, "panel 2: type is %d\n", type); - init_panel(dev, 2, type); - } - if (mdfld_hdmi) - /* HDMI panel */ - init_panel(dev, 0, HDMI); - return 0; -} - -void mdfld_output_setup(struct drm_device *dev) -{ - /* FIXME: this is not the right place for this stuff ! */ - if (IS_MFLD(dev)) { - if (mdfld_dpu) - mdfld_dbi_dpu_init(dev); - else - mdfld_dbi_dsr_init(dev); - } -} diff --git a/drivers/staging/gma500/mdfld_output.h b/drivers/staging/gma500/mdfld_output.h deleted file mode 100644 index daf33e7..0000000 --- a/drivers/staging/gma500/mdfld_output.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe -*/ - -#ifndef MDFLD_OUTPUT_H -#define MDFLD_OUTPUT_H - -int mdfld_output_init(struct drm_device *dev); -int mdfld_panel_dpi(struct drm_device *dev); -int mdfld_get_panel_type(struct drm_device *dev, int pipe); -void mdfld_disable_crtc (struct drm_device *dev, int pipe); - -extern const struct drm_crtc_helper_funcs mdfld_helper_funcs; -extern const struct drm_crtc_funcs mdfld_intel_crtc_funcs; - -extern void mdfld_output_setup(struct drm_device *dev); - -#endif diff --git a/drivers/staging/gma500/mdfld_pyr_cmd.c b/drivers/staging/gma500/mdfld_pyr_cmd.c deleted file mode 100644 index 523f2d8..0000000 --- a/drivers/staging/gma500/mdfld_pyr_cmd.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe -*/ - -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" -#include "mdfld_dsi_dbi_dpu.h" -#include "mdfld_dsi_pkg_sender.h" - -#include "displays/pyr_cmd.h" - -static struct drm_display_mode *pyr_cmd_get_config_mode(struct drm_device *dev) -{ - struct drm_display_mode *mode; - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) { - dev_err(dev->dev, "Out of memory\n"); - return NULL; - } - - dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); - dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); - dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); - dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); - dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); - dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); - dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); - dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); - dev_dbg(dev->dev, "clock is %d\n", mode->clock); - - mode->hdisplay = 480; - mode->vdisplay = 864; - mode->hsync_start = 487; - mode->hsync_end = 490; - mode->htotal = 499; - mode->vsync_start = 874; - mode->vsync_end = 878; - mode->vtotal = 886; - mode->clock = 25777; - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - mode->type |= DRM_MODE_TYPE_PREFERRED; - - return mode; -} - -static bool pyr_dsi_dbi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_display_mode *fixed_mode = pyr_cmd_get_config_mode(dev); - - if (fixed_mode) { - adjusted_mode->hdisplay = fixed_mode->hdisplay; - adjusted_mode->hsync_start = fixed_mode->hsync_start; - adjusted_mode->hsync_end = fixed_mode->hsync_end; - adjusted_mode->htotal = fixed_mode->htotal; - adjusted_mode->vdisplay = fixed_mode->vdisplay; - adjusted_mode->vsync_start = fixed_mode->vsync_start; - adjusted_mode->vsync_end = fixed_mode->vsync_end; - adjusted_mode->vtotal = fixed_mode->vtotal; - adjusted_mode->clock = fixed_mode->clock; - drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); - kfree(fixed_mode); - } - return true; -} - -static void pyr_dsi_dbi_set_power(struct drm_encoder *encoder, bool on) -{ - int ret = 0; - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 reg_offset = 0; - int pipe = (dbi_output->channel_num == 0) ? 0 : 2; - - dev_dbg(dev->dev, "pipe %d : %s, panel on: %s\n", pipe, - on ? "On" : "Off", - dbi_output->dbi_panel_on ? "True" : "False"); - - if (pipe == 2) { - if (on) - dev_priv->dual_mipi = true; - else - dev_priv->dual_mipi = false; - - reg_offset = MIPIC_REG_OFFSET; - } else { - if (!on) - dev_priv->dual_mipi = false; - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - - if (on) { - if (dbi_output->dbi_panel_on) - goto out_err; - - ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_ON); - if (ret) { - dev_err(dev->dev, "power on error\n"); - goto out_err; - } - - dbi_output->dbi_panel_on = true; - - if (pipe == 2) { - dev_priv->dbi_panel_on2 = true; - } else { - dev_priv->dbi_panel_on = true; - mdfld_enable_te(dev, 0); - } - } else { - if (!dbi_output->dbi_panel_on && !dbi_output->first_boot) - goto out_err; - - dbi_output->dbi_panel_on = false; - dbi_output->first_boot = false; - - if (pipe == 2) { - dev_priv->dbi_panel_on2 = false; - mdfld_disable_te(dev, 2); - } else { - dev_priv->dbi_panel_on = false; - mdfld_disable_te(dev, 0); - - if (dev_priv->dbi_panel_on2) - mdfld_enable_te(dev, 2); - } - - ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_OFF); - if (ret) { - dev_err(dev->dev, "power on error\n"); - goto out_err; - } - } - -out_err: - gma_power_end(dev); - - if (ret) - dev_err(dev->dev, "failed\n"); -} - -static void pyr_dsi_controller_dbi_init(struct mdfld_dsi_config *dsi_config, - int pipe) -{ - struct drm_device *dev = dsi_config->dev; - u32 reg_offset = pipe ? MIPIC_REG_OFFSET : 0; - int lane_count = dsi_config->lane_count; - u32 val = 0; - - dev_dbg(dev->dev, "Init DBI interface on pipe %d...\n", pipe); - - /* Un-ready device */ - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000000); - - /* Init dsi adapter before kicking off */ - REG_WRITE((MIPIA_CONTROL_REG + reg_offset), 0x00000018); - - /* TODO: figure out how to setup these registers */ - REG_WRITE((MIPIA_DPHY_PARAM_REG + reg_offset), 0x150c600F); - REG_WRITE((MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG + reg_offset), - 0x000a0014); - REG_WRITE((MIPIA_DBI_BW_CTRL_REG + reg_offset), 0x00000400); - REG_WRITE((MIPIA_HS_LS_DBI_ENABLE_REG + reg_offset), 0x00000000); - - /* Enable all interrupts */ - REG_WRITE((MIPIA_INTR_EN_REG + reg_offset), 0xffffffff); - /* Max value: 20 clock cycles of txclkesc */ - REG_WRITE((MIPIA_TURN_AROUND_TIMEOUT_REG + reg_offset), 0x0000001f); - /* Min 21 txclkesc, max: ffffh */ - REG_WRITE((MIPIA_DEVICE_RESET_TIMER_REG + reg_offset), 0x0000ffff); - /* Min: 7d0 max: 4e20 */ - REG_WRITE((MIPIA_INIT_COUNT_REG + reg_offset), 0x00000fa0); - - /* Set up func_prg */ - val |= lane_count; - val |= (dsi_config->channel_num << DSI_DBI_VIRT_CHANNEL_OFFSET); - val |= DSI_DBI_COLOR_FORMAT_OPTION2; - REG_WRITE((MIPIA_DSI_FUNC_PRG_REG + reg_offset), val); - - REG_WRITE((MIPIA_HS_TX_TIMEOUT_REG + reg_offset), 0x3fffff); - REG_WRITE((MIPIA_LP_RX_TIMEOUT_REG + reg_offset), 0xffff); - - /* De-assert dbi_stall when half of DBI FIFO is empty */ - /* REG_WRITE((MIPIA_DBI_FIFO_THROTTLE_REG + reg_offset), 0x00000000); */ - - REG_WRITE((MIPIA_HIGH_LOW_SWITCH_COUNT_REG + reg_offset), 0x46); - REG_WRITE((MIPIA_EOT_DISABLE_REG + reg_offset), 0x00000002); - REG_WRITE((MIPIA_LP_BYTECLK_REG + reg_offset), 0x00000004); - REG_WRITE((MIPIA_DEVICE_READY_REG + reg_offset), 0x00000001); -} - -static void pyr_dsi_dbi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - int ret = 0; - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dsi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct mdfld_dsi_config *dsi_config = - mdfld_dsi_encoder_get_config(dsi_encoder); - struct mdfld_dsi_connector *dsi_connector = dsi_config->connector; - int pipe = dsi_connector->pipe; - u8 param = 0; - - /* Regs */ - u32 mipi_reg = MIPI; - u32 dspcntr_reg = DSPACNTR; - u32 pipeconf_reg = PIPEACONF; - u32 reg_offset = 0; - - /* Values */ - u32 dspcntr_val = dev_priv->dspcntr; - u32 pipeconf_val = dev_priv->pipeconf; - u32 h_active_area = mode->hdisplay; - u32 v_active_area = mode->vdisplay; - u32 mipi_val = (PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX | - TE_TRIGGER_GPIO_PIN); - - dev_dbg(dev->dev, "mipi_val =0x%x\n", mipi_val); - - dev_dbg(dev->dev, "type %s\n", (pipe == 2) ? "MIPI2" : "MIPI"); - dev_dbg(dev->dev, "h %d v %d\n", mode->hdisplay, mode->vdisplay); - - if (pipe == 2) { - mipi_reg = MIPI_C; - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - - reg_offset = MIPIC_REG_OFFSET; - - dspcntr_val = dev_priv->dspcntr2; - pipeconf_val = dev_priv->pipeconf2; - } else { - mipi_val |= 0x2; /* Two lanes for port A and C respectively */ - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - /* Set up pipe related registers */ - REG_WRITE(mipi_reg, mipi_val); - REG_READ(mipi_reg); - - pyr_dsi_controller_dbi_init(dsi_config, pipe); - - msleep(20); - - REG_WRITE(dspcntr_reg, dspcntr_val); - REG_READ(dspcntr_reg); - - /* 20ms delay before sending exit_sleep_mode */ - msleep(20); - - /* Send exit_sleep_mode DCS */ - ret = mdfld_dsi_dbi_send_dcs(dsi_output, exit_sleep_mode, NULL, - 0, CMD_DATA_SRC_SYSTEM_MEM); - if (ret) { - dev_err(dev->dev, "sent exit_sleep_mode faild\n"); - goto out_err; - } - - /*send set_tear_on DCS*/ - ret = mdfld_dsi_dbi_send_dcs(dsi_output, set_tear_on, - ¶m, 1, CMD_DATA_SRC_SYSTEM_MEM); - if (ret) { - dev_err(dev->dev, "%s - sent set_tear_on faild\n", __func__); - goto out_err; - } - - /* Do some init stuff */ - mdfld_dsi_brightness_init(dsi_config, pipe); - mdfld_dsi_gen_fifo_ready(dev, (MIPIA_GEN_FIFO_STAT_REG + reg_offset), - HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); - - REG_WRITE(pipeconf_reg, pipeconf_val | PIPEACONF_DSR); - REG_READ(pipeconf_reg); - - /* TODO: this looks ugly, try to move it to CRTC mode setting */ - if (pipe == 2) - dev_priv->pipeconf2 |= PIPEACONF_DSR; - else - dev_priv->pipeconf |= PIPEACONF_DSR; - - dev_dbg(dev->dev, "pipeconf %x\n", REG_READ(pipeconf_reg)); - - ret = mdfld_dsi_dbi_update_area(dsi_output, 0, 0, - h_active_area - 1, v_active_area - 1); - if (ret) { - dev_err(dev->dev, "update area failed\n"); - goto out_err; - } - -out_err: - gma_power_end(dev); - - if (ret) - dev_err(dev->dev, "mode set failed\n"); - else - dev_dbg(dev->dev, "mode set done successfully\n"); -} - -static void pyr_dsi_dbi_prepare(struct drm_encoder *encoder) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - - dbi_output->mode_flags |= MODE_SETTING_IN_ENCODER; - dbi_output->mode_flags &= ~MODE_SETTING_ENCODER_DONE; - - pyr_dsi_dbi_set_power(encoder, false); -} - -static void pyr_dsi_dbi_commit(struct drm_encoder *encoder) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct drm_device *dev = dbi_output->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_drm_dpu_rect rect; - - pyr_dsi_dbi_set_power(encoder, true); - - dbi_output->mode_flags &= ~MODE_SETTING_IN_ENCODER; - - rect.x = rect.y = 0; - rect.width = 864; - rect.height = 480; - - if (dbi_output->channel_num == 1) { - dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_2; - /* If DPU enabled report a fullscreen damage */ - mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEC, &rect); - } else { - dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_0; - mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEA, &rect); - } - dbi_output->mode_flags |= MODE_SETTING_ENCODER_DONE; -} - -static void pyr_dsi_dbi_dpms(struct drm_encoder *encoder, int mode) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct drm_device *dev = dbi_output->dev; - - dev_dbg(dev->dev, "%s\n", (mode == DRM_MODE_DPMS_ON ? "on" : "off")); - - if (mode == DRM_MODE_DPMS_ON) - pyr_dsi_dbi_set_power(encoder, true); - else - pyr_dsi_dbi_set_power(encoder, false); -} - -/* - * Update the DBI MIPI Panel Frame Buffer. - */ -static void pyr_dsi_dbi_update_fb(struct mdfld_dsi_dbi_output *dbi_output, - int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = - mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); - struct drm_device *dev = dbi_output->dev; - struct drm_crtc *crtc = dbi_output->base.base.crtc; - struct psb_intel_crtc *psb_crtc = (crtc) ? - to_psb_intel_crtc(crtc) : NULL; - - u32 dpll_reg = MRST_DPLL_A; - u32 dspcntr_reg = DSPACNTR; - u32 pipeconf_reg = PIPEACONF; - u32 dsplinoff_reg = DSPALINOFF; - u32 dspsurf_reg = DSPASURF; - u32 hs_gen_ctrl_reg = HS_GEN_CTRL_REG; - u32 gen_fifo_stat_reg = GEN_FIFO_STAT_REG; - u32 reg_offset = 0; - - u32 intr_status; - u32 fifo_stat_reg_val; - u32 dpll_reg_val; - u32 dspcntr_reg_val; - u32 pipeconf_reg_val; - - /* If mode setting on-going, back off */ - if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || - (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING) || - !(dbi_output->mode_flags & MODE_SETTING_ENCODER_DONE)) - return; - - /* - * Look for errors here. In particular we're checking for whatever - * error status might have appeared during the last frame transmit - * (memory write). - * - * Normally, the bits we're testing here would be set infrequently, - * if at all. However, one panel (at least) returns at least one - * error bit on most frames. So we've disabled the kernel message - * for now. - * - * Still clear whatever error bits are set, except don't clear the - * ones that would make the Penwell DSI controller reset if we - * cleared them. - */ - intr_status = REG_READ(INTR_STAT_REG); - if ((intr_status & 0x26FFFFFF) != 0) { - /* dev_err(dev->dev, "DSI status: 0x%08X\n", intr_status); */ - intr_status &= 0x26F3FFFF; - REG_WRITE(INTR_STAT_REG, intr_status); - } - - if (pipe == 2) { - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - dsplinoff_reg = DSPCLINOFF; - dspsurf_reg = DSPCSURF; - - hs_gen_ctrl_reg = HS_GEN_CTRL_REG + MIPIC_REG_OFFSET; - gen_fifo_stat_reg = GEN_FIFO_STAT_REG + MIPIC_REG_OFFSET, - - reg_offset = MIPIC_REG_OFFSET; - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - fifo_stat_reg_val = REG_READ(MIPIA_GEN_FIFO_STAT_REG + reg_offset); - dpll_reg_val = REG_READ(dpll_reg); - dspcntr_reg_val = REG_READ(dspcntr_reg); - pipeconf_reg_val = REG_READ(pipeconf_reg); - - if (!(fifo_stat_reg_val & (1 << 27)) || - (dpll_reg_val & DPLL_VCO_ENABLE) || - !(dspcntr_reg_val & DISPLAY_PLANE_ENABLE) || - !(pipeconf_reg_val & DISPLAY_PLANE_ENABLE)) { - goto update_fb_out0; - } - - /* Refresh plane changes */ - REG_WRITE(dsplinoff_reg, REG_READ(dsplinoff_reg)); - REG_WRITE(dspsurf_reg, REG_READ(dspsurf_reg)); - REG_READ(dspsurf_reg); - - mdfld_dsi_send_dcs(sender, - write_mem_start, - NULL, - 0, - CMD_DATA_SRC_PIPE, - MDFLD_DSI_SEND_PACKAGE); - - /* - * The idea here is to transmit a Generic Read command after the - * Write Memory Start/Continue commands finish. This asks for - * the panel to return an "ACK No Errors," or (if it has errors - * to report) an Error Report. This allows us to monitor the - * panel's perception of the health of the DSI. - */ - mdfld_dsi_gen_fifo_ready(dev, gen_fifo_stat_reg, - HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); - REG_WRITE(hs_gen_ctrl_reg, (1 << WORD_COUNTS_POS) | GEN_READ_0); - - dbi_output->dsr_fb_update_done = true; -update_fb_out0: - gma_power_end(dev); -} - -/* - * TODO: will be removed later, should work out display interfaces for power - */ -void pyr_dsi_adapter_init(struct mdfld_dsi_config *dsi_config, int pipe) -{ - if (!dsi_config || (pipe != 0 && pipe != 2)) { - WARN_ON(1); - return; - } - pyr_dsi_controller_dbi_init(dsi_config, pipe); -} - -static int pyr_cmd_get_panel_info(struct drm_device *dev, int pipe, - struct panel_info *pi) -{ - if (!dev || !pi) - return -EINVAL; - - pi->width_mm = PYR_PANEL_WIDTH; - pi->height_mm = PYR_PANEL_HEIGHT; - - return 0; -} - -/* PYR DBI encoder helper funcs */ -static const struct drm_encoder_helper_funcs pyr_dsi_dbi_helper_funcs = { - .dpms = pyr_dsi_dbi_dpms, - .mode_fixup = pyr_dsi_dbi_mode_fixup, - .prepare = pyr_dsi_dbi_prepare, - .mode_set = pyr_dsi_dbi_mode_set, - .commit = pyr_dsi_dbi_commit, -}; - -/* PYR DBI encoder funcs */ -static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -void pyr_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs) -{ - p_funcs->encoder_funcs = &mdfld_dsi_dbi_encoder_funcs; - p_funcs->encoder_helper_funcs = &pyr_dsi_dbi_helper_funcs; - p_funcs->get_config_mode = &pyr_cmd_get_config_mode; - p_funcs->update_fb = pyr_dsi_dbi_update_fb; - p_funcs->get_panel_info = pyr_cmd_get_panel_info; -} diff --git a/drivers/staging/gma500/mdfld_tmd_vid.c b/drivers/staging/gma500/mdfld_tmd_vid.c deleted file mode 100644 index affdc09..0000000 --- a/drivers/staging/gma500/mdfld_tmd_vid.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Jim Liu - * Jackie Li - * Gideon Eaton - */ - -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" - -#include "mdfld_dsi_pkg_sender.h" - -#include "displays/tmd_vid.h" - -/* FIXME: static ? */ -struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev) -{ - struct drm_display_mode *mode; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; - bool use_gct = false; /*Disable GCT for now*/ - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) { - dev_err(dev->dev, "Out of memory\n"); - return NULL; - } - - if (use_gct) { - dev_dbg(dev->dev, "gct find MIPI panel.\n"); - - mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; - mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; - mode->hsync_start = mode->hdisplay + - ((ti->hsync_offset_hi << 8) | - ti->hsync_offset_lo); - mode->hsync_end = mode->hsync_start + - ((ti->hsync_pulse_width_hi << 8) | - ti->hsync_pulse_width_lo); - mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | - ti->hblank_lo); - mode->vsync_start = \ - mode->vdisplay + ((ti->vsync_offset_hi << 8) | - ti->vsync_offset_lo); - mode->vsync_end = \ - mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ - ti->vsync_pulse_width_lo); - mode->vtotal = mode->vdisplay + - ((ti->vblank_hi << 8) | ti->vblank_lo); - mode->clock = ti->pixel_clock * 10; - - dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); - dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); - dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); - dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); - dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); - dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); - dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); - dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); - dev_dbg(dev->dev, "clock is %d\n", mode->clock); - } else { - mode->hdisplay = 480; - mode->vdisplay = 854; - mode->hsync_start = 487; - mode->hsync_end = 490; - mode->htotal = 499; - mode->vsync_start = 861; - mode->vsync_end = 865; - mode->vtotal = 873; - mode->clock = 33264; - } - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - mode->type |= DRM_MODE_TYPE_PREFERRED; - - return mode; -} - -static int tmd_vid_get_panel_info(struct drm_device *dev, - int pipe, - struct panel_info *pi) -{ - if (!dev || !pi) - return -EINVAL; - - pi->width_mm = TMD_PANEL_WIDTH; - pi->height_mm = TMD_PANEL_HEIGHT; - - return 0; -} - -/* - * mdfld_init_TMD_MIPI - initialise a TMD interface - * @dsi_config: configuration - * @pipe: pipe to configure - * - * This function is called only by mrst_dsi_mode_set and - * restore_display_registers. since this function does not - * acquire the mutex, it is important that the calling function - * does! - */ - - -static void mdfld_dsi_tmd_drv_ic_init(struct mdfld_dsi_config *dsi_config, - int pipe) -{ - static u32 tmd_cmd_mcap_off[] = {0x000000b2}; - static u32 tmd_cmd_enable_lane_switch[] = {0x000101ef}; - static u32 tmd_cmd_set_lane_num[] = {0x006360ef}; - static u32 tmd_cmd_pushing_clock0[] = {0x00cc2fef}; - static u32 tmd_cmd_pushing_clock1[] = {0x00dd6eef}; - static u32 tmd_cmd_set_mode[] = {0x000000b3}; - static u32 tmd_cmd_set_sync_pulse_mode[] = {0x000961ef}; - static u32 tmd_cmd_set_column[] = {0x0100002a, 0x000000df}; - static u32 tmd_cmd_set_page[] = {0x0300002b, 0x00000055}; - static u32 tmd_cmd_set_video_mode[] = {0x00000153}; - /*no auto_bl,need add in furture*/ - static u32 tmd_cmd_enable_backlight[] = {0x00005ab4}; - static u32 tmd_cmd_set_backlight_dimming[] = {0x00000ebd}; - - struct mdfld_dsi_pkg_sender *sender - = mdfld_dsi_get_pkg_sender(dsi_config); - - DRM_INFO("Enter mdfld init TMD MIPI display.\n"); - - if (!sender) { - DRM_ERROR("Cannot get sender\n"); - return; - } - - if (dsi_config->dvr_ic_inited) - return; - - msleep(3); - - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_mcap_off, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_enable_lane_switch, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_lane_num, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_pushing_clock0, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_pushing_clock1, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_mode, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_sync_pulse_mode, 1, 0); - mdfld_dsi_send_mcs_long_lp(sender, tmd_cmd_set_column, 2, 0); - mdfld_dsi_send_mcs_long_lp(sender, tmd_cmd_set_page, 2, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_video_mode, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_enable_backlight, 1, 0); - mdfld_dsi_send_gen_long_lp(sender, tmd_cmd_set_backlight_dimming, 1, 0); - - dsi_config->dvr_ic_inited = 1; -} - -/* TMD DPI encoder helper funcs */ -static const struct drm_encoder_helper_funcs - mdfld_tpo_dpi_encoder_helper_funcs = { - .dpms = mdfld_dsi_dpi_dpms, - .mode_fixup = mdfld_dsi_dpi_mode_fixup, - .prepare = mdfld_dsi_dpi_prepare, - .mode_set = mdfld_dsi_dpi_mode_set, - .commit = mdfld_dsi_dpi_commit, -}; - -/* TMD DPI encoder funcs */ -static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -void tmd_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs) -{ - if (!dev || !p_funcs) { - dev_err(dev->dev, "Invalid parameters\n"); - return; - } - - p_funcs->encoder_funcs = &mdfld_tpo_dpi_encoder_funcs; - p_funcs->encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs; - p_funcs->get_config_mode = &tmd_vid_get_config_mode; - p_funcs->update_fb = NULL; - p_funcs->get_panel_info = tmd_vid_get_panel_info; - p_funcs->reset = mdfld_dsi_panel_reset; - p_funcs->drv_ic_init = mdfld_dsi_tmd_drv_ic_init; -} diff --git a/drivers/staging/gma500/mdfld_tpo_cmd.c b/drivers/staging/gma500/mdfld_tpo_cmd.c deleted file mode 100644 index c7f7c9c..0000000 --- a/drivers/staging/gma500/mdfld_tpo_cmd.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicensen - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Eaton - * Scott Rowe - */ - -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" -#include "mdfld_dsi_dbi_dpu.h" -#include "mdfld_dsi_pkg_sender.h" - -#include "displays/tpo_cmd.h" - -static struct drm_display_mode *tpo_cmd_get_config_mode(struct drm_device *dev) -{ - struct drm_display_mode *mode; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; - bool use_gct = false; - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) - return NULL; - - if (use_gct) { - dev_dbg(dev->dev, "gct find MIPI panel.\n"); - - mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; - mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; - mode->hsync_start = mode->hdisplay + \ - ((ti->hsync_offset_hi << 8) | \ - ti->hsync_offset_lo); - mode->hsync_end = mode->hsync_start + \ - ((ti->hsync_pulse_width_hi << 8) | \ - ti->hsync_pulse_width_lo); - mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ - ti->hblank_lo); - mode->vsync_start = \ - mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ - ti->vsync_offset_lo); - mode->vsync_end = \ - mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ - ti->vsync_pulse_width_lo); - mode->vtotal = mode->vdisplay + \ - ((ti->vblank_hi << 8) | ti->vblank_lo); - mode->clock = ti->pixel_clock * 10; - - dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); - dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); - dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); - dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); - dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); - dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); - dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); - dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); - dev_dbg(dev->dev, "clock is %d\n", mode->clock); - } else { - mode->hdisplay = 864; - mode->vdisplay = 480; - mode->hsync_start = 872; - mode->hsync_end = 876; - mode->htotal = 884; - mode->vsync_start = 482; - mode->vsync_end = 494; - mode->vtotal = 486; - mode->clock = 25777; - } - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - mode->type |= DRM_MODE_TYPE_PREFERRED; - - return mode; -} - -static bool mdfld_dsi_dbi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_display_mode *fixed_mode = tpo_cmd_get_config_mode(dev); - - if (fixed_mode) { - adjusted_mode->hdisplay = fixed_mode->hdisplay; - adjusted_mode->hsync_start = fixed_mode->hsync_start; - adjusted_mode->hsync_end = fixed_mode->hsync_end; - adjusted_mode->htotal = fixed_mode->htotal; - adjusted_mode->vdisplay = fixed_mode->vdisplay; - adjusted_mode->vsync_start = fixed_mode->vsync_start; - adjusted_mode->vsync_end = fixed_mode->vsync_end; - adjusted_mode->vtotal = fixed_mode->vtotal; - adjusted_mode->clock = fixed_mode->clock; - drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); - kfree(fixed_mode); - } - return true; -} - -static void mdfld_dsi_dbi_set_power(struct drm_encoder *encoder, bool on) -{ - int ret = 0; - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct mdfld_dsi_config *dsi_config = - mdfld_dsi_encoder_get_config(dsi_encoder); - struct mdfld_dsi_pkg_sender *sender = - mdfld_dsi_encoder_get_pkg_sender(dsi_encoder); - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 reg_offset = 0; - int pipe = (dbi_output->channel_num == 0) ? 0 : 2; - u32 data = 0; - - dev_dbg(dev->dev, "pipe %d : %s, panel on: %s\n", - pipe, on ? "On" : "Off", - dbi_output->dbi_panel_on ? "True" : "False"); - - if (pipe == 2) { - if (on) - dev_priv->dual_mipi = true; - else - dev_priv->dual_mipi = false; - reg_offset = MIPIC_REG_OFFSET; - } else { - if (!on) - dev_priv->dual_mipi = false; - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - if (on) { - if (dbi_output->dbi_panel_on) - goto out_err; - - ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_ON); - if (ret) { - dev_err(dev->dev, "power on error\n"); - goto out_err; - } - - dbi_output->dbi_panel_on = true; - - if (pipe == 2) - dev_priv->dbi_panel_on2 = true; - else - dev_priv->dbi_panel_on = true; - mdfld_enable_te(dev, pipe); - } else { - if (!dbi_output->dbi_panel_on && !dbi_output->first_boot) - goto out_err; - - dbi_output->dbi_panel_on = false; - dbi_output->first_boot = false; - - if (pipe == 2) - dev_priv->dbi_panel_on2 = false; - else - dev_priv->dbi_panel_on = false; - - mdfld_disable_te(dev, pipe); - - ret = mdfld_dsi_dbi_update_power(dbi_output, DRM_MODE_DPMS_OFF); - if (ret) { - dev_err(dev->dev, "power on error\n"); - goto out_err; - } - } - - /* - * FIXME: this is a WA for TPO panel crash on DPMS on & off around - * 83 times. the root cause of this issue is that Booster in - * drvIC crashed. Add this WA so that we can resume the driver IC - * once we found that booster has a fault - */ - mdfld_dsi_get_power_mode(dsi_config, - &data, - MDFLD_DSI_HS_TRANSMISSION); - - if (on && data && !(data & (1 << 7))) { - /* Soft reset */ - mdfld_dsi_send_dcs(sender, - DCS_SOFT_RESET, - NULL, - 0, - CMD_DATA_SRC_PIPE, - MDFLD_DSI_SEND_PACKAGE); - - /* Init drvIC */ - if (dbi_output->p_funcs->drv_ic_init) - dbi_output->p_funcs->drv_ic_init(dsi_config, - pipe); - } - -out_err: - gma_power_end(dev); - if (ret) - dev_err(dev->dev, "failed\n"); -} - - -static void mdfld_dsi_dbi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - int ret = 0; - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dsi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct mdfld_dsi_config *dsi_config = - mdfld_dsi_encoder_get_config(dsi_encoder); - struct mdfld_dsi_connector *dsi_connector = dsi_config->connector; - int pipe = dsi_connector->pipe; - u8 param = 0; - - /* Regs */ - u32 mipi_reg = MIPI; - u32 dspcntr_reg = DSPACNTR; - u32 pipeconf_reg = PIPEACONF; - u32 reg_offset = 0; - - /* Values */ - u32 dspcntr_val = dev_priv->dspcntr; - u32 pipeconf_val = dev_priv->pipeconf; - u32 h_active_area = mode->hdisplay; - u32 v_active_area = mode->vdisplay; - u32 mipi_val; - - mipi_val = (PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX | - TE_TRIGGER_GPIO_PIN); - - dev_dbg(dev->dev, "mipi_val =0x%x\n", mipi_val); - - dev_dbg(dev->dev, "type %s\n", (pipe == 2) ? "MIPI2" : "MIPI"); - dev_dbg(dev->dev, "h %d v %d\n", mode->hdisplay, mode->vdisplay); - - if (pipe == 2) { - mipi_reg = MIPI_C; - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - - reg_offset = MIPIC_REG_OFFSET; - - dspcntr_val = dev_priv->dspcntr2; - pipeconf_val = dev_priv->pipeconf2; - } else { - mipi_val |= 0x2; /*two lanes for port A and C respectively*/ - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - REG_WRITE(dspcntr_reg, dspcntr_val); - REG_READ(dspcntr_reg); - - /* 20ms delay before sending exit_sleep_mode */ - msleep(20); - - /* Send exit_sleep_mode DCS */ - ret = mdfld_dsi_dbi_send_dcs(dsi_output, DCS_EXIT_SLEEP_MODE, - NULL, 0, CMD_DATA_SRC_SYSTEM_MEM); - if (ret) { - dev_err(dev->dev, "sent exit_sleep_mode faild\n"); - goto out_err; - } - - /* Send set_tear_on DCS */ - ret = mdfld_dsi_dbi_send_dcs(dsi_output, DCS_SET_TEAR_ON, - ¶m, 1, CMD_DATA_SRC_SYSTEM_MEM); - if (ret) { - dev_err(dev->dev, "%s - sent set_tear_on faild\n", __func__); - goto out_err; - } - - /* Do some init stuff */ - REG_WRITE(pipeconf_reg, pipeconf_val | PIPEACONF_DSR); - REG_READ(pipeconf_reg); - - /* TODO: this looks ugly, try to move it to CRTC mode setting*/ - if (pipe == 2) - dev_priv->pipeconf2 |= PIPEACONF_DSR; - else - dev_priv->pipeconf |= PIPEACONF_DSR; - - dev_dbg(dev->dev, "pipeconf %x\n", REG_READ(pipeconf_reg)); - - ret = mdfld_dsi_dbi_update_area(dsi_output, 0, 0, - h_active_area - 1, v_active_area - 1); - if (ret) { - dev_err(dev->dev, "update area failed\n"); - goto out_err; - } - -out_err: - gma_power_end(dev); - - if (ret) - dev_err(dev->dev, "mode set failed\n"); -} - -static void mdfld_dsi_dbi_prepare(struct drm_encoder *encoder) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output - = MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - - dbi_output->mode_flags |= MODE_SETTING_IN_ENCODER; - dbi_output->mode_flags &= ~MODE_SETTING_ENCODER_DONE; - - mdfld_dsi_dbi_set_power(encoder, false); -} - -static void mdfld_dsi_dbi_commit(struct drm_encoder *encoder) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output = - MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct drm_device *dev = dbi_output->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_drm_dpu_rect rect; - - mdfld_dsi_dbi_set_power(encoder, true); - dbi_output->mode_flags &= ~MODE_SETTING_IN_ENCODER; - - rect.x = rect.y = 0; - rect.width = 864; - rect.height = 480; - - if (dbi_output->channel_num == 1) { - dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_2; - /*if dpu enabled report a fullscreen damage*/ - mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEC, &rect); - } else { - dev_priv->dsr_fb_update |= MDFLD_DSR_2D_3D_0; - mdfld_dbi_dpu_report_damage(dev, MDFLD_PLANEA, &rect); - } - dbi_output->mode_flags |= MODE_SETTING_ENCODER_DONE; -} - -static void mdfld_dsi_dbi_dpms(struct drm_encoder *encoder, int mode) -{ - struct mdfld_dsi_encoder *dsi_encoder = MDFLD_DSI_ENCODER(encoder); - struct mdfld_dsi_dbi_output *dbi_output - = MDFLD_DSI_DBI_OUTPUT(dsi_encoder); - struct drm_device *dev = dbi_output->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - static bool bdispoff; - - dev_dbg(dev->dev, "%s\n", (mode == DRM_MODE_DPMS_ON ? "on" : "off")); - - if (mode == DRM_MODE_DPMS_ON) { - /* - * FIXME: in case I am wrong! - * we don't need to exit dsr here to wake up plane/pipe/pll - * if everything goes right, hw_begin will resume them all - * during set_power. - */ - if (bdispoff /* FIXME && gbgfxsuspended */) { - mdfld_dsi_dbi_exit_dsr(dev, MDFLD_DSR_2D_3D); - bdispoff = false; - dev_priv->dispstatus = true; - } - - mdfld_dsi_dbi_set_power(encoder, true); - /* FIXME if (gbgfxsuspended) - gbgfxsuspended = false; */ - } else { - /* - * I am not sure whether this is the perfect place to - * turn rpm on since we still have a lot of CRTC turnning - * on work to do. - */ - bdispoff = true; - dev_priv->dispstatus = false; - mdfld_dsi_dbi_set_power(encoder, false); - } -} - - -/* - * Update the DBI MIPI Panel Frame Buffer. - */ -static void mdfld_dsi_dbi_update_fb(struct mdfld_dsi_dbi_output *dbi_output, - int pipe) -{ - struct mdfld_dsi_pkg_sender *sender = - mdfld_dsi_encoder_get_pkg_sender(&dbi_output->base); - struct drm_device *dev = dbi_output->dev; - struct drm_crtc *crtc = dbi_output->base.base.crtc; - struct psb_intel_crtc *psb_crtc = (crtc) ? - to_psb_intel_crtc(crtc) : NULL; - u32 dpll_reg = MRST_DPLL_A; - u32 dspcntr_reg = DSPACNTR; - u32 pipeconf_reg = PIPEACONF; - u32 dsplinoff_reg = DSPALINOFF; - u32 dspsurf_reg = DSPASURF; - u32 reg_offset = 0; - - /* If mode setting on-going, back off */ - if ((dbi_output->mode_flags & MODE_SETTING_ON_GOING) || - (psb_crtc && psb_crtc->mode_flags & MODE_SETTING_ON_GOING) || - !(dbi_output->mode_flags & MODE_SETTING_ENCODER_DONE)) - return; - - if (pipe == 2) { - dspcntr_reg = DSPCCNTR; - pipeconf_reg = PIPECCONF; - dsplinoff_reg = DSPCLINOFF; - dspsurf_reg = DSPCSURF; - reg_offset = MIPIC_REG_OFFSET; - } - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "hw begin failed\n"); - return; - } - - /* Check DBI FIFO status */ - if (!(REG_READ(dpll_reg) & DPLL_VCO_ENABLE) || - !(REG_READ(dspcntr_reg) & DISPLAY_PLANE_ENABLE) || - !(REG_READ(pipeconf_reg) & DISPLAY_PLANE_ENABLE)) - goto update_fb_out0; - - /* Refresh plane changes */ - REG_WRITE(dsplinoff_reg, REG_READ(dsplinoff_reg)); - REG_WRITE(dspsurf_reg, REG_READ(dspsurf_reg)); - REG_READ(dspsurf_reg); - - mdfld_dsi_send_dcs(sender, - DCS_WRITE_MEM_START, - NULL, - 0, - CMD_DATA_SRC_PIPE, - MDFLD_DSI_SEND_PACKAGE); - - dbi_output->dsr_fb_update_done = true; -update_fb_out0: - gma_power_end(dev); -} - -static int tpo_cmd_get_panel_info(struct drm_device *dev, - int pipe, - struct panel_info *pi) -{ - if (!dev || !pi) - return -EINVAL; - - pi->width_mm = TPO_PANEL_WIDTH; - pi->height_mm = TPO_PANEL_HEIGHT; - - return 0; -} - - -/* TPO DBI encoder helper funcs */ -static const struct drm_encoder_helper_funcs mdfld_dsi_dbi_helper_funcs = { - .dpms = mdfld_dsi_dbi_dpms, - .mode_fixup = mdfld_dsi_dbi_mode_fixup, - .prepare = mdfld_dsi_dbi_prepare, - .mode_set = mdfld_dsi_dbi_mode_set, - .commit = mdfld_dsi_dbi_commit, -}; - -/* TPO DBI encoder funcs */ -static const struct drm_encoder_funcs mdfld_dsi_dbi_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -void tpo_cmd_init(struct drm_device *dev, struct panel_funcs *p_funcs) -{ - p_funcs->encoder_funcs = &mdfld_dsi_dbi_encoder_funcs; - p_funcs->encoder_helper_funcs = &mdfld_dsi_dbi_helper_funcs; - p_funcs->get_config_mode = &tpo_cmd_get_config_mode; - p_funcs->update_fb = mdfld_dsi_dbi_update_fb; - p_funcs->get_panel_info = tpo_cmd_get_panel_info; - p_funcs->reset = mdfld_dsi_panel_reset; - p_funcs->drv_ic_init = mdfld_dsi_brightness_init; -} diff --git a/drivers/staging/gma500/mdfld_tpo_vid.c b/drivers/staging/gma500/mdfld_tpo_vid.c deleted file mode 100644 index 9549017..0000000 --- a/drivers/staging/gma500/mdfld_tpo_vid.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * jim liu - * Jackie Li - */ - -#include "mdfld_dsi_dbi.h" -#include "mdfld_dsi_dpi.h" -#include "mdfld_dsi_output.h" -#include "mdfld_output.h" - -#include "mdfld_dsi_pkg_sender.h" - -#include "displays/tpo_vid.h" - -static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev) -{ - struct drm_display_mode *mode; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; - bool use_gct = false; - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) { - dev_err(dev->dev, "out of memory\n"); - return NULL; - } - - if (use_gct) { - mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; - mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; - mode->hsync_start = mode->hdisplay + \ - ((ti->hsync_offset_hi << 8) | \ - ti->hsync_offset_lo); - mode->hsync_end = mode->hsync_start + \ - ((ti->hsync_pulse_width_hi << 8) | \ - ti->hsync_pulse_width_lo); - mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ - ti->hblank_lo); - mode->vsync_start = \ - mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ - ti->vsync_offset_lo); - mode->vsync_end = \ - mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ - ti->vsync_pulse_width_lo); - mode->vtotal = mode->vdisplay + \ - ((ti->vblank_hi << 8) | ti->vblank_lo); - mode->clock = ti->pixel_clock * 10; - - dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); - dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); - dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); - dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); - dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); - dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); - dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); - dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); - dev_dbg(dev->dev, "clock is %d\n", mode->clock); - } else { - mode->hdisplay = 864; - mode->vdisplay = 480; - mode->hsync_start = 873; - mode->hsync_end = 876; - mode->htotal = 887; - mode->vsync_start = 487; - mode->vsync_end = 490; - mode->vtotal = 499; - mode->clock = 33264; - } - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - mode->type |= DRM_MODE_TYPE_PREFERRED; - - return mode; -} - -static int tpo_vid_get_panel_info(struct drm_device *dev, - int pipe, - struct panel_info *pi) -{ - if (!dev || !pi) - return -EINVAL; - - pi->width_mm = TPO_PANEL_WIDTH; - pi->height_mm = TPO_PANEL_HEIGHT; - - return 0; -} - -/*TPO DPI encoder helper funcs*/ -static const struct drm_encoder_helper_funcs - mdfld_tpo_dpi_encoder_helper_funcs = { - .dpms = mdfld_dsi_dpi_dpms, - .mode_fixup = mdfld_dsi_dpi_mode_fixup, - .prepare = mdfld_dsi_dpi_prepare, - .mode_set = mdfld_dsi_dpi_mode_set, - .commit = mdfld_dsi_dpi_commit, -}; - -/*TPO DPI encoder funcs*/ -static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -void tpo_vid_init(struct drm_device *dev, struct panel_funcs *p_funcs) -{ - if (!dev || !p_funcs) { - dev_err(dev->dev, "tpo_vid_init: Invalid parameters\n"); - return; - } - - p_funcs->encoder_funcs = &mdfld_tpo_dpi_encoder_funcs; - p_funcs->encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs; - p_funcs->get_config_mode = &tpo_vid_get_config_mode; - p_funcs->update_fb = NULL; - p_funcs->get_panel_info = tpo_vid_get_panel_info; -} diff --git a/drivers/staging/gma500/medfield.h b/drivers/staging/gma500/medfield.h deleted file mode 100644 index 09e9687..0000000 --- a/drivers/staging/gma500/medfield.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/* Medfield DSI controller registers */ - -#define MIPIA_DEVICE_READY_REG 0xb000 -#define MIPIA_INTR_STAT_REG 0xb004 -#define MIPIA_INTR_EN_REG 0xb008 -#define MIPIA_DSI_FUNC_PRG_REG 0xb00c -#define MIPIA_HS_TX_TIMEOUT_REG 0xb010 -#define MIPIA_LP_RX_TIMEOUT_REG 0xb014 -#define MIPIA_TURN_AROUND_TIMEOUT_REG 0xb018 -#define MIPIA_DEVICE_RESET_TIMER_REG 0xb01c -#define MIPIA_DPI_RESOLUTION_REG 0xb020 -#define MIPIA_DBI_FIFO_THROTTLE_REG 0xb024 -#define MIPIA_HSYNC_COUNT_REG 0xb028 -#define MIPIA_HBP_COUNT_REG 0xb02c -#define MIPIA_HFP_COUNT_REG 0xb030 -#define MIPIA_HACTIVE_COUNT_REG 0xb034 -#define MIPIA_VSYNC_COUNT_REG 0xb038 -#define MIPIA_VBP_COUNT_REG 0xb03c -#define MIPIA_VFP_COUNT_REG 0xb040 -#define MIPIA_HIGH_LOW_SWITCH_COUNT_REG 0xb044 -#define MIPIA_DPI_CONTROL_REG 0xb048 -#define MIPIA_DPI_DATA_REG 0xb04c -#define MIPIA_INIT_COUNT_REG 0xb050 -#define MIPIA_MAX_RETURN_PACK_SIZE_REG 0xb054 -#define MIPIA_VIDEO_MODE_FORMAT_REG 0xb058 -#define MIPIA_EOT_DISABLE_REG 0xb05c -#define MIPIA_LP_BYTECLK_REG 0xb060 -#define MIPIA_LP_GEN_DATA_REG 0xb064 -#define MIPIA_HS_GEN_DATA_REG 0xb068 -#define MIPIA_LP_GEN_CTRL_REG 0xb06c -#define MIPIA_HS_GEN_CTRL_REG 0xb070 -#define MIPIA_GEN_FIFO_STAT_REG 0xb074 -#define MIPIA_HS_LS_DBI_ENABLE_REG 0xb078 -#define MIPIA_DPHY_PARAM_REG 0xb080 -#define MIPIA_DBI_BW_CTRL_REG 0xb084 -#define MIPIA_CLK_LANE_SWITCH_TIME_CNT_REG 0xb088 - -#define DSI_DEVICE_READY (0x1) -#define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1) -#define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1) -#define DSI_POWER_STATE_ULPS_OFFSET (0x1) - - -#define DSI_ONE_DATA_LANE (0x1) -#define DSI_TWO_DATA_LANE (0x2) -#define DSI_THREE_DATA_LANE (0X3) -#define DSI_FOUR_DATA_LANE (0x4) -#define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3) -#define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5) -#define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7) -#define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7) -#define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7) -#define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7) -#define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13) - -#define DSI_INTR_STATE_RXSOTERROR 1 - -#define DSI_INTR_STATE_SPL_PKG_SENT (1 << 30) -#define DSI_INTR_STATE_TE (1 << 31) - -#define DSI_HS_TX_TIMEOUT_MASK (0xffffff) - -#define DSI_LP_RX_TIMEOUT_MASK (0xffffff) - -#define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f) - -#define DSI_RESET_TIMER_MASK (0xffff) - -#define DSI_DBI_FIFO_WM_HALF (0x0) -#define DSI_DBI_FIFO_WM_QUARTER (0x1) -#define DSI_DBI_FIFO_WM_LOW (0x2) - -#define DSI_DPI_TIMING_MASK (0xffff) - -#define DSI_INIT_TIMER_MASK (0xffff) - -#define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff) - -#define DSI_LP_BYTECLK_MASK (0x0ffff) - -#define DSI_HS_CTRL_GEN_SHORT_W0 (0x03) -#define DSI_HS_CTRL_GEN_SHORT_W1 (0x13) -#define DSI_HS_CTRL_GEN_SHORT_W2 (0x23) -#define DSI_HS_CTRL_GEN_R0 (0x04) -#define DSI_HS_CTRL_GEN_R1 (0x14) -#define DSI_HS_CTRL_GEN_R2 (0x24) -#define DSI_HS_CTRL_GEN_LONG_W (0x29) -#define DSI_HS_CTRL_MCS_SHORT_W0 (0x05) -#define DSI_HS_CTRL_MCS_SHORT_W1 (0x15) -#define DSI_HS_CTRL_MCS_R0 (0x06) -#define DSI_HS_CTRL_MCS_LONG_W (0x39) -#define DSI_HS_CTRL_VC_OFFSET (0x06) -#define DSI_HS_CTRL_WC_OFFSET (0x08) - -#define DSI_FIFO_GEN_HS_DATA_FULL (1 << 0) -#define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY (1 << 1) -#define DSI_FIFO_GEN_HS_DATA_EMPTY (1 << 2) -#define DSI_FIFO_GEN_LP_DATA_FULL (1 << 8) -#define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY (1 << 9) -#define DSI_FIFO_GEN_LP_DATA_EMPTY (1 << 10) -#define DSI_FIFO_GEN_HS_CTRL_FULL (1 << 16) -#define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY (1 << 17) -#define DSI_FIFO_GEN_HS_CTRL_EMPTY (1 << 18) -#define DSI_FIFO_GEN_LP_CTRL_FULL (1 << 24) -#define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY (1 << 25) -#define DSI_FIFO_GEN_LP_CTRL_EMPTY (1 << 26) -#define DSI_FIFO_DBI_EMPTY (1 << 27) -#define DSI_FIFO_DPI_EMPTY (1 << 28) - -#define DSI_DBI_HS_LP_SWITCH_MASK (0x1) - -#define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0) -#define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16) - -#define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001) -#define DSI_DPI_CTRL_HS_TURN_ON (0x00000002) - -/* Medfield DSI adapter registers */ -#define MIPIA_CONTROL_REG 0xb104 -#define MIPIA_DATA_ADD_REG 0xb108 -#define MIPIA_DATA_LEN_REG 0xb10c -#define MIPIA_CMD_ADD_REG 0xb110 -#define MIPIA_CMD_LEN_REG 0xb114 - -/*dsi power modes*/ -#define DSI_POWER_MODE_DISPLAY_ON (1 << 2) -#define DSI_POWER_MODE_NORMAL_ON (1 << 3) -#define DSI_POWER_MODE_SLEEP_OUT (1 << 4) -#define DSI_POWER_MODE_PARTIAL_ON (1 << 5) -#define DSI_POWER_MODE_IDLE_ON (1 << 6) - -enum { - MDFLD_DSI_ENCODER_DBI = 0, - MDFLD_DSI_ENCODER_DPI, -}; - -enum { - MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1, - MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2, - MDFLD_DSI_VIDEO_BURST_MODE = 3, -}; - -#define DSI_DPI_COMPLETE_LAST_LINE (1 << 2) -#define DSI_DPI_DISABLE_BTA (1 << 3) -/* Panel types */ -enum { - TPO_CMD, - TPO_VID, - TMD_CMD, - TMD_VID, - PYR_CMD, - PYR_VID, - TPO, - TMD, - PYR, - HDMI, - GCT_DETECT -}; - -/* Junk that belongs elsewhere */ -#define TPO_PANEL_WIDTH 84 -#define TPO_PANEL_HEIGHT 46 -#define TMD_PANEL_WIDTH 39 -#define TMD_PANEL_HEIGHT 71 -#define PYR_PANEL_WIDTH 53 -#define PYR_PANEL_HEIGHT 95 - -/* Panel interface */ -struct panel_info { - u32 width_mm; - u32 height_mm; -}; - -struct mdfld_dsi_dbi_output; - -struct mdfld_dsi_connector_state { - u32 mipi_ctrl_reg; -}; - -struct mdfld_dsi_encoder_state { - -}; - -struct mdfld_dsi_connector { - /* - * This is ugly, but I have to use connector in it! :-( - * FIXME: use drm_connector instead. - */ - struct psb_intel_output base; - - int pipe; - void *private; - void *pkg_sender; - - /* Connection status */ - enum drm_connector_status status; -}; - -struct mdfld_dsi_encoder { - struct drm_encoder base; - void *private; -}; - -/* - * DSI config, consists of one DSI connector, two DSI encoders. - * DRM will pick up on DSI encoder basing on differents configs. - */ -struct mdfld_dsi_config { - struct drm_device *dev; - struct drm_display_mode *fixed_mode; - struct drm_display_mode *mode; - - struct mdfld_dsi_connector *connector; - struct mdfld_dsi_encoder *encoders[DRM_CONNECTOR_MAX_ENCODER]; - struct mdfld_dsi_encoder *encoder; - - int changed; - - int bpp; - int type; - int lane_count; - /*Virtual channel number for this encoder*/ - int channel_num; - /*video mode configure*/ - int video_mode; - - int dvr_ic_inited; -}; - -#define MDFLD_DSI_CONNECTOR(psb_output) \ - (container_of(psb_output, struct mdfld_dsi_connector, base)) - -#define MDFLD_DSI_ENCODER(encoder) \ - (container_of(encoder, struct mdfld_dsi_encoder, base)) - -struct panel_funcs { - const struct drm_encoder_funcs *encoder_funcs; - const struct drm_encoder_helper_funcs *encoder_helper_funcs; - struct drm_display_mode *(*get_config_mode) (struct drm_device *); - void (*update_fb) (struct mdfld_dsi_dbi_output *, int); - int (*get_panel_info) (struct drm_device *, int, struct panel_info *); - int (*reset)(int pipe); - void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe); -}; - diff --git a/drivers/staging/gma500/mid_bios.c b/drivers/staging/gma500/mid_bios.c deleted file mode 100644 index ee3c036..0000000 --- a/drivers/staging/gma500/mid_bios.c +++ /dev/null @@ -1,270 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -/* TODO - * - Split functions by vbt type - * - Make them all take drm_device - * - Check ioremap failures - */ - -#include -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" -#include "mid_bios.h" -#include "mdfld_output.h" - -static int panel_id = GCT_DETECT; -module_param_named(panel_id, panel_id, int, 0600); -MODULE_PARM_DESC(panel_id, "Panel Identifier"); - - -static void mid_get_fuse_settings(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - uint32_t fuse_value = 0; - uint32_t fuse_value_tmp = 0; - -#define FB_REG06 0xD0810600 -#define FB_MIPI_DISABLE (1 << 11) -#define FB_REG09 0xD0810900 -#define FB_REG09 0xD0810900 -#define FB_SKU_MASK 0x7000 -#define FB_SKU_SHIFT 12 -#define FB_SKU_100 0 -#define FB_SKU_100L 1 -#define FB_SKU_83 2 - pci_write_config_dword(pci_root, 0xD0, FB_REG06); - pci_read_config_dword(pci_root, 0xD4, &fuse_value); - - /* FB_MIPI_DISABLE doesn't mean LVDS on with Medfield */ - if (IS_MRST(dev)) - dev_priv->iLVDS_enable = fuse_value & FB_MIPI_DISABLE; - - DRM_INFO("internal display is %s\n", - dev_priv->iLVDS_enable ? "LVDS display" : "MIPI display"); - - /* Prevent runtime suspend at start*/ - if (dev_priv->iLVDS_enable) { - dev_priv->is_lvds_on = true; - dev_priv->is_mipi_on = false; - } else { - dev_priv->is_mipi_on = true; - dev_priv->is_lvds_on = false; - } - - dev_priv->video_device_fuse = fuse_value; - - pci_write_config_dword(pci_root, 0xD0, FB_REG09); - pci_read_config_dword(pci_root, 0xD4, &fuse_value); - - dev_dbg(dev->dev, "SKU values is 0x%x.\n", fuse_value); - fuse_value_tmp = (fuse_value & FB_SKU_MASK) >> FB_SKU_SHIFT; - - dev_priv->fuse_reg_value = fuse_value; - - switch (fuse_value_tmp) { - case FB_SKU_100: - dev_priv->core_freq = 200; - break; - case FB_SKU_100L: - dev_priv->core_freq = 100; - break; - case FB_SKU_83: - dev_priv->core_freq = 166; - break; - default: - dev_warn(dev->dev, "Invalid SKU values, SKU value = 0x%08x\n", - fuse_value_tmp); - dev_priv->core_freq = 0; - } - dev_dbg(dev->dev, "LNC core clk is %dMHz.\n", dev_priv->core_freq); - pci_dev_put(pci_root); -} - -/* - * Get the revison ID, B0:D2:F0;0x08 - */ -static void mid_get_pci_revID(struct drm_psb_private *dev_priv) -{ - uint32_t platform_rev_id = 0; - struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); - - pci_read_config_dword(pci_gfx_root, 0x08, &platform_rev_id); - dev_priv->platform_rev_id = (uint8_t) platform_rev_id; - pci_dev_put(pci_gfx_root); - dev_dbg(dev_priv->dev->dev, "platform_rev_id is %x\n", - dev_priv->platform_rev_id); -} - -static void mid_get_vbt_data(struct drm_psb_private *dev_priv) -{ - struct drm_device *dev = dev_priv->dev; - struct mrst_vbt *vbt = &dev_priv->vbt_data; - u32 addr; - u16 new_size; - u8 *vbt_virtual; - u8 bpi; - u8 number_desc = 0; - struct mrst_timing_info *dp_ti = &dev_priv->gct_data.DTD; - struct gct_r10_timing_info ti; - void *pGCT; - struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); - - /* Get the address of the platform config vbt, B0:D2:F0;0xFC */ - pci_read_config_dword(pci_gfx_root, 0xFC, &addr); - pci_dev_put(pci_gfx_root); - - dev_dbg(dev->dev, "drm platform config address is %x\n", addr); - - /* check for platform config address == 0. */ - /* this means fw doesn't support vbt */ - - if (addr == 0) { - vbt->size = 0; - return; - } - - /* get the virtual address of the vbt */ - vbt_virtual = ioremap(addr, sizeof(*vbt)); - - memcpy(vbt, vbt_virtual, sizeof(*vbt)); - iounmap(vbt_virtual); /* Free virtual address space */ - - dev_dbg(dev->dev, "GCT revision is %x\n", vbt->revision); - - switch (vbt->revision) { - case 0: - vbt->mrst_gct = ioremap(addr + sizeof(*vbt) - 4, - vbt->size - sizeof(*vbt) + 4); - pGCT = vbt->mrst_gct; - bpi = ((struct mrst_gct_v1 *)pGCT)->PD.BootPanelIndex; - dev_priv->gct_data.bpi = bpi; - dev_priv->gct_data.pt = - ((struct mrst_gct_v1 *)pGCT)->PD.PanelType; - memcpy(&dev_priv->gct_data.DTD, - &((struct mrst_gct_v1 *)pGCT)->panel[bpi].DTD, - sizeof(struct mrst_timing_info)); - dev_priv->gct_data.Panel_Port_Control = - ((struct mrst_gct_v1 *)pGCT)->panel[bpi].Panel_Port_Control; - dev_priv->gct_data.Panel_MIPI_Display_Descriptor = - ((struct mrst_gct_v1 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor; - break; - case 1: - vbt->mrst_gct = ioremap(addr + sizeof(*vbt) - 4, - vbt->size - sizeof(*vbt) + 4); - pGCT = vbt->mrst_gct; - bpi = ((struct mrst_gct_v2 *)pGCT)->PD.BootPanelIndex; - dev_priv->gct_data.bpi = bpi; - dev_priv->gct_data.pt = - ((struct mrst_gct_v2 *)pGCT)->PD.PanelType; - memcpy(&dev_priv->gct_data.DTD, - &((struct mrst_gct_v2 *)pGCT)->panel[bpi].DTD, - sizeof(struct mrst_timing_info)); - dev_priv->gct_data.Panel_Port_Control = - ((struct mrst_gct_v2 *)pGCT)->panel[bpi].Panel_Port_Control; - dev_priv->gct_data.Panel_MIPI_Display_Descriptor = - ((struct mrst_gct_v2 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor; - break; - case 0x10: - /*header definition changed from rev 01 (v2) to rev 10h. */ - /*so, some values have changed location*/ - new_size = vbt->checksum; /*checksum contains lo size byte*/ - /*LSB of mrst_gct contains hi size byte*/ - new_size |= ((0xff & (unsigned int)vbt->mrst_gct)) << 8; - - vbt->checksum = vbt->size; /*size contains the checksum*/ - if (new_size > 0xff) - vbt->size = 0xff; /*restrict size to 255*/ - else - vbt->size = new_size; - - /* number of descriptors defined in the GCT */ - number_desc = ((0xff00 & (unsigned int)vbt->mrst_gct)) >> 8; - bpi = ((0xff0000 & (unsigned int)vbt->mrst_gct)) >> 16; - vbt->mrst_gct = ioremap(addr + GCT_R10_HEADER_SIZE, - GCT_R10_DISPLAY_DESC_SIZE * number_desc); - pGCT = vbt->mrst_gct; - pGCT = (u8 *)pGCT + (bpi*GCT_R10_DISPLAY_DESC_SIZE); - dev_priv->gct_data.bpi = bpi; /*save boot panel id*/ - - /*copy the GCT display timings into a temp structure*/ - memcpy(&ti, pGCT, sizeof(struct gct_r10_timing_info)); - - /*now copy the temp struct into the dev_priv->gct_data*/ - dp_ti->pixel_clock = ti.pixel_clock; - dp_ti->hactive_hi = ti.hactive_hi; - dp_ti->hactive_lo = ti.hactive_lo; - dp_ti->hblank_hi = ti.hblank_hi; - dp_ti->hblank_lo = ti.hblank_lo; - dp_ti->hsync_offset_hi = ti.hsync_offset_hi; - dp_ti->hsync_offset_lo = ti.hsync_offset_lo; - dp_ti->hsync_pulse_width_hi = ti.hsync_pulse_width_hi; - dp_ti->hsync_pulse_width_lo = ti.hsync_pulse_width_lo; - dp_ti->vactive_hi = ti.vactive_hi; - dp_ti->vactive_lo = ti.vactive_lo; - dp_ti->vblank_hi = ti.vblank_hi; - dp_ti->vblank_lo = ti.vblank_lo; - dp_ti->vsync_offset_hi = ti.vsync_offset_hi; - dp_ti->vsync_offset_lo = ti.vsync_offset_lo; - dp_ti->vsync_pulse_width_hi = ti.vsync_pulse_width_hi; - dp_ti->vsync_pulse_width_lo = ti.vsync_pulse_width_lo; - - /* Move the MIPI_Display_Descriptor data from GCT to dev priv */ - dev_priv->gct_data.Panel_MIPI_Display_Descriptor = - *((u8 *)pGCT + 0x0d); - dev_priv->gct_data.Panel_MIPI_Display_Descriptor |= - (*((u8 *)pGCT + 0x0e)) << 8; - break; - default: - dev_err(dev->dev, "Unknown revision of GCT!\n"); - vbt->size = 0; - } - if (IS_MFLD(dev_priv->dev)) { - if (panel_id == GCT_DETECT) { - if (dev_priv->gct_data.bpi == 2) { - dev_info(dev->dev, "[GFX] PYR Panel Detected\n"); - dev_priv->panel_id = PYR_CMD; - panel_id = PYR_CMD; - } else if (dev_priv->gct_data.bpi == 0) { - dev_info(dev->dev, "[GFX] TMD Panel Detected.\n"); - dev_priv->panel_id = TMD_VID; - panel_id = TMD_VID; - } else { - dev_info(dev->dev, "[GFX] Default Panel (TPO)\n"); - dev_priv->panel_id = TPO_CMD; - panel_id = TPO_CMD; - } - } else { - dev_info(dev->dev, "[GFX] Panel Parameter Passed in through cmd line\n"); - dev_priv->panel_id = panel_id; - } - } -} - -int mid_chip_setup(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - mid_get_fuse_settings(dev); - mid_get_vbt_data(dev_priv); - mid_get_pci_revID(dev_priv); - return 0; -} diff --git a/drivers/staging/gma500/mid_bios.h b/drivers/staging/gma500/mid_bios.h deleted file mode 100644 index 00e7d56..0000000 --- a/drivers/staging/gma500/mid_bios.h +++ /dev/null @@ -1,21 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -extern int mid_chip_setup(struct drm_device *dev); - diff --git a/drivers/staging/gma500/mmu.c b/drivers/staging/gma500/mmu.c deleted file mode 100644 index c904d73..0000000 --- a/drivers/staging/gma500/mmu.c +++ /dev/null @@ -1,858 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ -#include -#include "psb_drv.h" -#include "psb_reg.h" - -/* - * Code for the SGX MMU: - */ - -/* - * clflush on one processor only: - * clflush should apparently flush the cache line on all processors in an - * SMP system. - */ - -/* - * kmap atomic: - * The usage of the slots must be completely encapsulated within a spinlock, and - * no other functions that may be using the locks for other purposed may be - * called from within the locked region. - * Since the slots are per processor, this will guarantee that we are the only - * user. - */ - -/* - * TODO: Inserting ptes from an interrupt handler: - * This may be desirable for some SGX functionality where the GPU can fault in - * needed pages. For that, we need to make an atomic insert_pages function, that - * may fail. - * If it fails, the caller need to insert the page using a workqueue function, - * but on average it should be fast. - */ - -struct psb_mmu_driver { - /* protects driver- and pd structures. Always take in read mode - * before taking the page table spinlock. - */ - struct rw_semaphore sem; - - /* protects page tables, directory tables and pt tables. - * and pt structures. - */ - spinlock_t lock; - - atomic_t needs_tlbflush; - - uint8_t __iomem *register_map; - struct psb_mmu_pd *default_pd; - /*uint32_t bif_ctrl;*/ - int has_clflush; - int clflush_add; - unsigned long clflush_mask; - - struct drm_psb_private *dev_priv; -}; - -struct psb_mmu_pd; - -struct psb_mmu_pt { - struct psb_mmu_pd *pd; - uint32_t index; - uint32_t count; - struct page *p; - uint32_t *v; -}; - -struct psb_mmu_pd { - struct psb_mmu_driver *driver; - int hw_context; - struct psb_mmu_pt **tables; - struct page *p; - struct page *dummy_pt; - struct page *dummy_page; - uint32_t pd_mask; - uint32_t invalid_pde; - uint32_t invalid_pte; -}; - -static inline uint32_t psb_mmu_pt_index(uint32_t offset) -{ - return (offset >> PSB_PTE_SHIFT) & 0x3FF; -} - -static inline uint32_t psb_mmu_pd_index(uint32_t offset) -{ - return offset >> PSB_PDE_SHIFT; -} - -static inline void psb_clflush(void *addr) -{ - __asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory"); -} - -static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, - void *addr) -{ - if (!driver->has_clflush) - return; - - mb(); - psb_clflush(addr); - mb(); -} - -static void psb_page_clflush(struct psb_mmu_driver *driver, struct page* page) -{ - uint32_t clflush_add = driver->clflush_add >> PAGE_SHIFT; - uint32_t clflush_count = PAGE_SIZE / clflush_add; - int i; - uint8_t *clf; - - clf = kmap_atomic(page, KM_USER0); - mb(); - for (i = 0; i < clflush_count; ++i) { - psb_clflush(clf); - clf += clflush_add; - } - mb(); - kunmap_atomic(clf, KM_USER0); -} - -static void psb_pages_clflush(struct psb_mmu_driver *driver, - struct page *page[], unsigned long num_pages) -{ - int i; - - if (!driver->has_clflush) - return ; - - for (i = 0; i < num_pages; i++) - psb_page_clflush(driver, *page++); -} - -static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, - int force) -{ - atomic_set(&driver->needs_tlbflush, 0); -} - -static void psb_mmu_flush_pd(struct psb_mmu_driver *driver, int force) -{ - down_write(&driver->sem); - psb_mmu_flush_pd_locked(driver, force); - up_write(&driver->sem); -} - -void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot) -{ - if (rc_prot) - down_write(&driver->sem); - if (rc_prot) - up_write(&driver->sem); -} - -void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context) -{ - /*ttm_tt_cache_flush(&pd->p, 1);*/ - psb_pages_clflush(pd->driver, &pd->p, 1); - down_write(&pd->driver->sem); - wmb(); - psb_mmu_flush_pd_locked(pd->driver, 1); - pd->hw_context = hw_context; - up_write(&pd->driver->sem); - -} - -static inline unsigned long psb_pd_addr_end(unsigned long addr, - unsigned long end) -{ - - addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK; - return (addr < end) ? addr : end; -} - -static inline uint32_t psb_mmu_mask_pte(uint32_t pfn, int type) -{ - uint32_t mask = PSB_PTE_VALID; - - if (type & PSB_MMU_CACHED_MEMORY) - mask |= PSB_PTE_CACHED; - if (type & PSB_MMU_RO_MEMORY) - mask |= PSB_PTE_RO; - if (type & PSB_MMU_WO_MEMORY) - mask |= PSB_PTE_WO; - - return (pfn << PAGE_SHIFT) | mask; -} - -struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, - int trap_pagefaults, int invalid_type) -{ - struct psb_mmu_pd *pd = kmalloc(sizeof(*pd), GFP_KERNEL); - uint32_t *v; - int i; - - if (!pd) - return NULL; - - pd->p = alloc_page(GFP_DMA32); - if (!pd->p) - goto out_err1; - pd->dummy_pt = alloc_page(GFP_DMA32); - if (!pd->dummy_pt) - goto out_err2; - pd->dummy_page = alloc_page(GFP_DMA32); - if (!pd->dummy_page) - goto out_err3; - - if (!trap_pagefaults) { - pd->invalid_pde = - psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt), - invalid_type); - pd->invalid_pte = - psb_mmu_mask_pte(page_to_pfn(pd->dummy_page), - invalid_type); - } else { - pd->invalid_pde = 0; - pd->invalid_pte = 0; - } - - v = kmap(pd->dummy_pt); - for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) - v[i] = pd->invalid_pte; - - kunmap(pd->dummy_pt); - - v = kmap(pd->p); - for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) - v[i] = pd->invalid_pde; - - kunmap(pd->p); - - clear_page(kmap(pd->dummy_page)); - kunmap(pd->dummy_page); - - pd->tables = vmalloc_user(sizeof(struct psb_mmu_pt *) * 1024); - if (!pd->tables) - goto out_err4; - - pd->hw_context = -1; - pd->pd_mask = PSB_PTE_VALID; - pd->driver = driver; - - return pd; - -out_err4: - __free_page(pd->dummy_page); -out_err3: - __free_page(pd->dummy_pt); -out_err2: - __free_page(pd->p); -out_err1: - kfree(pd); - return NULL; -} - -void psb_mmu_free_pt(struct psb_mmu_pt *pt) -{ - __free_page(pt->p); - kfree(pt); -} - -void psb_mmu_free_pagedir(struct psb_mmu_pd *pd) -{ - struct psb_mmu_driver *driver = pd->driver; - struct psb_mmu_pt *pt; - int i; - - down_write(&driver->sem); - if (pd->hw_context != -1) - psb_mmu_flush_pd_locked(driver, 1); - - /* Should take the spinlock here, but we don't need to do that - since we have the semaphore in write mode. */ - - for (i = 0; i < 1024; ++i) { - pt = pd->tables[i]; - if (pt) - psb_mmu_free_pt(pt); - } - - vfree(pd->tables); - __free_page(pd->dummy_page); - __free_page(pd->dummy_pt); - __free_page(pd->p); - kfree(pd); - up_write(&driver->sem); -} - -static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd) -{ - struct psb_mmu_pt *pt = kmalloc(sizeof(*pt), GFP_KERNEL); - void *v; - uint32_t clflush_add = pd->driver->clflush_add >> PAGE_SHIFT; - uint32_t clflush_count = PAGE_SIZE / clflush_add; - spinlock_t *lock = &pd->driver->lock; - uint8_t *clf; - uint32_t *ptes; - int i; - - if (!pt) - return NULL; - - pt->p = alloc_page(GFP_DMA32); - if (!pt->p) { - kfree(pt); - return NULL; - } - - spin_lock(lock); - - v = kmap_atomic(pt->p, KM_USER0); - clf = (uint8_t *) v; - ptes = (uint32_t *) v; - for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) - *ptes++ = pd->invalid_pte; - - - if (pd->driver->has_clflush && pd->hw_context != -1) { - mb(); - for (i = 0; i < clflush_count; ++i) { - psb_clflush(clf); - clf += clflush_add; - } - mb(); - } - - kunmap_atomic(v, KM_USER0); - spin_unlock(lock); - - pt->count = 0; - pt->pd = pd; - pt->index = 0; - - return pt; -} - -struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd, - unsigned long addr) -{ - uint32_t index = psb_mmu_pd_index(addr); - struct psb_mmu_pt *pt; - uint32_t *v; - spinlock_t *lock = &pd->driver->lock; - - spin_lock(lock); - pt = pd->tables[index]; - while (!pt) { - spin_unlock(lock); - pt = psb_mmu_alloc_pt(pd); - if (!pt) - return NULL; - spin_lock(lock); - - if (pd->tables[index]) { - spin_unlock(lock); - psb_mmu_free_pt(pt); - spin_lock(lock); - pt = pd->tables[index]; - continue; - } - - v = kmap_atomic(pd->p, KM_USER0); - pd->tables[index] = pt; - v[index] = (page_to_pfn(pt->p) << 12) | pd->pd_mask; - pt->index = index; - kunmap_atomic((void *) v, KM_USER0); - - if (pd->hw_context != -1) { - psb_mmu_clflush(pd->driver, (void *) &v[index]); - atomic_set(&pd->driver->needs_tlbflush, 1); - } - } - pt->v = kmap_atomic(pt->p, KM_USER0); - return pt; -} - -static struct psb_mmu_pt *psb_mmu_pt_map_lock(struct psb_mmu_pd *pd, - unsigned long addr) -{ - uint32_t index = psb_mmu_pd_index(addr); - struct psb_mmu_pt *pt; - spinlock_t *lock = &pd->driver->lock; - - spin_lock(lock); - pt = pd->tables[index]; - if (!pt) { - spin_unlock(lock); - return NULL; - } - pt->v = kmap_atomic(pt->p, KM_USER0); - return pt; -} - -static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt) -{ - struct psb_mmu_pd *pd = pt->pd; - uint32_t *v; - - kunmap_atomic(pt->v, KM_USER0); - if (pt->count == 0) { - v = kmap_atomic(pd->p, KM_USER0); - v[pt->index] = pd->invalid_pde; - pd->tables[pt->index] = NULL; - - if (pd->hw_context != -1) { - psb_mmu_clflush(pd->driver, - (void *) &v[pt->index]); - atomic_set(&pd->driver->needs_tlbflush, 1); - } - kunmap_atomic(pt->v, KM_USER0); - spin_unlock(&pd->driver->lock); - psb_mmu_free_pt(pt); - return; - } - spin_unlock(&pd->driver->lock); -} - -static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt, - unsigned long addr, uint32_t pte) -{ - pt->v[psb_mmu_pt_index(addr)] = pte; -} - -static inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt, - unsigned long addr) -{ - pt->v[psb_mmu_pt_index(addr)] = pt->pd->invalid_pte; -} - - -void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, - uint32_t mmu_offset, uint32_t gtt_start, - uint32_t gtt_pages) -{ - uint32_t *v; - uint32_t start = psb_mmu_pd_index(mmu_offset); - struct psb_mmu_driver *driver = pd->driver; - int num_pages = gtt_pages; - - down_read(&driver->sem); - spin_lock(&driver->lock); - - v = kmap_atomic(pd->p, KM_USER0); - v += start; - - while (gtt_pages--) { - *v++ = gtt_start | pd->pd_mask; - gtt_start += PAGE_SIZE; - } - - /*ttm_tt_cache_flush(&pd->p, num_pages);*/ - psb_pages_clflush(pd->driver, &pd->p, num_pages); - kunmap_atomic(v, KM_USER0); - spin_unlock(&driver->lock); - - if (pd->hw_context != -1) - atomic_set(&pd->driver->needs_tlbflush, 1); - - up_read(&pd->driver->sem); - psb_mmu_flush_pd(pd->driver, 0); -} - -struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver) -{ - struct psb_mmu_pd *pd; - - /* down_read(&driver->sem); */ - pd = driver->default_pd; - /* up_read(&driver->sem); */ - - return pd; -} - -/* Returns the physical address of the PD shared by sgx/msvdx */ -uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver) -{ - struct psb_mmu_pd *pd; - - pd = psb_mmu_get_default_pd(driver); - return page_to_pfn(pd->p) << PAGE_SHIFT; -} - -void psb_mmu_driver_takedown(struct psb_mmu_driver *driver) -{ - psb_mmu_free_pagedir(driver->default_pd); - kfree(driver); -} - -struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, - int trap_pagefaults, - int invalid_type, - struct drm_psb_private *dev_priv) -{ - struct psb_mmu_driver *driver; - - driver = kmalloc(sizeof(*driver), GFP_KERNEL); - - if (!driver) - return NULL; - driver->dev_priv = dev_priv; - - driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults, - invalid_type); - if (!driver->default_pd) - goto out_err1; - - spin_lock_init(&driver->lock); - init_rwsem(&driver->sem); - down_write(&driver->sem); - driver->register_map = registers; - atomic_set(&driver->needs_tlbflush, 1); - - driver->has_clflush = 0; - - if (boot_cpu_has(X86_FEATURE_CLFLSH)) { - uint32_t tfms, misc, cap0, cap4, clflush_size; - - /* - * clflush size is determined at kernel setup for x86_64 - * but not for i386. We have to do it here. - */ - - cpuid(0x00000001, &tfms, &misc, &cap0, &cap4); - clflush_size = ((misc >> 8) & 0xff) * 8; - driver->has_clflush = 1; - driver->clflush_add = - PAGE_SIZE * clflush_size / sizeof(uint32_t); - driver->clflush_mask = driver->clflush_add - 1; - driver->clflush_mask = ~driver->clflush_mask; - } - - up_write(&driver->sem); - return driver; - -out_err1: - kfree(driver); - return NULL; -} - -static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride) -{ - struct psb_mmu_pt *pt; - uint32_t rows = 1; - uint32_t i; - unsigned long addr; - unsigned long end; - unsigned long next; - unsigned long add; - unsigned long row_add; - unsigned long clflush_add = pd->driver->clflush_add; - unsigned long clflush_mask = pd->driver->clflush_mask; - - if (!pd->driver->has_clflush) { - /*ttm_tt_cache_flush(&pd->p, num_pages);*/ - psb_pages_clflush(pd->driver, &pd->p, num_pages); - return; - } - - if (hw_tile_stride) - rows = num_pages / desired_tile_stride; - else - desired_tile_stride = num_pages; - - add = desired_tile_stride << PAGE_SHIFT; - row_add = hw_tile_stride << PAGE_SHIFT; - mb(); - for (i = 0; i < rows; ++i) { - - addr = address; - end = addr + add; - - do { - next = psb_pd_addr_end(addr, end); - pt = psb_mmu_pt_map_lock(pd, addr); - if (!pt) - continue; - do { - psb_clflush(&pt->v - [psb_mmu_pt_index(addr)]); - } while (addr += - clflush_add, - (addr & clflush_mask) < next); - - psb_mmu_pt_unmap_unlock(pt); - } while (addr = next, next != end); - address += row_add; - } - mb(); -} - -void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, - unsigned long address, uint32_t num_pages) -{ - struct psb_mmu_pt *pt; - unsigned long addr; - unsigned long end; - unsigned long next; - unsigned long f_address = address; - - down_read(&pd->driver->sem); - - addr = address; - end = addr + (num_pages << PAGE_SHIFT); - - do { - next = psb_pd_addr_end(addr, end); - pt = psb_mmu_pt_alloc_map_lock(pd, addr); - if (!pt) - goto out; - do { - psb_mmu_invalidate_pte(pt, addr); - --pt->count; - } while (addr += PAGE_SIZE, addr < next); - psb_mmu_pt_unmap_unlock(pt); - - } while (addr = next, next != end); - -out: - if (pd->hw_context != -1) - psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1); - - up_read(&pd->driver->sem); - - if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 0); - - return; -} - -void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address, - uint32_t num_pages, uint32_t desired_tile_stride, - uint32_t hw_tile_stride) -{ - struct psb_mmu_pt *pt; - uint32_t rows = 1; - uint32_t i; - unsigned long addr; - unsigned long end; - unsigned long next; - unsigned long add; - unsigned long row_add; - unsigned long f_address = address; - - if (hw_tile_stride) - rows = num_pages / desired_tile_stride; - else - desired_tile_stride = num_pages; - - add = desired_tile_stride << PAGE_SHIFT; - row_add = hw_tile_stride << PAGE_SHIFT; - - /* down_read(&pd->driver->sem); */ - - /* Make sure we only need to flush this processor's cache */ - - for (i = 0; i < rows; ++i) { - - addr = address; - end = addr + add; - - do { - next = psb_pd_addr_end(addr, end); - pt = psb_mmu_pt_map_lock(pd, addr); - if (!pt) - continue; - do { - psb_mmu_invalidate_pte(pt, addr); - --pt->count; - - } while (addr += PAGE_SIZE, addr < next); - psb_mmu_pt_unmap_unlock(pt); - - } while (addr = next, next != end); - address += row_add; - } - if (pd->hw_context != -1) - psb_mmu_flush_ptes(pd, f_address, num_pages, - desired_tile_stride, hw_tile_stride); - - /* up_read(&pd->driver->sem); */ - - if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 0); -} - -int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn, - unsigned long address, uint32_t num_pages, - int type) -{ - struct psb_mmu_pt *pt; - uint32_t pte; - unsigned long addr; - unsigned long end; - unsigned long next; - unsigned long f_address = address; - int ret = 0; - - down_read(&pd->driver->sem); - - addr = address; - end = addr + (num_pages << PAGE_SHIFT); - - do { - next = psb_pd_addr_end(addr, end); - pt = psb_mmu_pt_alloc_map_lock(pd, addr); - if (!pt) { - ret = -ENOMEM; - goto out; - } - do { - pte = psb_mmu_mask_pte(start_pfn++, type); - psb_mmu_set_pte(pt, addr, pte); - pt->count++; - } while (addr += PAGE_SIZE, addr < next); - psb_mmu_pt_unmap_unlock(pt); - - } while (addr = next, next != end); - -out: - if (pd->hw_context != -1) - psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1); - - up_read(&pd->driver->sem); - - if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 1); - - return ret; -} - -int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride, int type) -{ - struct psb_mmu_pt *pt; - uint32_t rows = 1; - uint32_t i; - uint32_t pte; - unsigned long addr; - unsigned long end; - unsigned long next; - unsigned long add; - unsigned long row_add; - unsigned long f_address = address; - int ret = 0; - - if (hw_tile_stride) { - if (num_pages % desired_tile_stride != 0) - return -EINVAL; - rows = num_pages / desired_tile_stride; - } else { - desired_tile_stride = num_pages; - } - - add = desired_tile_stride << PAGE_SHIFT; - row_add = hw_tile_stride << PAGE_SHIFT; - - down_read(&pd->driver->sem); - - for (i = 0; i < rows; ++i) { - - addr = address; - end = addr + add; - - do { - next = psb_pd_addr_end(addr, end); - pt = psb_mmu_pt_alloc_map_lock(pd, addr); - if (!pt) { - ret = -ENOMEM; - goto out; - } - do { - pte = - psb_mmu_mask_pte(page_to_pfn(*pages++), - type); - psb_mmu_set_pte(pt, addr, pte); - pt->count++; - } while (addr += PAGE_SIZE, addr < next); - psb_mmu_pt_unmap_unlock(pt); - - } while (addr = next, next != end); - - address += row_add; - } -out: - if (pd->hw_context != -1) - psb_mmu_flush_ptes(pd, f_address, num_pages, - desired_tile_stride, hw_tile_stride); - - up_read(&pd->driver->sem); - - if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 1); - - return ret; -} - -int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, - unsigned long *pfn) -{ - int ret; - struct psb_mmu_pt *pt; - uint32_t tmp; - spinlock_t *lock = &pd->driver->lock; - - down_read(&pd->driver->sem); - pt = psb_mmu_pt_map_lock(pd, virtual); - if (!pt) { - uint32_t *v; - - spin_lock(lock); - v = kmap_atomic(pd->p, KM_USER0); - tmp = v[psb_mmu_pd_index(virtual)]; - kunmap_atomic(v, KM_USER0); - spin_unlock(lock); - - if (tmp != pd->invalid_pde || !(tmp & PSB_PTE_VALID) || - !(pd->invalid_pte & PSB_PTE_VALID)) { - ret = -EINVAL; - goto out; - } - ret = 0; - *pfn = pd->invalid_pte >> PAGE_SHIFT; - goto out; - } - tmp = pt->v[psb_mmu_pt_index(virtual)]; - if (!(tmp & PSB_PTE_VALID)) { - ret = -EINVAL; - } else { - ret = 0; - *pfn = tmp >> PAGE_SHIFT; - } - psb_mmu_pt_unmap_unlock(pt); -out: - up_read(&pd->driver->sem); - return ret; -} diff --git a/drivers/staging/gma500/mrst.h b/drivers/staging/gma500/mrst.h deleted file mode 100644 index b563dbc..0000000 --- a/drivers/staging/gma500/mrst.h +++ /dev/null @@ -1,252 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -/* MID device specific descriptors */ - -struct mrst_vbt { - s8 signature[4]; /*4 bytes,"$GCT" */ - u8 revision; - u8 size; - u8 checksum; - void *mrst_gct; -} __packed; - -struct mrst_timing_info { - u16 pixel_clock; - u8 hactive_lo; - u8 hblank_lo; - u8 hblank_hi:4; - u8 hactive_hi:4; - u8 vactive_lo; - u8 vblank_lo; - u8 vblank_hi:4; - u8 vactive_hi:4; - u8 hsync_offset_lo; - u8 hsync_pulse_width_lo; - u8 vsync_pulse_width_lo:4; - u8 vsync_offset_lo:4; - u8 vsync_pulse_width_hi:2; - u8 vsync_offset_hi:2; - u8 hsync_pulse_width_hi:2; - u8 hsync_offset_hi:2; - u8 width_mm_lo; - u8 height_mm_lo; - u8 height_mm_hi:4; - u8 width_mm_hi:4; - u8 hborder; - u8 vborder; - u8 unknown0:1; - u8 hsync_positive:1; - u8 vsync_positive:1; - u8 separate_sync:2; - u8 stereo:1; - u8 unknown6:1; - u8 interlaced:1; -} __packed; - -struct gct_r10_timing_info { - u16 pixel_clock; - u32 hactive_lo:8; - u32 hactive_hi:4; - u32 hblank_lo:8; - u32 hblank_hi:4; - u32 hsync_offset_lo:8; - u16 hsync_offset_hi:2; - u16 hsync_pulse_width_lo:8; - u16 hsync_pulse_width_hi:2; - u16 hsync_positive:1; - u16 rsvd_1:3; - u8 vactive_lo:8; - u16 vactive_hi:4; - u16 vblank_lo:8; - u16 vblank_hi:4; - u16 vsync_offset_lo:4; - u16 vsync_offset_hi:2; - u16 vsync_pulse_width_lo:4; - u16 vsync_pulse_width_hi:2; - u16 vsync_positive:1; - u16 rsvd_2:3; -} __packed; - -struct mrst_panel_descriptor_v1 { - u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */ - /* 0x61190 if MIPI */ - u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/ - u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/ - u32 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 dword */ - /* Register 0x61210 */ - struct mrst_timing_info DTD;/*18 bytes, Standard definition */ - u16 Panel_Backlight_Inverter_Descriptor;/* 16 bits, as follows */ - /* Bit 0, Frequency, 15 bits,0 - 32767Hz */ - /* Bit 15, Polarity, 1 bit, 0: Normal, 1: Inverted */ - u16 Panel_MIPI_Display_Descriptor; - /*16 bits, Defined as follows: */ - /* if MIPI, 0x0000 if LVDS */ - /* Bit 0, Type, 2 bits, */ - /* 0: Type-1, */ - /* 1: Type-2, */ - /* 2: Type-3, */ - /* 3: Type-4 */ - /* Bit 2, Pixel Format, 4 bits */ - /* Bit0: 16bpp (not supported in LNC), */ - /* Bit1: 18bpp loosely packed, */ - /* Bit2: 18bpp packed, */ - /* Bit3: 24bpp */ - /* Bit 6, Reserved, 2 bits, 00b */ - /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */ - /* Bit 14, Reserved, 2 bits, 00b */ -} __packed; - -struct mrst_panel_descriptor_v2 { - u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */ - /* 0x61190 if MIPI */ - u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/ - u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/ - u8 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 byte */ - /* Register 0x61210 */ - struct mrst_timing_info DTD;/*18 bytes, Standard definition */ - u16 Panel_Backlight_Inverter_Descriptor;/*16 bits, as follows*/ - /*Bit 0, Frequency, 16 bits, 0 - 32767Hz*/ - u8 Panel_Initial_Brightness;/* [7:0] 0 - 100% */ - /*Bit 7, Polarity, 1 bit,0: Normal, 1: Inverted*/ - u16 Panel_MIPI_Display_Descriptor; - /*16 bits, Defined as follows: */ - /* if MIPI, 0x0000 if LVDS */ - /* Bit 0, Type, 2 bits, */ - /* 0: Type-1, */ - /* 1: Type-2, */ - /* 2: Type-3, */ - /* 3: Type-4 */ - /* Bit 2, Pixel Format, 4 bits */ - /* Bit0: 16bpp (not supported in LNC), */ - /* Bit1: 18bpp loosely packed, */ - /* Bit2: 18bpp packed, */ - /* Bit3: 24bpp */ - /* Bit 6, Reserved, 2 bits, 00b */ - /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */ - /* Bit 14, Reserved, 2 bits, 00b */ -} __packed; - -union mrst_panel_rx { - struct { - u16 NumberOfLanes:2; /*Num of Lanes, 2 bits,0 = 1 lane,*/ - /* 1 = 2 lanes, 2 = 3 lanes, 3 = 4 lanes. */ - u16 MaxLaneFreq:3; /* 0: 100MHz, 1: 200MHz, 2: 300MHz, */ - /*3: 400MHz, 4: 500MHz, 5: 600MHz, 6: 700MHz, 7: 800MHz.*/ - u16 SupportedVideoTransferMode:2; /*0: Non-burst only */ - /* 1: Burst and non-burst */ - /* 2/3: Reserved */ - u16 HSClkBehavior:1; /*0: Continuous, 1: Non-continuous*/ - u16 DuoDisplaySupport:1; /*1 bit,0: No, 1: Yes*/ - u16 ECC_ChecksumCapabilities:1;/*1 bit,0: No, 1: Yes*/ - u16 BidirectionalCommunication:1;/*1 bit,0: No, 1: Yes */ - u16 Rsvd:5;/*5 bits,00000b */ - } panelrx; - u16 panel_receiver; -} __packed; - -struct mrst_gct_v1 { - union { /*8 bits,Defined as follows: */ - struct { - u8 PanelType:4; /*4 bits, Bit field for panels*/ - /* 0 - 3: 0 = LVDS, 1 = MIPI*/ - /*2 bits,Specifies which of the*/ - u8 BootPanelIndex:2; - /* 4 panels to use by default*/ - u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/ - /* the 4 MIPI DSI receivers to use*/ - } PD; - u8 PanelDescriptor; - }; - struct mrst_panel_descriptor_v1 panel[4];/*panel descrs,38 bytes each*/ - union mrst_panel_rx panelrx[4]; /* panel receivers*/ -} __packed; - -struct mrst_gct_v2 { - union { /*8 bits,Defined as follows: */ - struct { - u8 PanelType:4; /*4 bits, Bit field for panels*/ - /* 0 - 3: 0 = LVDS, 1 = MIPI*/ - /*2 bits,Specifies which of the*/ - u8 BootPanelIndex:2; - /* 4 panels to use by default*/ - u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/ - /* the 4 MIPI DSI receivers to use*/ - } PD; - u8 PanelDescriptor; - }; - struct mrst_panel_descriptor_v2 panel[4];/*panel descrs,38 bytes each*/ - union mrst_panel_rx panelrx[4]; /* panel receivers*/ -} __packed; - -struct mrst_gct_data { - u8 bpi; /* boot panel index, number of panel used during boot */ - u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */ - struct mrst_timing_info DTD; /* timing info for the selected panel */ - u32 Panel_Port_Control; - u32 PP_On_Sequencing;/*1 dword,Register 0x61208,*/ - u32 PP_Off_Sequencing;/*1 dword,Register 0x6120C,*/ - u32 PP_Cycle_Delay; - u16 Panel_Backlight_Inverter_Descriptor; - u16 Panel_MIPI_Display_Descriptor; -} __packed; - -#define MODE_SETTING_IN_CRTC 0x1 -#define MODE_SETTING_IN_ENCODER 0x2 -#define MODE_SETTING_ON_GOING 0x3 -#define MODE_SETTING_IN_DSR 0x4 -#define MODE_SETTING_ENCODER_DONE 0x8 - -#define GCT_R10_HEADER_SIZE 16 -#define GCT_R10_DISPLAY_DESC_SIZE 28 - -/* - * Moorestown HDMI interfaces - */ - -struct mrst_hdmi_dev { - struct pci_dev *dev; - void __iomem *regs; - unsigned int mmio, mmio_len; - int dpms_mode; - struct hdmi_i2c_dev *i2c_dev; - - /* register state */ - u32 saveDPLL_CTRL; - u32 saveDPLL_DIV_CTRL; - u32 saveDPLL_ADJUST; - u32 saveDPLL_UPDATE; - u32 saveDPLL_CLK_ENABLE; - u32 savePCH_HTOTAL_B; - u32 savePCH_HBLANK_B; - u32 savePCH_HSYNC_B; - u32 savePCH_VTOTAL_B; - u32 savePCH_VBLANK_B; - u32 savePCH_VSYNC_B; - u32 savePCH_PIPEBCONF; - u32 savePCH_PIPEBSRC; -}; - -extern void mrst_hdmi_setup(struct drm_device *dev); -extern void mrst_hdmi_teardown(struct drm_device *dev); -extern int mrst_hdmi_i2c_init(struct pci_dev *dev); -extern void mrst_hdmi_i2c_exit(struct pci_dev *dev); -extern void mrst_hdmi_save(struct drm_device *dev); -extern void mrst_hdmi_restore(struct drm_device *dev); -extern void mrst_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); diff --git a/drivers/staging/gma500/mrst_crtc.c b/drivers/staging/gma500/mrst_crtc.c deleted file mode 100644 index 980837e..0000000 --- a/drivers/staging/gma500/mrst_crtc.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright © 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include - -#include -#include "framebuffer.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "psb_intel_display.h" -#include "power.h" - -struct psb_intel_range_t { - int min, max; -}; - -struct mrst_limit_t { - struct psb_intel_range_t dot, m, p1; -}; - -struct mrst_clock_t { - /* derived values */ - int dot; - int m; - int p1; -}; - -#define MRST_LIMIT_LVDS_100L 0 -#define MRST_LIMIT_LVDS_83 1 -#define MRST_LIMIT_LVDS_100 2 - -#define MRST_DOT_MIN 19750 -#define MRST_DOT_MAX 120000 -#define MRST_M_MIN_100L 20 -#define MRST_M_MIN_100 10 -#define MRST_M_MIN_83 12 -#define MRST_M_MAX_100L 34 -#define MRST_M_MAX_100 17 -#define MRST_M_MAX_83 20 -#define MRST_P1_MIN 2 -#define MRST_P1_MAX_0 7 -#define MRST_P1_MAX_1 8 - -static const struct mrst_limit_t mrst_limits[] = { - { /* MRST_LIMIT_LVDS_100L */ - .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, - .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L}, - .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, - }, - { /* MRST_LIMIT_LVDS_83L */ - .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, - .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83}, - .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0}, - }, - { /* MRST_LIMIT_LVDS_100 */ - .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, - .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100}, - .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, - }, -}; - -#define MRST_M_MIN 10 -static const u32 mrst_m_converts[] = { - 0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C, - 0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25, - 0x12, 0x09, 0x24, 0x32, 0x39, 0x1c, -}; - -static const struct mrst_limit_t *mrst_limit(struct drm_crtc *crtc) -{ - const struct mrst_limit_t *limit = NULL; - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) - || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { - switch (dev_priv->core_freq) { - case 100: - limit = &mrst_limits[MRST_LIMIT_LVDS_100L]; - break; - case 166: - limit = &mrst_limits[MRST_LIMIT_LVDS_83]; - break; - case 200: - limit = &mrst_limits[MRST_LIMIT_LVDS_100]; - break; - } - } else { - limit = NULL; - dev_err(dev->dev, "mrst_limit Wrong display type.\n"); - } - - return limit; -} - -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ -static void mrst_clock(int refclk, struct mrst_clock_t *clock) -{ - clock->dot = (refclk * clock->m) / (14 * clock->p1); -} - -void mrstPrintPll(char *prefix, struct mrst_clock_t *clock) -{ - pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n", - prefix, clock->dot, clock->m, clock->p1); -} - -/** - * Returns a set of divisors for the desired target clock with the given refclk, - * or FALSE. Divisor values are the actual divisors for - */ -static bool -mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, - struct mrst_clock_t *best_clock) -{ - struct mrst_clock_t clock; - const struct mrst_limit_t *limit = mrst_limit(crtc); - int err = target; - - memset(best_clock, 0, sizeof(*best_clock)); - - for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { - for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; - clock.p1++) { - int this_err; - - mrst_clock(refclk, &clock); - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err); - return err != target; -} - -/** - * Sets the power management mode of the pipe and plane. - * - * This code should probably grow support for turning the cursor off and back - * on appropriately at the same time as we're turning the pipe off/on. - */ -static void mrst_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - u32 temp; - bool enabled; - - if (!gma_power_begin(dev, true)) - return; - - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable the DPLL */ - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) == 0) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - /* Enable the plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, - temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - } - - psb_intel_crtc_load_lut(crtc); - - /* Give the overlay scaler a chance to enable - if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, true); TODO */ - break; - case DRM_MODE_DPMS_OFF: - /* Give the overlay scaler a chance to disable - * if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - /* Disable display plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, - temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); - } - - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); - } - /* Wait for for the pipe disable to take effect. */ - psb_intel_wait_for_vblank(dev); - - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - } - - /* Wait for the clocks to turn off. */ - udelay(150); - break; - } - - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - - /*Set FIFO Watermarks*/ - REG_WRITE(DSPARB, 0x3FFF); - REG_WRITE(DSPFW1, 0x3F88080A); - REG_WRITE(DSPFW2, 0x0b060808); - REG_WRITE(DSPFW3, 0x0); - REG_WRITE(DSPFW4, 0x08030404); - REG_WRITE(DSPFW5, 0x04040404); - REG_WRITE(DSPFW6, 0x78); - REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000); - /* Must write Bit 14 of the Chicken Bit Register */ - - gma_power_end(dev); -} - -/** - * Return the pipe currently connected to the panel fitter, - * or -1 if the panel fitter is not present or not in use - */ -static int mrst_panel_fitter_pipe(struct drm_device *dev) -{ - u32 pfit_control; - - pfit_control = REG_READ(PFIT_CONTROL); - - /* See if the panel fitter is in use */ - if ((pfit_control & PFIT_ENABLE) == 0) - return -1; - return (pfit_control >> 29) & 3; -} - -static int mrst_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct drm_psb_private *dev_priv = dev->dev_private; - int pipe = psb_intel_crtc->pipe; - int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int refclk = 0; - struct mrst_clock_t clock; - u32 dpll = 0, fp = 0, dspcntr, pipeconf; - bool ok, is_sdvo = false; - bool is_crt = false, is_lvds = false, is_tv = false; - bool is_mipi = false; - struct drm_mode_config *mode_config = &dev->mode_config; - struct psb_intel_output *psb_intel_output = NULL; - uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; - struct drm_encoder *encoder; - - if (!gma_power_begin(dev, true)) - return 0; - - memcpy(&psb_intel_crtc->saved_mode, - mode, - sizeof(struct drm_display_mode)); - memcpy(&psb_intel_crtc->saved_adjusted_mode, - adjusted_mode, - sizeof(struct drm_display_mode)); - - list_for_each_entry(encoder, &mode_config->encoder_list, head) { - - if (encoder->crtc != crtc) - continue; - - psb_intel_output = enc_to_psb_intel_output(encoder); - switch (psb_intel_output->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - case INTEL_OUTPUT_SDVO: - is_sdvo = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; - case INTEL_OUTPUT_MIPI: - is_mipi = true; - break; - } - } - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* Disable the panel fitter if it was on our pipe */ - if (mrst_panel_fitter_pipe(dev) == pipe) - REG_WRITE(PFIT_CONTROL, 0); - - REG_WRITE(pipesrc_reg, - ((mode->crtc_hdisplay - 1) << 16) | - (mode->crtc_vdisplay - 1)); - - if (psb_intel_output) - drm_connector_property_get_value(&psb_intel_output->base, - dev->mode_config.scaling_mode_property, &scalingType); - - if (scalingType == DRM_MODE_SCALE_NO_SCALE) { - /* Moorestown doesn't have register support for centering so - * we need to mess with the h/vblank and h/vsync start and - * ends to get centering */ - int offsetX = 0, offsetY = 0; - - offsetX = (adjusted_mode->crtc_hdisplay - - mode->crtc_hdisplay) / 2; - offsetY = (adjusted_mode->crtc_vdisplay - - mode->crtc_vdisplay) / 2; - - REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, - (adjusted_mode->crtc_hblank_start - offsetX - 1) | - ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); - REG_WRITE(hsync_reg, - (adjusted_mode->crtc_hsync_start - offsetX - 1) | - ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); - REG_WRITE(vblank_reg, - (adjusted_mode->crtc_vblank_start - offsetY - 1) | - ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); - REG_WRITE(vsync_reg, - (adjusted_mode->crtc_vsync_start - offsetY - 1) | - ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); - } else { - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | - ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | - ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | - ((adjusted_mode->crtc_vsync_end - 1) << 16)); - } - - /* Flush the plane changes */ - { - struct drm_crtc_helper_funcs *crtc_funcs = - crtc->helper_private; - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - } - - /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); - - /* Set up the display plane register */ - dspcntr = REG_READ(dspcntr_reg); - dspcntr |= DISPPLANE_GAMMA_ENABLE; - - if (pipe == 0) - dspcntr |= DISPPLANE_SEL_PIPE_A; - else - dspcntr |= DISPPLANE_SEL_PIPE_B; - - dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE; - dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE; - - if (is_mipi) - goto mrst_crtc_mode_set_exit; - - refclk = dev_priv->core_freq * 1000; - - dpll = 0; /*BIT16 = 0 for 100MHz reference */ - - ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock); - - if (!ok) { - dev_dbg(dev->dev, "mrstFindBestPLL fail in mrst_crtc_mode_set.\n"); - } else { - dev_dbg(dev->dev, "mrst_crtc_mode_set pixel clock = %d," - "m = %x, p1 = %x.\n", clock.dot, clock.m, - clock.p1); - } - - fp = mrst_m_converts[(clock.m - MRST_M_MIN)] << 8; - - dpll |= DPLL_VGA_MODE_DIS; - - - dpll |= DPLL_VCO_ENABLE; - - if (is_lvds) - dpll |= DPLLA_MODE_LVDS; - else - dpll |= DPLLB_MODE_DAC_SERIAL; - - if (is_sdvo) { - int sdvo_pixel_multiply = - adjusted_mode->clock / mode->clock; - - dpll |= DPLL_DVO_HIGH_SPEED; - dpll |= - (sdvo_pixel_multiply - - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - } - - - /* compute bitmask from p1 value */ - dpll |= (1 << (clock.p1 - 2)) << 17; - - dpll |= DPLL_VCO_ENABLE; - - mrstPrintPll("chosen", &clock); - - if (dpll & DPLL_VCO_ENABLE) { - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Check the DPLLA lock bit PIPEACONF[29] */ - udelay(150); - } - - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - - /* write it again -- the BIOS does, after all */ - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - psb_intel_wait_for_vblank(dev); - - REG_WRITE(dspcntr_reg, dspcntr); - psb_intel_wait_for_vblank(dev); - -mrst_crtc_mode_set_exit: - gma_power_end(dev); - return 0; -} - -static bool mrst_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -int mrst_pipe_set_base(struct drm_crtc *crtc, - int x, int y, struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); - int pipe = psb_intel_crtc->pipe; - unsigned long start, offset; - - int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - u32 dspcntr; - int ret = 0; - - /* no fb bound */ - if (!crtc->fb) { - dev_dbg(dev->dev, "No FB bound\n"); - return 0; - } - - if (!gma_power_begin(dev, true)) - return 0; - - start = psbfb->gtt->offset; - offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - - REG_WRITE(dspstride, crtc->fb->pitches[0]); - - dspcntr = REG_READ(dspcntr_reg); - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - - switch (crtc->fb->bits_per_pixel) { - case 8: - dspcntr |= DISPPLANE_8BPP; - break; - case 16: - if (crtc->fb->depth == 15) - dspcntr |= DISPPLANE_15_16BPP; - else - dspcntr |= DISPPLANE_16BPP; - break; - case 24: - case 32: - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; - break; - default: - dev_err(dev->dev, "Unknown color depth\n"); - ret = -EINVAL; - goto pipe_set_base_exit; - } - REG_WRITE(dspcntr_reg, dspcntr); - - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - -pipe_set_base_exit: - gma_power_end(dev); - return ret; -} - -static void mrst_crtc_prepare(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); -} - -static void mrst_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -} - -const struct drm_crtc_helper_funcs mrst_helper_funcs = { - .dpms = mrst_crtc_dpms, - .mode_fixup = mrst_crtc_mode_fixup, - .mode_set = mrst_crtc_mode_set, - .mode_set_base = mrst_pipe_set_base, - .prepare = mrst_crtc_prepare, - .commit = mrst_crtc_commit, -}; - diff --git a/drivers/staging/gma500/mrst_device.c b/drivers/staging/gma500/mrst_device.c deleted file mode 100644 index 6707faf..0000000 --- a/drivers/staging/gma500/mrst_device.c +++ /dev/null @@ -1,634 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include -#include -#include "mid_bios.h" - -static int devtype; - -module_param_named(type, devtype, int, 0600); -MODULE_PARM_DESC(type, "Moorestown/Oaktrail device type"); - -#define DEVICE_MOORESTOWN 1 -#define DEVICE_OAKTRAIL 2 -#define DEVICE_MOORESTOWN_MM 3 - -static int mrst_device_ident(struct drm_device *dev) -{ - /* User forced */ - if (devtype) - return devtype; - if (dmi_match(DMI_PRODUCT_NAME, "OakTrail") || - dmi_match(DMI_PRODUCT_NAME, "OakTrail platform")) - return DEVICE_OAKTRAIL; -#if defined(CONFIG_X86_MRST) - if (dmi_match(DMI_PRODUCT_NAME, "MM") || - dmi_match(DMI_PRODUCT_NAME, "MM 10")) - return DEVICE_MOORESTOWN_MM; - if (mrst_identify_cpu()) - return DEVICE_MOORESTOWN; -#endif - return DEVICE_OAKTRAIL; -} - - -/* IPC message and command defines used to enable/disable mipi panel voltages */ -#define IPC_MSG_PANEL_ON_OFF 0xE9 -#define IPC_CMD_PANEL_ON 1 -#define IPC_CMD_PANEL_OFF 0 - -static int mrst_output_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - if (dev_priv->iLVDS_enable) - mrst_lvds_init(dev, &dev_priv->mode_dev); - else - dev_err(dev->dev, "DSI is not supported\n"); - if (dev_priv->hdmi_priv) - mrst_hdmi_init(dev, &dev_priv->mode_dev); - return 0; -} - -/* - * Provide the low level interfaces for the Moorestown backlight - */ - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - -#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF -#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ -#define BLC_PWM_FREQ_CALC_CONSTANT 32 -#define MHz 1000000 -#define BLC_ADJUSTMENT_MAX 100 - -static struct backlight_device *mrst_backlight_device; -static int mrst_brightness; - -static int mrst_set_brightness(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(mrst_backlight_device); - struct drm_psb_private *dev_priv = dev->dev_private; - int level = bd->props.brightness; - u32 blc_pwm_ctl; - u32 max_pwm_blc; - - /* Percentage 1-100% being valid */ - if (level < 1) - level = 1; - - if (gma_power_begin(dev, 0)) { - /* Calculate and set the brightness value */ - max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; - blc_pwm_ctl = level * max_pwm_blc / 100; - - /* Adjust the backlight level with the percent in - * dev_priv->blc_adj1; - */ - blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; - blc_pwm_ctl = blc_pwm_ctl / 100; - - /* Adjust the backlight level with the percent in - * dev_priv->blc_adj2; - */ - blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; - blc_pwm_ctl = blc_pwm_ctl / 100; - - /* force PWM bit on */ - REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); - REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); - gma_power_end(dev); - } - mrst_brightness = level; - return 0; -} - -static int mrst_get_brightness(struct backlight_device *bd) -{ - /* return locally cached var instead of HW read (due to DPST etc.) */ - /* FIXME: ideally return actual value in case firmware fiddled with - it */ - return mrst_brightness; -} - -static int device_backlight_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long core_clock; - u16 bl_max_freq; - uint32_t value; - uint32_t blc_pwm_precision_factor; - - dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; - dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; - bl_max_freq = 256; - /* this needs to be set elsewhere */ - blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; - - core_clock = dev_priv->core_freq; - - value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; - value *= blc_pwm_precision_factor; - value /= bl_max_freq; - value /= blc_pwm_precision_factor; - - if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) - return -ERANGE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); - REG_WRITE(BLC_PWM_CTL, value | (value << 16)); - gma_power_end(dev); - } - return 0; -} - -static const struct backlight_ops mrst_ops = { - .get_brightness = mrst_get_brightness, - .update_status = mrst_set_brightness, -}; - -int mrst_backlight_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - int ret; - struct backlight_properties props; - - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 100; - props.type = BACKLIGHT_PLATFORM; - - mrst_backlight_device = backlight_device_register("mrst-bl", - NULL, (void *)dev, &mrst_ops, &props); - - if (IS_ERR(mrst_backlight_device)) - return PTR_ERR(mrst_backlight_device); - - ret = device_backlight_init(dev); - if (ret < 0) { - backlight_device_unregister(mrst_backlight_device); - return ret; - } - mrst_backlight_device->props.brightness = 100; - mrst_backlight_device->props.max_brightness = 100; - backlight_update_status(mrst_backlight_device); - dev_priv->backlight_device = mrst_backlight_device; - return 0; -} - -#endif - -/* - * Provide the Moorestown specific chip logic and low level methods - * for power management - */ - -static void mrst_init_pm(struct drm_device *dev) -{ -} - -/** - * mrst_save_display_registers - save registers lost on suspend - * @dev: our DRM device - * - * Save the state we need in order to be able to restore the interface - * upon resume from suspend - */ -static int mrst_save_display_registers(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - int i; - u32 pp_stat; - - /* Display arbitration control + watermarks */ - dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); - dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); - dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); - dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); - dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); - dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); - dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); - dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); - - /* Pipe & plane A info */ - dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF); - dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC); - dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0); - dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1); - dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); - dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); - dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A); - dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A); - dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); - dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A); - dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A); - dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); - dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR); - dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); - dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE); - dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF); - dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); - dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); - - /* Save cursor regs */ - dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); - dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); - dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); - - /* Save palette (gamma) */ - for (i = 0; i < 256; i++) - dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); - - if (dev_priv->hdmi_priv) - mrst_hdmi_save(dev); - - /* Save performance state */ - dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); - - /* LVDS state */ - dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL); - dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); - dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); - dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); - dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); - dev_priv->saveLVDS = PSB_RVDC32(LVDS); - dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); - dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); - dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); - dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); - - /* HW overlay */ - dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); - dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); - dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); - dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); - dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); - dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); - dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); - - /* DPST registers */ - dev_priv->saveHISTOGRAM_INT_CONTROL_REG = - PSB_RVDC32(HISTOGRAM_INT_CONTROL); - dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG = - PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); - dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); - - if (dev_priv->iLVDS_enable) { - /* Shut down the panel */ - PSB_WVDC32(0, PP_CONTROL); - - do { - pp_stat = PSB_RVDC32(PP_STATUS); - } while (pp_stat & 0x80000000); - - /* Turn off the plane */ - PSB_WVDC32(0x58000000, DSPACNTR); - /* Trigger the plane disable */ - PSB_WVDC32(0, DSPASURF); - - /* Wait ~4 ticks */ - msleep(4); - - /* Turn off pipe */ - PSB_WVDC32(0x0, PIPEACONF); - /* Wait ~8 ticks */ - msleep(8); - - /* Turn off PLLs */ - PSB_WVDC32(0, MRST_DPLL_A); - } - return 0; -} - -/** - * mrst_restore_display_registers - restore lost register state - * @dev: our DRM device - * - * Restore register state that was lost during suspend and resume. - */ -static int mrst_restore_display_registers(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 pp_stat; - int i; - - /* Display arbitration + watermarks */ - PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); - PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); - PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); - PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); - PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); - PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); - PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); - PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); - - /* Make sure VGA plane is off. it initializes to on after reset!*/ - PSB_WVDC32(0x80000000, VGACNTRL); - - /* set the plls */ - PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0); - PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1); - - /* Actually enable it */ - PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A); - DRM_UDELAY(150); - - /* Restore mode */ - PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A); - PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A); - PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A); - PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A); - PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A); - PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A); - PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC); - PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A); - - /* Restore performance mode*/ - PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE); - - /* Enable the pipe*/ - if (dev_priv->iLVDS_enable) - PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF); - - /* Set up the plane*/ - PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF); - PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE); - PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF); - - /* Enable the plane */ - PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR); - PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF); - - /* Enable Cursor A */ - PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); - PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); - PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); - - /* Restore palette (gamma) */ - for (i = 0; i < 256; i++) - PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2)); - - if (dev_priv->hdmi_priv) - mrst_hdmi_restore(dev); - - if (dev_priv->iLVDS_enable) { - PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2); - PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/ - PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); - PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); - PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); - PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL); - PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON); - PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF); - PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE); - PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL); - } - - /* Wait for cycle delay */ - do { - pp_stat = PSB_RVDC32(PP_STATUS); - } while (pp_stat & 0x08000000); - - /* Wait for panel power up */ - do { - pp_stat = PSB_RVDC32(PP_STATUS); - } while (pp_stat & 0x10000000); - - /* Restore HW overlay */ - PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); - PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); - PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); - PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); - PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); - PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); - PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); - - /* DPST registers */ - PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG, - HISTOGRAM_INT_CONTROL); - PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG, - HISTOGRAM_LOGIC_CONTROL); - PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); - - return 0; -} - -/** - * mrst_power_down - power down the display island - * @dev: our DRM device - * - * Power down the display interface of our device - */ -static int mrst_power_down(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 pwr_mask ; - u32 pwr_sts; - - pwr_mask = PSB_PWRGT_DISPLAY_MASK; - outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); - - while (true) { - pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); - if ((pwr_sts & pwr_mask) == pwr_mask) - break; - else - udelay(10); - } - return 0; -} - -/* - * mrst_power_up - * - * Restore power to the specified island(s) (powergating) - */ -static int mrst_power_up(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; - u32 pwr_sts, pwr_cnt; - - pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); - pwr_cnt &= ~pwr_mask; - outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); - - while (true) { - pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); - if ((pwr_sts & pwr_mask) == 0) - break; - else - udelay(10); - } - return 0; -} - -#if defined(CONFIG_X86_MRST) -static void mrst_lvds_cache_bl(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - intel_scu_ipc_ioread8(0x28, &(dev_priv->saveBKLTCNT)); - intel_scu_ipc_ioread8(0x29, &(dev_priv->saveBKLTREQ)); - intel_scu_ipc_ioread8(0x2A, &(dev_priv->saveBKLTBRTL)); -} - -static void mrst_mm_bl_power(struct drm_device *dev, bool on) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - if (on) { - intel_scu_ipc_iowrite8(0x2A, dev_priv->saveBKLTBRTL); - intel_scu_ipc_iowrite8(0x28, dev_priv->saveBKLTCNT); - intel_scu_ipc_iowrite8(0x29, dev_priv->saveBKLTREQ); - } else { - intel_scu_ipc_iowrite8(0x2A, 0); - intel_scu_ipc_iowrite8(0x28, 0); - intel_scu_ipc_iowrite8(0x29, 0); - } -} - -static const struct psb_ops mrst_mm_chip_ops = { - .name = "Moorestown MM ", - .accel_2d = 1, - .pipes = 1, - .crtcs = 1, - .sgx_offset = MRST_SGX_OFFSET, - - .crtc_helper = &mrst_helper_funcs, - .crtc_funcs = &psb_intel_crtc_funcs, - - .output_init = mrst_output_init, - - .lvds_bl_power = mrst_mm_bl_power, -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - .backlight_init = mrst_backlight_init, -#endif - - .init_pm = mrst_init_pm, - .save_regs = mrst_save_display_registers, - .restore_regs = mrst_restore_display_registers, - .power_down = mrst_power_down, - .power_up = mrst_power_up, - - .i2c_bus = 0, -}; - -#endif - -static void oaktrail_teardown(struct drm_device *dev) -{ - mrst_hdmi_teardown(dev); -} - -static const struct psb_ops oaktrail_chip_ops = { - .name = "Oaktrail", - .accel_2d = 1, - .pipes = 2, - .crtcs = 2, - .sgx_offset = MRST_SGX_OFFSET, - - .chip_setup = mid_chip_setup, - .chip_teardown = oaktrail_teardown, - .crtc_helper = &mrst_helper_funcs, - .crtc_funcs = &psb_intel_crtc_funcs, - - .output_init = mrst_output_init, - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - .backlight_init = mrst_backlight_init, -#endif - - .init_pm = mrst_init_pm, - .save_regs = mrst_save_display_registers, - .restore_regs = mrst_restore_display_registers, - .power_down = mrst_power_down, - .power_up = mrst_power_up, - - .i2c_bus = 1, -}; - -/** - * mrst_chip_setup - perform the initial chip init - * @dev: Our drm_device - * - * Figure out which incarnation we are and then scan the firmware for - * tables and information. - */ -static int mrst_chip_setup(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - switch (mrst_device_ident(dev)) { - case DEVICE_OAKTRAIL: - /* Dual CRTC, PC compatible, HDMI, I2C #2 */ - dev_priv->ops = &oaktrail_chip_ops; - mrst_hdmi_setup(dev); - return mid_chip_setup(dev); -#if defined(CONFIG_X86_MRST) - case DEVICE_MOORESTOWN_MM: - /* Single CRTC, No HDMI, I2C #0, BL control */ - mrst_lvds_cache_bl(dev); - dev_priv->ops = &mrst_mm_chip_ops; - return mid_chip_setup(dev); - case DEVICE_MOORESTOWN: - /* Dual CRTC, No HDMI(?), I2C #1 */ - return mid_chip_setup(dev); -#endif - default: - dev_err(dev->dev, "unsupported device type.\n"); - return -ENODEV; - } -} - -const struct psb_ops mrst_chip_ops = { - .name = "Moorestown", - .accel_2d = 1, - .pipes = 2, - .crtcs = 2, - .sgx_offset = MRST_SGX_OFFSET, - - .chip_setup = mrst_chip_setup, - .crtc_helper = &mrst_helper_funcs, - .crtc_funcs = &psb_intel_crtc_funcs, - - .output_init = mrst_output_init, - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - .backlight_init = mrst_backlight_init, -#endif - - .init_pm = mrst_init_pm, - .save_regs = mrst_save_display_registers, - .restore_regs = mrst_restore_display_registers, - .power_down = mrst_power_down, - .power_up = mrst_power_up, - - .i2c_bus = 2, -}; - diff --git a/drivers/staging/gma500/mrst_hdmi.c b/drivers/staging/gma500/mrst_hdmi.c deleted file mode 100644 index e66607e..0000000 --- a/drivers/staging/gma500/mrst_hdmi.c +++ /dev/null @@ -1,852 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Li Peng - */ - -#include -#include -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "psb_drv.h" - -#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) -#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) - -#define HDMI_HCR 0x1000 -#define HCR_ENABLE_HDCP (1 << 5) -#define HCR_ENABLE_AUDIO (1 << 2) -#define HCR_ENABLE_PIXEL (1 << 1) -#define HCR_ENABLE_TMDS (1 << 0) - -#define HDMI_HICR 0x1004 -#define HDMI_HSR 0x1008 -#define HDMI_HISR 0x100C -#define HDMI_DETECT_HDP (1 << 0) - -#define HDMI_VIDEO_REG 0x3000 -#define HDMI_UNIT_EN (1 << 7) -#define HDMI_MODE_OUTPUT (1 << 0) -#define HDMI_HBLANK_A 0x3100 - -#define HDMI_AUDIO_CTRL 0x4000 -#define HDMI_ENABLE_AUDIO (1 << 0) - -#define PCH_HTOTAL_B 0x3100 -#define PCH_HBLANK_B 0x3104 -#define PCH_HSYNC_B 0x3108 -#define PCH_VTOTAL_B 0x310C -#define PCH_VBLANK_B 0x3110 -#define PCH_VSYNC_B 0x3114 -#define PCH_PIPEBSRC 0x311C - -#define PCH_PIPEB_DSL 0x3800 -#define PCH_PIPEB_SLC 0x3804 -#define PCH_PIPEBCONF 0x3808 -#define PCH_PIPEBSTAT 0x3824 - -#define CDVO_DFT 0x5000 -#define CDVO_SLEWRATE 0x5004 -#define CDVO_STRENGTH 0x5008 -#define CDVO_RCOMP 0x500C - -#define DPLL_CTRL 0x6000 -#define DPLL_PDIV_SHIFT 16 -#define DPLL_PDIV_MASK (0xf << 16) -#define DPLL_PWRDN (1 << 4) -#define DPLL_RESET (1 << 3) -#define DPLL_FASTEN (1 << 2) -#define DPLL_ENSTAT (1 << 1) -#define DPLL_DITHEN (1 << 0) - -#define DPLL_DIV_CTRL 0x6004 -#define DPLL_CLKF_MASK 0xffffffc0 -#define DPLL_CLKR_MASK (0x3f) - -#define DPLL_CLK_ENABLE 0x6008 -#define DPLL_EN_DISP (1 << 31) -#define DPLL_SEL_HDMI (1 << 8) -#define DPLL_EN_HDMI (1 << 1) -#define DPLL_EN_VGA (1 << 0) - -#define DPLL_ADJUST 0x600C -#define DPLL_STATUS 0x6010 -#define DPLL_UPDATE 0x6014 -#define DPLL_DFT 0x6020 - -struct intel_range { - int min, max; -}; - -struct mrst_hdmi_limit { - struct intel_range vco, np, nr, nf; -}; - -struct mrst_hdmi_clock { - int np; - int nr; - int nf; - int dot; -}; - -#define VCO_MIN 320000 -#define VCO_MAX 1650000 -#define NP_MIN 1 -#define NP_MAX 15 -#define NR_MIN 1 -#define NR_MAX 64 -#define NF_MIN 2 -#define NF_MAX 4095 - -static const struct mrst_hdmi_limit mrst_hdmi_limit = { - .vco = { .min = VCO_MIN, .max = VCO_MAX }, - .np = { .min = NP_MIN, .max = NP_MAX }, - .nr = { .min = NR_MIN, .max = NR_MAX }, - .nf = { .min = NF_MIN, .max = NF_MAX }, -}; - -static void wait_for_vblank(struct drm_device *dev) -{ - /* FIXME: Can we do this as a sleep ? */ - /* Wait for 20ms, i.e. one cycle at 50hz. */ - mdelay(20); -} - -static void scu_busy_loop(void *scu_base) -{ - u32 status = 0; - u32 loop_count = 0; - - status = readl(scu_base + 0x04); - while (status & 1) { - udelay(1); /* scu processing time is in few u secods */ - status = readl(scu_base + 0x04); - loop_count++; - /* break if scu doesn't reset busy bit after huge retry */ - if (loop_count > 1000) { - DRM_DEBUG_KMS("SCU IPC timed out"); - return; - } - } -} - -static void mrst_hdmi_reset(struct drm_device *dev) -{ - void *base; - /* FIXME: at least make these defines */ - unsigned int scu_ipc_mmio = 0xff11c000; - int scu_len = 1024; - - base = ioremap((resource_size_t)scu_ipc_mmio, scu_len); - if (base == NULL) { - DRM_ERROR("failed to map SCU mmio\n"); - return; - } - - /* scu ipc: assert hdmi controller reset */ - writel(0xff11d118, base + 0x0c); - writel(0x7fffffdf, base + 0x80); - writel(0x42005, base + 0x0); - scu_busy_loop(base); - - /* scu ipc: de-assert hdmi controller reset */ - writel(0xff11d118, base + 0x0c); - writel(0x7fffffff, base + 0x80); - writel(0x42005, base + 0x0); - scu_busy_loop(base); - - iounmap(base); -} - -static void mrst_hdmi_audio_enable(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - - HDMI_WRITE(HDMI_HCR, 0x67); - HDMI_READ(HDMI_HCR); - - HDMI_WRITE(0x51a8, 0x10); - HDMI_READ(0x51a8); - - HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1); - HDMI_READ(HDMI_AUDIO_CTRL); -} - -static void mrst_hdmi_audio_disable(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - - HDMI_WRITE(0x51a8, 0x0); - HDMI_READ(0x51a8); - - HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0); - HDMI_READ(HDMI_AUDIO_CTRL); - - HDMI_WRITE(HDMI_HCR, 0x47); - HDMI_READ(HDMI_HCR); -} - -void mrst_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - u32 temp; - - switch (mode) { - case DRM_MODE_DPMS_OFF: - /* Disable VGACNTRL */ - REG_WRITE(VGACNTRL, 0x80000000); - - /* Disable plane */ - temp = REG_READ(DSPBCNTR); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); - REG_READ(DSPBCNTR); - /* Flush the plane changes */ - REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); - REG_READ(DSPBSURF); - } - - /* Disable pipe B */ - temp = REG_READ(PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE); - REG_READ(PIPEBCONF); - } - - /* Disable LNW Pipes, etc */ - temp = REG_READ(PCH_PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE); - REG_READ(PCH_PIPEBCONF); - } - /* wait for pipe off */ - udelay(150); - /* Disable dpll */ - temp = REG_READ(DPLL_CTRL); - if ((temp & DPLL_PWRDN) == 0) { - REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET)); - REG_WRITE(DPLL_STATUS, 0x1); - } - /* wait for dpll off */ - udelay(150); - break; - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable dpll */ - temp = REG_READ(DPLL_CTRL); - if ((temp & DPLL_PWRDN) != 0) { - REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET)); - temp = REG_READ(DPLL_CLK_ENABLE); - REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI); - REG_READ(DPLL_CLK_ENABLE); - } - /* wait for dpll warm up */ - udelay(150); - - /* Enable pipe B */ - temp = REG_READ(PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) == 0) { - REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE); - REG_READ(PIPEBCONF); - } - - /* Enable LNW Pipe B */ - temp = REG_READ(PCH_PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) == 0) { - REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE); - REG_READ(PCH_PIPEBCONF); - } - wait_for_vblank(dev); - - /* Enable plane */ - temp = REG_READ(DSPBCNTR); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); - REG_READ(DSPBSURF); - } - psb_intel_crtc_load_lut(crtc); - } - /* DSPARB */ - REG_WRITE(DSPARB, 0x00003fbf); - /* FW1 */ - REG_WRITE(0x70034, 0x3f880a0a); - /* FW2 */ - REG_WRITE(0x70038, 0x0b060808); - /* FW4 */ - REG_WRITE(0x70050, 0x08030404); - /* FW5 */ - REG_WRITE(0x70054, 0x04040404); - /* LNC Chicken Bits */ - REG_WRITE(0x70400, 0x4000); -} - - -static void mrst_hdmi_dpms(struct drm_encoder *encoder, int mode) -{ - static int dpms_mode = -1; - - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - u32 temp; - - if (dpms_mode == mode) - return; - - if (mode != DRM_MODE_DPMS_ON) - temp = 0x0; - else - temp = 0x99; - - dpms_mode = mode; - HDMI_WRITE(HDMI_VIDEO_REG, temp); -} - -static unsigned int htotal_calculate(struct drm_display_mode *mode) -{ - u32 htotal, new_crtc_htotal; - - htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16); - - /* - * 1024 x 768 new_crtc_htotal = 0x1024; - * 1280 x 1024 new_crtc_htotal = 0x0c34; - */ - new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; - - return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16); -} - -static void mrst_hdmi_find_dpll(struct drm_crtc *crtc, int target, - int refclk, struct mrst_hdmi_clock *best_clock) -{ - int np_min, np_max, nr_min, nr_max; - int np, nr, nf; - - np_min = DIV_ROUND_UP(mrst_hdmi_limit.vco.min, target * 10); - np_max = mrst_hdmi_limit.vco.max / (target * 10); - if (np_min < mrst_hdmi_limit.np.min) - np_min = mrst_hdmi_limit.np.min; - if (np_max > mrst_hdmi_limit.np.max) - np_max = mrst_hdmi_limit.np.max; - - nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max)); - nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min)); - if (nr_min < mrst_hdmi_limit.nr.min) - nr_min = mrst_hdmi_limit.nr.min; - if (nr_max > mrst_hdmi_limit.nr.max) - nr_max = mrst_hdmi_limit.nr.max; - - np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max)); - nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np)); - nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk); - DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf); - - /* - * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000; - * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000; - */ - best_clock->np = np; - best_clock->nr = nr - 1; - best_clock->nf = (nf << 14); -} - -int mrst_crtc_hdmi_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - int pipe = 1; - int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int refclk; - struct mrst_hdmi_clock clock; - u32 dspcntr, pipeconf, dpll, temp; - int dspcntr_reg = DSPBCNTR; - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* XXX: Disable the panel fitter if it was on our pipe */ - - /* Disable dpll if necessary */ - dpll = REG_READ(DPLL_CTRL); - if ((dpll & DPLL_PWRDN) == 0) { - REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET)); - REG_WRITE(DPLL_DIV_CTRL, 0x00000000); - REG_WRITE(DPLL_STATUS, 0x1); - } - udelay(150); - - /* reset controller: FIXME - can we sort out the ioremap mess ? */ - iounmap(hdmi_dev->regs); - mrst_hdmi_reset(dev); - - /* program and enable dpll */ - refclk = 25000; - mrst_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); - - /* Setting DPLL */ - dpll = REG_READ(DPLL_CTRL); - dpll &= ~DPLL_PDIV_MASK; - dpll &= ~(DPLL_PWRDN | DPLL_RESET); - REG_WRITE(DPLL_CTRL, 0x00000008); - REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr)); - REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1)); - REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN)); - REG_WRITE(DPLL_UPDATE, 0x80000000); - REG_WRITE(DPLL_CLK_ENABLE, 0x80050102); - udelay(150); - - hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); - if (hdmi_dev->regs == NULL) { - DRM_ERROR("failed to do hdmi mmio mapping\n"); - return -ENOMEM; - } - - /* configure HDMI */ - HDMI_WRITE(0x1004, 0x1fd); - HDMI_WRITE(0x2000, 0x1); - HDMI_WRITE(0x2008, 0x0); - HDMI_WRITE(0x3130, 0x8); - HDMI_WRITE(0x101c, 0x1800810); - - temp = htotal_calculate(adjusted_mode); - REG_WRITE(htot_reg, temp); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); - REG_WRITE(pipesrc_reg, - ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); - - REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); - REG_WRITE(PCH_PIPEBSRC, - ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); - - temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start; - HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp); - - REG_WRITE(dspsize_reg, - ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - - /* Flush the plane changes */ - { - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - } - - /* Set up the display plane register */ - dspcntr = REG_READ(dspcntr_reg); - dspcntr |= DISPPLANE_GAMMA_ENABLE; - dspcntr |= DISPPLANE_SEL_PIPE_B; - dspcntr |= DISPLAY_PLANE_ENABLE; - - /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); - pipeconf |= PIPEACONF_ENABLE; - - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - - REG_WRITE(PCH_PIPEBCONF, pipeconf); - REG_READ(PCH_PIPEBCONF); - wait_for_vblank(dev); - - REG_WRITE(dspcntr_reg, dspcntr); - wait_for_vblank(dev); - - return 0; -} - -static int mrst_hdmi_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - if (mode->clock > 165000) - return MODE_CLOCK_HIGH; - if (mode->clock < 20000) - return MODE_CLOCK_LOW; - - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - return MODE_OK; -} - -static bool mrst_hdmi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static enum drm_connector_status -mrst_hdmi_detect(struct drm_connector *connector, bool force) -{ - enum drm_connector_status status; - struct drm_device *dev = connector->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - u32 temp; - - temp = HDMI_READ(HDMI_HSR); - DRM_DEBUG_KMS("HDMI_HSR %x\n", temp); - - if ((temp & HDMI_DETECT_HDP) != 0) - status = connector_status_connected; - else - status = connector_status_disconnected; - - return status; -} - -static const unsigned char raw_edid[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0, - 0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78, - 0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5, - 0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, - 0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35, - 0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44, - 0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20, - 0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d -}; - -static int mrst_hdmi_get_modes(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct i2c_adapter *i2c_adap; - struct edid *edid; - struct drm_display_mode *mode, *t; - int i = 0, ret = 0; - - i2c_adap = i2c_get_adapter(3); - if (i2c_adap == NULL) { - DRM_ERROR("No ddc adapter available!\n"); - edid = (struct edid *)raw_edid; - } else { - edid = (struct edid *)raw_edid; - /* FIXME ? edid = drm_get_edid(connector, i2c_adap); */ - } - - if (edid) { - drm_mode_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); - connector->display_info.raw_edid = NULL; - } - - /* - * prune modes that require frame buffer bigger than stolen mem - */ - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { - if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) { - i++; - drm_mode_remove(connector, mode); - } - } - return ret - i; -} - -static void mrst_hdmi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - - mrst_hdmi_audio_enable(dev); - return; -} - -static void mrst_hdmi_destroy(struct drm_connector *connector) -{ - return; -} - -static const struct drm_encoder_helper_funcs mrst_hdmi_helper_funcs = { - .dpms = mrst_hdmi_dpms, - .mode_fixup = mrst_hdmi_mode_fixup, - .prepare = psb_intel_encoder_prepare, - .mode_set = mrst_hdmi_mode_set, - .commit = psb_intel_encoder_commit, -}; - -static const struct drm_connector_helper_funcs - mrst_hdmi_connector_helper_funcs = { - .get_modes = mrst_hdmi_get_modes, - .mode_valid = mrst_hdmi_mode_valid, - .best_encoder = psb_intel_best_encoder, -}; - -static const struct drm_connector_funcs mrst_hdmi_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = mrst_hdmi_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = mrst_hdmi_destroy, -}; - -static void mrst_hdmi_enc_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - -static const struct drm_encoder_funcs mrst_hdmi_enc_funcs = { - .destroy = mrst_hdmi_enc_destroy, -}; - -void mrst_hdmi_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev) -{ - struct psb_intel_output *psb_intel_output; - struct drm_connector *connector; - struct drm_encoder *encoder; - - psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); - if (!psb_intel_output) - return; - - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - drm_connector_init(dev, &psb_intel_output->base, - &mrst_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_DVID); - - drm_encoder_init(dev, &psb_intel_output->enc, - &mrst_hdmi_enc_funcs, - DRM_MODE_ENCODER_TMDS); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - - psb_intel_output->type = INTEL_OUTPUT_HDMI; - drm_encoder_helper_add(encoder, &mrst_hdmi_helper_funcs); - drm_connector_helper_add(connector, &mrst_hdmi_connector_helper_funcs); - - connector->display_info.subpixel_order = SubPixelHorizontalRGB; - connector->interlace_allowed = false; - connector->doublescan_allowed = false; - drm_sysfs_connector_add(connector); - - return; -} - -static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) }, - {} -}; - -void mrst_hdmi_setup(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct pci_dev *pdev; - struct mrst_hdmi_dev *hdmi_dev; - int ret; - - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL); - if (!pdev) - return; - - hdmi_dev = kzalloc(sizeof(struct mrst_hdmi_dev), GFP_KERNEL); - if (!hdmi_dev) { - dev_err(dev->dev, "failed to allocate memory\n"); - goto out; - } - - - ret = pci_enable_device(pdev); - if (ret) { - dev_err(dev->dev, "failed to enable hdmi controller\n"); - goto free; - } - - hdmi_dev->mmio = pci_resource_start(pdev, 0); - hdmi_dev->mmio_len = pci_resource_len(pdev, 0); - hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); - if (!hdmi_dev->regs) { - dev_err(dev->dev, "failed to map hdmi mmio\n"); - goto free; - } - - hdmi_dev->dev = pdev; - pci_set_drvdata(pdev, hdmi_dev); - - /* Initialize i2c controller */ - ret = mrst_hdmi_i2c_init(hdmi_dev->dev); - if (ret) - dev_err(dev->dev, "HDMI I2C initialization failed\n"); - - dev_priv->hdmi_priv = hdmi_dev; - mrst_hdmi_audio_disable(dev); - return; - -free: - kfree(hdmi_dev); -out: - return; -} - -void mrst_hdmi_teardown(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - struct pci_dev *pdev; - - if (hdmi_dev) { - pdev = hdmi_dev->dev; - pci_set_drvdata(pdev, NULL); - mrst_hdmi_i2c_exit(pdev); - iounmap(hdmi_dev->regs); - kfree(hdmi_dev); - pci_dev_put(pdev); - } -} - -/* save HDMI register state */ -void mrst_hdmi_save(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - int i; - - /* dpll */ - hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL); - hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL); - hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST); - hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE); - hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE); - - /* pipe B */ - dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); - dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); - dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); - dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B); - dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B); - dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); - dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B); - dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B); - - hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); - hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); - hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B); - hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B); - hdmi_dev->savePCH_HSYNC_B = PSB_RVDC32(PCH_HSYNC_B); - hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B); - hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B); - hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B); - - /* plane */ - dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); - dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); - dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE); - dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF); - dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); - dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); - - /* cursor B */ - dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); - dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); - dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); - - /* save palette */ - for (i = 0; i < 256; i++) - dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); -} - -/* restore HDMI register state */ -void mrst_hdmi_restore(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - int i; - - /* dpll */ - PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL); - PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL); - PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST); - PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE); - PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE); - DRM_UDELAY(150); - - /* pipe */ - PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC); - PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B); - PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B); - PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B); - PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B); - PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B); - PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B); - - PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); - PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); - PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B); - PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B, PCH_HSYNC_B); - PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B); - PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B); - PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B); - - PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF); - PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF); - - /* plane */ - PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF); - PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE); - PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF); - PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR); - PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF); - - /* cursor B */ - PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); - PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); - PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); - - /* restore palette */ - for (i = 0; i < 256; i++) - PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2)); -} diff --git a/drivers/staging/gma500/mrst_hdmi_i2c.c b/drivers/staging/gma500/mrst_hdmi_i2c.c deleted file mode 100644 index 36e7edc..0000000 --- a/drivers/staging/gma500/mrst_hdmi_i2c.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Li Peng - */ - -#include -#include -#include -#include -#include -#include -#include "psb_drv.h" - -#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg)) -#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg)) - -#define HDMI_HCR 0x1000 -#define HCR_DETECT_HDP (1 << 6) -#define HCR_ENABLE_HDCP (1 << 5) -#define HCR_ENABLE_AUDIO (1 << 2) -#define HCR_ENABLE_PIXEL (1 << 1) -#define HCR_ENABLE_TMDS (1 << 0) -#define HDMI_HICR 0x1004 -#define HDMI_INTR_I2C_ERROR (1 << 4) -#define HDMI_INTR_I2C_FULL (1 << 3) -#define HDMI_INTR_I2C_DONE (1 << 2) -#define HDMI_INTR_HPD (1 << 0) -#define HDMI_HSR 0x1008 -#define HDMI_HISR 0x100C -#define HDMI_HI2CRDB0 0x1200 -#define HDMI_HI2CHCR 0x1240 -#define HI2C_HDCP_WRITE (0 << 2) -#define HI2C_HDCP_RI_READ (1 << 2) -#define HI2C_HDCP_READ (2 << 2) -#define HI2C_EDID_READ (3 << 2) -#define HI2C_READ_CONTINUE (1 << 1) -#define HI2C_ENABLE_TRANSACTION (1 << 0) - -#define HDMI_ICRH 0x1100 -#define HDMI_HI2CTDR0 0x1244 -#define HDMI_HI2CTDR1 0x1248 - -#define I2C_STAT_INIT 0 -#define I2C_READ_DONE 1 -#define I2C_TRANSACTION_DONE 2 - -struct hdmi_i2c_dev { - struct i2c_adapter *adap; - struct mutex i2c_lock; - struct completion complete; - int status; - struct i2c_msg *msg; - int buf_offset; -}; - -static void hdmi_i2c_irq_enable(struct mrst_hdmi_dev *hdmi_dev) -{ - u32 temp; - - temp = HDMI_READ(HDMI_HICR); - temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE); - HDMI_WRITE(HDMI_HICR, temp); - HDMI_READ(HDMI_HICR); -} - -static void hdmi_i2c_irq_disable(struct mrst_hdmi_dev *hdmi_dev) -{ - HDMI_WRITE(HDMI_HICR, 0x0); - HDMI_READ(HDMI_HICR); -} - -static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) -{ - struct mrst_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); - struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - u32 temp; - - i2c_dev->status = I2C_STAT_INIT; - i2c_dev->msg = pmsg; - i2c_dev->buf_offset = 0; - INIT_COMPLETION(i2c_dev->complete); - - /* Enable I2C transaction */ - temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION; - HDMI_WRITE(HDMI_HI2CHCR, temp); - HDMI_READ(HDMI_HI2CHCR); - - while (i2c_dev->status != I2C_TRANSACTION_DONE) - wait_for_completion_interruptible_timeout(&i2c_dev->complete, - 10 * HZ); - - return 0; -} - -static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg) -{ - /* - * XXX: i2c write seems isn't useful for EDID probe, don't do anything - */ - return 0; -} - -static int mrst_hdmi_i2c_access(struct i2c_adapter *adap, - struct i2c_msg *pmsg, - int num) -{ - struct mrst_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); - struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - int i, err = 0; - - mutex_lock(&i2c_dev->i2c_lock); - - /* Enable i2c unit */ - HDMI_WRITE(HDMI_ICRH, 0x00008760); - - /* Enable irq */ - hdmi_i2c_irq_enable(hdmi_dev); - for (i = 0; i < num; i++) { - if (pmsg->len && pmsg->buf) { - if (pmsg->flags & I2C_M_RD) - err = xfer_read(adap, pmsg); - else - err = xfer_write(adap, pmsg); - } - pmsg++; /* next message */ - } - - /* Disable irq */ - hdmi_i2c_irq_disable(hdmi_dev); - - mutex_unlock(&i2c_dev->i2c_lock); - - return i; -} - -static u32 mrst_hdmi_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; -} - -static const struct i2c_algorithm mrst_hdmi_i2c_algorithm = { - .master_xfer = mrst_hdmi_i2c_access, - .functionality = mrst_hdmi_i2c_func, -}; - -static struct i2c_adapter mrst_hdmi_i2c_adapter = { - .name = "mrst_hdmi_i2c", - .nr = 3, - .owner = THIS_MODULE, - .class = I2C_CLASS_DDC, - .algo = &mrst_hdmi_i2c_algorithm, -}; - -static void hdmi_i2c_read(struct mrst_hdmi_dev *hdmi_dev) -{ - struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - struct i2c_msg *msg = i2c_dev->msg; - u8 *buf = msg->buf; - u32 temp; - int i, offset; - - offset = i2c_dev->buf_offset; - for (i = 0; i < 0x10; i++) { - temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4)); - memcpy(buf + (offset + i * 4), &temp, 4); - } - i2c_dev->buf_offset += (0x10 * 4); - - /* clearing read buffer full intr */ - temp = HDMI_READ(HDMI_HISR); - HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL); - HDMI_READ(HDMI_HISR); - - /* continue read transaction */ - temp = HDMI_READ(HDMI_HI2CHCR); - HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE); - HDMI_READ(HDMI_HI2CHCR); - - i2c_dev->status = I2C_READ_DONE; - return; -} - -static void hdmi_i2c_transaction_done(struct mrst_hdmi_dev *hdmi_dev) -{ - struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - u32 temp; - - /* clear transaction done intr */ - temp = HDMI_READ(HDMI_HISR); - HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE); - HDMI_READ(HDMI_HISR); - - - temp = HDMI_READ(HDMI_HI2CHCR); - HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION); - HDMI_READ(HDMI_HI2CHCR); - - i2c_dev->status = I2C_TRANSACTION_DONE; - return; -} - -static irqreturn_t mrst_hdmi_i2c_handler(int this_irq, void *dev) -{ - struct mrst_hdmi_dev *hdmi_dev = dev; - struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - u32 stat; - - stat = HDMI_READ(HDMI_HISR); - - if (stat & HDMI_INTR_HPD) { - HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD); - HDMI_READ(HDMI_HISR); - } - - if (stat & HDMI_INTR_I2C_FULL) - hdmi_i2c_read(hdmi_dev); - - if (stat & HDMI_INTR_I2C_DONE) - hdmi_i2c_transaction_done(hdmi_dev); - - complete(&i2c_dev->complete); - - return IRQ_HANDLED; -} - -/* - * choose alternate function 2 of GPIO pin 52, 53, - * which is used by HDMI I2C logic - */ -static void mrst_hdmi_i2c_gpio_fix(void) -{ - void *base; - unsigned int gpio_base = 0xff12c000; - int gpio_len = 0x1000; - u32 temp; - - base = ioremap((resource_size_t)gpio_base, gpio_len); - if (base == NULL) { - DRM_ERROR("gpio ioremap fail\n"); - return; - } - - temp = readl(base + 0x44); - DRM_DEBUG_DRIVER("old gpio val %x\n", temp); - writel((temp | 0x00000a00), (base + 0x44)); - temp = readl(base + 0x44); - DRM_DEBUG_DRIVER("new gpio val %x\n", temp); - - iounmap(base); -} - -int mrst_hdmi_i2c_init(struct pci_dev *dev) -{ - struct mrst_hdmi_dev *hdmi_dev; - struct hdmi_i2c_dev *i2c_dev; - int ret; - - hdmi_dev = pci_get_drvdata(dev); - - i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL); - if (i2c_dev == NULL) { - DRM_ERROR("Can't allocate interface\n"); - ret = -ENOMEM; - goto exit; - } - - i2c_dev->adap = &mrst_hdmi_i2c_adapter; - i2c_dev->status = I2C_STAT_INIT; - init_completion(&i2c_dev->complete); - mutex_init(&i2c_dev->i2c_lock); - i2c_set_adapdata(&mrst_hdmi_i2c_adapter, hdmi_dev); - hdmi_dev->i2c_dev = i2c_dev; - - /* Enable HDMI I2C function on gpio */ - mrst_hdmi_i2c_gpio_fix(); - - /* request irq */ - ret = request_irq(dev->irq, mrst_hdmi_i2c_handler, IRQF_SHARED, - mrst_hdmi_i2c_adapter.name, hdmi_dev); - if (ret) { - DRM_ERROR("Failed to request IRQ for I2C controller\n"); - goto err; - } - - /* Adapter registration */ - ret = i2c_add_numbered_adapter(&mrst_hdmi_i2c_adapter); - return ret; - -err: - kfree(i2c_dev); -exit: - return ret; -} - -void mrst_hdmi_i2c_exit(struct pci_dev *dev) -{ - struct mrst_hdmi_dev *hdmi_dev; - struct hdmi_i2c_dev *i2c_dev; - - hdmi_dev = pci_get_drvdata(dev); - if (i2c_del_adapter(&mrst_hdmi_i2c_adapter)) - DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n"); - - i2c_dev = hdmi_dev->i2c_dev; - kfree(i2c_dev); - free_irq(dev->irq, hdmi_dev); -} diff --git a/drivers/staging/gma500/mrst_lvds.c b/drivers/staging/gma500/mrst_lvds.c deleted file mode 100644 index e7999a2..0000000 --- a/drivers/staging/gma500/mrst_lvds.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright © 2006-2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - * Dave Airlie - * Jesse Barnes - */ - -#include -#include -#include - -#include "intel_bios.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "power.h" -#include - -/* The max/min PWM frequency in BPCR[31:17] - */ -/* The smallest number is 1 (not 0) that can fit in the - * 15-bit field of the and then*/ -/* shifts to the left by one bit to get the actual 16-bit - * value that the 15-bits correspond to.*/ -#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF -#define BRIGHTNESS_MAX_LEVEL 100 - -/** - * Sets the power state for the panel. - */ -static void mrst_lvds_set_power(struct drm_device *dev, - struct psb_intel_output *output, bool on) -{ - u32 pp_status; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!gma_power_begin(dev, true)) - return; - - if (on) { - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | - POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); - dev_priv->is_lvds_on = true; - if (dev_priv->ops->lvds_bl_power) - dev_priv->ops->lvds_bl_power(dev, true); - } else { - if (dev_priv->ops->lvds_bl_power) - dev_priv->ops->lvds_bl_power(dev, false); - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & - ~POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while (pp_status & PP_ON); - dev_priv->is_lvds_on = false; - pm_request_idle(&dev->pdev->dev); - } - gma_power_end(dev); -} - -static void mrst_lvds_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - - if (mode == DRM_MODE_DPMS_ON) - mrst_lvds_set_power(dev, output, true); - else - mrst_lvds_set_power(dev, output, false); - - /* XXX: We never power down the LVDS pairs. */ -} - -static void mrst_lvds_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct psb_intel_mode_device *mode_dev = - enc_to_psb_intel_output(encoder)->mode_dev; - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 lvds_port; - uint64_t v = DRM_MODE_SCALE_FULLSCREEN; - - if (!gma_power_begin(dev, true)) - return; - - /* - * The LVDS pin pair will already have been turned on in the - * psb_intel_crtc_mode_set since it has a large impact on the DPLL - * settings. - */ - lvds_port = (REG_READ(LVDS) & - (~LVDS_PIPEB_SELECT)) | - LVDS_PORT_EN | - LVDS_BORDER_EN; - - /* If the firmware says dither on Moorestown, or the BIOS does - on Oaktrail then enable dithering */ - if (mode_dev->panel_wants_dither || dev_priv->lvds_dither) - lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE; - - REG_WRITE(LVDS, lvds_port); - - drm_connector_property_get_value( - &enc_to_psb_intel_output(encoder)->base, - dev->mode_config.scaling_mode_property, - &v); - - if (v == DRM_MODE_SCALE_NO_SCALE) - REG_WRITE(PFIT_CONTROL, 0); - else if (v == DRM_MODE_SCALE_ASPECT) { - if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) || - (mode->hdisplay != adjusted_mode->crtc_hdisplay)) { - if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) == - (mode->hdisplay * adjusted_mode->crtc_vdisplay)) - REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); - else if ((adjusted_mode->crtc_hdisplay * - mode->vdisplay) > (mode->hdisplay * - adjusted_mode->crtc_vdisplay)) - REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | - PFIT_SCALING_MODE_PILLARBOX); - else - REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | - PFIT_SCALING_MODE_LETTERBOX); - } else - REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); - } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/ - REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); - - gma_power_end(dev); -} - -static void mrst_lvds_prepare(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_mode_device *mode_dev = output->mode_dev; - - if (!gma_power_begin(dev, true)) - return; - - mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); - mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); - mrst_lvds_set_power(dev, output, false); - gma_power_end(dev); -} - -static u32 mrst_lvds_get_max_backlight(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 ret; - - if (gma_power_begin(dev, false)) { - ret = ((REG_READ(BLC_PWM_CTL) & - BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; - - gma_power_end(dev); - } else - ret = ((dev_priv->saveBLC_PWM_CTL & - BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; - - return ret; -} - -static void mrst_lvds_commit(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_mode_device *mode_dev = output->mode_dev; - - if (mode_dev->backlight_duty_cycle == 0) - mode_dev->backlight_duty_cycle = - mrst_lvds_get_max_backlight(dev); - mrst_lvds_set_power(dev, output, true); -} - -static const struct drm_encoder_helper_funcs mrst_lvds_helper_funcs = { - .dpms = mrst_lvds_dpms, - .mode_fixup = psb_intel_lvds_mode_fixup, - .prepare = mrst_lvds_prepare, - .mode_set = mrst_lvds_mode_set, - .commit = mrst_lvds_commit, -}; - -static struct drm_display_mode lvds_configuration_modes[] = { - /* hard coded fixed mode for TPO LTPS LPJ040K001A */ - { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, - 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, - /* hard coded fixed mode for LVDS 800x480 */ - { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, - 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, - /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ - { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, - 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, - /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ - { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, - 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, - /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ - { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, - 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, - /* hard coded fixed mode for LVDS 1024x768 */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, - /* hard coded fixed mode for LVDS 1366x768 */ - { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, - 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, -}; - -/* Returns the panel fixed mode from configuration. */ - -static struct drm_display_mode * -mrst_lvds_get_configuration_mode(struct drm_device *dev) -{ - struct drm_display_mode *mode = NULL; - struct drm_psb_private *dev_priv = dev->dev_private; - struct mrst_timing_info *ti = &dev_priv->gct_data.DTD; - - if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/ - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) - return NULL; - - mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; - mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; - mode->hsync_start = mode->hdisplay + \ - ((ti->hsync_offset_hi << 8) | \ - ti->hsync_offset_lo); - mode->hsync_end = mode->hsync_start + \ - ((ti->hsync_pulse_width_hi << 8) | \ - ti->hsync_pulse_width_lo); - mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ - ti->hblank_lo); - mode->vsync_start = \ - mode->vdisplay + ((ti->vsync_offset_hi << 4) | \ - ti->vsync_offset_lo); - mode->vsync_end = \ - mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \ - ti->vsync_pulse_width_lo); - mode->vtotal = mode->vdisplay + \ - ((ti->vblank_hi << 8) | ti->vblank_lo); - mode->clock = ti->pixel_clock * 10; -#if 0 - printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay); - printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay); - printk(KERN_INFO "HSS is %d\n", mode->hsync_start); - printk(KERN_INFO "HSE is %d\n", mode->hsync_end); - printk(KERN_INFO "htotal is %d\n", mode->htotal); - printk(KERN_INFO "VSS is %d\n", mode->vsync_start); - printk(KERN_INFO "VSE is %d\n", mode->vsync_end); - printk(KERN_INFO "vtotal is %d\n", mode->vtotal); - printk(KERN_INFO "clock is %d\n", mode->clock); -#endif - } else - mode = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - return mode; -} - -/** - * mrst_lvds_init - setup LVDS connectors on this device - * @dev: drm device - * - * Create the connector, register the LVDS DDC bus, and try to figure out what - * modes we can display on the LVDS panel (if present). - */ -void mrst_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev) -{ - struct psb_intel_output *psb_intel_output; - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - struct edid *edid; - int ret = 0; - struct i2c_adapter *i2c_adap; - struct drm_display_mode *scan; /* *modes, *bios_mode; */ - - psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); - if (!psb_intel_output) - return; - - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - dev_priv->is_lvds_on = true; - drm_connector_init(dev, &psb_intel_output->base, - &psb_intel_lvds_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - - drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_lvds_enc_funcs, - DRM_MODE_ENCODER_LVDS); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - psb_intel_output->type = INTEL_OUTPUT_LVDS; - - drm_encoder_helper_add(encoder, &mrst_lvds_helper_funcs); - drm_connector_helper_add(connector, - &psb_intel_lvds_connector_helper_funcs); - connector->display_info.subpixel_order = SubPixelHorizontalRGB; - connector->interlace_allowed = false; - connector->doublescan_allowed = false; - - drm_connector_attach_property(connector, - dev->mode_config.scaling_mode_property, - DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, - dev_priv->backlight_property, - BRIGHTNESS_MAX_LEVEL); - - mode_dev->panel_wants_dither = false; - if (dev_priv->vbt_data.size != 0x00) - mode_dev->panel_wants_dither = (dev_priv->gct_data. - Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); - - /* - * LVDS discovery: - * 1) check for EDID on DDC - * 2) check for VBT data - * 3) check to see if LVDS is already on - * if none of the above, no panel - * 4) make sure lid is open - * if closed, act like it's not there for now - */ - - i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); - - if (i2c_adap == NULL) - dev_err(dev->dev, "No ddc adapter available!\n"); - /* - * Attempt to get the fixed panel mode from DDC. Assume that the - * preferred mode is the right one. - */ - if (i2c_adap) { - edid = drm_get_edid(connector, i2c_adap); - if (edid) { - drm_mode_connector_update_edid_property(connector, - edid); - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - } - - list_for_each_entry(scan, &connector->probed_modes, head) { - if (scan->type & DRM_MODE_TYPE_PREFERRED) { - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, scan); - goto out; /* FIXME: check for quirks */ - } - } - } - /* - * If we didn't get EDID, try geting panel timing - * from configuration data - */ - mode_dev->panel_fixed_mode = mrst_lvds_get_configuration_mode(dev); - - if (mode_dev->panel_fixed_mode) { - mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - goto out; /* FIXME: check for quirks */ - } - - /* If we still don't have a mode after all that, give up. */ - if (!mode_dev->panel_fixed_mode) { - dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); - goto failed_find; - } - -out: - drm_sysfs_connector_add(connector); - return; - -failed_find: - dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); - if (psb_intel_output->ddc_bus) - psb_intel_i2c_destroy(psb_intel_output->ddc_bus); - -/* failed_ddc: */ - - drm_encoder_cleanup(encoder); - drm_connector_cleanup(connector); - kfree(connector); -} - diff --git a/drivers/staging/gma500/power.c b/drivers/staging/gma500/power.c deleted file mode 100644 index 4082570..0000000 --- a/drivers/staging/gma500/power.c +++ /dev/null @@ -1,318 +0,0 @@ -/************************************************************************** - * Copyright (c) 2009-2011, Intel Corporation. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Benjamin Defnet - * Rajesh Poornachandran - * Massively reworked - * Alan Cox - */ - -#include "power.h" -#include "psb_drv.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include -#include - -static struct mutex power_mutex; /* Serialize power ops */ -static spinlock_t power_ctrl_lock; /* Serialize power claim */ - -/** - * gma_power_init - initialise power manager - * @dev: our device - * - * Set up for power management tracking of our hardware. - */ -void gma_power_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - /* FIXME: Move APM/OSPM base into relevant device code */ - dev_priv->apm_base = dev_priv->apm_reg & 0xffff; - dev_priv->ospm_base &= 0xffff; - - dev_priv->display_power = true; /* We start active */ - dev_priv->display_count = 0; /* Currently no users */ - dev_priv->suspended = false; /* And not suspended */ - spin_lock_init(&power_ctrl_lock); - mutex_init(&power_mutex); - - dev_priv->ops->init_pm(dev); -} - -/** - * gma_power_uninit - end power manager - * @dev: device to end for - * - * Undo the effects of gma_power_init - */ -void gma_power_uninit(struct drm_device *dev) -{ - pm_runtime_disable(&dev->pdev->dev); - pm_runtime_set_suspended(&dev->pdev->dev); -} - -/** - * gma_suspend_display - suspend the display logic - * @dev: our DRM device - * - * Suspend the display logic of the graphics interface - */ -static void gma_suspend_display(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!dev_priv->display_power) - return; - dev_priv->ops->save_regs(dev); - dev_priv->ops->power_down(dev); - dev_priv->display_power = false; -} - -/** - * gma_resume_display - resume display side logic - * - * Resume the display hardware restoring state and enabling - * as necessary. - */ -static void gma_resume_display(struct pci_dev *pdev) -{ - struct drm_device *dev = pci_get_drvdata(pdev); - struct drm_psb_private *dev_priv = dev->dev_private; - - if (dev_priv->display_power) - return; - - /* turn on the display power island */ - dev_priv->ops->power_up(dev); - dev_priv->suspended = false; - dev_priv->display_power = true; - - PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); - pci_write_config_word(pdev, PSB_GMCH_CTRL, - dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); - dev_priv->ops->restore_regs(dev); -} - -/** - * gma_suspend_pci - suspend PCI side - * @pdev: PCI device - * - * Perform the suspend processing on our PCI device state - */ -static void gma_suspend_pci(struct pci_dev *pdev) -{ - struct drm_device *dev = pci_get_drvdata(pdev); - struct drm_psb_private *dev_priv = dev->dev_private; - int bsm, vbt; - - if (dev_priv->suspended) - return; - - pci_save_state(pdev); - pci_read_config_dword(pdev, 0x5C, &bsm); - dev_priv->saveBSM = bsm; - pci_read_config_dword(pdev, 0xFC, &vbt); - dev_priv->saveVBT = vbt; - pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr); - pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data); - - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - - dev_priv->suspended = true; -} - -/** - * gma_resume_pci - resume helper - * @dev: our PCI device - * - * Perform the resume processing on our PCI device state - rewrite - * register state and re-enable the PCI device - */ -static bool gma_resume_pci(struct pci_dev *pdev) -{ - struct drm_device *dev = pci_get_drvdata(pdev); - struct drm_psb_private *dev_priv = dev->dev_private; - int ret; - - if (!dev_priv->suspended) - return true; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM); - pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT); - /* restoring MSI address and data in PCIx space */ - pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr); - pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data); - ret = pci_enable_device(pdev); - - if (ret != 0) - dev_err(&pdev->dev, "pci_enable failed: %d\n", ret); - else - dev_priv->suspended = false; - return !dev_priv->suspended; -} - -/** - * gma_power_suspend - bus callback for suspend - * @pdev: our PCI device - * @state: suspend type - * - * Called back by the PCI layer during a suspend of the system. We - * perform the necessary shut down steps and save enough state that - * we can undo this when resume is called. - */ -int gma_power_suspend(struct device *_dev) -{ - struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev); - struct drm_device *dev = pci_get_drvdata(pdev); - struct drm_psb_private *dev_priv = dev->dev_private; - - mutex_lock(&power_mutex); - if (!dev_priv->suspended) { - if (dev_priv->display_count) { - mutex_unlock(&power_mutex); - return -EBUSY; - } - psb_irq_uninstall(dev); - gma_suspend_display(dev); - gma_suspend_pci(pdev); - } - mutex_unlock(&power_mutex); - return 0; -} - -/** - * gma_power_resume - resume power - * @pdev: PCI device - * - * Resume the PCI side of the graphics and then the displays - */ -int gma_power_resume(struct device *_dev) -{ - struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev); - struct drm_device *dev = pci_get_drvdata(pdev); - - mutex_lock(&power_mutex); - gma_resume_pci(pdev); - gma_resume_display(pdev); - psb_irq_preinstall(dev); - psb_irq_postinstall(dev); - mutex_unlock(&power_mutex); - return 0; -} - -/** - * gma_power_is_on - returne true if power is on - * @dev: our DRM device - * - * Returns true if the display island power is on at this moment - */ -bool gma_power_is_on(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - return dev_priv->display_power; -} - -/** - * gma_power_begin - begin requiring power - * @dev: our DRM device - * @force_on: true to force power on - * - * Begin an action that requires the display power island is enabled. - * We refcount the islands. - */ -bool gma_power_begin(struct drm_device *dev, bool force_on) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - int ret; - unsigned long flags; - - spin_lock_irqsave(&power_ctrl_lock, flags); - /* Power already on ? */ - if (dev_priv->display_power) { - dev_priv->display_count++; - pm_runtime_get(&dev->pdev->dev); - spin_unlock_irqrestore(&power_ctrl_lock, flags); - return true; - } - if (force_on == false) - goto out_false; - - /* Ok power up needed */ - ret = gma_resume_pci(dev->pdev); - if (ret == 0) { - /* FIXME: we want to defer this for Medfield/Oaktrail */ - gma_resume_display(dev->pdev); - psb_irq_preinstall(dev); - psb_irq_postinstall(dev); - pm_runtime_get(&dev->pdev->dev); - dev_priv->display_count++; - spin_unlock_irqrestore(&power_ctrl_lock, flags); - return true; - } -out_false: - spin_unlock_irqrestore(&power_ctrl_lock, flags); - return false; -} - -/** - * gma_power_end - end use of power - * @dev: Our DRM device - * - * Indicate that one of our gma_power_begin() requested periods when - * the diplay island power is needed has completed. - */ -void gma_power_end(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long flags; - spin_lock_irqsave(&power_ctrl_lock, flags); - dev_priv->display_count--; - WARN_ON(dev_priv->display_count < 0); - spin_unlock_irqrestore(&power_ctrl_lock, flags); - pm_runtime_put(&dev->pdev->dev); -} - -int psb_runtime_suspend(struct device *dev) -{ - return gma_power_suspend(dev); -} - -int psb_runtime_resume(struct device *dev) -{ - return gma_power_resume(dev);; -} - -int psb_runtime_idle(struct device *dev) -{ - struct drm_device *drmdev = pci_get_drvdata(to_pci_dev(dev)); - struct drm_psb_private *dev_priv = drmdev->dev_private; - if (dev_priv->display_count) - return 0; - else - return 1; -} diff --git a/drivers/staging/gma500/power.h b/drivers/staging/gma500/power.h deleted file mode 100644 index 1969d2e..0000000 --- a/drivers/staging/gma500/power.h +++ /dev/null @@ -1,67 +0,0 @@ -/************************************************************************** - * Copyright (c) 2009-2011, Intel Corporation. - * All Rights Reserved. - - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Benjamin Defnet - * Rajesh Poornachandran - * Massively reworked - * Alan Cox - */ -#ifndef _PSB_POWERMGMT_H_ -#define _PSB_POWERMGMT_H_ - -#include -#include - -void gma_power_init(struct drm_device *dev); -void gma_power_uninit(struct drm_device *dev); - -/* - * The kernel bus power management will call these functions - */ -int gma_power_suspend(struct device *dev); -int gma_power_resume(struct device *dev); - -/* - * These are the functions the driver should use to wrap all hw access - * (i.e. register reads and writes) - */ -bool gma_power_begin(struct drm_device *dev, bool force); -void gma_power_end(struct drm_device *dev); - -/* - * Use this function to do an instantaneous check for if the hw is on. - * Only use this in cases where you know the mutex is already held such - * as in irq install/uninstall and you need to - * prevent a deadlock situation. Otherwise use gma_power_begin(). - */ -bool gma_power_is_on(struct drm_device *dev); - -/* - * GFX-Runtime PM callbacks - */ -int psb_runtime_suspend(struct device *dev); -int psb_runtime_resume(struct device *dev); -int psb_runtime_idle(struct device *dev); - -#endif /*_PSB_POWERMGMT_H_*/ diff --git a/drivers/staging/gma500/psb_device.c b/drivers/staging/gma500/psb_device.c deleted file mode 100644 index b97aa78..0000000 --- a/drivers/staging/gma500/psb_device.c +++ /dev/null @@ -1,321 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include "intel_bios.h" - - -static int psb_output_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - psb_intel_lvds_init(dev, &dev_priv->mode_dev); - psb_intel_sdvo_init(dev, SDVOB); - return 0; -} - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - -/* - * Poulsbo Backlight Interfaces - */ - -#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ -#define BLC_PWM_FREQ_CALC_CONSTANT 32 -#define MHz 1000000 - -#define PSB_BLC_PWM_PRECISION_FACTOR 10 -#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE -#define PSB_BLC_MIN_PWM_REG_FREQ 0x2 - -#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) -#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) - -static int psb_brightness; -static struct backlight_device *psb_backlight_device; - -static int psb_get_brightness(struct backlight_device *bd) -{ - /* return locally cached var instead of HW read (due to DPST etc.) */ - /* FIXME: ideally return actual value in case firmware fiddled with - it */ - return psb_brightness; -} - - -static int psb_backlight_setup(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long core_clock; - /* u32 bl_max_freq; */ - /* unsigned long value; */ - u16 bl_max_freq; - uint32_t value; - uint32_t blc_pwm_precision_factor; - - /* get bl_max_freq and pol from dev_priv*/ - if (!dev_priv->lvds_bl) { - dev_err(dev->dev, "Has no valid LVDS backlight info\n"); - return -ENOENT; - } - bl_max_freq = dev_priv->lvds_bl->freq; - blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; - - core_clock = dev_priv->core_freq; - - value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; - value *= blc_pwm_precision_factor; - value /= bl_max_freq; - value /= blc_pwm_precision_factor; - - if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || - value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) - return -ERANGE; - else { - value &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; - REG_WRITE(BLC_PWM_CTL, - (value << PSB_BACKLIGHT_PWM_CTL_SHIFT) | (value)); - } - return 0; -} - -static int psb_set_brightness(struct backlight_device *bd) -{ - struct drm_device *dev = bl_get_data(psb_backlight_device); - int level = bd->props.brightness; - - /* Percentage 1-100% being valid */ - if (level < 1) - level = 1; - - psb_intel_lvds_set_brightness(dev, level); - psb_brightness = level; - return 0; -} - -static const struct backlight_ops psb_ops = { - .get_brightness = psb_get_brightness, - .update_status = psb_set_brightness, -}; - -static int psb_backlight_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - int ret; - struct backlight_properties props; - - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 100; - props.type = BACKLIGHT_PLATFORM; - - psb_backlight_device = backlight_device_register("psb-bl", - NULL, (void *)dev, &psb_ops, &props); - if (IS_ERR(psb_backlight_device)) - return PTR_ERR(psb_backlight_device); - - ret = psb_backlight_setup(dev); - if (ret < 0) { - backlight_device_unregister(psb_backlight_device); - psb_backlight_device = NULL; - return ret; - } - psb_backlight_device->props.brightness = 100; - psb_backlight_device->props.max_brightness = 100; - backlight_update_status(psb_backlight_device); - dev_priv->backlight_device = psb_backlight_device; - return 0; -} - -#endif - -/* - * Provide the Poulsbo specific chip logic and low level methods - * for power management - */ - -static void psb_init_pm(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL); - gating &= ~3; /* Disable 2D clock gating */ - gating |= 1; - PSB_WSGX32(gating, PSB_CR_CLKGATECTL); - PSB_RSGX32(PSB_CR_CLKGATECTL); -} - -/** - * psb_save_display_registers - save registers lost on suspend - * @dev: our DRM device - * - * Save the state we need in order to be able to restore the interface - * upon resume from suspend - */ -static int psb_save_display_registers(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc; - struct drm_connector *connector; - - /* Display arbitration control + watermarks */ - dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); - dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); - dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); - dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); - dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); - dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); - dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); - dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); - - /* Save crtc and output state */ - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (drm_helper_crtc_in_use(crtc)) - crtc->funcs->save(crtc); - } - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->save(connector); - - mutex_unlock(&dev->mode_config.mutex); - return 0; -} - -/** - * psb_restore_display_registers - restore lost register state - * @dev: our DRM device - * - * Restore register state that was lost during suspend and resume. - */ -static int psb_restore_display_registers(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc; - struct drm_connector *connector; - - /* Display arbitration + watermarks */ - PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); - PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); - PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); - PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); - PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); - PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); - PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); - PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); - - /*make sure VGA plane is off. it initializes to on after reset!*/ - PSB_WVDC32(0x80000000, VGACNTRL); - - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - if (drm_helper_crtc_in_use(crtc)) - crtc->funcs->restore(crtc); - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->restore(connector); - - mutex_unlock(&dev->mode_config.mutex); - return 0; -} - -static int psb_power_down(struct drm_device *dev) -{ - return 0; -} - -static int psb_power_up(struct drm_device *dev) -{ - return 0; -} - -static void psb_get_core_freq(struct drm_device *dev) -{ - uint32_t clock; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - struct drm_psb_private *dev_priv = dev->dev_private; - - /*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/ - /*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/ - - pci_write_config_dword(pci_root, 0xD0, 0xD0050300); - pci_read_config_dword(pci_root, 0xD4, &clock); - pci_dev_put(pci_root); - - switch (clock & 0x07) { - case 0: - dev_priv->core_freq = 100; - break; - case 1: - dev_priv->core_freq = 133; - break; - case 2: - dev_priv->core_freq = 150; - break; - case 3: - dev_priv->core_freq = 178; - break; - case 4: - dev_priv->core_freq = 200; - break; - case 5: - case 6: - case 7: - dev_priv->core_freq = 266; - default: - dev_priv->core_freq = 0; - } -} - -static int psb_chip_setup(struct drm_device *dev) -{ - psb_get_core_freq(dev); - gma_intel_opregion_init(dev); - psb_intel_init_bios(dev); - return 0; -} - -const struct psb_ops psb_chip_ops = { - .name = "Poulsbo", - .accel_2d = 1, - .pipes = 2, - .crtcs = 2, - .sgx_offset = PSB_SGX_OFFSET, - .chip_setup = psb_chip_setup, - - .crtc_helper = &psb_intel_helper_funcs, - .crtc_funcs = &psb_intel_crtc_funcs, - - .output_init = psb_output_init, - -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - .backlight_init = psb_backlight_init, -#endif - - .init_pm = psb_init_pm, - .save_regs = psb_save_display_registers, - .restore_regs = psb_restore_display_registers, - .power_down = psb_power_down, - .power_up = psb_power_up, -}; - diff --git a/drivers/staging/gma500/psb_drm.h b/drivers/staging/gma500/psb_drm.h deleted file mode 100644 index 0da8468..0000000 --- a/drivers/staging/gma500/psb_drm.h +++ /dev/null @@ -1,219 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * Copyright (c) 2008, Tungsten Graphics Inc. Cedar Park, TX., USA. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#ifndef _PSB_DRM_H_ -#define _PSB_DRM_H_ - -#define PSB_NUM_PIPE 3 - -#define PSB_GPU_ACCESS_READ (1ULL << 32) -#define PSB_GPU_ACCESS_WRITE (1ULL << 33) -#define PSB_GPU_ACCESS_MASK (PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE) - -#define PSB_BO_FLAG_COMMAND (1ULL << 52) - -/* - * Feedback components: - */ - -struct drm_psb_sizes_arg { - u32 ta_mem_size; - u32 mmu_size; - u32 pds_size; - u32 rastgeom_size; - u32 tt_size; - u32 vram_size; -}; - -struct drm_psb_dpst_lut_arg { - uint8_t lut[256]; - int output_id; -}; - -#define PSB_DC_CRTC_SAVE 0x01 -#define PSB_DC_CRTC_RESTORE 0x02 -#define PSB_DC_OUTPUT_SAVE 0x04 -#define PSB_DC_OUTPUT_RESTORE 0x08 -#define PSB_DC_CRTC_MASK 0x03 -#define PSB_DC_OUTPUT_MASK 0x0C - -struct drm_psb_dc_state_arg { - u32 flags; - u32 obj_id; -}; - -struct drm_psb_mode_operation_arg { - u32 obj_id; - u16 operation; - struct drm_mode_modeinfo mode; - void *data; -}; - -struct drm_psb_stolen_memory_arg { - u32 base; - u32 size; -}; - -/*Display Register Bits*/ -#define REGRWBITS_PFIT_CONTROLS (1 << 0) -#define REGRWBITS_PFIT_AUTOSCALE_RATIOS (1 << 1) -#define REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS (1 << 2) -#define REGRWBITS_PIPEASRC (1 << 3) -#define REGRWBITS_PIPEBSRC (1 << 4) -#define REGRWBITS_VTOTAL_A (1 << 5) -#define REGRWBITS_VTOTAL_B (1 << 6) -#define REGRWBITS_DSPACNTR (1 << 8) -#define REGRWBITS_DSPBCNTR (1 << 9) -#define REGRWBITS_DSPCCNTR (1 << 10) - -/*Overlay Register Bits*/ -#define OV_REGRWBITS_OVADD (1 << 0) -#define OV_REGRWBITS_OGAM_ALL (1 << 1) - -#define OVC_REGRWBITS_OVADD (1 << 2) -#define OVC_REGRWBITS_OGAM_ALL (1 << 3) - -struct drm_psb_register_rw_arg { - u32 b_force_hw_on; - - u32 display_read_mask; - u32 display_write_mask; - - struct { - u32 pfit_controls; - u32 pfit_autoscale_ratios; - u32 pfit_programmed_scale_ratios; - u32 pipeasrc; - u32 pipebsrc; - u32 vtotal_a; - u32 vtotal_b; - } display; - - u32 overlay_read_mask; - u32 overlay_write_mask; - - struct { - u32 OVADD; - u32 OGAMC0; - u32 OGAMC1; - u32 OGAMC2; - u32 OGAMC3; - u32 OGAMC4; - u32 OGAMC5; - u32 IEP_ENABLED; - u32 IEP_BLE_MINMAX; - u32 IEP_BSSCC_CONTROL; - u32 b_wait_vblank; - } overlay; - - u32 sprite_enable_mask; - u32 sprite_disable_mask; - - struct { - u32 dspa_control; - u32 dspa_key_value; - u32 dspa_key_mask; - u32 dspc_control; - u32 dspc_stride; - u32 dspc_position; - u32 dspc_linear_offset; - u32 dspc_size; - u32 dspc_surface; - } sprite; - - u32 subpicture_enable_mask; - u32 subpicture_disable_mask; -}; - -/* Controlling the kernel modesetting buffers */ - -#define DRM_PSB_SIZES 0x07 -#define DRM_PSB_FUSE_REG 0x08 -#define DRM_PSB_DC_STATE 0x0A -#define DRM_PSB_ADB 0x0B -#define DRM_PSB_MODE_OPERATION 0x0C -#define DRM_PSB_STOLEN_MEMORY 0x0D -#define DRM_PSB_REGISTER_RW 0x0E - -/* - * NOTE: Add new commands here, but increment - * the values below and increment their - * corresponding defines where they're - * defined elsewhere. - */ - -#define DRM_PSB_GEM_CREATE 0x10 -#define DRM_PSB_2D_OP 0x11 -#define DRM_PSB_GEM_MMAP 0x12 -#define DRM_PSB_DPST 0x1B -#define DRM_PSB_GAMMA 0x1C -#define DRM_PSB_DPST_BL 0x1D -#define DRM_PSB_GET_PIPE_FROM_CRTC_ID 0x1F - -#define PSB_MODE_OPERATION_MODE_VALID 0x01 -#define PSB_MODE_OPERATION_SET_DC_BASE 0x02 - -struct drm_psb_get_pipe_from_crtc_id_arg { - /** ID of CRTC being requested **/ - u32 crtc_id; - - /** pipe of requested CRTC **/ - u32 pipe; -}; - -/* FIXME: move this into a medfield header once we are sure it isn't needed for an - ioctl */ -struct psb_drm_dpu_rect { - int x, y; - int width, height; -}; - -struct drm_psb_gem_create { - __u64 size; - __u32 handle; - __u32 flags; -#define PSB_GEM_CREATE_STOLEN 1 /* Stolen memory can be used */ -}; - -#define PSB_2D_OP_BUFLEN 16 - -struct drm_psb_2d_op { - __u32 src; /* Handles, only src supported right now */ - __u32 dst; - __u32 mask; - __u32 pat; - __u32 size; /* In dwords of command */ - __u32 spare; /* And bumps array to u64 align */ - __u32 cmd[PSB_2D_OP_BUFLEN]; -}; - -struct drm_psb_gem_mmap { - __u32 handle; - __u32 pad; - /** - * Fake offset to use for subsequent mmap call - * - * This is a fixed-size type for 32/64 compatibility. - */ - __u64 offset; -}; - -#endif diff --git a/drivers/staging/gma500/psb_drv.c b/drivers/staging/gma500/psb_drv.c deleted file mode 100644 index 9581680..0000000 --- a/drivers/staging/gma500/psb_drv.c +++ /dev/null @@ -1,1230 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * Copyright (c) 2008, Tungsten Graphics, Inc. Cedar Park, TX., USA. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include "psb_drm.h" -#include "psb_drv.h" -#include "framebuffer.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include "intel_bios.h" -#include "mid_bios.h" -#include "mdfld_dsi_dbi.h" -#include -#include "power.h" -#include -#include -#include -#include -#include -#include - -static int drm_psb_trap_pagefaults; - -int drm_psb_no_fb; - -static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent); - -MODULE_PARM_DESC(no_fb, "Disable FBdev"); -MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults"); -module_param_named(no_fb, drm_psb_no_fb, int, 0600); -module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600); - - -static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { - { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, - { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, -#if defined(CONFIG_DRM_PSB_MRST) - { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, - { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mrst_chip_ops}, -#endif -#if defined(CONFIG_DRM_PSB_MFLD) - { 0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, - { 0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, -#endif -#if defined(CONFIG_DRM_PSB_CDV) - { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, -#endif - { 0, 0, 0} -}; -MODULE_DEVICE_TABLE(pci, pciidlist); - -/* - * Standard IOCTLs. - */ - -#define DRM_IOCTL_PSB_SIZES \ - DRM_IOR(DRM_PSB_SIZES + DRM_COMMAND_BASE, \ - struct drm_psb_sizes_arg) -#define DRM_IOCTL_PSB_FUSE_REG \ - DRM_IOWR(DRM_PSB_FUSE_REG + DRM_COMMAND_BASE, uint32_t) -#define DRM_IOCTL_PSB_DC_STATE \ - DRM_IOW(DRM_PSB_DC_STATE + DRM_COMMAND_BASE, \ - struct drm_psb_dc_state_arg) -#define DRM_IOCTL_PSB_ADB \ - DRM_IOWR(DRM_PSB_ADB + DRM_COMMAND_BASE, uint32_t) -#define DRM_IOCTL_PSB_MODE_OPERATION \ - DRM_IOWR(DRM_PSB_MODE_OPERATION + DRM_COMMAND_BASE, \ - struct drm_psb_mode_operation_arg) -#define DRM_IOCTL_PSB_STOLEN_MEMORY \ - DRM_IOWR(DRM_PSB_STOLEN_MEMORY + DRM_COMMAND_BASE, \ - struct drm_psb_stolen_memory_arg) -#define DRM_IOCTL_PSB_REGISTER_RW \ - DRM_IOWR(DRM_PSB_REGISTER_RW + DRM_COMMAND_BASE, \ - struct drm_psb_register_rw_arg) -#define DRM_IOCTL_PSB_DPST \ - DRM_IOWR(DRM_PSB_DPST + DRM_COMMAND_BASE, \ - uint32_t) -#define DRM_IOCTL_PSB_GAMMA \ - DRM_IOWR(DRM_PSB_GAMMA + DRM_COMMAND_BASE, \ - struct drm_psb_dpst_lut_arg) -#define DRM_IOCTL_PSB_DPST_BL \ - DRM_IOWR(DRM_PSB_DPST_BL + DRM_COMMAND_BASE, \ - uint32_t) -#define DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID \ - DRM_IOWR(DRM_PSB_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \ - struct drm_psb_get_pipe_from_crtc_id_arg) -#define DRM_IOCTL_PSB_GEM_CREATE \ - DRM_IOWR(DRM_PSB_GEM_CREATE + DRM_COMMAND_BASE, \ - struct drm_psb_gem_create) -#define DRM_IOCTL_PSB_2D_OP \ - DRM_IOW(DRM_PSB_2D_OP + DRM_COMMAND_BASE, \ - struct drm_psb_2d_op) -#define DRM_IOCTL_PSB_GEM_MMAP \ - DRM_IOWR(DRM_PSB_GEM_MMAP + DRM_COMMAND_BASE, \ - struct drm_psb_gem_mmap) - -static int psb_sizes_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_dc_state_ioctl(struct drm_device *dev, void * data, - struct drm_file *file_priv); -static int psb_adb_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_register_rw_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_dpst_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_gamma_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -#define PSB_IOCTL_DEF(ioctl, func, flags) \ - [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func} - -static struct drm_ioctl_desc psb_ioctls[] = { - PSB_IOCTL_DEF(DRM_IOCTL_PSB_SIZES, psb_sizes_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_DC_STATE, psb_dc_state_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_ADB, psb_adb_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_MODE_OPERATION, psb_mode_operation_ioctl, - DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_STOLEN_MEMORY, psb_stolen_memory_ioctl, - DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_REGISTER_RW, psb_register_rw_ioctl, - DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST, psb_dpst_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GAMMA, psb_gamma_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID, - psb_intel_get_pipe_from_crtc_id, 0), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_CREATE, psb_gem_create_ioctl, - DRM_UNLOCKED | DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_2D_OP, psb_accel_ioctl, - DRM_UNLOCKED| DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_MMAP, psb_gem_mmap_ioctl, - DRM_UNLOCKED | DRM_AUTH), -}; - -static void psb_lastclose(struct drm_device *dev) -{ - return; -} - -static void psb_do_takedown(struct drm_device *dev) -{ -} - -static int psb_do_init(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_gtt *pg = &dev_priv->gtt; - - uint32_t stolen_gtt; - - int ret = -ENOMEM; - - if (pg->mmu_gatt_start & 0x0FFFFFFF) { - dev_err(dev->dev, "Gatt must be 256M aligned. This is a bug.\n"); - ret = -EINVAL; - goto out_err; - } - - - stolen_gtt = (pg->stolen_size >> PAGE_SHIFT) * 4; - stolen_gtt = (stolen_gtt + PAGE_SIZE - 1) >> PAGE_SHIFT; - stolen_gtt = - (stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages; - - dev_priv->gatt_free_offset = pg->mmu_gatt_start + - (stolen_gtt << PAGE_SHIFT) * 1024; - - if (1 || drm_debug) { - uint32_t core_id = PSB_RSGX32(PSB_CR_CORE_ID); - uint32_t core_rev = PSB_RSGX32(PSB_CR_CORE_REVISION); - DRM_INFO("SGX core id = 0x%08x\n", core_id); - DRM_INFO("SGX core rev major = 0x%02x, minor = 0x%02x\n", - (core_rev & _PSB_CC_REVISION_MAJOR_MASK) >> - _PSB_CC_REVISION_MAJOR_SHIFT, - (core_rev & _PSB_CC_REVISION_MINOR_MASK) >> - _PSB_CC_REVISION_MINOR_SHIFT); - DRM_INFO - ("SGX core rev maintenance = 0x%02x, designer = 0x%02x\n", - (core_rev & _PSB_CC_REVISION_MAINTENANCE_MASK) >> - _PSB_CC_REVISION_MAINTENANCE_SHIFT, - (core_rev & _PSB_CC_REVISION_DESIGNER_MASK) >> - _PSB_CC_REVISION_DESIGNER_SHIFT); - } - - - spin_lock_init(&dev_priv->irqmask_lock); - spin_lock_init(&dev_priv->lock_2d); - - PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0); - PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1); - PSB_RSGX32(PSB_CR_BIF_BANK1); - PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_MMU_ER_MASK, - PSB_CR_BIF_CTRL); - psb_spank(dev_priv); - - /* mmu_gatt ?? */ - PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); - return 0; -out_err: - psb_do_takedown(dev); - return ret; -} - -static int psb_driver_unload(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - /* Kill vblank etc here */ - - gma_backlight_exit(dev); - - if (drm_psb_no_fb == 0) - psb_modeset_cleanup(dev); - - if (dev_priv) { - psb_lid_timer_takedown(dev_priv); - gma_intel_opregion_exit(dev); - - if (dev_priv->ops->chip_teardown) - dev_priv->ops->chip_teardown(dev); - psb_do_takedown(dev); - - - if (dev_priv->pf_pd) { - psb_mmu_free_pagedir(dev_priv->pf_pd); - dev_priv->pf_pd = NULL; - } - if (dev_priv->mmu) { - struct psb_gtt *pg = &dev_priv->gtt; - - down_read(&pg->sem); - psb_mmu_remove_pfn_sequence( - psb_mmu_get_default_pd - (dev_priv->mmu), - pg->mmu_gatt_start, - dev_priv->vram_stolen_size >> PAGE_SHIFT); - up_read(&pg->sem); - psb_mmu_driver_takedown(dev_priv->mmu); - dev_priv->mmu = NULL; - } - psb_gtt_takedown(dev); - if (dev_priv->scratch_page) { - __free_page(dev_priv->scratch_page); - dev_priv->scratch_page = NULL; - } - if (dev_priv->vdc_reg) { - iounmap(dev_priv->vdc_reg); - dev_priv->vdc_reg = NULL; - } - if (dev_priv->sgx_reg) { - iounmap(dev_priv->sgx_reg); - dev_priv->sgx_reg = NULL; - } - - kfree(dev_priv); - dev->dev_private = NULL; - - /*destroy VBT data*/ - psb_intel_destroy_bios(dev); - } - - gma_power_uninit(dev); - - return 0; -} - - -static int psb_driver_load(struct drm_device *dev, unsigned long chipset) -{ - struct drm_psb_private *dev_priv; - unsigned long resource_start; - struct psb_gtt *pg; - unsigned long irqflags; - int ret = -ENOMEM; - uint32_t tt_pages; - struct drm_connector *connector; - struct psb_intel_output *psb_intel_output; - - dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); - if (dev_priv == NULL) - return -ENOMEM; - - dev_priv->ops = (struct psb_ops *)chipset; - dev_priv->dev = dev; - dev->dev_private = (void *) dev_priv; - - if (!IS_PSB(dev)) { - if (pci_enable_msi(dev->pdev)) - dev_warn(dev->dev, "Enabling MSI failed!\n"); - } - - dev_priv->num_pipe = dev_priv->ops->pipes; - - resource_start = pci_resource_start(dev->pdev, PSB_MMIO_RESOURCE); - - dev_priv->vdc_reg = - ioremap(resource_start + PSB_VDC_OFFSET, PSB_VDC_SIZE); - if (!dev_priv->vdc_reg) - goto out_err; - - dev_priv->sgx_reg = ioremap(resource_start + dev_priv->ops->sgx_offset, - PSB_SGX_SIZE); - if (!dev_priv->sgx_reg) - goto out_err; - - ret = dev_priv->ops->chip_setup(dev); - if (ret) - goto out_err; - - /* Init OSPM support */ - gma_power_init(dev); - - ret = -ENOMEM; - - dev_priv->scratch_page = alloc_page(GFP_DMA32 | __GFP_ZERO); - if (!dev_priv->scratch_page) - goto out_err; - - set_pages_uc(dev_priv->scratch_page, 1); - - ret = psb_gtt_init(dev, 0); - if (ret) - goto out_err; - - dev_priv->mmu = psb_mmu_driver_init((void *)0, - drm_psb_trap_pagefaults, 0, - dev_priv); - if (!dev_priv->mmu) - goto out_err; - - pg = &dev_priv->gtt; - - tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? - (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT; - - - dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0); - if (!dev_priv->pf_pd) - goto out_err; - - psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); - psb_mmu_set_pd_context(dev_priv->pf_pd, 1); - - ret = psb_do_init(dev); - if (ret) - return ret; - - PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE); - PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE); - -/* igd_opregion_init(&dev_priv->opregion_dev); */ - acpi_video_register(); - if (dev_priv->lid_state) - psb_lid_timer_init(dev_priv); - - ret = drm_vblank_init(dev, dev_priv->num_pipe); - if (ret) - goto out_err; - - /* - * Install interrupt handlers prior to powering off SGX or else we will - * crash. - */ - dev_priv->vdc_irq_mask = 0; - dev_priv->pipestat[0] = 0; - dev_priv->pipestat[1] = 0; - dev_priv->pipestat[2] = 0; - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); - PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - if (IS_PSB(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) - drm_irq_install(dev); - - dev->vblank_disable_allowed = 1; - - dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - - dev->driver->get_vblank_counter = psb_get_vblank_counter; - -#if defined(CONFIG_DRM_PSB_MFLD) - /* FIXME: this is not the right place for this stuff ! */ - mdfld_output_setup(dev); -#endif - if (drm_psb_no_fb == 0) { - psb_modeset_init(dev); - psb_fbdev_init(dev); - drm_kms_helper_poll_init(dev); - } - - /* Only add backlight support if we have LVDS output */ - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { - psb_intel_output = to_psb_intel_output(connector); - - switch (psb_intel_output->type) { - case INTEL_OUTPUT_LVDS: - case INTEL_OUTPUT_MIPI: - ret = gma_backlight_init(dev); - break; - } - } - - if (ret) - return ret; - - /* Enable runtime pm at last */ - pm_runtime_set_active(&dev->pdev->dev); - return 0; -out_err: - psb_driver_unload(dev); - return ret; -} - -int psb_driver_device_is_agp(struct drm_device *dev) -{ - return 0; -} - - -static int psb_sizes_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - struct drm_psb_sizes_arg *arg = data; - - *arg = dev_priv->sizes; - return 0; -} - -static int psb_dc_state_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - uint32_t flags; - uint32_t obj_id; - struct drm_mode_object *obj; - struct drm_connector *connector; - struct drm_crtc *crtc; - struct drm_psb_dc_state_arg *arg = data; - - - /* Double check MRST case */ - if (IS_MRST(dev) || IS_MFLD(dev)) - return -EOPNOTSUPP; - - flags = arg->flags; - obj_id = arg->obj_id; - - if (flags & PSB_DC_CRTC_MASK) { - obj = drm_mode_object_find(dev, obj_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { - dev_dbg(dev->dev, "Invalid CRTC object.\n"); - return -EINVAL; - } - - crtc = obj_to_crtc(obj); - - mutex_lock(&dev->mode_config.mutex); - if (drm_helper_crtc_in_use(crtc)) { - if (flags & PSB_DC_CRTC_SAVE) - crtc->funcs->save(crtc); - else - crtc->funcs->restore(crtc); - } - mutex_unlock(&dev->mode_config.mutex); - - return 0; - } else if (flags & PSB_DC_OUTPUT_MASK) { - obj = drm_mode_object_find(dev, obj_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { - dev_dbg(dev->dev, "Invalid connector id.\n"); - return -EINVAL; - } - - connector = obj_to_connector(obj); - if (flags & PSB_DC_OUTPUT_SAVE) - connector->funcs->save(connector); - else - connector->funcs->restore(connector); - - return 0; - } - return -EINVAL; -} - -static inline void get_brightness(struct backlight_device *bd) -{ -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - if (bd) { - bd->props.brightness = bd->ops->get_brightness(bd); - backlight_update_status(bd); - } -#endif -} - -static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - uint32_t *arg = data; - - dev_priv->blc_adj2 = *arg; - get_brightness(dev_priv->backlight_device); - return 0; -} - -static int psb_adb_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - uint32_t *arg = data; - - dev_priv->blc_adj1 = *arg; - get_brightness(dev_priv->backlight_device); - return 0; -} - -/* return the current mode to the dpst module */ -static int psb_dpst_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - uint32_t *arg = data; - uint32_t x; - uint32_t y; - uint32_t reg; - - if (!gma_power_begin(dev, 0)) - return -EIO; - - reg = PSB_RVDC32(PIPEASRC); - - gma_power_end(dev); - - /* horizontal is the left 16 bits */ - x = reg >> 16; - /* vertical is the right 16 bits */ - y = reg & 0x0000ffff; - - /* the values are the image size minus one */ - x++; - y++; - - *arg = (x << 16) | y; - - return 0; -} -static int psb_gamma_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_dpst_lut_arg *lut_arg = data; - struct drm_mode_object *obj; - struct drm_crtc *crtc; - struct drm_connector *connector; - struct psb_intel_crtc *psb_intel_crtc; - int i = 0; - int32_t obj_id; - - obj_id = lut_arg->output_id; - obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { - dev_dbg(dev->dev, "Invalid Connector object.\n"); - return -EINVAL; - } - - connector = obj_to_connector(obj); - crtc = connector->encoder->crtc; - psb_intel_crtc = to_psb_intel_crtc(crtc); - - for (i = 0; i < 256; i++) - psb_intel_crtc->lut_adj[i] = lut_arg->lut[i]; - - psb_intel_crtc_load_lut(crtc); - - return 0; -} - -static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - uint32_t obj_id; - uint16_t op; - struct drm_mode_modeinfo *umode; - struct drm_display_mode *mode = NULL; - struct drm_psb_mode_operation_arg *arg; - struct drm_mode_object *obj; - struct drm_connector *connector; - struct drm_framebuffer *drm_fb; - struct psb_framebuffer *psb_fb; - struct drm_connector_helper_funcs *connector_funcs; - int ret = 0; - int resp = MODE_OK; - struct drm_psb_private *dev_priv = psb_priv(dev); - - arg = (struct drm_psb_mode_operation_arg *)data; - obj_id = arg->obj_id; - op = arg->operation; - - switch (op) { - case PSB_MODE_OPERATION_SET_DC_BASE: - obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_FB); - if (!obj) { - dev_dbg(dev->dev, "Invalid FB id %d\n", obj_id); - return -EINVAL; - } - - drm_fb = obj_to_fb(obj); - psb_fb = to_psb_fb(drm_fb); - - if (gma_power_begin(dev, 0)) { - REG_WRITE(DSPASURF, psb_fb->gtt->offset); - REG_READ(DSPASURF); - gma_power_end(dev); - } else { - dev_priv->saveDSPASURF = psb_fb->gtt->offset; - } - - return 0; - case PSB_MODE_OPERATION_MODE_VALID: - umode = &arg->mode; - - mutex_lock(&dev->mode_config.mutex); - - obj = drm_mode_object_find(dev, obj_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { - ret = -EINVAL; - goto mode_op_out; - } - - connector = obj_to_connector(obj); - - mode = drm_mode_create(dev); - if (!mode) { - ret = -ENOMEM; - goto mode_op_out; - } - - /* drm_crtc_convert_umode(mode, umode); */ - { - mode->clock = umode->clock; - mode->hdisplay = umode->hdisplay; - mode->hsync_start = umode->hsync_start; - mode->hsync_end = umode->hsync_end; - mode->htotal = umode->htotal; - mode->hskew = umode->hskew; - mode->vdisplay = umode->vdisplay; - mode->vsync_start = umode->vsync_start; - mode->vsync_end = umode->vsync_end; - mode->vtotal = umode->vtotal; - mode->vscan = umode->vscan; - mode->vrefresh = umode->vrefresh; - mode->flags = umode->flags; - mode->type = umode->type; - strncpy(mode->name, umode->name, DRM_DISPLAY_MODE_LEN); - mode->name[DRM_DISPLAY_MODE_LEN-1] = 0; - } - - connector_funcs = (struct drm_connector_helper_funcs *) - connector->helper_private; - - if (connector_funcs->mode_valid) { - resp = connector_funcs->mode_valid(connector, mode); - arg->data = (void *)resp; - } - - /*do some clean up work*/ - if (mode) - drm_mode_destroy(dev, mode); -mode_op_out: - mutex_unlock(&dev->mode_config.mutex); - return ret; - - default: - dev_dbg(dev->dev, "Unsupported psb mode operation\n"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - struct drm_psb_stolen_memory_arg *arg = data; - - arg->base = dev_priv->stolen_base; - arg->size = dev_priv->vram_stolen_size; - - return 0; -} - -/* FIXME: needs Medfield changes */ -static int psb_register_rw_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - struct drm_psb_register_rw_arg *arg = data; - bool usage = arg->b_force_hw_on ? true : false; - - if (arg->display_write_mask != 0) { - if (gma_power_begin(dev, usage)) { - if (arg->display_write_mask & REGRWBITS_PFIT_CONTROLS) - PSB_WVDC32(arg->display.pfit_controls, - PFIT_CONTROL); - if (arg->display_write_mask & - REGRWBITS_PFIT_AUTOSCALE_RATIOS) - PSB_WVDC32(arg->display.pfit_autoscale_ratios, - PFIT_AUTO_RATIOS); - if (arg->display_write_mask & - REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) - PSB_WVDC32( - arg->display.pfit_programmed_scale_ratios, - PFIT_PGM_RATIOS); - if (arg->display_write_mask & REGRWBITS_PIPEASRC) - PSB_WVDC32(arg->display.pipeasrc, - PIPEASRC); - if (arg->display_write_mask & REGRWBITS_PIPEBSRC) - PSB_WVDC32(arg->display.pipebsrc, - PIPEBSRC); - if (arg->display_write_mask & REGRWBITS_VTOTAL_A) - PSB_WVDC32(arg->display.vtotal_a, - VTOTAL_A); - if (arg->display_write_mask & REGRWBITS_VTOTAL_B) - PSB_WVDC32(arg->display.vtotal_b, - VTOTAL_B); - gma_power_end(dev); - } else { - if (arg->display_write_mask & REGRWBITS_PFIT_CONTROLS) - dev_priv->savePFIT_CONTROL = - arg->display.pfit_controls; - if (arg->display_write_mask & - REGRWBITS_PFIT_AUTOSCALE_RATIOS) - dev_priv->savePFIT_AUTO_RATIOS = - arg->display.pfit_autoscale_ratios; - if (arg->display_write_mask & - REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) - dev_priv->savePFIT_PGM_RATIOS = - arg->display.pfit_programmed_scale_ratios; - if (arg->display_write_mask & REGRWBITS_PIPEASRC) - dev_priv->savePIPEASRC = arg->display.pipeasrc; - if (arg->display_write_mask & REGRWBITS_PIPEBSRC) - dev_priv->savePIPEBSRC = arg->display.pipebsrc; - if (arg->display_write_mask & REGRWBITS_VTOTAL_A) - dev_priv->saveVTOTAL_A = arg->display.vtotal_a; - if (arg->display_write_mask & REGRWBITS_VTOTAL_B) - dev_priv->saveVTOTAL_B = arg->display.vtotal_b; - } - } - - if (arg->display_read_mask != 0) { - if (gma_power_begin(dev, usage)) { - if (arg->display_read_mask & - REGRWBITS_PFIT_CONTROLS) - arg->display.pfit_controls = - PSB_RVDC32(PFIT_CONTROL); - if (arg->display_read_mask & - REGRWBITS_PFIT_AUTOSCALE_RATIOS) - arg->display.pfit_autoscale_ratios = - PSB_RVDC32(PFIT_AUTO_RATIOS); - if (arg->display_read_mask & - REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) - arg->display.pfit_programmed_scale_ratios = - PSB_RVDC32(PFIT_PGM_RATIOS); - if (arg->display_read_mask & REGRWBITS_PIPEASRC) - arg->display.pipeasrc = PSB_RVDC32(PIPEASRC); - if (arg->display_read_mask & REGRWBITS_PIPEBSRC) - arg->display.pipebsrc = PSB_RVDC32(PIPEBSRC); - if (arg->display_read_mask & REGRWBITS_VTOTAL_A) - arg->display.vtotal_a = PSB_RVDC32(VTOTAL_A); - if (arg->display_read_mask & REGRWBITS_VTOTAL_B) - arg->display.vtotal_b = PSB_RVDC32(VTOTAL_B); - gma_power_end(dev); - } else { - if (arg->display_read_mask & - REGRWBITS_PFIT_CONTROLS) - arg->display.pfit_controls = - dev_priv->savePFIT_CONTROL; - if (arg->display_read_mask & - REGRWBITS_PFIT_AUTOSCALE_RATIOS) - arg->display.pfit_autoscale_ratios = - dev_priv->savePFIT_AUTO_RATIOS; - if (arg->display_read_mask & - REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) - arg->display.pfit_programmed_scale_ratios = - dev_priv->savePFIT_PGM_RATIOS; - if (arg->display_read_mask & REGRWBITS_PIPEASRC) - arg->display.pipeasrc = dev_priv->savePIPEASRC; - if (arg->display_read_mask & REGRWBITS_PIPEBSRC) - arg->display.pipebsrc = dev_priv->savePIPEBSRC; - if (arg->display_read_mask & REGRWBITS_VTOTAL_A) - arg->display.vtotal_a = dev_priv->saveVTOTAL_A; - if (arg->display_read_mask & REGRWBITS_VTOTAL_B) - arg->display.vtotal_b = dev_priv->saveVTOTAL_B; - } - } - - if (arg->overlay_write_mask != 0) { - if (gma_power_begin(dev, usage)) { - if (arg->overlay_write_mask & OV_REGRWBITS_OGAM_ALL) { - PSB_WVDC32(arg->overlay.OGAMC5, OV_OGAMC5); - PSB_WVDC32(arg->overlay.OGAMC4, OV_OGAMC4); - PSB_WVDC32(arg->overlay.OGAMC3, OV_OGAMC3); - PSB_WVDC32(arg->overlay.OGAMC2, OV_OGAMC2); - PSB_WVDC32(arg->overlay.OGAMC1, OV_OGAMC1); - PSB_WVDC32(arg->overlay.OGAMC0, OV_OGAMC0); - } - if (arg->overlay_write_mask & OVC_REGRWBITS_OGAM_ALL) { - PSB_WVDC32(arg->overlay.OGAMC5, OVC_OGAMC5); - PSB_WVDC32(arg->overlay.OGAMC4, OVC_OGAMC4); - PSB_WVDC32(arg->overlay.OGAMC3, OVC_OGAMC3); - PSB_WVDC32(arg->overlay.OGAMC2, OVC_OGAMC2); - PSB_WVDC32(arg->overlay.OGAMC1, OVC_OGAMC1); - PSB_WVDC32(arg->overlay.OGAMC0, OVC_OGAMC0); - } - - if (arg->overlay_write_mask & OV_REGRWBITS_OVADD) { - PSB_WVDC32(arg->overlay.OVADD, OV_OVADD); - - if (arg->overlay.b_wait_vblank) { - /* Wait for 20ms.*/ - unsigned long vblank_timeout = jiffies - + HZ/50; - uint32_t temp; - while (time_before_eq(jiffies, - vblank_timeout)) { - temp = PSB_RVDC32(OV_DOVASTA); - if ((temp & (0x1 << 31)) != 0) - break; - cpu_relax(); - } - } - } - if (arg->overlay_write_mask & OVC_REGRWBITS_OVADD) { - PSB_WVDC32(arg->overlay.OVADD, OVC_OVADD); - if (arg->overlay.b_wait_vblank) { - /* Wait for 20ms.*/ - unsigned long vblank_timeout = - jiffies + HZ/50; - uint32_t temp; - while (time_before_eq(jiffies, - vblank_timeout)) { - temp = PSB_RVDC32(OVC_DOVCSTA); - if ((temp & (0x1 << 31)) != 0) - break; - cpu_relax(); - } - } - } - gma_power_end(dev); - } else { - if (arg->overlay_write_mask & OV_REGRWBITS_OGAM_ALL) { - dev_priv->saveOV_OGAMC5 = arg->overlay.OGAMC5; - dev_priv->saveOV_OGAMC4 = arg->overlay.OGAMC4; - dev_priv->saveOV_OGAMC3 = arg->overlay.OGAMC3; - dev_priv->saveOV_OGAMC2 = arg->overlay.OGAMC2; - dev_priv->saveOV_OGAMC1 = arg->overlay.OGAMC1; - dev_priv->saveOV_OGAMC0 = arg->overlay.OGAMC0; - } - if (arg->overlay_write_mask & OVC_REGRWBITS_OGAM_ALL) { - dev_priv->saveOVC_OGAMC5 = arg->overlay.OGAMC5; - dev_priv->saveOVC_OGAMC4 = arg->overlay.OGAMC4; - dev_priv->saveOVC_OGAMC3 = arg->overlay.OGAMC3; - dev_priv->saveOVC_OGAMC2 = arg->overlay.OGAMC2; - dev_priv->saveOVC_OGAMC1 = arg->overlay.OGAMC1; - dev_priv->saveOVC_OGAMC0 = arg->overlay.OGAMC0; - } - if (arg->overlay_write_mask & OV_REGRWBITS_OVADD) - dev_priv->saveOV_OVADD = arg->overlay.OVADD; - if (arg->overlay_write_mask & OVC_REGRWBITS_OVADD) - dev_priv->saveOVC_OVADD = arg->overlay.OVADD; - } - } - - if (arg->overlay_read_mask != 0) { - if (gma_power_begin(dev, usage)) { - if (arg->overlay_read_mask & OV_REGRWBITS_OGAM_ALL) { - arg->overlay.OGAMC5 = PSB_RVDC32(OV_OGAMC5); - arg->overlay.OGAMC4 = PSB_RVDC32(OV_OGAMC4); - arg->overlay.OGAMC3 = PSB_RVDC32(OV_OGAMC3); - arg->overlay.OGAMC2 = PSB_RVDC32(OV_OGAMC2); - arg->overlay.OGAMC1 = PSB_RVDC32(OV_OGAMC1); - arg->overlay.OGAMC0 = PSB_RVDC32(OV_OGAMC0); - } - if (arg->overlay_read_mask & OVC_REGRWBITS_OGAM_ALL) { - arg->overlay.OGAMC5 = PSB_RVDC32(OVC_OGAMC5); - arg->overlay.OGAMC4 = PSB_RVDC32(OVC_OGAMC4); - arg->overlay.OGAMC3 = PSB_RVDC32(OVC_OGAMC3); - arg->overlay.OGAMC2 = PSB_RVDC32(OVC_OGAMC2); - arg->overlay.OGAMC1 = PSB_RVDC32(OVC_OGAMC1); - arg->overlay.OGAMC0 = PSB_RVDC32(OVC_OGAMC0); - } - if (arg->overlay_read_mask & OV_REGRWBITS_OVADD) - arg->overlay.OVADD = PSB_RVDC32(OV_OVADD); - if (arg->overlay_read_mask & OVC_REGRWBITS_OVADD) - arg->overlay.OVADD = PSB_RVDC32(OVC_OVADD); - gma_power_end(dev); - } else { - if (arg->overlay_read_mask & OV_REGRWBITS_OGAM_ALL) { - arg->overlay.OGAMC5 = dev_priv->saveOV_OGAMC5; - arg->overlay.OGAMC4 = dev_priv->saveOV_OGAMC4; - arg->overlay.OGAMC3 = dev_priv->saveOV_OGAMC3; - arg->overlay.OGAMC2 = dev_priv->saveOV_OGAMC2; - arg->overlay.OGAMC1 = dev_priv->saveOV_OGAMC1; - arg->overlay.OGAMC0 = dev_priv->saveOV_OGAMC0; - } - if (arg->overlay_read_mask & OVC_REGRWBITS_OGAM_ALL) { - arg->overlay.OGAMC5 = dev_priv->saveOVC_OGAMC5; - arg->overlay.OGAMC4 = dev_priv->saveOVC_OGAMC4; - arg->overlay.OGAMC3 = dev_priv->saveOVC_OGAMC3; - arg->overlay.OGAMC2 = dev_priv->saveOVC_OGAMC2; - arg->overlay.OGAMC1 = dev_priv->saveOVC_OGAMC1; - arg->overlay.OGAMC0 = dev_priv->saveOVC_OGAMC0; - } - if (arg->overlay_read_mask & OV_REGRWBITS_OVADD) - arg->overlay.OVADD = dev_priv->saveOV_OVADD; - if (arg->overlay_read_mask & OVC_REGRWBITS_OVADD) - arg->overlay.OVADD = dev_priv->saveOVC_OVADD; - } - } - - if (arg->sprite_enable_mask != 0) { - if (gma_power_begin(dev, usage)) { - PSB_WVDC32(0x1F3E, DSPARB); - PSB_WVDC32(arg->sprite.dspa_control - | PSB_RVDC32(DSPACNTR), DSPACNTR); - PSB_WVDC32(arg->sprite.dspa_key_value, DSPAKEYVAL); - PSB_WVDC32(arg->sprite.dspa_key_mask, DSPAKEYMASK); - PSB_WVDC32(PSB_RVDC32(DSPASURF), DSPASURF); - PSB_RVDC32(DSPASURF); - PSB_WVDC32(arg->sprite.dspc_control, DSPCCNTR); - PSB_WVDC32(arg->sprite.dspc_stride, DSPCSTRIDE); - PSB_WVDC32(arg->sprite.dspc_position, DSPCPOS); - PSB_WVDC32(arg->sprite.dspc_linear_offset, DSPCLINOFF); - PSB_WVDC32(arg->sprite.dspc_size, DSPCSIZE); - PSB_WVDC32(arg->sprite.dspc_surface, DSPCSURF); - PSB_RVDC32(DSPCSURF); - gma_power_end(dev); - } - } - - if (arg->sprite_disable_mask != 0) { - if (gma_power_begin(dev, usage)) { - PSB_WVDC32(0x3F3E, DSPARB); - PSB_WVDC32(0x0, DSPCCNTR); - PSB_WVDC32(arg->sprite.dspc_surface, DSPCSURF); - PSB_RVDC32(DSPCSURF); - gma_power_end(dev); - } - } - - if (arg->subpicture_enable_mask != 0) { - if (gma_power_begin(dev, usage)) { - uint32_t temp; - if (arg->subpicture_enable_mask & REGRWBITS_DSPACNTR) { - temp = PSB_RVDC32(DSPACNTR); - temp &= ~DISPPLANE_PIXFORMAT_MASK; - temp &= ~DISPPLANE_BOTTOM; - temp |= DISPPLANE_32BPP; - PSB_WVDC32(temp, DSPACNTR); - - temp = PSB_RVDC32(DSPABASE); - PSB_WVDC32(temp, DSPABASE); - PSB_RVDC32(DSPABASE); - temp = PSB_RVDC32(DSPASURF); - PSB_WVDC32(temp, DSPASURF); - PSB_RVDC32(DSPASURF); - } - if (arg->subpicture_enable_mask & REGRWBITS_DSPBCNTR) { - temp = PSB_RVDC32(DSPBCNTR); - temp &= ~DISPPLANE_PIXFORMAT_MASK; - temp &= ~DISPPLANE_BOTTOM; - temp |= DISPPLANE_32BPP; - PSB_WVDC32(temp, DSPBCNTR); - - temp = PSB_RVDC32(DSPBBASE); - PSB_WVDC32(temp, DSPBBASE); - PSB_RVDC32(DSPBBASE); - temp = PSB_RVDC32(DSPBSURF); - PSB_WVDC32(temp, DSPBSURF); - PSB_RVDC32(DSPBSURF); - } - if (arg->subpicture_enable_mask & REGRWBITS_DSPCCNTR) { - temp = PSB_RVDC32(DSPCCNTR); - temp &= ~DISPPLANE_PIXFORMAT_MASK; - temp &= ~DISPPLANE_BOTTOM; - temp |= DISPPLANE_32BPP; - PSB_WVDC32(temp, DSPCCNTR); - - temp = PSB_RVDC32(DSPCBASE); - PSB_WVDC32(temp, DSPCBASE); - PSB_RVDC32(DSPCBASE); - temp = PSB_RVDC32(DSPCSURF); - PSB_WVDC32(temp, DSPCSURF); - PSB_RVDC32(DSPCSURF); - } - gma_power_end(dev); - } - } - - if (arg->subpicture_disable_mask != 0) { - if (gma_power_begin(dev, usage)) { - uint32_t temp; - if (arg->subpicture_disable_mask & REGRWBITS_DSPACNTR) { - temp = PSB_RVDC32(DSPACNTR); - temp &= ~DISPPLANE_PIXFORMAT_MASK; - temp |= DISPPLANE_32BPP_NO_ALPHA; - PSB_WVDC32(temp, DSPACNTR); - - temp = PSB_RVDC32(DSPABASE); - PSB_WVDC32(temp, DSPABASE); - PSB_RVDC32(DSPABASE); - temp = PSB_RVDC32(DSPASURF); - PSB_WVDC32(temp, DSPASURF); - PSB_RVDC32(DSPASURF); - } - if (arg->subpicture_disable_mask & REGRWBITS_DSPBCNTR) { - temp = PSB_RVDC32(DSPBCNTR); - temp &= ~DISPPLANE_PIXFORMAT_MASK; - temp |= DISPPLANE_32BPP_NO_ALPHA; - PSB_WVDC32(temp, DSPBCNTR); - - temp = PSB_RVDC32(DSPBBASE); - PSB_WVDC32(temp, DSPBBASE); - PSB_RVDC32(DSPBBASE); - temp = PSB_RVDC32(DSPBSURF); - PSB_WVDC32(temp, DSPBSURF); - PSB_RVDC32(DSPBSURF); - } - if (arg->subpicture_disable_mask & REGRWBITS_DSPCCNTR) { - temp = PSB_RVDC32(DSPCCNTR); - temp &= ~DISPPLANE_PIXFORMAT_MASK; - temp |= DISPPLANE_32BPP_NO_ALPHA; - PSB_WVDC32(temp, DSPCCNTR); - - temp = PSB_RVDC32(DSPCBASE); - PSB_WVDC32(temp, DSPCBASE); - PSB_RVDC32(DSPCBASE); - temp = PSB_RVDC32(DSPCSURF); - PSB_WVDC32(temp, DSPCSURF); - PSB_RVDC32(DSPCSURF); - } - gma_power_end(dev); - } - } - - return 0; -} - -static int psb_driver_open(struct drm_device *dev, struct drm_file *priv) -{ - return 0; -} - -static void psb_driver_close(struct drm_device *dev, struct drm_file *priv) -{ -} - -static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct drm_file *file_priv = filp->private_data; - struct drm_device *dev = file_priv->minor->dev; - int ret; - - pm_runtime_forbid(dev->dev); - ret = drm_ioctl(filp, cmd, arg); - pm_runtime_allow(dev->dev); - return ret; - /* FIXME: do we need to wrap the other side of this */ -} - - -/* When a client dies: - * - Check for and clean up flipped page state - */ -void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) -{ -} - -static void psb_remove(struct pci_dev *pdev) -{ - struct drm_device *dev = pci_get_drvdata(pdev); - drm_put_dev(dev); -} - -static const struct dev_pm_ops psb_pm_ops = { - .suspend = gma_power_suspend, - .resume = gma_power_resume, - .freeze = gma_power_suspend, - .thaw = gma_power_resume, - .poweroff = gma_power_suspend, - .restore = gma_power_resume, - .runtime_suspend = psb_runtime_suspend, - .runtime_resume = psb_runtime_resume, - .runtime_idle = psb_runtime_idle, -}; - -static struct vm_operations_struct psb_gem_vm_ops = { - .fault = psb_gem_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - -static const struct file_operations gma500_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = psb_unlocked_ioctl, - .mmap = drm_gem_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - .read = drm_read, -}; - -static struct drm_driver driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \ - DRIVER_IRQ_VBL | DRIVER_MODESET | DRIVER_GEM , - .load = psb_driver_load, - .unload = psb_driver_unload, - - .ioctls = psb_ioctls, - .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls), - .device_is_agp = psb_driver_device_is_agp, - .irq_preinstall = psb_irq_preinstall, - .irq_postinstall = psb_irq_postinstall, - .irq_uninstall = psb_irq_uninstall, - .irq_handler = psb_irq_handler, - .enable_vblank = psb_enable_vblank, - .disable_vblank = psb_disable_vblank, - .get_vblank_counter = psb_get_vblank_counter, - .lastclose = psb_lastclose, - .open = psb_driver_open, - .preclose = psb_driver_preclose, - .postclose = psb_driver_close, - .reclaim_buffers = drm_core_reclaim_buffers, - - .gem_init_object = psb_gem_init_object, - .gem_free_object = psb_gem_free_object, - .gem_vm_ops = &psb_gem_vm_ops, - .dumb_create = psb_gem_dumb_create, - .dumb_map_offset = psb_gem_dumb_map_gtt, - .dumb_destroy = psb_gem_dumb_destroy, - .fops = &gma500_driver_fops, - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = PSB_DRM_DRIVER_DATE, - .major = PSB_DRM_DRIVER_MAJOR, - .minor = PSB_DRM_DRIVER_MINOR, - .patchlevel = PSB_DRM_DRIVER_PATCHLEVEL -}; - -static struct pci_driver psb_pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = psb_probe, - .remove = psb_remove, - .driver.pm = &psb_pm_ops, -}; - -static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - return drm_get_pci_dev(pdev, ent, &driver); -} - -static int __init psb_init(void) -{ - return drm_pci_init(&driver, &psb_pci_driver); -} - -static void __exit psb_exit(void) -{ - drm_pci_exit(&driver, &psb_pci_driver); -} - -late_initcall(psb_init); -module_exit(psb_exit); - -MODULE_AUTHOR("Alan Cox and others"); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/gma500/psb_drv.h b/drivers/staging/gma500/psb_drv.h deleted file mode 100644 index 11d963a..0000000 --- a/drivers/staging/gma500/psb_drv.h +++ /dev/null @@ -1,952 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#ifndef _PSB_DRV_H_ -#define _PSB_DRV_H_ - -#include - -#include -#include "drm_global.h" -#include "gem_glue.h" -#include "psb_drm.h" -#include "psb_reg.h" -#include "psb_intel_drv.h" -#include "gtt.h" -#include "power.h" -#include "mrst.h" -#include "medfield.h" - -/* Append new drm mode definition here, align with libdrm definition */ -#define DRM_MODE_SCALE_NO_SCALE 2 - -enum { - CHIP_PSB_8108 = 0, /* Poulsbo */ - CHIP_PSB_8109 = 1, /* Poulsbo */ - CHIP_MRST_4100 = 2, /* Moorestown/Oaktrail */ - CHIP_MFLD_0130 = 3, /* Medfield */ -}; - -#define IS_PSB(dev) (((dev)->pci_device & 0xfffe) == 0x8108) -#define IS_MRST(dev) (((dev)->pci_device & 0xfffc) == 0x4100) -#define IS_MFLD(dev) (((dev)->pci_device & 0xfff8) == 0x0130) - -/* - * Driver definitions - */ - -#define DRIVER_NAME "gma500" -#define DRIVER_DESC "DRM driver for the Intel GMA500" - -#define PSB_DRM_DRIVER_DATE "2011-06-06" -#define PSB_DRM_DRIVER_MAJOR 1 -#define PSB_DRM_DRIVER_MINOR 0 -#define PSB_DRM_DRIVER_PATCHLEVEL 0 - -/* - * Hardware offsets - */ -#define PSB_VDC_OFFSET 0x00000000 -#define PSB_VDC_SIZE 0x000080000 -#define MRST_MMIO_SIZE 0x0000C0000 -#define MDFLD_MMIO_SIZE 0x000100000 -#define PSB_SGX_SIZE 0x8000 -#define PSB_SGX_OFFSET 0x00040000 -#define MRST_SGX_OFFSET 0x00080000 -/* - * PCI resource identifiers - */ -#define PSB_MMIO_RESOURCE 0 -#define PSB_GATT_RESOURCE 2 -#define PSB_GTT_RESOURCE 3 -/* - * PCI configuration - */ -#define PSB_GMCH_CTRL 0x52 -#define PSB_BSM 0x5C -#define _PSB_GMCH_ENABLED 0x4 -#define PSB_PGETBL_CTL 0x2020 -#define _PSB_PGETBL_ENABLED 0x00000001 -#define PSB_SGX_2D_SLAVE_PORT 0x4000 - -/* To get rid of */ -#define PSB_TT_PRIV0_LIMIT (256*1024*1024) -#define PSB_TT_PRIV0_PLIMIT (PSB_TT_PRIV0_LIMIT >> PAGE_SHIFT) - -/* - * SGX side MMU definitions (these can probably go) - */ - -/* - * Flags for external memory type field. - */ -#define PSB_MMU_CACHED_MEMORY 0x0001 /* Bind to MMU only */ -#define PSB_MMU_RO_MEMORY 0x0002 /* MMU RO memory */ -#define PSB_MMU_WO_MEMORY 0x0004 /* MMU WO memory */ -/* - * PTE's and PDE's - */ -#define PSB_PDE_MASK 0x003FFFFF -#define PSB_PDE_SHIFT 22 -#define PSB_PTE_SHIFT 12 -/* - * Cache control - */ -#define PSB_PTE_VALID 0x0001 /* PTE / PDE valid */ -#define PSB_PTE_WO 0x0002 /* Write only */ -#define PSB_PTE_RO 0x0004 /* Read only */ -#define PSB_PTE_CACHED 0x0008 /* CPU cache coherent */ - -/* - * VDC registers and bits - */ -#define PSB_MSVDX_CLOCKGATING 0x2064 -#define PSB_TOPAZ_CLOCKGATING 0x2068 -#define PSB_HWSTAM 0x2098 -#define PSB_INSTPM 0x20C0 -#define PSB_INT_IDENTITY_R 0x20A4 -#define _MDFLD_PIPEC_EVENT_FLAG (1<<2) -#define _MDFLD_PIPEC_VBLANK_FLAG (1<<3) -#define _PSB_DPST_PIPEB_FLAG (1<<4) -#define _MDFLD_PIPEB_EVENT_FLAG (1<<4) -#define _PSB_VSYNC_PIPEB_FLAG (1<<5) -#define _PSB_DPST_PIPEA_FLAG (1<<6) -#define _PSB_PIPEA_EVENT_FLAG (1<<6) -#define _PSB_VSYNC_PIPEA_FLAG (1<<7) -#define _MDFLD_MIPIA_FLAG (1<<16) -#define _MDFLD_MIPIC_FLAG (1<<17) -#define _PSB_IRQ_SGX_FLAG (1<<18) -#define _PSB_IRQ_MSVDX_FLAG (1<<19) -#define _LNC_IRQ_TOPAZ_FLAG (1<<20) - -#define _PSB_PIPE_EVENT_FLAG (_PSB_VSYNC_PIPEA_FLAG | \ - _PSB_VSYNC_PIPEB_FLAG) - -/* This flag includes all the display IRQ bits excepts the vblank irqs. */ -#define _MDFLD_DISP_ALL_IRQ_FLAG (_MDFLD_PIPEC_EVENT_FLAG | \ - _MDFLD_PIPEB_EVENT_FLAG | \ - _PSB_PIPEA_EVENT_FLAG | \ - _PSB_VSYNC_PIPEA_FLAG | \ - _MDFLD_MIPIA_FLAG | \ - _MDFLD_MIPIC_FLAG) -#define PSB_INT_IDENTITY_R 0x20A4 -#define PSB_INT_MASK_R 0x20A8 -#define PSB_INT_ENABLE_R 0x20A0 - -#define _PSB_MMU_ER_MASK 0x0001FF00 -#define _PSB_MMU_ER_HOST (1 << 16) -#define GPIOA 0x5010 -#define GPIOB 0x5014 -#define GPIOC 0x5018 -#define GPIOD 0x501c -#define GPIOE 0x5020 -#define GPIOF 0x5024 -#define GPIOG 0x5028 -#define GPIOH 0x502c -#define GPIO_CLOCK_DIR_MASK (1 << 0) -#define GPIO_CLOCK_DIR_IN (0 << 1) -#define GPIO_CLOCK_DIR_OUT (1 << 1) -#define GPIO_CLOCK_VAL_MASK (1 << 2) -#define GPIO_CLOCK_VAL_OUT (1 << 3) -#define GPIO_CLOCK_VAL_IN (1 << 4) -#define GPIO_CLOCK_PULLUP_DISABLE (1 << 5) -#define GPIO_DATA_DIR_MASK (1 << 8) -#define GPIO_DATA_DIR_IN (0 << 9) -#define GPIO_DATA_DIR_OUT (1 << 9) -#define GPIO_DATA_VAL_MASK (1 << 10) -#define GPIO_DATA_VAL_OUT (1 << 11) -#define GPIO_DATA_VAL_IN (1 << 12) -#define GPIO_DATA_PULLUP_DISABLE (1 << 13) - -#define VCLK_DIVISOR_VGA0 0x6000 -#define VCLK_DIVISOR_VGA1 0x6004 -#define VCLK_POST_DIV 0x6010 - -#define PSB_COMM_2D (PSB_ENGINE_2D << 4) -#define PSB_COMM_3D (PSB_ENGINE_3D << 4) -#define PSB_COMM_TA (PSB_ENGINE_TA << 4) -#define PSB_COMM_HP (PSB_ENGINE_HP << 4) -#define PSB_COMM_USER_IRQ (1024 >> 2) -#define PSB_COMM_USER_IRQ_LOST (PSB_COMM_USER_IRQ + 1) -#define PSB_COMM_FW (2048 >> 2) - -#define PSB_UIRQ_VISTEST 1 -#define PSB_UIRQ_OOM_REPLY 2 -#define PSB_UIRQ_FIRE_TA_REPLY 3 -#define PSB_UIRQ_FIRE_RASTER_REPLY 4 - -#define PSB_2D_SIZE (256*1024*1024) -#define PSB_MAX_RELOC_PAGES 1024 - -#define PSB_LOW_REG_OFFS 0x0204 -#define PSB_HIGH_REG_OFFS 0x0600 - -#define PSB_NUM_VBLANKS 2 - - -#define PSB_2D_SIZE (256*1024*1024) -#define PSB_MAX_RELOC_PAGES 1024 - -#define PSB_LOW_REG_OFFS 0x0204 -#define PSB_HIGH_REG_OFFS 0x0600 - -#define PSB_NUM_VBLANKS 2 -#define PSB_WATCHDOG_DELAY (DRM_HZ * 2) -#define PSB_LID_DELAY (DRM_HZ / 10) - -#define MDFLD_PNW_B0 0x04 -#define MDFLD_PNW_C0 0x08 - -#define MDFLD_DSR_2D_3D_0 (1 << 0) -#define MDFLD_DSR_2D_3D_2 (1 << 1) -#define MDFLD_DSR_CURSOR_0 (1 << 2) -#define MDFLD_DSR_CURSOR_2 (1 << 3) -#define MDFLD_DSR_OVERLAY_0 (1 << 4) -#define MDFLD_DSR_OVERLAY_2 (1 << 5) -#define MDFLD_DSR_MIPI_CONTROL (1 << 6) -#define MDFLD_DSR_DAMAGE_MASK_0 ((1 << 0) | (1 << 2) | (1 << 4)) -#define MDFLD_DSR_DAMAGE_MASK_2 ((1 << 1) | (1 << 3) | (1 << 5)) -#define MDFLD_DSR_2D_3D (MDFLD_DSR_2D_3D_0 | MDFLD_DSR_2D_3D_2) - -#define MDFLD_DSR_RR 45 -#define MDFLD_DPU_ENABLE (1 << 31) -#define MDFLD_DSR_FULLSCREEN (1 << 30) -#define MDFLD_DSR_DELAY (DRM_HZ / MDFLD_DSR_RR) - -#define PSB_PWR_STATE_ON 1 -#define PSB_PWR_STATE_OFF 2 - -#define PSB_PMPOLICY_NOPM 0 -#define PSB_PMPOLICY_CLOCKGATING 1 -#define PSB_PMPOLICY_POWERDOWN 2 - -#define PSB_PMSTATE_POWERUP 0 -#define PSB_PMSTATE_CLOCKGATED 1 -#define PSB_PMSTATE_POWERDOWN 2 -#define PSB_PCIx_MSI_ADDR_LOC 0x94 -#define PSB_PCIx_MSI_DATA_LOC 0x98 - -/* Medfield crystal settings */ -#define KSEL_CRYSTAL_19 1 -#define KSEL_BYPASS_19 5 -#define KSEL_BYPASS_25 6 -#define KSEL_BYPASS_83_100 7 - -struct opregion_header; -struct opregion_acpi; -struct opregion_swsci; -struct opregion_asle; - -struct psb_intel_opregion { - struct opregion_header *header; - struct opregion_acpi *acpi; - struct opregion_swsci *swsci; - struct opregion_asle *asle; - int enabled; -}; - -struct psb_ops; - -struct drm_psb_private { - struct drm_device *dev; - const struct psb_ops *ops; - - struct psb_gtt gtt; - - /* GTT Memory manager */ - struct psb_gtt_mm *gtt_mm; - struct page *scratch_page; - u32 *gtt_map; - uint32_t stolen_base; - void *vram_addr; - unsigned long vram_stolen_size; - int gtt_initialized; - u16 gmch_ctrl; /* Saved GTT setup */ - u32 pge_ctl; - - struct mutex gtt_mutex; - struct resource *gtt_mem; /* Our PCI resource */ - - struct psb_mmu_driver *mmu; - struct psb_mmu_pd *pf_pd; - - /* - * Register base - */ - - uint8_t *sgx_reg; - uint8_t *vdc_reg; - uint32_t gatt_free_offset; - - /* - * Fencing / irq. - */ - - uint32_t vdc_irq_mask; - uint32_t pipestat[PSB_NUM_PIPE]; - - spinlock_t irqmask_lock; - - /* - * Power - */ - - bool suspended; - bool display_power; - int display_count; - - /* - * Modesetting - */ - struct psb_intel_mode_device mode_dev; - - struct drm_crtc *plane_to_crtc_mapping[PSB_NUM_PIPE]; - struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE]; - uint32_t num_pipe; - - /* - * OSPM info (Power management base) (can go ?) - */ - uint32_t ospm_base; - - /* - * Sizes info - */ - - struct drm_psb_sizes_arg sizes; - - u32 fuse_reg_value; - u32 video_device_fuse; - - /* PCI revision ID for B0:D2:F0 */ - uint8_t platform_rev_id; - - /* - * LVDS info - */ - int backlight_duty_cycle; /* restore backlight to this value */ - bool panel_wants_dither; - struct drm_display_mode *panel_fixed_mode; - struct drm_display_mode *lfp_lvds_vbt_mode; - struct drm_display_mode *sdvo_lvds_vbt_mode; - - struct bdb_lvds_backlight *lvds_bl; /* LVDS backlight info from VBT */ - struct psb_intel_i2c_chan *lvds_i2c_bus; - - /* Feature bits from the VBIOS */ - unsigned int int_tv_support:1; - unsigned int lvds_dither:1; - unsigned int lvds_vbt:1; - unsigned int int_crt_support:1; - unsigned int lvds_use_ssc:1; - int lvds_ssc_freq; - bool is_lvds_on; - bool is_mipi_on; - u32 mipi_ctrl_display; - - unsigned int core_freq; - uint32_t iLVDS_enable; - - /* Runtime PM state */ - int rpm_enabled; - - /* MID specific */ - struct mrst_vbt vbt_data; - struct mrst_gct_data gct_data; - - /* MIPI Panel type etc */ - int panel_id; - bool dual_mipi; /* dual display - DPI & DBI */ - bool dpi_panel_on; /* The DPI panel power is on */ - bool dpi_panel_on2; /* The DPI panel power is on */ - bool dbi_panel_on; /* The DBI panel power is on */ - bool dbi_panel_on2; /* The DBI panel power is on */ - u32 dsr_fb_update; /* DSR FB update counter */ - - /* Moorestown HDMI state */ - struct mrst_hdmi_dev *hdmi_priv; - - /* Moorestown pipe config register value cache */ - uint32_t pipeconf; - uint32_t pipeconf1; - uint32_t pipeconf2; - - /* Moorestown plane control register value cache */ - uint32_t dspcntr; - uint32_t dspcntr1; - uint32_t dspcntr2; - - /* Moorestown MM backlight cache */ - uint8_t saveBKLTCNT; - uint8_t saveBKLTREQ; - uint8_t saveBKLTBRTL; - - /* - * Register state - */ - uint32_t saveDSPACNTR; - uint32_t saveDSPBCNTR; - uint32_t savePIPEACONF; - uint32_t savePIPEBCONF; - uint32_t savePIPEASRC; - uint32_t savePIPEBSRC; - uint32_t saveFPA0; - uint32_t saveFPA1; - uint32_t saveDPLL_A; - uint32_t saveDPLL_A_MD; - uint32_t saveHTOTAL_A; - uint32_t saveHBLANK_A; - uint32_t saveHSYNC_A; - uint32_t saveVTOTAL_A; - uint32_t saveVBLANK_A; - uint32_t saveVSYNC_A; - uint32_t saveDSPASTRIDE; - uint32_t saveDSPASIZE; - uint32_t saveDSPAPOS; - uint32_t saveDSPABASE; - uint32_t saveDSPASURF; - uint32_t saveDSPASTATUS; - uint32_t saveFPB0; - uint32_t saveFPB1; - uint32_t saveDPLL_B; - uint32_t saveDPLL_B_MD; - uint32_t saveHTOTAL_B; - uint32_t saveHBLANK_B; - uint32_t saveHSYNC_B; - uint32_t saveVTOTAL_B; - uint32_t saveVBLANK_B; - uint32_t saveVSYNC_B; - uint32_t saveDSPBSTRIDE; - uint32_t saveDSPBSIZE; - uint32_t saveDSPBPOS; - uint32_t saveDSPBBASE; - uint32_t saveDSPBSURF; - uint32_t saveDSPBSTATUS; - uint32_t saveVCLK_DIVISOR_VGA0; - uint32_t saveVCLK_DIVISOR_VGA1; - uint32_t saveVCLK_POST_DIV; - uint32_t saveVGACNTRL; - uint32_t saveADPA; - uint32_t saveLVDS; - uint32_t saveDVOA; - uint32_t saveDVOB; - uint32_t saveDVOC; - uint32_t savePP_ON; - uint32_t savePP_OFF; - uint32_t savePP_CONTROL; - uint32_t savePP_CYCLE; - uint32_t savePFIT_CONTROL; - uint32_t savePaletteA[256]; - uint32_t savePaletteB[256]; - uint32_t saveBLC_PWM_CTL2; - uint32_t saveBLC_PWM_CTL; - uint32_t saveCLOCKGATING; - uint32_t saveDSPARB; - uint32_t saveDSPATILEOFF; - uint32_t saveDSPBTILEOFF; - uint32_t saveDSPAADDR; - uint32_t saveDSPBADDR; - uint32_t savePFIT_AUTO_RATIOS; - uint32_t savePFIT_PGM_RATIOS; - uint32_t savePP_ON_DELAYS; - uint32_t savePP_OFF_DELAYS; - uint32_t savePP_DIVISOR; - uint32_t saveBSM; - uint32_t saveVBT; - uint32_t saveBCLRPAT_A; - uint32_t saveBCLRPAT_B; - uint32_t saveDSPALINOFF; - uint32_t saveDSPBLINOFF; - uint32_t savePERF_MODE; - uint32_t saveDSPFW1; - uint32_t saveDSPFW2; - uint32_t saveDSPFW3; - uint32_t saveDSPFW4; - uint32_t saveDSPFW5; - uint32_t saveDSPFW6; - uint32_t saveCHICKENBIT; - uint32_t saveDSPACURSOR_CTRL; - uint32_t saveDSPBCURSOR_CTRL; - uint32_t saveDSPACURSOR_BASE; - uint32_t saveDSPBCURSOR_BASE; - uint32_t saveDSPACURSOR_POS; - uint32_t saveDSPBCURSOR_POS; - uint32_t save_palette_a[256]; - uint32_t save_palette_b[256]; - uint32_t saveOV_OVADD; - uint32_t saveOV_OGAMC0; - uint32_t saveOV_OGAMC1; - uint32_t saveOV_OGAMC2; - uint32_t saveOV_OGAMC3; - uint32_t saveOV_OGAMC4; - uint32_t saveOV_OGAMC5; - uint32_t saveOVC_OVADD; - uint32_t saveOVC_OGAMC0; - uint32_t saveOVC_OGAMC1; - uint32_t saveOVC_OGAMC2; - uint32_t saveOVC_OGAMC3; - uint32_t saveOVC_OGAMC4; - uint32_t saveOVC_OGAMC5; - - /* MSI reg save */ - uint32_t msi_addr; - uint32_t msi_data; - - /* Medfield specific register save state */ - uint32_t saveHDMIPHYMISCCTL; - uint32_t saveHDMIB_CONTROL; - uint32_t saveDSPCCNTR; - uint32_t savePIPECCONF; - uint32_t savePIPECSRC; - uint32_t saveHTOTAL_C; - uint32_t saveHBLANK_C; - uint32_t saveHSYNC_C; - uint32_t saveVTOTAL_C; - uint32_t saveVBLANK_C; - uint32_t saveVSYNC_C; - uint32_t saveDSPCSTRIDE; - uint32_t saveDSPCSIZE; - uint32_t saveDSPCPOS; - uint32_t saveDSPCSURF; - uint32_t saveDSPCSTATUS; - uint32_t saveDSPCLINOFF; - uint32_t saveDSPCTILEOFF; - uint32_t saveDSPCCURSOR_CTRL; - uint32_t saveDSPCCURSOR_BASE; - uint32_t saveDSPCCURSOR_POS; - uint32_t save_palette_c[256]; - uint32_t saveOV_OVADD_C; - uint32_t saveOV_OGAMC0_C; - uint32_t saveOV_OGAMC1_C; - uint32_t saveOV_OGAMC2_C; - uint32_t saveOV_OGAMC3_C; - uint32_t saveOV_OGAMC4_C; - uint32_t saveOV_OGAMC5_C; - - /* DSI register save */ - uint32_t saveDEVICE_READY_REG; - uint32_t saveINTR_EN_REG; - uint32_t saveDSI_FUNC_PRG_REG; - uint32_t saveHS_TX_TIMEOUT_REG; - uint32_t saveLP_RX_TIMEOUT_REG; - uint32_t saveTURN_AROUND_TIMEOUT_REG; - uint32_t saveDEVICE_RESET_REG; - uint32_t saveDPI_RESOLUTION_REG; - uint32_t saveHORIZ_SYNC_PAD_COUNT_REG; - uint32_t saveHORIZ_BACK_PORCH_COUNT_REG; - uint32_t saveHORIZ_FRONT_PORCH_COUNT_REG; - uint32_t saveHORIZ_ACTIVE_AREA_COUNT_REG; - uint32_t saveVERT_SYNC_PAD_COUNT_REG; - uint32_t saveVERT_BACK_PORCH_COUNT_REG; - uint32_t saveVERT_FRONT_PORCH_COUNT_REG; - uint32_t saveHIGH_LOW_SWITCH_COUNT_REG; - uint32_t saveINIT_COUNT_REG; - uint32_t saveMAX_RET_PAK_REG; - uint32_t saveVIDEO_FMT_REG; - uint32_t saveEOT_DISABLE_REG; - uint32_t saveLP_BYTECLK_REG; - uint32_t saveHS_LS_DBI_ENABLE_REG; - uint32_t saveTXCLKESC_REG; - uint32_t saveDPHY_PARAM_REG; - uint32_t saveMIPI_CONTROL_REG; - uint32_t saveMIPI; - uint32_t saveMIPI_C; - - /* DPST register save */ - uint32_t saveHISTOGRAM_INT_CONTROL_REG; - uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG; - uint32_t savePWM_CONTROL_LOGIC; - - /* - * DSI info. - */ - void * dbi_dsr_info; - void * dbi_dpu_info; - void * dsi_configs[2]; - /* - * LID-Switch - */ - spinlock_t lid_lock; - struct timer_list lid_timer; - struct psb_intel_opregion opregion; - u32 *lid_state; - u32 lid_last_state; - - /* - * Watchdog - */ - - uint32_t apm_reg; - uint16_t apm_base; - - /* - * Used for modifying backlight from - * xrandr -- consider removing and using HAL instead - */ - struct backlight_device *backlight_device; - struct drm_property *backlight_property; - uint32_t blc_adj1; - uint32_t blc_adj2; - - void *fbdev; - /* DPST state */ - uint32_t dsr_idle_count; - bool is_in_idle; - bool dsr_enable; - void (*exit_idle)(struct drm_device *dev, u32 update_src); - - /* 2D acceleration */ - spinlock_t lock_2d; - - /* FIXME: Arrays anyone ? */ - struct mdfld_dsi_encoder *encoder0; - struct mdfld_dsi_encoder *encoder2; - struct mdfld_dsi_dbi_output * dbi_output; - struct mdfld_dsi_dbi_output * dbi_output2; - u32 bpp; - u32 bpp2; - - bool dispstatus; -}; - - -/* - * Operations for each board type - */ - -struct psb_ops { - const char *name; - unsigned int accel_2d:1; - int pipes; /* Number of output pipes */ - int crtcs; /* Number of CRTCs */ - int sgx_offset; /* Base offset of SGX device */ - - /* Sub functions */ - struct drm_crtc_helper_funcs const *crtc_helper; - struct drm_crtc_funcs const *crtc_funcs; - - /* Setup hooks */ - int (*chip_setup)(struct drm_device *dev); - void (*chip_teardown)(struct drm_device *dev); - - /* Display management hooks */ - int (*output_init)(struct drm_device *dev); - /* Power management hooks */ - void (*init_pm)(struct drm_device *dev); - int (*save_regs)(struct drm_device *dev); - int (*restore_regs)(struct drm_device *dev); - int (*power_up)(struct drm_device *dev); - int (*power_down)(struct drm_device *dev); - - void (*lvds_bl_power)(struct drm_device *dev, bool on); -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - /* Backlight */ - int (*backlight_init)(struct drm_device *dev); -#endif - int i2c_bus; /* I2C bus identifier for Moorestown */ -}; - - - -struct psb_mmu_driver; - -extern int drm_crtc_probe_output_modes(struct drm_device *dev, int, int); -extern int drm_pick_crtcs(struct drm_device *dev); - -static inline struct drm_psb_private *psb_priv(struct drm_device *dev) -{ - return (struct drm_psb_private *) dev->dev_private; -} - -/* - * MMU stuff. - */ - -extern struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, - int trap_pagefaults, - int invalid_type, - struct drm_psb_private *dev_priv); -extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver); -extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver - *driver); -extern void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, uint32_t mmu_offset, - uint32_t gtt_start, uint32_t gtt_pages); -extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, - int trap_pagefaults, - int invalid_type); -extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd); -extern void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot); -extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, - unsigned long address, - uint32_t num_pages); -extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, - uint32_t start_pfn, - unsigned long address, - uint32_t num_pages, int type); -extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, - unsigned long *pfn); - -/* - * Enable / disable MMU for different requestors. - */ - - -extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context); -extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride, int type); -extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride); -/* - *psb_irq.c - */ - -extern irqreturn_t psb_irq_handler(DRM_IRQ_ARGS); -extern int psb_irq_enable_dpst(struct drm_device *dev); -extern int psb_irq_disable_dpst(struct drm_device *dev); -extern void psb_irq_preinstall(struct drm_device *dev); -extern int psb_irq_postinstall(struct drm_device *dev); -extern void psb_irq_uninstall(struct drm_device *dev); -extern void psb_irq_turn_on_dpst(struct drm_device *dev); -extern void psb_irq_turn_off_dpst(struct drm_device *dev); - -extern void psb_irq_uninstall_islands(struct drm_device *dev, int hw_islands); -extern int psb_vblank_wait2(struct drm_device *dev, unsigned int *sequence); -extern int psb_vblank_wait(struct drm_device *dev, unsigned int *sequence); -extern int psb_enable_vblank(struct drm_device *dev, int crtc); -extern void psb_disable_vblank(struct drm_device *dev, int crtc); -void -psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); - -void -psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); - -extern u32 psb_get_vblank_counter(struct drm_device *dev, int crtc); - -extern int mdfld_enable_te(struct drm_device *dev, int pipe); -extern void mdfld_disable_te(struct drm_device *dev, int pipe); - -/* - * intel_opregion.c - */ -extern int gma_intel_opregion_init(struct drm_device *dev); -extern int gma_intel_opregion_exit(struct drm_device *dev); - -/* - * framebuffer.c - */ -extern int psbfb_probed(struct drm_device *dev); -extern int psbfb_remove(struct drm_device *dev, - struct drm_framebuffer *fb); -/* - * accel_2d.c - */ -extern void psbfb_copyarea(struct fb_info *info, - const struct fb_copyarea *region); -extern int psbfb_sync(struct fb_info *info); -extern void psb_spank(struct drm_psb_private *dev_priv); -extern int psb_accel_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); - -/* - * psb_reset.c - */ - -extern void psb_lid_timer_init(struct drm_psb_private *dev_priv); -extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv); -extern void psb_print_pagefault(struct drm_psb_private *dev_priv); - -/* modesetting */ -extern void psb_modeset_init(struct drm_device *dev); -extern void psb_modeset_cleanup(struct drm_device *dev); -extern int psb_fbdev_init(struct drm_device *dev); - -/* backlight.c */ -int gma_backlight_init(struct drm_device *dev); -void gma_backlight_exit(struct drm_device *dev); - -/* mrst_crtc.c */ -extern const struct drm_crtc_helper_funcs mrst_helper_funcs; - -/* mrst_lvds.c */ -extern void mrst_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev); - -/* psb_intel_display.c */ -extern const struct drm_crtc_helper_funcs psb_intel_helper_funcs; -extern const struct drm_crtc_funcs psb_intel_crtc_funcs; - -/* psb_intel_lvds.c */ -extern const struct drm_connector_helper_funcs - psb_intel_lvds_connector_helper_funcs; -extern const struct drm_connector_funcs psb_intel_lvds_connector_funcs; - -/* gem.c */ -extern int psb_gem_init_object(struct drm_gem_object *obj); -extern void psb_gem_free_object(struct drm_gem_object *obj); -extern int psb_gem_get_aperture(struct drm_device *dev, void *data, - struct drm_file *file); -extern int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, - struct drm_mode_create_dumb *args); -extern int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle); -extern int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset); -extern int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); -extern int psb_gem_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); -extern int psb_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); - -/* psb_device.c */ -extern const struct psb_ops psb_chip_ops; - -/* mrst_device.c */ -extern const struct psb_ops mrst_chip_ops; - -/* mdfld_device.c */ -extern const struct psb_ops mdfld_chip_ops; - -/* cdv_device.c */ -extern const struct psb_ops cdv_chip_ops; - -/* - * Debug print bits setting - */ -#define PSB_D_GENERAL (1 << 0) -#define PSB_D_INIT (1 << 1) -#define PSB_D_IRQ (1 << 2) -#define PSB_D_ENTRY (1 << 3) -/* debug the get H/V BP/FP count */ -#define PSB_D_HV (1 << 4) -#define PSB_D_DBI_BF (1 << 5) -#define PSB_D_PM (1 << 6) -#define PSB_D_RENDER (1 << 7) -#define PSB_D_REG (1 << 8) -#define PSB_D_MSVDX (1 << 9) -#define PSB_D_TOPAZ (1 << 10) - -extern int drm_psb_no_fb; -extern int drm_idle_check_interval; - -/* - * Utilities - */ - -static inline u32 MRST_MSG_READ32(uint port, uint offset) -{ - int mcr = (0xD0<<24) | (port << 16) | (offset << 8); - uint32_t ret_val = 0; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_read_config_dword(pci_root, 0xD4, &ret_val); - pci_dev_put(pci_root); - return ret_val; -} -static inline void MRST_MSG_WRITE32(uint port, uint offset, u32 value) -{ - int mcr = (0xE0<<24) | (port << 16) | (offset << 8) | 0xF0; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - pci_write_config_dword(pci_root, 0xD4, value); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_dev_put(pci_root); -} -static inline u32 MDFLD_MSG_READ32(uint port, uint offset) -{ - int mcr = (0x10<<24) | (port << 16) | (offset << 8); - uint32_t ret_val = 0; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_read_config_dword(pci_root, 0xD4, &ret_val); - pci_dev_put(pci_root); - return ret_val; -} -static inline void MDFLD_MSG_WRITE32(uint port, uint offset, u32 value) -{ - int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - pci_write_config_dword(pci_root, 0xD4, value); - pci_write_config_dword(pci_root, 0xD0, mcr); - pci_dev_put(pci_root); -} - -static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - return ioread32(dev_priv->vdc_reg + reg); -} - -#define REG_READ(reg) REGISTER_READ(dev, (reg)) - -static inline void REGISTER_WRITE(struct drm_device *dev, uint32_t reg, - uint32_t val) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - iowrite32((val), dev_priv->vdc_reg + (reg)); -} - -#define REG_WRITE(reg, val) REGISTER_WRITE(dev, (reg), (val)) - -static inline void REGISTER_WRITE16(struct drm_device *dev, - uint32_t reg, uint32_t val) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - iowrite16((val), dev_priv->vdc_reg + (reg)); -} - -#define REG_WRITE16(reg, val) REGISTER_WRITE16(dev, (reg), (val)) - -static inline void REGISTER_WRITE8(struct drm_device *dev, - uint32_t reg, uint32_t val) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - iowrite8((val), dev_priv->vdc_reg + (reg)); -} - -#define REG_WRITE8(reg, val) REGISTER_WRITE8(dev, (reg), (val)) - -#define PSB_WVDC32(_val, _offs) iowrite32(_val, dev_priv->vdc_reg + (_offs)) -#define PSB_RVDC32(_offs) ioread32(dev_priv->vdc_reg + (_offs)) - -/* #define TRAP_SGX_PM_FAULT 1 */ -#ifdef TRAP_SGX_PM_FAULT -#define PSB_RSGX32(_offs) \ -({ \ - if (inl(dev_priv->apm_base + PSB_APM_STS) & 0x3) { \ - printk(KERN_ERR \ - "access sgx when it's off!! (READ) %s, %d\n", \ - __FILE__, __LINE__); \ - melay(1000); \ - } \ - ioread32(dev_priv->sgx_reg + (_offs)); \ -}) -#else -#define PSB_RSGX32(_offs) ioread32(dev_priv->sgx_reg + (_offs)) -#endif -#define PSB_WSGX32(_val, _offs) iowrite32(_val, dev_priv->sgx_reg + (_offs)) - -#define MSVDX_REG_DUMP 0 - -#define PSB_WMSVDX32(_val, _offs) iowrite32(_val, dev_priv->msvdx_reg + (_offs)) -#define PSB_RMSVDX32(_offs) ioread32(dev_priv->msvdx_reg + (_offs)) - -#endif diff --git a/drivers/staging/gma500/psb_intel_display.c b/drivers/staging/gma500/psb_intel_display.c deleted file mode 100644 index 8565961..0000000 --- a/drivers/staging/gma500/psb_intel_display.c +++ /dev/null @@ -1,1429 +0,0 @@ -/* - * Copyright © 2006-2011 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - */ - -#include -#include - -#include -#include "framebuffer.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "psb_intel_display.h" -#include "power.h" - -#include "mdfld_output.h" - -struct psb_intel_clock_t { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -}; - -struct psb_intel_range_t { - int min, max; -}; - -struct psb_intel_p2_t { - int dot_limit; - int p2_slow, p2_fast; -}; - -#define INTEL_P2_NUM 2 - -struct psb_intel_limit_t { - struct psb_intel_range_t dot, vco, n, m, m1, m2, p, p1; - struct psb_intel_p2_t p2; -}; - -#define I8XX_DOT_MIN 25000 -#define I8XX_DOT_MAX 350000 -#define I8XX_VCO_MIN 930000 -#define I8XX_VCO_MAX 1400000 -#define I8XX_N_MIN 3 -#define I8XX_N_MAX 16 -#define I8XX_M_MIN 96 -#define I8XX_M_MAX 140 -#define I8XX_M1_MIN 18 -#define I8XX_M1_MAX 26 -#define I8XX_M2_MIN 6 -#define I8XX_M2_MAX 16 -#define I8XX_P_MIN 4 -#define I8XX_P_MAX 128 -#define I8XX_P1_MIN 2 -#define I8XX_P1_MAX 33 -#define I8XX_P1_LVDS_MIN 1 -#define I8XX_P1_LVDS_MAX 6 -#define I8XX_P2_SLOW 4 -#define I8XX_P2_FAST 2 -#define I8XX_P2_LVDS_SLOW 14 -#define I8XX_P2_LVDS_FAST 14 /* No fast option */ -#define I8XX_P2_SLOW_LIMIT 165000 - -#define I9XX_DOT_MIN 20000 -#define I9XX_DOT_MAX 400000 -#define I9XX_VCO_MIN 1400000 -#define I9XX_VCO_MAX 2800000 -#define I9XX_N_MIN 3 -#define I9XX_N_MAX 8 -#define I9XX_M_MIN 70 -#define I9XX_M_MAX 120 -#define I9XX_M1_MIN 10 -#define I9XX_M1_MAX 20 -#define I9XX_M2_MIN 5 -#define I9XX_M2_MAX 9 -#define I9XX_P_SDVO_DAC_MIN 5 -#define I9XX_P_SDVO_DAC_MAX 80 -#define I9XX_P_LVDS_MIN 7 -#define I9XX_P_LVDS_MAX 98 -#define I9XX_P1_MIN 1 -#define I9XX_P1_MAX 8 -#define I9XX_P2_SDVO_DAC_SLOW 10 -#define I9XX_P2_SDVO_DAC_FAST 5 -#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000 -#define I9XX_P2_LVDS_SLOW 14 -#define I9XX_P2_LVDS_FAST 7 -#define I9XX_P2_LVDS_SLOW_LIMIT 112000 - -#define INTEL_LIMIT_I8XX_DVO_DAC 0 -#define INTEL_LIMIT_I8XX_LVDS 1 -#define INTEL_LIMIT_I9XX_SDVO_DAC 2 -#define INTEL_LIMIT_I9XX_LVDS 3 - -static const struct psb_intel_limit_t psb_intel_limits[] = { - { /* INTEL_LIMIT_I8XX_DVO_DAC */ - .dot = {.min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX}, - .vco = {.min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX}, - .n = {.min = I8XX_N_MIN, .max = I8XX_N_MAX}, - .m = {.min = I8XX_M_MIN, .max = I8XX_M_MAX}, - .m1 = {.min = I8XX_M1_MIN, .max = I8XX_M1_MAX}, - .m2 = {.min = I8XX_M2_MIN, .max = I8XX_M2_MAX}, - .p = {.min = I8XX_P_MIN, .max = I8XX_P_MAX}, - .p1 = {.min = I8XX_P1_MIN, .max = I8XX_P1_MAX}, - .p2 = {.dot_limit = I8XX_P2_SLOW_LIMIT, - .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST}, - }, - { /* INTEL_LIMIT_I8XX_LVDS */ - .dot = {.min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX}, - .vco = {.min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX}, - .n = {.min = I8XX_N_MIN, .max = I8XX_N_MAX}, - .m = {.min = I8XX_M_MIN, .max = I8XX_M_MAX}, - .m1 = {.min = I8XX_M1_MIN, .max = I8XX_M1_MAX}, - .m2 = {.min = I8XX_M2_MIN, .max = I8XX_M2_MAX}, - .p = {.min = I8XX_P_MIN, .max = I8XX_P_MAX}, - .p1 = {.min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX}, - .p2 = {.dot_limit = I8XX_P2_SLOW_LIMIT, - .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST}, - }, - { /* INTEL_LIMIT_I9XX_SDVO_DAC */ - .dot = {.min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, - .vco = {.min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX}, - .n = {.min = I9XX_N_MIN, .max = I9XX_N_MAX}, - .m = {.min = I9XX_M_MIN, .max = I9XX_M_MAX}, - .m1 = {.min = I9XX_M1_MIN, .max = I9XX_M1_MAX}, - .m2 = {.min = I9XX_M2_MIN, .max = I9XX_M2_MAX}, - .p = {.min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX}, - .p1 = {.min = I9XX_P1_MIN, .max = I9XX_P1_MAX}, - .p2 = {.dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, - .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = - I9XX_P2_SDVO_DAC_FAST}, - }, - { /* INTEL_LIMIT_I9XX_LVDS */ - .dot = {.min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, - .vco = {.min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX}, - .n = {.min = I9XX_N_MIN, .max = I9XX_N_MAX}, - .m = {.min = I9XX_M_MIN, .max = I9XX_M_MAX}, - .m1 = {.min = I9XX_M1_MIN, .max = I9XX_M1_MAX}, - .m2 = {.min = I9XX_M2_MIN, .max = I9XX_M2_MAX}, - .p = {.min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX}, - .p1 = {.min = I9XX_P1_MIN, .max = I9XX_P1_MAX}, - /* The single-channel range is 25-112Mhz, and dual-channel - * is 80-224Mhz. Prefer single channel as much as possible. - */ - .p2 = {.dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, - .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST}, - }, -}; - -static const struct psb_intel_limit_t *psb_intel_limit(struct drm_crtc *crtc) -{ - const struct psb_intel_limit_t *limit; - - if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - limit = &psb_intel_limits[INTEL_LIMIT_I9XX_LVDS]; - else - limit = &psb_intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; - return limit; -} - -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ - -static void i8xx_clock(int refclk, struct psb_intel_clock_t *clock) -{ - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); - clock->p = clock->p1 * clock->p2; - clock->vco = refclk * clock->m / (clock->n + 2); - clock->dot = clock->vco / clock->p; -} - -/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ - -static void i9xx_clock(int refclk, struct psb_intel_clock_t *clock) -{ - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); - clock->p = clock->p1 * clock->p2; - clock->vco = refclk * clock->m / (clock->n + 2); - clock->dot = clock->vco / clock->p; -} - -static void psb_intel_clock(struct drm_device *dev, int refclk, - struct psb_intel_clock_t *clock) -{ - return i9xx_clock(refclk, clock); -} - -/** - * Returns whether any output on the specified pipe is of the specified type - */ -bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_connector *l_entry; - - list_for_each_entry(l_entry, &mode_config->connector_list, head) { - if (l_entry->encoder && l_entry->encoder->crtc == crtc) { - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(l_entry); - if (psb_intel_output->type == type) - return true; - } - } - return false; -} - -#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } -/** - * Returns whether the given set of divisors are valid for a given refclk with - * the given connectors. - */ - -static bool psb_intel_PLL_is_valid(struct drm_crtc *crtc, - struct psb_intel_clock_t *clock) -{ - const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); - - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - INTELPllInvalid("p1 out of range\n"); - if (clock->p < limit->p.min || limit->p.max < clock->p) - INTELPllInvalid("p out of range\n"); - if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) - INTELPllInvalid("m2 out of range\n"); - if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) - INTELPllInvalid("m1 out of range\n"); - if (clock->m1 <= clock->m2) - INTELPllInvalid("m1 <= m2\n"); - if (clock->m < limit->m.min || limit->m.max < clock->m) - INTELPllInvalid("m out of range\n"); - if (clock->n < limit->n.min || limit->n.max < clock->n) - INTELPllInvalid("n out of range\n"); - if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) - INTELPllInvalid("vco out of range\n"); - /* XXX: We may need to be checking "Dot clock" - * depending on the multiplier, connector, etc., - * rather than just a single range. - */ - if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) - INTELPllInvalid("dot out of range\n"); - - return true; -} - -/** - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - */ -static bool psb_intel_find_best_PLL(struct drm_crtc *crtc, int target, - int refclk, - struct psb_intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_clock_t clock; - const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); - int err = target; - - if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { - /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. - */ - if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) - clock.p2 = limit->p2.p2_fast; - else - clock.p2 = limit->p2.p2_slow; - } else { - if (target < limit->p2.dot_limit) - clock.p2 = limit->p2.p2_slow; - else - clock.p2 = limit->p2.p2_fast; - } - - memset(best_clock, 0, sizeof(*best_clock)); - - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; - clock.m1++) { - for (clock.m2 = limit->m2.min; - clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; - clock.m2++) { - for (clock.n = limit->n.min; - clock.n <= limit->n.max; clock.n++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; - clock.p1++) { - int this_err; - - psb_intel_clock(dev, refclk, &clock); - - if (!psb_intel_PLL_is_valid - (crtc, &clock)) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - } - - return err != target; -} - -void psb_intel_wait_for_vblank(struct drm_device *dev) -{ - /* Wait for 20ms, i.e. one cycle at 50hz. */ - mdelay(20); -} - -int psb_intel_pipe_set_base(struct drm_crtc *crtc, - int x, int y, struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); - int pipe = psb_intel_crtc->pipe; - unsigned long start, offset; - int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - u32 dspcntr; - int ret = 0; - - if (!gma_power_begin(dev, true)) - return 0; - - /* no fb bound */ - if (!crtc->fb) { - dev_dbg(dev->dev, "No FB bound\n"); - goto psb_intel_pipe_cleaner; - } - - /* We are displaying this buffer, make sure it is actually loaded - into the GTT */ - ret = psb_gtt_pin(psbfb->gtt); - if (ret < 0) - goto psb_intel_pipe_set_base_exit; - start = psbfb->gtt->offset; - - offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); - - REG_WRITE(dspstride, crtc->fb->pitches[0]); - - dspcntr = REG_READ(dspcntr_reg); - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - - switch (crtc->fb->bits_per_pixel) { - case 8: - dspcntr |= DISPPLANE_8BPP; - break; - case 16: - if (crtc->fb->depth == 15) - dspcntr |= DISPPLANE_15_16BPP; - else - dspcntr |= DISPPLANE_16BPP; - break; - case 24: - case 32: - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; - break; - default: - dev_err(dev->dev, "Unknown color depth\n"); - ret = -EINVAL; - psb_gtt_unpin(psbfb->gtt); - goto psb_intel_pipe_set_base_exit; - } - REG_WRITE(dspcntr_reg, dspcntr); - - - if (0 /* FIXMEAC - check what PSB needs */) { - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - } else { - REG_WRITE(dspbase, start + offset); - REG_READ(dspbase); - } - -psb_intel_pipe_cleaner: - /* If there was a previous display we can now unpin it */ - if (old_fb) - psb_gtt_unpin(to_psb_fb(old_fb)->gtt); - -psb_intel_pipe_set_base_exit: - gma_power_end(dev); - return ret; -} - -/** - * Sets the power management mode of the pipe and plane. - * - * This code should probably grow support for turning the cursor off and back - * on appropriately at the same time as we're turning the pipe off/on. - */ -static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ - /* struct drm_i915_private *dev_priv = dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - u32 temp; - bool enabled; - - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable the DPLL */ - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) == 0) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } - - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - - /* Enable the plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(dspcntr_reg, - temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - } - - psb_intel_crtc_load_lut(crtc); - - /* Give the overlay scaler a chance to enable - * if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, true); TODO */ - break; - case DRM_MODE_DPMS_OFF: - /* Give the overlay scaler a chance to disable - * if it's on this pipe */ - /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* Disable display plane */ - temp = REG_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(dspcntr_reg, - temp & ~DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); - REG_READ(dspbase_reg); - } - - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); - } - - /* Wait for vblank for the disable to take effect. */ - psb_intel_wait_for_vblank(dev); - - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - } - - /* Wait for the clocks to turn off. */ - udelay(150); - break; - } - - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - - /*Set FIFO Watermarks*/ - REG_WRITE(DSPARB, 0x3F3E); -} - -static void psb_intel_crtc_prepare(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); -} - -static void psb_intel_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -} - -void psb_intel_encoder_prepare(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - /* lvds has its own version of prepare see psb_intel_lvds_prepare */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); -} - -void psb_intel_encoder_commit(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - /* lvds has its own version of commit see psb_intel_lvds_commit */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); -} - -static bool psb_intel_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - - -/** - * Return the pipe currently connected to the panel fitter, - * or -1 if the panel fitter is not present or not in use - */ -static int psb_intel_panel_fitter_pipe(struct drm_device *dev) -{ - u32 pfit_control; - - pfit_control = REG_READ(PFIT_CONTROL); - - /* See if the panel fitter is in use */ - if ((pfit_control & PFIT_ENABLE) == 0) - return -1; - /* Must be on PIPE 1 for PSB */ - return 1; -} - -static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - int pipe = psb_intel_crtc->pipe; - int fp_reg = (pipe == 0) ? FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int refclk; - struct psb_intel_clock_t clock; - u32 dpll = 0, fp = 0, dspcntr, pipeconf; - bool ok, is_sdvo = false, is_dvo = false; - bool is_crt = false, is_lvds = false, is_tv = false; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_connector *connector; - - /* No scan out no play */ - if (crtc->fb == NULL) { - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - return 0; - } - - list_for_each_entry(connector, &mode_config->connector_list, head) { - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - if (!connector->encoder - || connector->encoder->crtc != crtc) - continue; - - switch (psb_intel_output->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - case INTEL_OUTPUT_SDVO: - is_sdvo = true; - break; - case INTEL_OUTPUT_DVO: - is_dvo = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; - } - } - - refclk = 96000; - - ok = psb_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, - &clock); - if (!ok) { - dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); - return 0; - } - - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - - dpll = DPLL_VGA_MODE_DIS; - if (is_lvds) { - dpll |= DPLLB_MODE_LVDS; - dpll |= DPLL_DVO_HIGH_SPEED; - } else - dpll |= DPLLB_MODE_DAC_SERIAL; - if (is_sdvo) { - int sdvo_pixel_multiply = - adjusted_mode->clock / mode->clock; - dpll |= DPLL_DVO_HIGH_SPEED; - dpll |= - (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - } - - /* compute bitmask from p1 value */ - dpll |= (1 << (clock.p1 - 1)) << 16; - switch (clock.p2) { - case 5: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; - break; - case 7: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; - break; - case 10: - dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; - break; - case 14: - dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; - break; - } - - if (is_tv) { - /* XXX: just matching BIOS for now */ -/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; - } - dpll |= PLL_REF_INPUT_DREFCLK; - - /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); - - /* Set up the display plane register */ - dspcntr = DISPPLANE_GAMMA_ENABLE; - - if (pipe == 0) - dspcntr |= DISPPLANE_SEL_PIPE_A; - else - dspcntr |= DISPPLANE_SEL_PIPE_B; - - dspcntr |= DISPLAY_PLANE_ENABLE; - pipeconf |= PIPEACONF_ENABLE; - dpll |= DPLL_VCO_ENABLE; - - - /* Disable the panel fitter if it was on our pipe */ - if (psb_intel_panel_fitter_pipe(dev) == pipe) - REG_WRITE(PFIT_CONTROL, 0); - - drm_mode_debug_printmodeline(mode); - - if (dpll & DPLL_VCO_ENABLE) { - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - udelay(150); - } - - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (is_lvds) { - u32 lvds = REG_READ(LVDS); - - lvds &= ~LVDS_PIPEB_SELECT; - if (pipe == 1) - lvds |= LVDS_PIPEB_SELECT; - - lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - /* Set the B0-B3 data pairs corresponding to - * whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - if (clock.p2 == 7) - lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more - * thoroughly into how panels behave in the two modes. - */ - - REG_WRITE(LVDS, lvds); - REG_READ(LVDS); - } - - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - - /* write it again -- the BIOS does, after all */ - REG_WRITE(dpll_reg, dpll); - - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | - ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | - ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | - ((adjusted_mode->crtc_vsync_end - 1) << 16)); - /* pipesrc and dspsize control the size that is scaled from, - * which should always be the user's requested size. - */ - REG_WRITE(dspsize_reg, - ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - REG_WRITE(pipesrc_reg, - ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - - psb_intel_wait_for_vblank(dev); - - REG_WRITE(dspcntr_reg, dspcntr); - - /* Flush the plane changes */ - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - - psb_intel_wait_for_vblank(dev); - - return 0; -} - -/** Loads the palette/gamma unit for the CRTC with the prepared values */ -void psb_intel_crtc_load_lut(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int palreg = PALETTE_A; - int i; - - /* The clocks have to be on to load the palette. */ - if (!crtc->enabled) - return; - - switch (psb_intel_crtc->pipe) { - case 0: - break; - case 1: - palreg = PALETTE_B; - break; - case 2: - palreg = PALETTE_C; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return; - } - - if (gma_power_begin(dev, false)) { - for (i = 0; i < 256; i++) { - REG_WRITE(palreg + 4 * i, - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i])); - } - gma_power_end(dev); - } else { - for (i = 0; i < 256; i++) { - dev_priv->save_palette_a[i] = - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i]); - } - - } -} - -/** - * Save HW states of giving crtc - */ -static void psb_intel_crtc_save(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_err(dev->dev, "No CRTC state found\n"); - return; - } - - crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); - crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); - crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); - crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); - crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); - crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); - crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); - crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); - crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); - crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); - crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); - crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); - crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); - - /*NOTE: DSPSIZE DSPPOS only for psb*/ - crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); - crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); - - crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); -} - -/** - * Restore HW states of giving crtc - */ -static void psb_intel_crtc_restore(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private * dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_err(dev->dev, "No crtc state\n"); - return; - } - - if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { - REG_WRITE(pipeA ? DPLL_A : DPLL_B, - crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); - REG_READ(pipeA ? DPLL_A : DPLL_B); - udelay(150); - } - - REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); - REG_READ(pipeA ? FPA0 : FPB0); - - REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); - REG_READ(pipeA ? FPA1 : FPB1); - - REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); - REG_READ(pipeA ? DPLL_A : DPLL_B); - udelay(150); - - REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); - REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); - REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); - REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); - REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); - REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); - REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); - - REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); - REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); - - REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); - - psb_intel_wait_for_vblank(dev); - - REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - - psb_intel_wait_for_vblank(dev); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); -} - -static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; - uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; - uint32_t temp; - size_t addr = 0; - struct gtt_range *gt; - struct drm_gem_object *obj; - int ret; - - /* if we want to turn of the cursor ignore width and height */ - if (!handle) { - /* turn off the cursor */ - temp = CURSOR_MODE_DISABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, 0); - gma_power_end(dev); - } - - /* Unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - - return 0; - } - - /* Currently we only support 64x64 cursors */ - if (width != 64 || height != 64) { - dev_dbg(dev->dev, "we currently only support 64x64 cursors\n"); - return -EINVAL; - } - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; - - if (obj->size < width * height * 4) { - dev_dbg(dev->dev, "buffer is to small\n"); - return -ENOMEM; - } - - gt = container_of(obj, struct gtt_range, gem); - - /* Pin the memory into the GTT */ - ret = psb_gtt_pin(gt); - if (ret) { - dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); - return ret; - } - - - addr = gt->offset; /* Or resource.start ??? */ - - psb_intel_crtc->cursor_addr = addr; - - temp = 0; - /* set the pipe for the cursor */ - temp |= (pipe << 28); - temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, addr); - gma_power_end(dev); - } - - /* unpin the old bo */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = obj; - } - return 0; -} - -static int psb_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t temp = 0; - uint32_t addr; - - - if (x < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); - x = -x; - } - if (y < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); - y = -y; - } - - temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); - temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); - - addr = psb_intel_crtc->cursor_addr; - - if (gma_power_begin(dev, false)) { - REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); - REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, addr); - gma_power_end(dev); - } - return 0; -} - -void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t type, uint32_t size) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int i; - - if (size != 256) - return; - - for (i = 0; i < 256; i++) { - psb_intel_crtc->lut_r[i] = red[i] >> 8; - psb_intel_crtc->lut_g[i] = green[i] >> 8; - psb_intel_crtc->lut_b[i] = blue[i] >> 8; - } - - psb_intel_crtc_load_lut(crtc); -} - -static int psb_crtc_set_config(struct drm_mode_set *set) -{ - int ret; - struct drm_device *dev = set->crtc->dev; - - pm_runtime_forbid(&dev->pdev->dev); - ret = drm_crtc_helper_set_config(set); - pm_runtime_allow(&dev->pdev->dev); - return ret; -} - -/* Returns the clock of the currently programmed mode of the given pipe. */ -static int psb_intel_crtc_clock_get(struct drm_device *dev, - struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - u32 dpll; - u32 fp; - struct psb_intel_clock_t clock; - bool is_lvds; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (gma_power_begin(dev, false)) { - dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); - if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = REG_READ((pipe == 0) ? FPA0 : FPB0); - else - fp = REG_READ((pipe == 0) ? FPA1 : FPB1); - is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); - gma_power_end(dev); - } else { - dpll = (pipe == 0) ? - dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; - - if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = (pipe == 0) ? - dev_priv->saveFPA0 : - dev_priv->saveFPB0; - else - fp = (pipe == 0) ? - dev_priv->saveFPA1 : - dev_priv->saveFPB1; - - is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN); - } - - clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; - clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; - clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; - - if (is_lvds) { - clock.p1 = - ffs((dpll & - DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - clock.p2 = 14; - - if ((dpll & PLL_REF_INPUT_MASK) == - PLLB_REF_INPUT_SPREADSPECTRUMIN) { - /* XXX: might not be 66MHz */ - i8xx_clock(66000, &clock); - } else - i8xx_clock(48000, &clock); - } else { - if (dpll & PLL_P1_DIVIDE_BY_TWO) - clock.p1 = 2; - else { - clock.p1 = - ((dpll & - DPLL_FPA01_P1_POST_DIV_MASK_I830) >> - DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; - } - if (dpll & PLL_P2_DIVIDE_BY_4) - clock.p2 = 4; - else - clock.p2 = 2; - - i8xx_clock(48000, &clock); - } - - /* XXX: It would be nice to validate the clocks, but we can't reuse - * i830PllIsValid() because it relies on the xf86_config connector - * configuration being accurate, which it isn't necessarily. - */ - - return clock.dot; -} - -/** Returns the currently programmed mode of the given pipe. */ -struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, - struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - struct drm_display_mode *mode; - int htot; - int hsync; - int vtot; - int vsync; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (gma_power_begin(dev, false)) { - htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); - hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); - vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); - vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); - gma_power_end(dev); - } else { - htot = (pipe == 0) ? - dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; - hsync = (pipe == 0) ? - dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; - vtot = (pipe == 0) ? - dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; - vsync = (pipe == 0) ? - dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; - } - - mode = kzalloc(sizeof(*mode), GFP_KERNEL); - if (!mode) - return NULL; - - mode->clock = psb_intel_crtc_clock_get(dev, crtc); - mode->hdisplay = (htot & 0xffff) + 1; - mode->htotal = ((htot & 0xffff0000) >> 16) + 1; - mode->hsync_start = (hsync & 0xffff) + 1; - mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; - mode->vdisplay = (vtot & 0xffff) + 1; - mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; - mode->vsync_start = (vsync & 0xffff) + 1; - mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; - - drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); - - return mode; -} - -void psb_intel_crtc_destroy(struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct gtt_range *gt; - - /* Unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - kfree(psb_intel_crtc->crtc_state); - drm_crtc_cleanup(crtc); - kfree(psb_intel_crtc); -} - -const struct drm_crtc_helper_funcs psb_intel_helper_funcs = { - .dpms = psb_intel_crtc_dpms, - .mode_fixup = psb_intel_crtc_mode_fixup, - .mode_set = psb_intel_crtc_mode_set, - .mode_set_base = psb_intel_pipe_set_base, - .prepare = psb_intel_crtc_prepare, - .commit = psb_intel_crtc_commit, -}; - -const struct drm_crtc_funcs psb_intel_crtc_funcs = { - .save = psb_intel_crtc_save, - .restore = psb_intel_crtc_restore, - .cursor_set = psb_intel_crtc_cursor_set, - .cursor_move = psb_intel_crtc_cursor_move, - .gamma_set = psb_intel_crtc_gamma_set, - .set_config = psb_crtc_set_config, - .destroy = psb_intel_crtc_destroy, -}; - -/* - * Set the default value of cursor control and base register - * to zero. This is a workaround for h/w defect on Oaktrail - */ -static void psb_intel_cursor_init(struct drm_device *dev, int pipe) -{ - u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; - u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; - - REG_WRITE(control[pipe], 0); - REG_WRITE(base[pipe], 0); -} - -void psb_intel_crtc_init(struct drm_device *dev, int pipe, - struct psb_intel_mode_device *mode_dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc; - int i; - uint16_t *r_base, *g_base, *b_base; - - /* We allocate a extra array of drm_connector pointers - * for fbdev after the crtc */ - psb_intel_crtc = - kzalloc(sizeof(struct psb_intel_crtc) + - (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), - GFP_KERNEL); - if (psb_intel_crtc == NULL) - return; - - psb_intel_crtc->crtc_state = - kzalloc(sizeof(struct psb_intel_crtc_state), GFP_KERNEL); - if (!psb_intel_crtc->crtc_state) { - dev_err(dev->dev, "Crtc state error: No memory\n"); - kfree(psb_intel_crtc); - return; - } - - /* Set the CRTC operations from the chip specific data */ - drm_crtc_init(dev, &psb_intel_crtc->base, dev_priv->ops->crtc_funcs); - - drm_mode_crtc_set_gamma_size(&psb_intel_crtc->base, 256); - psb_intel_crtc->pipe = pipe; - psb_intel_crtc->plane = pipe; - - r_base = psb_intel_crtc->base.gamma_store; - g_base = r_base + 256; - b_base = g_base + 256; - for (i = 0; i < 256; i++) { - psb_intel_crtc->lut_r[i] = i; - psb_intel_crtc->lut_g[i] = i; - psb_intel_crtc->lut_b[i] = i; - r_base[i] = i << 8; - g_base[i] = i << 8; - b_base[i] = i << 8; - - psb_intel_crtc->lut_adj[i] = 0; - } - - psb_intel_crtc->mode_dev = mode_dev; - psb_intel_crtc->cursor_addr = 0; - - drm_crtc_helper_add(&psb_intel_crtc->base, - dev_priv->ops->crtc_helper); - - /* Setup the array of drm_connector pointer array */ - psb_intel_crtc->mode_set.crtc = &psb_intel_crtc->base; - BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || - dev_priv->plane_to_crtc_mapping[psb_intel_crtc->plane] != NULL); - dev_priv->plane_to_crtc_mapping[psb_intel_crtc->plane] = - &psb_intel_crtc->base; - dev_priv->pipe_to_crtc_mapping[psb_intel_crtc->pipe] = - &psb_intel_crtc->base; - psb_intel_crtc->mode_set.connectors = - (struct drm_connector **) (psb_intel_crtc + 1); - psb_intel_crtc->mode_set.num_connectors = 0; - psb_intel_cursor_init(dev, pipe); -} - -int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_psb_get_pipe_from_crtc_id_arg *pipe_from_crtc_id = data; - struct drm_mode_object *drmmode_obj; - struct psb_intel_crtc *crtc; - - if (!dev_priv) { - dev_err(dev->dev, "called with no initialization\n"); - return -EINVAL; - } - - drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, - DRM_MODE_OBJECT_CRTC); - - if (!drmmode_obj) { - dev_err(dev->dev, "no such CRTC id\n"); - return -EINVAL; - } - - crtc = to_psb_intel_crtc(obj_to_crtc(drmmode_obj)); - pipe_from_crtc_id->pipe = crtc->pipe; - - return 0; -} - -struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) -{ - struct drm_crtc *crtc = NULL; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - if (psb_intel_crtc->pipe == pipe) - break; - } - return crtc; -} - -int psb_intel_connector_clones(struct drm_device *dev, int type_mask) -{ - int index_mask = 0; - struct drm_connector *connector; - int entry = 0; - - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - if (type_mask & (1 << psb_intel_output->type)) - index_mask |= (1 << entry); - entry++; - } - return index_mask; -} - - -void psb_intel_modeset_cleanup(struct drm_device *dev) -{ - drm_mode_config_cleanup(dev); -} - - -/* current intel driver doesn't take advantage of encoders - always give back the encoder for the connector -*/ -struct drm_encoder *psb_intel_best_encoder(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - return &psb_intel_output->enc; -} - diff --git a/drivers/staging/gma500/psb_intel_display.h b/drivers/staging/gma500/psb_intel_display.h deleted file mode 100644 index 535b49a..0000000 --- a/drivers/staging/gma500/psb_intel_display.h +++ /dev/null @@ -1,28 +0,0 @@ -/* copyright (c) 2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - */ - -#ifndef _INTEL_DISPLAY_H_ -#define _INTEL_DISPLAY_H_ - -bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type); -void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t type, uint32_t size); -void psb_intel_crtc_destroy(struct drm_crtc *crtc); - -#endif diff --git a/drivers/staging/gma500/psb_intel_drv.h b/drivers/staging/gma500/psb_intel_drv.h deleted file mode 100644 index 36b554b..0000000 --- a/drivers/staging/gma500/psb_intel_drv.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2009-2011, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#ifndef __INTEL_DRV_H__ -#define __INTEL_DRV_H__ - -#include -#include -#include -#include -#include - -/* - * Display related stuff - */ - -/* store information about an Ixxx DVO */ -/* The i830->i865 use multiple DVOs with multiple i2cs */ -/* the i915, i945 have a single sDVO i2c bus - which is different */ -#define MAX_OUTPUTS 6 -/* maximum connectors per crtcs in the mode set */ -#define INTELFB_CONN_LIMIT 4 - -#define INTEL_I2C_BUS_DVO 1 -#define INTEL_I2C_BUS_SDVO 2 - -/* these are outputs from the chip - integrated only - * external chips are via DVO or SDVO output */ -#define INTEL_OUTPUT_UNUSED 0 -#define INTEL_OUTPUT_ANALOG 1 -#define INTEL_OUTPUT_DVO 2 -#define INTEL_OUTPUT_SDVO 3 -#define INTEL_OUTPUT_LVDS 4 -#define INTEL_OUTPUT_TVOUT 5 -#define INTEL_OUTPUT_HDMI 6 -#define INTEL_OUTPUT_MIPI 7 -#define INTEL_OUTPUT_MIPI2 8 - -#define INTEL_DVO_CHIP_NONE 0 -#define INTEL_DVO_CHIP_LVDS 1 -#define INTEL_DVO_CHIP_TMDS 2 -#define INTEL_DVO_CHIP_TVOUT 4 - -/* - * Hold information useally put on the device driver privates here, - * since it needs to be shared across multiple of devices drivers privates. - */ -struct psb_intel_mode_device { - - /* - * Abstracted memory manager operations - */ - size_t(*bo_offset) (struct drm_device *dev, void *bo); - - /* - * Cursor (Can go ?) - */ - int cursor_needs_physical; - - /* - * LVDS info - */ - int backlight_duty_cycle; /* restore backlight to this value */ - bool panel_wants_dither; - struct drm_display_mode *panel_fixed_mode; - struct drm_display_mode *panel_fixed_mode2; - struct drm_display_mode *vbt_mode; /* if any */ - - uint32_t saveBLC_PWM_CTL; -}; - -struct psb_intel_i2c_chan { - /* for getting at dev. private (mmio etc.) */ - struct drm_device *drm_dev; - u32 reg; /* GPIO reg */ - struct i2c_adapter adapter; - struct i2c_algo_bit_data algo; - u8 slave_addr; -}; - -struct psb_intel_output { - struct drm_connector base; - - struct drm_encoder enc; - int type; - - struct psb_intel_i2c_chan *i2c_bus; /* for control functions */ - struct psb_intel_i2c_chan *ddc_bus; /* for DDC only stuff */ - bool load_detect_temp; - void *dev_priv; - - struct psb_intel_mode_device *mode_dev; - struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */ -}; - -struct psb_intel_crtc_state { - uint32_t saveDSPCNTR; - uint32_t savePIPECONF; - uint32_t savePIPESRC; - uint32_t saveDPLL; - uint32_t saveFP0; - uint32_t saveFP1; - uint32_t saveHTOTAL; - uint32_t saveHBLANK; - uint32_t saveHSYNC; - uint32_t saveVTOTAL; - uint32_t saveVBLANK; - uint32_t saveVSYNC; - uint32_t saveDSPSTRIDE; - uint32_t saveDSPSIZE; - uint32_t saveDSPPOS; - uint32_t saveDSPBASE; - uint32_t savePalette[256]; -}; - -struct psb_intel_crtc { - struct drm_crtc base; - int pipe; - int plane; - uint32_t cursor_addr; - u8 lut_r[256], lut_g[256], lut_b[256]; - u8 lut_adj[256]; - struct psb_intel_framebuffer *fbdev_fb; - /* a mode_set for fbdev users on this crtc */ - struct drm_mode_set mode_set; - - /* GEM object that holds our cursor */ - struct drm_gem_object *cursor_obj; - - struct drm_display_mode saved_mode; - struct drm_display_mode saved_adjusted_mode; - - struct psb_intel_mode_device *mode_dev; - - /*crtc mode setting flags*/ - u32 mode_flags; - - /* Saved Crtc HW states */ - struct psb_intel_crtc_state *crtc_state; -}; - -#define to_psb_intel_crtc(x) \ - container_of(x, struct psb_intel_crtc, base) -#define to_psb_intel_output(x) \ - container_of(x, struct psb_intel_output, base) -#define enc_to_psb_intel_output(x) \ - container_of(x, struct psb_intel_output, enc) -#define to_psb_intel_framebuffer(x) \ - container_of(x, struct psb_intel_framebuffer, base) - -struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, - const u32 reg, const char *name); -void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan); -int psb_intel_ddc_get_modes(struct psb_intel_output *psb_intel_output); -extern bool psb_intel_ddc_probe(struct psb_intel_output *psb_intel_output); - -extern void psb_intel_crtc_init(struct drm_device *dev, int pipe, - struct psb_intel_mode_device *mode_dev); -extern void psb_intel_crt_init(struct drm_device *dev); -extern void psb_intel_sdvo_init(struct drm_device *dev, int output_device); -extern void psb_intel_dvo_init(struct drm_device *dev); -extern void psb_intel_tv_init(struct drm_device *dev); -extern void psb_intel_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev); -extern void psb_intel_lvds_set_brightness(struct drm_device *dev, int level); -extern void mrst_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev); -extern void mrst_wait_for_INTR_PKT_SENT(struct drm_device *dev); -extern void mrst_dsi_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev); -extern void mid_dsi_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev, int dsi_num); - -extern void psb_intel_crtc_load_lut(struct drm_crtc *crtc); -extern void psb_intel_encoder_prepare(struct drm_encoder *encoder); -extern void psb_intel_encoder_commit(struct drm_encoder *encoder); - -extern struct drm_encoder *psb_intel_best_encoder(struct drm_connector - *connector); - -extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, - struct drm_crtc *crtc); -extern void psb_intel_wait_for_vblank(struct drm_device *dev); -extern int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, - int pipe); -extern struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, - int sdvoB); -extern int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector); -extern void psb_intel_sdvo_set_hotplug(struct drm_connector *connector, - int enable); -extern int intelfb_probe(struct drm_device *dev); -extern int intelfb_remove(struct drm_device *dev, - struct drm_framebuffer *fb); -extern struct drm_framebuffer *psb_intel_framebuffer_create(struct drm_device - *dev, struct - drm_mode_fb_cmd - *mode_cmd, - void *mm_private); -extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -extern int psb_intel_lvds_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode); -extern int psb_intel_lvds_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t value); -extern void psb_intel_lvds_destroy(struct drm_connector *connector); -extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs; - -extern void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe); -extern void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe); - -#endif /* __INTEL_DRV_H__ */ diff --git a/drivers/staging/gma500/psb_intel_lvds.c b/drivers/staging/gma500/psb_intel_lvds.c deleted file mode 100644 index 21022e1..0000000 --- a/drivers/staging/gma500/psb_intel_lvds.c +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright © 2006-2007 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - * Dave Airlie - * Jesse Barnes - */ - -#include -#include - -#include "intel_bios.h" -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "power.h" -#include - -/* - * LVDS I2C backlight control macros - */ -#define BRIGHTNESS_MAX_LEVEL 100 -#define BRIGHTNESS_MASK 0xFF -#define BLC_I2C_TYPE 0x01 -#define BLC_PWM_TYPT 0x02 - -#define BLC_POLARITY_NORMAL 0 -#define BLC_POLARITY_INVERSE 1 - -#define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE) -#define PSB_BLC_MIN_PWM_REG_FREQ (0x2) -#define PSB_BLC_PWM_PRECISION_FACTOR (10) -#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) -#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) - -struct psb_intel_lvds_priv { - /* - * Saved LVDO output states - */ - uint32_t savePP_ON; - uint32_t savePP_OFF; - uint32_t saveLVDS; - uint32_t savePP_CONTROL; - uint32_t savePP_CYCLE; - uint32_t savePFIT_CONTROL; - uint32_t savePFIT_PGM_RATIOS; - uint32_t saveBLC_PWM_CTL; -}; - - -/* - * Returns the maximum level of the backlight duty cycle field. - */ -static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 ret; - - if (gma_power_begin(dev, false)) { - ret = REG_READ(BLC_PWM_CTL); - gma_power_end(dev); - } else /* Powered off, use the saved value */ - ret = dev_priv->saveBLC_PWM_CTL; - - /* Top 15bits hold the frequency mask */ - ret = (ret & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT; - - ret *= 2; /* Return a 16bit range as needed for setting */ - if (ret == 0) - dev_err(dev->dev, "BL bug: Reg %08x save %08X\n", - REG_READ(BLC_PWM_CTL), dev_priv->saveBLC_PWM_CTL); - return ret; -} - -/* - * Set LVDS backlight level by I2C command - * - * FIXME: at some point we need to both track this for PM and also - * disable runtime pm on MRST if the brightness is nil (ie blanked) - */ -static int psb_lvds_i2c_set_brightness(struct drm_device *dev, - unsigned int level) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - - struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; - u8 out_buf[2]; - unsigned int blc_i2c_brightness; - - struct i2c_msg msgs[] = { - { - .addr = lvds_i2c_bus->slave_addr, - .flags = 0, - .len = 2, - .buf = out_buf, - } - }; - - blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level * - BRIGHTNESS_MASK / - BRIGHTNESS_MAX_LEVEL); - - if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) - blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness; - - out_buf[0] = dev_priv->lvds_bl->brightnesscmd; - out_buf[1] = (u8)blc_i2c_brightness; - - if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) { - dev_dbg(dev->dev, "I2C set brightness.(command, value) (%d, %d)\n", - dev_priv->lvds_bl->brightnesscmd, - blc_i2c_brightness); - return 0; - } - - dev_err(dev->dev, "I2C transfer error\n"); - return -1; -} - - -static int psb_lvds_pwm_set_brightness(struct drm_device *dev, int level) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - - u32 max_pwm_blc; - u32 blc_pwm_duty_cycle; - - max_pwm_blc = psb_intel_lvds_get_max_backlight(dev); - - /*BLC_PWM_CTL Should be initiated while backlight device init*/ - BUG_ON(max_pwm_blc == 0); - - blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL; - - if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE) - blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle; - - blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR; - REG_WRITE(BLC_PWM_CTL, - (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | - (blc_pwm_duty_cycle)); - - dev_info(dev->dev, "Backlight lvds set brightness %08x\n", - (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) | - (blc_pwm_duty_cycle)); - - return 0; -} - -/* - * Set LVDS backlight level either by I2C or PWM - */ -void psb_intel_lvds_set_brightness(struct drm_device *dev, int level) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - - dev_dbg(dev->dev, "backlight level is %d\n", level); - - if (!dev_priv->lvds_bl) { - dev_err(dev->dev, "NO LVDS backlight info\n"); - return; - } - - if (dev_priv->lvds_bl->type == BLC_I2C_TYPE) - psb_lvds_i2c_set_brightness(dev, level); - else - psb_lvds_pwm_set_brightness(dev, level); -} - -/* - * Sets the backlight level. - * - * level: backlight level, from 0 to psb_intel_lvds_get_max_backlight(). - */ -static void psb_intel_lvds_set_backlight(struct drm_device *dev, int level) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - u32 blc_pwm_ctl; - - if (gma_power_begin(dev, false)) { - blc_pwm_ctl = REG_READ(BLC_PWM_CTL); - blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; - REG_WRITE(BLC_PWM_CTL, - (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); - dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); - gma_power_end(dev); - } else { - blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & - ~BACKLIGHT_DUTY_CYCLE_MASK; - dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); - } -} - -/* - * Sets the power state for the panel. - */ -static void psb_intel_lvds_set_power(struct drm_device *dev, - struct psb_intel_output *output, bool on) -{ - u32 pp_status; - - if (!gma_power_begin(dev, true)) { - dev_err(dev->dev, "set power, chip off!\n"); - return; - } - - if (on) { - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | - POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); - - psb_intel_lvds_set_backlight(dev, - output-> - mode_dev->backlight_duty_cycle); - } else { - psb_intel_lvds_set_backlight(dev, 0); - - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & - ~POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while (pp_status & PP_ON); - } - - gma_power_end(dev); -} - -static void psb_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - - if (mode == DRM_MODE_DPMS_ON) - psb_intel_lvds_set_power(dev, output, true); - else - psb_intel_lvds_set_power(dev, output, false); - - /* XXX: We never power down the LVDS pairs. */ -} - -static void psb_intel_lvds_save(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_lvds_priv *lvds_priv = - (struct psb_intel_lvds_priv *)psb_intel_output->dev_priv; - - lvds_priv->savePP_ON = REG_READ(LVDSPP_ON); - lvds_priv->savePP_OFF = REG_READ(LVDSPP_OFF); - lvds_priv->saveLVDS = REG_READ(LVDS); - lvds_priv->savePP_CONTROL = REG_READ(PP_CONTROL); - lvds_priv->savePP_CYCLE = REG_READ(PP_CYCLE); - /*lvds_priv->savePP_DIVISOR = REG_READ(PP_DIVISOR);*/ - lvds_priv->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); - lvds_priv->savePFIT_CONTROL = REG_READ(PFIT_CONTROL); - lvds_priv->savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS); - - /*TODO: move backlight_duty_cycle to psb_intel_lvds_priv*/ - dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); - - /* - * If the light is off at server startup, - * just make it full brightness - */ - if (dev_priv->backlight_duty_cycle == 0) - dev_priv->backlight_duty_cycle = - psb_intel_lvds_get_max_backlight(dev); - - dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", - lvds_priv->savePP_ON, - lvds_priv->savePP_OFF, - lvds_priv->saveLVDS, - lvds_priv->savePP_CONTROL, - lvds_priv->savePP_CYCLE, - lvds_priv->saveBLC_PWM_CTL); -} - -static void psb_intel_lvds_restore(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - u32 pp_status; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_lvds_priv *lvds_priv = - (struct psb_intel_lvds_priv *)psb_intel_output->dev_priv; - - dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", - lvds_priv->savePP_ON, - lvds_priv->savePP_OFF, - lvds_priv->saveLVDS, - lvds_priv->savePP_CONTROL, - lvds_priv->savePP_CYCLE, - lvds_priv->saveBLC_PWM_CTL); - - REG_WRITE(BLC_PWM_CTL, lvds_priv->saveBLC_PWM_CTL); - REG_WRITE(PFIT_CONTROL, lvds_priv->savePFIT_CONTROL); - REG_WRITE(PFIT_PGM_RATIOS, lvds_priv->savePFIT_PGM_RATIOS); - REG_WRITE(LVDSPP_ON, lvds_priv->savePP_ON); - REG_WRITE(LVDSPP_OFF, lvds_priv->savePP_OFF); - /*REG_WRITE(PP_DIVISOR, lvds_priv->savePP_DIVISOR);*/ - REG_WRITE(PP_CYCLE, lvds_priv->savePP_CYCLE); - REG_WRITE(PP_CONTROL, lvds_priv->savePP_CONTROL); - REG_WRITE(LVDS, lvds_priv->saveLVDS); - - if (lvds_priv->savePP_CONTROL & POWER_TARGET_ON) { - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | - POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); - } else { - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & - ~POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while (pp_status & PP_ON); - } -} - -int psb_intel_lvds_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct drm_display_mode *fixed_mode = - psb_intel_output->mode_dev->panel_fixed_mode; - - if (psb_intel_output->type == INTEL_OUTPUT_MIPI2) - fixed_mode = psb_intel_output->mode_dev->panel_fixed_mode2; - - /* just in case */ - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - /* just in case */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - return MODE_NO_INTERLACE; - - if (fixed_mode) { - if (mode->hdisplay > fixed_mode->hdisplay) - return MODE_PANEL; - if (mode->vdisplay > fixed_mode->vdisplay) - return MODE_PANEL; - } - return MODE_OK; -} - -bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct psb_intel_mode_device *mode_dev = - enc_to_psb_intel_output(encoder)->mode_dev; - struct drm_device *dev = encoder->dev; - struct psb_intel_crtc *psb_intel_crtc = - to_psb_intel_crtc(encoder->crtc); - struct drm_encoder *tmp_encoder; - struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; - struct psb_intel_output *psb_intel_output = - enc_to_psb_intel_output(encoder); - - if (psb_intel_output->type == INTEL_OUTPUT_MIPI2) - panel_fixed_mode = mode_dev->panel_fixed_mode2; - - /* PSB requires the LVDS is on pipe B, MRST has only one pipe anyway */ - if (!IS_MRST(dev) && psb_intel_crtc->pipe == 0) { - printk(KERN_ERR "Can't support LVDS on pipe A\n"); - return false; - } - if (IS_MRST(dev) && psb_intel_crtc->pipe != 0) { - printk(KERN_ERR "Must use PIPE A\n"); - return false; - } - /* Should never happen!! */ - list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, - head) { - if (tmp_encoder != encoder - && tmp_encoder->crtc == encoder->crtc) { - printk(KERN_ERR "Can't enable LVDS and another " - "encoder on the same pipe\n"); - return false; - } - } - - /* - * If we have timings from the BIOS for the panel, put them in - * to the adjusted mode. The CRTC will be set up for this mode, - * with the panel scaling set up to source from the H/VDisplay - * of the original mode. - */ - if (panel_fixed_mode != NULL) { - adjusted_mode->hdisplay = panel_fixed_mode->hdisplay; - adjusted_mode->hsync_start = panel_fixed_mode->hsync_start; - adjusted_mode->hsync_end = panel_fixed_mode->hsync_end; - adjusted_mode->htotal = panel_fixed_mode->htotal; - adjusted_mode->vdisplay = panel_fixed_mode->vdisplay; - adjusted_mode->vsync_start = panel_fixed_mode->vsync_start; - adjusted_mode->vsync_end = panel_fixed_mode->vsync_end; - adjusted_mode->vtotal = panel_fixed_mode->vtotal; - adjusted_mode->clock = panel_fixed_mode->clock; - drm_mode_set_crtcinfo(adjusted_mode, - CRTC_INTERLACE_HALVE_V); - } - - /* - * XXX: It would be nice to support lower refresh rates on the - * panels to reduce power consumption, and perhaps match the - * user's requested refresh rate. - */ - - return true; -} - -static void psb_intel_lvds_prepare(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_mode_device *mode_dev = output->mode_dev; - - if (!gma_power_begin(dev, true)) - return; - - mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); - mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); - - psb_intel_lvds_set_power(dev, output, false); - - gma_power_end(dev); -} - -static void psb_intel_lvds_commit(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_mode_device *mode_dev = output->mode_dev; - - if (mode_dev->backlight_duty_cycle == 0) - mode_dev->backlight_duty_cycle = - psb_intel_lvds_get_max_backlight(dev); - - psb_intel_lvds_set_power(dev, output, true); -} - -static void psb_intel_lvds_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 pfit_control; - - /* - * The LVDS pin pair will already have been turned on in the - * psb_intel_crtc_mode_set since it has a large impact on the DPLL - * settings. - */ - - /* - * Enable automatic panel scaling so that non-native modes fill the - * screen. Should be enabled before the pipe is enabled, according to - * register description and PRM. - */ - if (mode->hdisplay != adjusted_mode->hdisplay || - mode->vdisplay != adjusted_mode->vdisplay) - pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | - HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - else - pfit_control = 0; - - if (dev_priv->lvds_dither) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - - REG_WRITE(PFIT_CONTROL, pfit_control); -} - -/* - * Detect the LVDS connection. - * - * This always returns CONNECTOR_STATUS_CONNECTED. - * This connector should only have - * been set up if the LVDS was actually connected anyway. - */ -static enum drm_connector_status psb_intel_lvds_detect(struct drm_connector - *connector, bool force) -{ - return connector_status_connected; -} - -/* - * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. - */ -static int psb_intel_lvds_get_modes(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_mode_device *mode_dev = - psb_intel_output->mode_dev; - int ret = 0; - - if (!IS_MRST(dev)) - ret = psb_intel_ddc_get_modes(psb_intel_output); - - if (ret) - return ret; - - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - - if (mode_dev->panel_fixed_mode != NULL) { - struct drm_display_mode *mode = - drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); - drm_mode_probed_add(connector, mode); - return 1; - } - - return 0; -} - -/** - * psb_intel_lvds_destroy - unregister and free LVDS structures - * @connector: connector to free - * - * Unregister the DDC bus for this connector then free the driver private - * structure. - */ -void psb_intel_lvds_destroy(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - if (psb_intel_output->ddc_bus) - psb_intel_i2c_destroy(psb_intel_output->ddc_bus); - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(connector); -} - -int psb_intel_lvds_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t value) -{ - struct drm_encoder *encoder = connector->encoder; - - if (!encoder) - return -1; - - if (!strcmp(property->name, "scaling mode")) { - struct psb_intel_crtc *crtc = - to_psb_intel_crtc(encoder->crtc); - uint64_t curval; - - if (!crtc) - goto set_prop_error; - - switch (value) { - case DRM_MODE_SCALE_FULLSCREEN: - break; - case DRM_MODE_SCALE_NO_SCALE: - break; - case DRM_MODE_SCALE_ASPECT: - break; - default: - goto set_prop_error; - } - - if (drm_connector_property_get_value(connector, - property, - &curval)) - goto set_prop_error; - - if (curval == value) - goto set_prop_done; - - if (drm_connector_property_set_value(connector, - property, - value)) - goto set_prop_error; - - if (crtc->saved_mode.hdisplay != 0 && - crtc->saved_mode.vdisplay != 0) { - if (!drm_crtc_helper_set_mode(encoder->crtc, - &crtc->saved_mode, - encoder->crtc->x, - encoder->crtc->y, - encoder->crtc->fb)) - goto set_prop_error; - } - } else if (!strcmp(property->name, "backlight")) { - if (drm_connector_property_set_value(connector, - property, - value)) - goto set_prop_error; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *devp = - encoder->dev->dev_private; - struct backlight_device *bd = devp->backlight_device; - if (bd) { - bd->props.brightness = value; - backlight_update_status(bd); - } -#endif - } - } else if (!strcmp(property->name, "DPMS")) { - struct drm_encoder_helper_funcs *hfuncs - = encoder->helper_private; - hfuncs->dpms(encoder, value); - } - -set_prop_done: - return 0; -set_prop_error: - return -1; -} - -static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = { - .dpms = psb_intel_lvds_encoder_dpms, - .mode_fixup = psb_intel_lvds_mode_fixup, - .prepare = psb_intel_lvds_prepare, - .mode_set = psb_intel_lvds_mode_set, - .commit = psb_intel_lvds_commit, -}; - -const struct drm_connector_helper_funcs - psb_intel_lvds_connector_helper_funcs = { - .get_modes = psb_intel_lvds_get_modes, - .mode_valid = psb_intel_lvds_mode_valid, - .best_encoder = psb_intel_best_encoder, -}; - -const struct drm_connector_funcs psb_intel_lvds_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .save = psb_intel_lvds_save, - .restore = psb_intel_lvds_restore, - .detect = psb_intel_lvds_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = psb_intel_lvds_set_property, - .destroy = psb_intel_lvds_destroy, -}; - - -static void psb_intel_lvds_enc_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - -const struct drm_encoder_funcs psb_intel_lvds_enc_funcs = { - .destroy = psb_intel_lvds_enc_destroy, -}; - - - -/** - * psb_intel_lvds_init - setup LVDS connectors on this device - * @dev: drm device - * - * Create the connector, register the LVDS DDC bus, and try to figure out what - * modes we can display on the LVDS panel (if present). - */ -void psb_intel_lvds_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev) -{ - struct psb_intel_output *psb_intel_output; - struct psb_intel_lvds_priv *lvds_priv; - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_display_mode *scan; /* *modes, *bios_mode; */ - struct drm_crtc *crtc; - struct drm_psb_private *dev_priv = dev->dev_private; - u32 lvds; - int pipe; - - psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL); - if (!psb_intel_output) - return; - - lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL); - if (!lvds_priv) { - kfree(psb_intel_output); - dev_err(dev->dev, "LVDS private allocation error\n"); - return; - } - - psb_intel_output->dev_priv = lvds_priv; - psb_intel_output->mode_dev = mode_dev; - - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - drm_connector_init(dev, &psb_intel_output->base, - &psb_intel_lvds_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); - - drm_encoder_init(dev, &psb_intel_output->enc, - &psb_intel_lvds_enc_funcs, - DRM_MODE_ENCODER_LVDS); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - psb_intel_output->type = INTEL_OUTPUT_LVDS; - - drm_encoder_helper_add(encoder, &psb_intel_lvds_helper_funcs); - drm_connector_helper_add(connector, - &psb_intel_lvds_connector_helper_funcs); - connector->display_info.subpixel_order = SubPixelHorizontalRGB; - connector->interlace_allowed = false; - connector->doublescan_allowed = false; - - /*Attach connector properties*/ - drm_connector_attach_property(connector, - dev->mode_config.scaling_mode_property, - DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, - dev_priv->backlight_property, - BRIGHTNESS_MAX_LEVEL); - - /* - * Set up I2C bus - * FIXME: distroy i2c_bus when exit - */ - psb_intel_output->i2c_bus = psb_intel_i2c_create(dev, - GPIOB, - "LVDSBLC_B"); - if (!psb_intel_output->i2c_bus) { - dev_printk(KERN_ERR, - &dev->pdev->dev, "I2C bus registration failed.\n"); - goto failed_blc_i2c; - } - psb_intel_output->i2c_bus->slave_addr = 0x2C; - dev_priv->lvds_i2c_bus = psb_intel_output->i2c_bus; - - /* - * LVDS discovery: - * 1) check for EDID on DDC - * 2) check for VBT data - * 3) check to see if LVDS is already on - * if none of the above, no panel - * 4) make sure lid is open - * if closed, act like it's not there for now - */ - - /* Set up the DDC bus. */ - psb_intel_output->ddc_bus = psb_intel_i2c_create(dev, - GPIOC, - "LVDSDDC_C"); - if (!psb_intel_output->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, - "DDC bus registration " "failed.\n"); - goto failed_ddc; - } - - /* - * Attempt to get the fixed panel mode from DDC. Assume that the - * preferred mode is the right one. - */ - psb_intel_ddc_get_modes(psb_intel_output); - list_for_each_entry(scan, &connector->probed_modes, head) { - if (scan->type & DRM_MODE_TYPE_PREFERRED) { - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, scan); - goto out; /* FIXME: check for quirks */ - } - } - - /* Failed to get EDID, what about VBT? do we need this? */ - if (mode_dev->vbt_mode) - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, mode_dev->vbt_mode); - - if (!mode_dev->panel_fixed_mode) - if (dev_priv->lfp_lvds_vbt_mode) - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, - dev_priv->lfp_lvds_vbt_mode); - - /* - * If we didn't get EDID, try checking if the panel is already turned - * on. If so, assume that whatever is currently programmed is the - * correct mode. - */ - lvds = REG_READ(LVDS); - pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; - crtc = psb_intel_get_crtc_from_pipe(dev, pipe); - - if (crtc && (lvds & LVDS_PORT_EN)) { - mode_dev->panel_fixed_mode = - psb_intel_crtc_mode_get(dev, crtc); - if (mode_dev->panel_fixed_mode) { - mode_dev->panel_fixed_mode->type |= - DRM_MODE_TYPE_PREFERRED; - goto out; /* FIXME: check for quirks */ - } - } - - /* If we still don't have a mode after all that, give up. */ - if (!mode_dev->panel_fixed_mode) { - dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); - goto failed_find; - } - - /* - * Blacklist machines with BIOSes that list an LVDS panel without - * actually having one. - */ -out: - drm_sysfs_connector_add(connector); - return; - -failed_find: - if (psb_intel_output->ddc_bus) - psb_intel_i2c_destroy(psb_intel_output->ddc_bus); -failed_ddc: - if (psb_intel_output->i2c_bus) - psb_intel_i2c_destroy(psb_intel_output->i2c_bus); -failed_blc_i2c: - drm_encoder_cleanup(encoder); - drm_connector_cleanup(connector); - kfree(connector); -} - diff --git a/drivers/staging/gma500/psb_intel_modes.c b/drivers/staging/gma500/psb_intel_modes.c deleted file mode 100644 index bde1aff..0000000 --- a/drivers/staging/gma500/psb_intel_modes.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2007 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authers: Jesse Barnes - */ - -#include -#include -#include -#include "psb_intel_drv.h" - -/** - * psb_intel_ddc_probe - * - */ -bool psb_intel_ddc_probe(struct psb_intel_output *psb_intel_output) -{ - u8 out_buf[] = { 0x0, 0x0 }; - u8 buf[2]; - int ret; - struct i2c_msg msgs[] = { - { - .addr = 0x50, - .flags = 0, - .len = 1, - .buf = out_buf, - }, - { - .addr = 0x50, - .flags = I2C_M_RD, - .len = 1, - .buf = buf, - } - }; - - ret = i2c_transfer(&psb_intel_output->ddc_bus->adapter, msgs, 2); - if (ret == 2) - return true; - - return false; -} - -/** - * psb_intel_ddc_get_modes - get modelist from monitor - * @connector: DRM connector device to use - * - * Fetch the EDID information from @connector using the DDC bus. - */ -int psb_intel_ddc_get_modes(struct psb_intel_output *psb_intel_output) -{ - struct edid *edid; - int ret = 0; - - edid = - drm_get_edid(&psb_intel_output->base, - &psb_intel_output->ddc_bus->adapter); - if (edid) { - drm_mode_connector_update_edid_property(&psb_intel_output-> - base, edid); - ret = drm_add_edid_modes(&psb_intel_output->base, edid); - kfree(edid); - } - return ret; -} diff --git a/drivers/staging/gma500/psb_intel_reg.h b/drivers/staging/gma500/psb_intel_reg.h deleted file mode 100644 index 1ac16aa..0000000 --- a/drivers/staging/gma500/psb_intel_reg.h +++ /dev/null @@ -1,1235 +0,0 @@ -/* - * Copyright (c) 2009, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef __PSB_INTEL_REG_H__ -#define __PSB_INTEL_REG_H__ - -#define BLC_PWM_CTL 0x61254 -#define BLC_PWM_CTL2 0x61250 -#define BLC_PWM_CTL_C 0x62254 -#define BLC_PWM_CTL2_C 0x62250 -#define BACKLIGHT_MODULATION_FREQ_SHIFT (17) -/* - * This is the most significant 15 bits of the number of backlight cycles in a - * complete cycle of the modulated backlight control. - * - * The actual value is this field multiplied by two. - */ -#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17) -#define BLM_LEGACY_MODE (1 << 16) -/* - * This is the number of cycles out of the backlight modulation cycle for which - * the backlight is on. - * - * This field must be no greater than the number of cycles in the complete - * backlight modulation cycle. - */ -#define BACKLIGHT_DUTY_CYCLE_SHIFT (0) -#define BACKLIGHT_DUTY_CYCLE_MASK (0xffff) - -#define I915_GCFGC 0xf0 -#define I915_LOW_FREQUENCY_ENABLE (1 << 7) -#define I915_DISPLAY_CLOCK_190_200_MHZ (0 << 4) -#define I915_DISPLAY_CLOCK_333_MHZ (4 << 4) -#define I915_DISPLAY_CLOCK_MASK (7 << 4) - -#define I855_HPLLCC 0xc0 -#define I855_CLOCK_CONTROL_MASK (3 << 0) -#define I855_CLOCK_133_200 (0 << 0) -#define I855_CLOCK_100_200 (1 << 0) -#define I855_CLOCK_100_133 (2 << 0) -#define I855_CLOCK_166_250 (3 << 0) - -/* I830 CRTC registers */ -#define HTOTAL_A 0x60000 -#define HBLANK_A 0x60004 -#define HSYNC_A 0x60008 -#define VTOTAL_A 0x6000c -#define VBLANK_A 0x60010 -#define VSYNC_A 0x60014 -#define PIPEASRC 0x6001c -#define BCLRPAT_A 0x60020 -#define VSYNCSHIFT_A 0x60028 - -#define HTOTAL_B 0x61000 -#define HBLANK_B 0x61004 -#define HSYNC_B 0x61008 -#define VTOTAL_B 0x6100c -#define VBLANK_B 0x61010 -#define VSYNC_B 0x61014 -#define PIPEBSRC 0x6101c -#define BCLRPAT_B 0x61020 -#define VSYNCSHIFT_B 0x61028 - -#define HTOTAL_C 0x62000 -#define HBLANK_C 0x62004 -#define HSYNC_C 0x62008 -#define VTOTAL_C 0x6200c -#define VBLANK_C 0x62010 -#define VSYNC_C 0x62014 -#define PIPECSRC 0x6201c -#define BCLRPAT_C 0x62020 -#define VSYNCSHIFT_C 0x62028 - -#define PP_STATUS 0x61200 -# define PP_ON (1 << 31) -/* - * Indicates that all dependencies of the panel are on: - * - * - PLL enabled - * - pipe enabled - * - LVDS/DVOB/DVOC on - */ -#define PP_READY (1 << 30) -#define PP_SEQUENCE_NONE (0 << 28) -#define PP_SEQUENCE_ON (1 << 28) -#define PP_SEQUENCE_OFF (2 << 28) -#define PP_SEQUENCE_MASK 0x30000000 -#define PP_CONTROL 0x61204 -#define POWER_TARGET_ON (1 << 0) - -#define LVDSPP_ON 0x61208 -#define LVDSPP_OFF 0x6120c -#define PP_CYCLE 0x61210 - -#define PFIT_CONTROL 0x61230 -#define PFIT_ENABLE (1 << 31) -#define PFIT_PIPE_MASK (3 << 29) -#define PFIT_PIPE_SHIFT 29 -#define PFIT_SCALING_MODE_PILLARBOX (1 << 27) -#define PFIT_SCALING_MODE_LETTERBOX (3 << 26) -#define VERT_INTERP_DISABLE (0 << 10) -#define VERT_INTERP_BILINEAR (1 << 10) -#define VERT_INTERP_MASK (3 << 10) -#define VERT_AUTO_SCALE (1 << 9) -#define HORIZ_INTERP_DISABLE (0 << 6) -#define HORIZ_INTERP_BILINEAR (1 << 6) -#define HORIZ_INTERP_MASK (3 << 6) -#define HORIZ_AUTO_SCALE (1 << 5) -#define PANEL_8TO6_DITHER_ENABLE (1 << 3) - -#define PFIT_PGM_RATIOS 0x61234 -#define PFIT_VERT_SCALE_MASK 0xfff00000 -#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 - -#define PFIT_AUTO_RATIOS 0x61238 - -#define DPLL_A 0x06014 -#define DPLL_B 0x06018 -#define DPLL_VCO_ENABLE (1 << 31) -#define DPLL_DVO_HIGH_SPEED (1 << 30) -#define DPLL_SYNCLOCK_ENABLE (1 << 29) -#define DPLL_VGA_MODE_DIS (1 << 28) -#define DPLLB_MODE_DAC_SERIAL (1 << 26) /* i915 */ -#define DPLLB_MODE_LVDS (2 << 26) /* i915 */ -#define DPLL_MODE_MASK (3 << 26) -#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10 (0 << 24) /* i915 */ -#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 (1 << 24) /* i915 */ -#define DPLLB_LVDS_P2_CLOCK_DIV_14 (0 << 24) /* i915 */ -#define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ -#define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ -#define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ -#define DPLL_LOCK (1 << 15) /* CDV */ - -/* - * The i830 generation, in DAC/serial mode, defines p1 as two plus this - * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set. - */ -# define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 -/* - * The i830 generation, in LVDS mode, defines P1 as the bit number set within - * this field (only one bit may be set). - */ -#define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000 -#define DPLL_FPA01_P1_POST_DIV_SHIFT 16 -#define PLL_P2_DIVIDE_BY_4 (1 << 23) /* i830, required - * in DVO non-gang */ -# define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */ -#define PLL_REF_INPUT_DREFCLK (0 << 13) -#define PLL_REF_INPUT_TVCLKINA (1 << 13) /* i830 */ -#define PLL_REF_INPUT_TVCLKINBC (2 << 13) /* SDVO - * TVCLKIN */ -#define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13) -#define PLL_REF_INPUT_MASK (3 << 13) -#define PLL_LOAD_PULSE_PHASE_SHIFT 9 -/* - * Parallel to Serial Load Pulse phase selection. - * Selects the phase for the 10X DPLL clock for the PCIe - * digital display port. The range is 4 to 13; 10 or more - * is just a flip delay. The default is 6 - */ -#define PLL_LOAD_PULSE_PHASE_MASK (0xf << PLL_LOAD_PULSE_PHASE_SHIFT) -#define DISPLAY_RATE_SELECT_FPA1 (1 << 8) - -/* - * SDVO multiplier for 945G/GM. Not used on 965. - * - * DPLL_MD_UDI_MULTIPLIER_MASK - */ -#define SDVO_MULTIPLIER_MASK 0x000000ff -#define SDVO_MULTIPLIER_SHIFT_HIRES 4 -#define SDVO_MULTIPLIER_SHIFT_VGA 0 - -/* - * PLL_MD - */ -/* Pipe A SDVO/UDI clock multiplier/divider register for G965. */ -#define DPLL_A_MD 0x0601c -/* Pipe B SDVO/UDI clock multiplier/divider register for G965. */ -#define DPLL_B_MD 0x06020 -/* - * UDI pixel divider, controlling how many pixels are stuffed into a packet. - * - * Value is pixels minus 1. Must be set to 1 pixel for SDVO. - */ -#define DPLL_MD_UDI_DIVIDER_MASK 0x3f000000 -#define DPLL_MD_UDI_DIVIDER_SHIFT 24 -/* UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */ -#define DPLL_MD_VGA_UDI_DIVIDER_MASK 0x003f0000 -#define DPLL_MD_VGA_UDI_DIVIDER_SHIFT 16 -/* - * SDVO/UDI pixel multiplier. - * - * SDVO requires that the bus clock rate be between 1 and 2 Ghz, and the bus - * clock rate is 10 times the DPLL clock. At low resolution/refresh rate - * modes, the bus rate would be below the limits, so SDVO allows for stuffing - * dummy bytes in the datastream at an increased clock rate, with both sides of - * the link knowing how many bytes are fill. - * - * So, for a mode with a dotclock of 65Mhz, we would want to double the clock - * rate to 130Mhz to get a bus rate of 1.30Ghz. The DPLL clock rate would be - * set to 130Mhz, and the SDVO multiplier set to 2x in this register and - * through an SDVO command. - * - * This register field has values of multiplication factor minus 1, with - * a maximum multiplier of 5 for SDVO. - */ -#define DPLL_MD_UDI_MULTIPLIER_MASK 0x00003f00 -#define DPLL_MD_UDI_MULTIPLIER_SHIFT 8 -/* - * SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK. - * This best be set to the default value (3) or the CRT won't work. No, - * I don't entirely understand what this does... - */ -#define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f -#define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0 - -#define DPLL_TEST 0x606c -#define DPLLB_TEST_SDVO_DIV_1 (0 << 22) -#define DPLLB_TEST_SDVO_DIV_2 (1 << 22) -#define DPLLB_TEST_SDVO_DIV_4 (2 << 22) -#define DPLLB_TEST_SDVO_DIV_MASK (3 << 22) -#define DPLLB_TEST_N_BYPASS (1 << 19) -#define DPLLB_TEST_M_BYPASS (1 << 18) -#define DPLLB_INPUT_BUFFER_ENABLE (1 << 16) -#define DPLLA_TEST_N_BYPASS (1 << 3) -#define DPLLA_TEST_M_BYPASS (1 << 2) -#define DPLLA_INPUT_BUFFER_ENABLE (1 << 0) - -#define ADPA 0x61100 -#define ADPA_DAC_ENABLE (1 << 31) -#define ADPA_DAC_DISABLE 0 -#define ADPA_PIPE_SELECT_MASK (1 << 30) -#define ADPA_PIPE_A_SELECT 0 -#define ADPA_PIPE_B_SELECT (1 << 30) -#define ADPA_USE_VGA_HVPOLARITY (1 << 15) -#define ADPA_SETS_HVPOLARITY 0 -#define ADPA_VSYNC_CNTL_DISABLE (1 << 11) -#define ADPA_VSYNC_CNTL_ENABLE 0 -#define ADPA_HSYNC_CNTL_DISABLE (1 << 10) -#define ADPA_HSYNC_CNTL_ENABLE 0 -#define ADPA_VSYNC_ACTIVE_HIGH (1 << 4) -#define ADPA_VSYNC_ACTIVE_LOW 0 -#define ADPA_HSYNC_ACTIVE_HIGH (1 << 3) -#define ADPA_HSYNC_ACTIVE_LOW 0 - -#define FPA0 0x06040 -#define FPA1 0x06044 -#define FPB0 0x06048 -#define FPB1 0x0604c -#define FP_N_DIV_MASK 0x003f0000 -#define FP_N_DIV_SHIFT 16 -#define FP_M1_DIV_MASK 0x00003f00 -#define FP_M1_DIV_SHIFT 8 -#define FP_M2_DIV_MASK 0x0000003f -#define FP_M2_DIV_SHIFT 0 - -#define PORT_HOTPLUG_EN 0x61110 -#define SDVOB_HOTPLUG_INT_EN (1 << 26) -#define SDVOC_HOTPLUG_INT_EN (1 << 25) -#define TV_HOTPLUG_INT_EN (1 << 18) -#define CRT_HOTPLUG_INT_EN (1 << 9) -#define CRT_HOTPLUG_FORCE_DETECT (1 << 3) -/* CDV.. */ -#define CRT_HOTPLUG_ACTIVATION_PERIOD_64 (1 << 8) -#define CRT_HOTPLUG_DAC_ON_TIME_2M (0 << 7) -#define CRT_HOTPLUG_DAC_ON_TIME_4M (1 << 7) -#define CRT_HOTPLUG_VOLTAGE_COMPARE_40 (0 << 5) -#define CRT_HOTPLUG_VOLTAGE_COMPARE_50 (1 << 5) -#define CRT_HOTPLUG_VOLTAGE_COMPARE_60 (2 << 5) -#define CRT_HOTPLUG_VOLTAGE_COMPARE_70 (3 << 5) -#define CRT_HOTPLUG_VOLTAGE_COMPARE_MASK (3 << 5) -#define CRT_HOTPLUG_DETECT_DELAY_1G (0 << 4) -#define CRT_HOTPLUG_DETECT_DELAY_2G (1 << 4) -#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2) -#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) -#define CRT_HOTPLUG_DETECT_MASK 0x000000F8 - -#define PORT_HOTPLUG_STAT 0x61114 -#define CRT_HOTPLUG_INT_STATUS (1 << 11) -#define TV_HOTPLUG_INT_STATUS (1 << 10) -#define CRT_HOTPLUG_MONITOR_MASK (3 << 8) -#define CRT_HOTPLUG_MONITOR_COLOR (3 << 8) -#define CRT_HOTPLUG_MONITOR_MONO (2 << 8) -#define CRT_HOTPLUG_MONITOR_NONE (0 << 8) -#define SDVOC_HOTPLUG_INT_STATUS (1 << 7) -#define SDVOB_HOTPLUG_INT_STATUS (1 << 6) - -#define SDVOB 0x61140 -#define SDVOC 0x61160 -#define SDVO_ENABLE (1 << 31) -#define SDVO_PIPE_B_SELECT (1 << 30) -#define SDVO_STALL_SELECT (1 << 29) -#define SDVO_INTERRUPT_ENABLE (1 << 26) - -/** - * 915G/GM SDVO pixel multiplier. - * - * Programmed value is multiplier - 1, up to 5x. - * - * DPLL_MD_UDI_MULTIPLIER_MASK - */ -#define SDVO_PORT_MULTIPLY_MASK (7 << 23) -#define SDVO_PORT_MULTIPLY_SHIFT 23 -#define SDVO_PHASE_SELECT_MASK (15 << 19) -#define SDVO_PHASE_SELECT_DEFAULT (6 << 19) -#define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) -#define SDVOC_GANG_MODE (1 << 16) -#define SDVO_BORDER_ENABLE (1 << 7) -#define SDVOB_PCIE_CONCURRENCY (1 << 3) -#define SDVO_DETECTED (1 << 2) -/* Bits to be preserved when writing */ -#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14)) -#define SDVOC_PRESERVE_MASK (1 << 17) - -/* - * This register controls the LVDS output enable, pipe selection, and data - * format selection. - * - * All of the clock/data pairs are force powered down by power sequencing. - */ -#define LVDS 0x61180 -/* - * Enables the LVDS port. This bit must be set before DPLLs are enabled, as - * the DPLL semantics change when the LVDS is assigned to that pipe. - */ -#define LVDS_PORT_EN (1 << 31) -/* Selects pipe B for LVDS data. Must be set on pre-965. */ -#define LVDS_PIPEB_SELECT (1 << 30) - -/* Turns on border drawing to allow centered display. */ -#define LVDS_BORDER_EN (1 << 15) - -/* - * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per - * pixel. - */ -#define LVDS_A0A2_CLKA_POWER_MASK (3 << 8) -#define LVDS_A0A2_CLKA_POWER_DOWN (0 << 8) -#define LVDS_A0A2_CLKA_POWER_UP (3 << 8) -/* - * Controls the A3 data pair, which contains the additional LSBs for 24 bit - * mode. Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be - * on. - */ -#define LVDS_A3_POWER_MASK (3 << 6) -#define LVDS_A3_POWER_DOWN (0 << 6) -#define LVDS_A3_POWER_UP (3 << 6) -/* - * Controls the CLKB pair. This should only be set when LVDS_B0B3_POWER_UP - * is set. - */ -#define LVDS_CLKB_POWER_MASK (3 << 4) -#define LVDS_CLKB_POWER_DOWN (0 << 4) -#define LVDS_CLKB_POWER_UP (3 << 4) -/* - * Controls the B0-B3 data pairs. This must be set to match the DPLL p2 - * setting for whether we are in dual-channel mode. The B3 pair will - * additionally only be powered up when LVDS_A3_POWER_UP is set. - */ -#define LVDS_B0B3_POWER_MASK (3 << 2) -#define LVDS_B0B3_POWER_DOWN (0 << 2) -#define LVDS_B0B3_POWER_UP (3 << 2) - -#define PIPEACONF 0x70008 -#define PIPEACONF_ENABLE (1 << 31) -#define PIPEACONF_DISABLE 0 -#define PIPEACONF_DOUBLE_WIDE (1 << 30) -#define PIPECONF_ACTIVE (1 << 30) -#define I965_PIPECONF_ACTIVE (1 << 30) -#define PIPECONF_DSIPLL_LOCK (1 << 29) -#define PIPEACONF_SINGLE_WIDE 0 -#define PIPEACONF_PIPE_UNLOCKED 0 -#define PIPEACONF_DSR (1 << 26) -#define PIPEACONF_PIPE_LOCKED (1 << 25) -#define PIPEACONF_PALETTE 0 -#define PIPECONF_FORCE_BORDER (1 << 25) -#define PIPEACONF_GAMMA (1 << 24) -#define PIPECONF_PROGRESSIVE (0 << 21) -#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) -#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) -#define PIPECONF_PLANE_OFF (1 << 19) -#define PIPECONF_CURSOR_OFF (1 << 18) - -#define PIPEBCONF 0x71008 -#define PIPEBCONF_ENABLE (1 << 31) -#define PIPEBCONF_DISABLE 0 -#define PIPEBCONF_DOUBLE_WIDE (1 << 30) -#define PIPEBCONF_DISABLE 0 -#define PIPEBCONF_GAMMA (1 << 24) -#define PIPEBCONF_PALETTE 0 - -#define PIPECCONF 0x72008 - -#define PIPEBGCMAXRED 0x71010 -#define PIPEBGCMAXGREEN 0x71014 -#define PIPEBGCMAXBLUE 0x71018 - -#define PIPEASTAT 0x70024 -#define PIPEBSTAT 0x71024 -#define PIPECSTAT 0x72024 -#define PIPE_VBLANK_INTERRUPT_STATUS (1UL << 1) -#define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL << 2) -#define PIPE_VBLANK_CLEAR (1 << 1) -#define PIPE_VBLANK_STATUS (1 << 1) -#define PIPE_TE_STATUS (1UL << 6) -#define PIPE_DPST_EVENT_STATUS (1UL << 7) -#define PIPE_VSYNC_CLEAR (1UL << 9) -#define PIPE_VSYNC_STATUS (1UL << 9) -#define PIPE_HDMI_AUDIO_UNDERRUN_STATUS (1UL << 10) -#define PIPE_HDMI_AUDIO_BUFFER_DONE_STATUS (1UL << 11) -#define PIPE_VBLANK_INTERRUPT_ENABLE (1UL << 17) -#define PIPE_START_VBLANK_INTERRUPT_ENABLE (1UL << 18) -#define PIPE_TE_ENABLE (1UL << 22) -#define PIPE_DPST_EVENT_ENABLE (1UL << 23) -#define PIPE_VSYNC_ENABL (1UL << 25) -#define PIPE_HDMI_AUDIO_UNDERRUN (1UL << 26) -#define PIPE_HDMI_AUDIO_BUFFER_DONE (1UL << 27) -#define PIPE_HDMI_AUDIO_INT_MASK (PIPE_HDMI_AUDIO_UNDERRUN | \ - PIPE_HDMI_AUDIO_BUFFER_DONE) -#define PIPE_EVENT_MASK ((1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)|(1 << 22)|(1 << 21)|(1 << 20)|(1 << 16)) -#define PIPE_VBLANK_MASK ((1 << 25)|(1 << 24)|(1 << 18)|(1 << 17)) -#define HISTOGRAM_INT_CONTROL 0x61268 -#define HISTOGRAM_BIN_DATA 0X61264 -#define HISTOGRAM_LOGIC_CONTROL 0x61260 -#define PWM_CONTROL_LOGIC 0x61250 -#define PIPE_HOTPLUG_INTERRUPT_STATUS (1UL << 10) -#define HISTOGRAM_INTERRUPT_ENABLE (1UL << 31) -#define HISTOGRAM_LOGIC_ENABLE (1UL << 31) -#define PWM_LOGIC_ENABLE (1UL << 31) -#define PWM_PHASEIN_ENABLE (1UL << 25) -#define PWM_PHASEIN_INT_ENABLE (1UL << 24) -#define PWM_PHASEIN_VB_COUNT 0x00001f00 -#define PWM_PHASEIN_INC 0x0000001f -#define HISTOGRAM_INT_CTRL_CLEAR (1UL << 30) -#define DPST_YUV_LUMA_MODE 0 - -struct dpst_ie_histogram_control { - union { - uint32_t data; - struct { - uint32_t bin_reg_index:7; - uint32_t reserved:4; - uint32_t bin_reg_func_select:1; - uint32_t sync_to_phase_in:1; - uint32_t alt_enhancement_mode:2; - uint32_t reserved1:1; - uint32_t sync_to_phase_in_count:8; - uint32_t histogram_mode_select:1; - uint32_t reserved2:4; - uint32_t ie_pipe_assignment:1; - uint32_t ie_mode_table_enabled:1; - uint32_t ie_histogram_enable:1; - }; - }; -}; - -struct dpst_guardband { - union { - uint32_t data; - struct { - uint32_t guardband:22; - uint32_t guardband_interrupt_delay:8; - uint32_t interrupt_status:1; - uint32_t interrupt_enable:1; - }; - }; -}; - -#define PIPEAFRAMEHIGH 0x70040 -#define PIPEAFRAMEPIXEL 0x70044 -#define PIPEBFRAMEHIGH 0x71040 -#define PIPEBFRAMEPIXEL 0x71044 -#define PIPECFRAMEHIGH 0x72040 -#define PIPECFRAMEPIXEL 0x72044 -#define PIPE_FRAME_HIGH_MASK 0x0000ffff -#define PIPE_FRAME_HIGH_SHIFT 0 -#define PIPE_FRAME_LOW_MASK 0xff000000 -#define PIPE_FRAME_LOW_SHIFT 24 -#define PIPE_PIXEL_MASK 0x00ffffff -#define PIPE_PIXEL_SHIFT 0 - -#define DSPARB 0x70030 -#define DSPFW1 0x70034 -#define DSPFW2 0x70038 -#define DSPFW3 0x7003c -#define DSPFW4 0x70050 -#define DSPFW5 0x70054 -#define DSPFW6 0x70058 -#define DSPCHICKENBIT 0x70400 -#define DSPACNTR 0x70180 -#define DSPBCNTR 0x71180 -#define DSPCCNTR 0x72180 -#define DISPLAY_PLANE_ENABLE (1 << 31) -#define DISPLAY_PLANE_DISABLE 0 -#define DISPPLANE_GAMMA_ENABLE (1 << 30) -#define DISPPLANE_GAMMA_DISABLE 0 -#define DISPPLANE_PIXFORMAT_MASK (0xf << 26) -#define DISPPLANE_8BPP (0x2 << 26) -#define DISPPLANE_15_16BPP (0x4 << 26) -#define DISPPLANE_16BPP (0x5 << 26) -#define DISPPLANE_32BPP_NO_ALPHA (0x6 << 26) -#define DISPPLANE_32BPP (0x7 << 26) -#define DISPPLANE_STEREO_ENABLE (1 << 25) -#define DISPPLANE_STEREO_DISABLE 0 -#define DISPPLANE_SEL_PIPE_MASK (1 << 24) -#define DISPPLANE_SEL_PIPE_POS 24 -#define DISPPLANE_SEL_PIPE_A 0 -#define DISPPLANE_SEL_PIPE_B (1 << 24) -#define DISPPLANE_SRC_KEY_ENABLE (1 << 22) -#define DISPPLANE_SRC_KEY_DISABLE 0 -#define DISPPLANE_LINE_DOUBLE (1 << 20) -#define DISPPLANE_NO_LINE_DOUBLE 0 -#define DISPPLANE_STEREO_POLARITY_FIRST 0 -#define DISPPLANE_STEREO_POLARITY_SECOND (1 << 18) -/* plane B only */ -#define DISPPLANE_ALPHA_TRANS_ENABLE (1 << 15) -#define DISPPLANE_ALPHA_TRANS_DISABLE 0 -#define DISPPLANE_SPRITE_ABOVE_DISPLAYA 0 -#define DISPPLANE_SPRITE_ABOVE_OVERLAY (1) -#define DISPPLANE_BOTTOM (4) - -#define DSPABASE 0x70184 -#define DSPALINOFF 0x70184 -#define DSPASTRIDE 0x70188 - -#define DSPBBASE 0x71184 -#define DSPBLINOFF 0X71184 -#define DSPBADDR DSPBBASE -#define DSPBSTRIDE 0x71188 - -#define DSPCBASE 0x72184 -#define DSPCLINOFF 0x72184 -#define DSPCSTRIDE 0x72188 - -#define DSPAKEYVAL 0x70194 -#define DSPAKEYMASK 0x70198 - -#define DSPAPOS 0x7018C /* reserved */ -#define DSPASIZE 0x70190 -#define DSPBPOS 0x7118C -#define DSPBSIZE 0x71190 -#define DSPCPOS 0x7218C -#define DSPCSIZE 0x72190 - -#define DSPASURF 0x7019C -#define DSPATILEOFF 0x701A4 - -#define DSPBSURF 0x7119C -#define DSPBTILEOFF 0x711A4 - -#define DSPCSURF 0x7219C -#define DSPCTILEOFF 0x721A4 -#define DSPCKEYMAXVAL 0x721A0 -#define DSPCKEYMINVAL 0x72194 -#define DSPCKEYMSK 0x72198 - -#define VGACNTRL 0x71400 -#define VGA_DISP_DISABLE (1 << 31) -#define VGA_2X_MODE (1 << 30) -#define VGA_PIPE_B_SELECT (1 << 29) - -/* - * Overlay registers - */ -#define OV_C_OFFSET 0x08000 -#define OV_OVADD 0x30000 -#define OV_DOVASTA 0x30008 -# define OV_PIPE_SELECT ((1 << 6)|(1 << 7)) -# define OV_PIPE_SELECT_POS 6 -# define OV_PIPE_A 0 -# define OV_PIPE_C 1 -#define OV_OGAMC5 0x30010 -#define OV_OGAMC4 0x30014 -#define OV_OGAMC3 0x30018 -#define OV_OGAMC2 0x3001C -#define OV_OGAMC1 0x30020 -#define OV_OGAMC0 0x30024 -#define OVC_OVADD 0x38000 -#define OVC_DOVCSTA 0x38008 -#define OVC_OGAMC5 0x38010 -#define OVC_OGAMC4 0x38014 -#define OVC_OGAMC3 0x38018 -#define OVC_OGAMC2 0x3801C -#define OVC_OGAMC1 0x38020 -#define OVC_OGAMC0 0x38024 - -/* - * Some BIOS scratch area registers. The 845 (and 830?) store the amount - * of video memory available to the BIOS in SWF1. - */ -#define SWF0 0x71410 -#define SWF1 0x71414 -#define SWF2 0x71418 -#define SWF3 0x7141c -#define SWF4 0x71420 -#define SWF5 0x71424 -#define SWF6 0x71428 - -/* - * 855 scratch registers. - */ -#define SWF00 0x70410 -#define SWF01 0x70414 -#define SWF02 0x70418 -#define SWF03 0x7041c -#define SWF04 0x70420 -#define SWF05 0x70424 -#define SWF06 0x70428 - -#define SWF10 SWF0 -#define SWF11 SWF1 -#define SWF12 SWF2 -#define SWF13 SWF3 -#define SWF14 SWF4 -#define SWF15 SWF5 -#define SWF16 SWF6 - -#define SWF30 0x72414 -#define SWF31 0x72418 -#define SWF32 0x7241c - - -/* - * Palette registers - */ -#define PALETTE_A 0x0a000 -#define PALETTE_B 0x0a800 -#define PALETTE_C 0x0ac00 - -/* Cursor A & B regs */ -#define CURACNTR 0x70080 -#define CURSOR_MODE_DISABLE 0x00 -#define CURSOR_MODE_64_32B_AX 0x07 -#define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX) -#define MCURSOR_GAMMA_ENABLE (1 << 26) -#define CURABASE 0x70084 -#define CURAPOS 0x70088 -#define CURSOR_POS_MASK 0x007FF -#define CURSOR_POS_SIGN 0x8000 -#define CURSOR_X_SHIFT 0 -#define CURSOR_Y_SHIFT 16 -#define CURBCNTR 0x700c0 -#define CURBBASE 0x700c4 -#define CURBPOS 0x700c8 -#define CURCCNTR 0x700e0 -#define CURCBASE 0x700e4 -#define CURCPOS 0x700e8 - -/* - * Interrupt Registers - */ -#define IER 0x020a0 -#define IIR 0x020a4 -#define IMR 0x020a8 -#define ISR 0x020ac - -/* - * MOORESTOWN delta registers - */ -#define MRST_DPLL_A 0x0f014 -#define MDFLD_DPLL_B 0x0f018 -#define MDFLD_INPUT_REF_SEL (1 << 14) -#define MDFLD_VCO_SEL (1 << 16) -#define DPLLA_MODE_LVDS (2 << 26) /* mrst */ -#define MDFLD_PLL_LATCHEN (1 << 28) -#define MDFLD_PWR_GATE_EN (1 << 30) -#define MDFLD_P1_MASK (0x1FF << 17) -#define MRST_FPA0 0x0f040 -#define MRST_FPA1 0x0f044 -#define MDFLD_DPLL_DIV0 0x0f048 -#define MDFLD_DPLL_DIV1 0x0f04c -#define MRST_PERF_MODE 0x020f4 - -/* - * MEDFIELD HDMI registers - */ -#define HDMIPHYMISCCTL 0x61134 -#define HDMI_PHY_POWER_DOWN 0x7f -#define HDMIB_CONTROL 0x61140 -#define HDMIB_PORT_EN (1 << 31) -#define HDMIB_PIPE_B_SELECT (1 << 30) -#define HDMIB_NULL_PACKET (1 << 9) -#define HDMIB_HDCP_PORT (1 << 5) - -/* #define LVDS 0x61180 */ -#define MRST_PANEL_8TO6_DITHER_ENABLE (1 << 25) -#define MRST_PANEL_24_DOT_1_FORMAT (1 << 24) -#define LVDS_A3_POWER_UP_0_OUTPUT (1 << 6) - -#define MIPI 0x61190 -#define MIPI_C 0x62190 -#define MIPI_PORT_EN (1 << 31) -/* Turns on border drawing to allow centered display. */ -#define SEL_FLOPPED_HSTX (1 << 23) -#define PASS_FROM_SPHY_TO_AFE (1 << 16) -#define MIPI_BORDER_EN (1 << 15) -#define MIPIA_3LANE_MIPIC_1LANE 0x1 -#define MIPIA_2LANE_MIPIC_2LANE 0x2 -#define TE_TRIGGER_DSI_PROTOCOL (1 << 2) -#define TE_TRIGGER_GPIO_PIN (1 << 3) -#define MIPI_TE_COUNT 0x61194 - -/* #define PP_CONTROL 0x61204 */ -#define POWER_DOWN_ON_RESET (1 << 1) - -/* #define PFIT_CONTROL 0x61230 */ -#define PFIT_PIPE_SELECT (3 << 29) -#define PFIT_PIPE_SELECT_SHIFT (29) - -/* #define BLC_PWM_CTL 0x61254 */ -#define MRST_BACKLIGHT_MODULATION_FREQ_SHIFT (16) -#define MRST_BACKLIGHT_MODULATION_FREQ_MASK (0xffff << 16) - -/* #define PIPEACONF 0x70008 */ -#define PIPEACONF_PIPE_STATE (1 << 30) -/* #define DSPACNTR 0x70180 */ - -#define MRST_DSPABASE 0x7019c -#define MRST_DSPBBASE 0x7119c -#define MDFLD_DSPCBASE 0x7219c - -/* - * Moorestown registers. - */ - -/* - * MIPI IP registers - */ -#define MIPIC_REG_OFFSET 0x800 - -#define DEVICE_READY_REG 0xb000 -#define LP_OUTPUT_HOLD (1 << 16) -#define EXIT_ULPS_DEV_READY 0x3 -#define LP_OUTPUT_HOLD_RELEASE 0x810000 -# define ENTERING_ULPS (2 << 1) -# define EXITING_ULPS (1 << 1) -# define ULPS_MASK (3 << 1) -# define BUS_POSSESSION (1 << 3) -#define INTR_STAT_REG 0xb004 -#define RX_SOT_ERROR (1 << 0) -#define RX_SOT_SYNC_ERROR (1 << 1) -#define RX_ESCAPE_MODE_ENTRY_ERROR (1 << 3) -#define RX_LP_TX_SYNC_ERROR (1 << 4) -#define RX_HS_RECEIVE_TIMEOUT_ERROR (1 << 5) -#define RX_FALSE_CONTROL_ERROR (1 << 6) -#define RX_ECC_SINGLE_BIT_ERROR (1 << 7) -#define RX_ECC_MULTI_BIT_ERROR (1 << 8) -#define RX_CHECKSUM_ERROR (1 << 9) -#define RX_DSI_DATA_TYPE_NOT_RECOGNIZED (1 << 10) -#define RX_DSI_VC_ID_INVALID (1 << 11) -#define TX_FALSE_CONTROL_ERROR (1 << 12) -#define TX_ECC_SINGLE_BIT_ERROR (1 << 13) -#define TX_ECC_MULTI_BIT_ERROR (1 << 14) -#define TX_CHECKSUM_ERROR (1 << 15) -#define TX_DSI_DATA_TYPE_NOT_RECOGNIZED (1 << 16) -#define TX_DSI_VC_ID_INVALID (1 << 17) -#define HIGH_CONTENTION (1 << 18) -#define LOW_CONTENTION (1 << 19) -#define DPI_FIFO_UNDER_RUN (1 << 20) -#define HS_TX_TIMEOUT (1 << 21) -#define LP_RX_TIMEOUT (1 << 22) -#define TURN_AROUND_ACK_TIMEOUT (1 << 23) -#define ACK_WITH_NO_ERROR (1 << 24) -#define HS_GENERIC_WR_FIFO_FULL (1 << 27) -#define LP_GENERIC_WR_FIFO_FULL (1 << 28) -#define SPL_PKT_SENT (1 << 30) -#define INTR_EN_REG 0xb008 -#define DSI_FUNC_PRG_REG 0xb00c -#define DPI_CHANNEL_NUMBER_POS 0x03 -#define DBI_CHANNEL_NUMBER_POS 0x05 -#define FMT_DPI_POS 0x07 -#define FMT_DBI_POS 0x0A -#define DBI_DATA_WIDTH_POS 0x0D - -/* DPI PIXEL FORMATS */ -#define RGB_565_FMT 0x01 /* RGB 565 FORMAT */ -#define RGB_666_FMT 0x02 /* RGB 666 FORMAT */ -#define LRGB_666_FMT 0x03 /* RGB LOOSELY PACKED - * 666 FORMAT - */ -#define RGB_888_FMT 0x04 /* RGB 888 FORMAT */ -#define VIRTUAL_CHANNEL_NUMBER_0 0x00 /* Virtual channel 0 */ -#define VIRTUAL_CHANNEL_NUMBER_1 0x01 /* Virtual channel 1 */ -#define VIRTUAL_CHANNEL_NUMBER_2 0x02 /* Virtual channel 2 */ -#define VIRTUAL_CHANNEL_NUMBER_3 0x03 /* Virtual channel 3 */ - -#define DBI_NOT_SUPPORTED 0x00 /* command mode - * is not supported - */ -#define DBI_DATA_WIDTH_16BIT 0x01 /* 16 bit data */ -#define DBI_DATA_WIDTH_9BIT 0x02 /* 9 bit data */ -#define DBI_DATA_WIDTH_8BIT 0x03 /* 8 bit data */ -#define DBI_DATA_WIDTH_OPT1 0x04 /* option 1 */ -#define DBI_DATA_WIDTH_OPT2 0x05 /* option 2 */ - -#define HS_TX_TIMEOUT_REG 0xb010 -#define LP_RX_TIMEOUT_REG 0xb014 -#define TURN_AROUND_TIMEOUT_REG 0xb018 -#define DEVICE_RESET_REG 0xb01C -#define DPI_RESOLUTION_REG 0xb020 -#define RES_V_POS 0x10 -#define DBI_RESOLUTION_REG 0xb024 /* Reserved for MDFLD */ -#define HORIZ_SYNC_PAD_COUNT_REG 0xb028 -#define HORIZ_BACK_PORCH_COUNT_REG 0xb02C -#define HORIZ_FRONT_PORCH_COUNT_REG 0xb030 -#define HORIZ_ACTIVE_AREA_COUNT_REG 0xb034 -#define VERT_SYNC_PAD_COUNT_REG 0xb038 -#define VERT_BACK_PORCH_COUNT_REG 0xb03c -#define VERT_FRONT_PORCH_COUNT_REG 0xb040 -#define HIGH_LOW_SWITCH_COUNT_REG 0xb044 -#define DPI_CONTROL_REG 0xb048 -#define DPI_SHUT_DOWN (1 << 0) -#define DPI_TURN_ON (1 << 1) -#define DPI_COLOR_MODE_ON (1 << 2) -#define DPI_COLOR_MODE_OFF (1 << 3) -#define DPI_BACK_LIGHT_ON (1 << 4) -#define DPI_BACK_LIGHT_OFF (1 << 5) -#define DPI_LP (1 << 6) -#define DPI_DATA_REG 0xb04c -#define DPI_BACK_LIGHT_ON_DATA 0x07 -#define DPI_BACK_LIGHT_OFF_DATA 0x17 -#define INIT_COUNT_REG 0xb050 -#define MAX_RET_PAK_REG 0xb054 -#define VIDEO_FMT_REG 0xb058 -#define COMPLETE_LAST_PCKT (1 << 2) -#define EOT_DISABLE_REG 0xb05c -#define ENABLE_CLOCK_STOPPING (1 << 1) -#define LP_BYTECLK_REG 0xb060 -#define LP_GEN_DATA_REG 0xb064 -#define HS_GEN_DATA_REG 0xb068 -#define LP_GEN_CTRL_REG 0xb06C -#define HS_GEN_CTRL_REG 0xb070 -#define DCS_CHANNEL_NUMBER_POS 0x6 -#define MCS_COMMANDS_POS 0x8 -#define WORD_COUNTS_POS 0x8 -#define MCS_PARAMETER_POS 0x10 -#define GEN_FIFO_STAT_REG 0xb074 -#define HS_DATA_FIFO_FULL (1 << 0) -#define HS_DATA_FIFO_HALF_EMPTY (1 << 1) -#define HS_DATA_FIFO_EMPTY (1 << 2) -#define LP_DATA_FIFO_FULL (1 << 8) -#define LP_DATA_FIFO_HALF_EMPTY (1 << 9) -#define LP_DATA_FIFO_EMPTY (1 << 10) -#define HS_CTRL_FIFO_FULL (1 << 16) -#define HS_CTRL_FIFO_HALF_EMPTY (1 << 17) -#define HS_CTRL_FIFO_EMPTY (1 << 18) -#define LP_CTRL_FIFO_FULL (1 << 24) -#define LP_CTRL_FIFO_HALF_EMPTY (1 << 25) -#define LP_CTRL_FIFO_EMPTY (1 << 26) -#define DBI_FIFO_EMPTY (1 << 27) -#define DPI_FIFO_EMPTY (1 << 28) -#define HS_LS_DBI_ENABLE_REG 0xb078 -#define TXCLKESC_REG 0xb07c -#define DPHY_PARAM_REG 0xb080 -#define DBI_BW_CTRL_REG 0xb084 -#define CLK_LANE_SWT_REG 0xb088 - -/* - * MIPI Adapter registers - */ -#define MIPI_CONTROL_REG 0xb104 -#define MIPI_2X_CLOCK_BITS ((1 << 0) | (1 << 1)) -#define MIPI_DATA_ADDRESS_REG 0xb108 -#define MIPI_DATA_LENGTH_REG 0xb10C -#define MIPI_COMMAND_ADDRESS_REG 0xb110 -#define MIPI_COMMAND_LENGTH_REG 0xb114 -#define MIPI_READ_DATA_RETURN_REG0 0xb118 -#define MIPI_READ_DATA_RETURN_REG1 0xb11C -#define MIPI_READ_DATA_RETURN_REG2 0xb120 -#define MIPI_READ_DATA_RETURN_REG3 0xb124 -#define MIPI_READ_DATA_RETURN_REG4 0xb128 -#define MIPI_READ_DATA_RETURN_REG5 0xb12C -#define MIPI_READ_DATA_RETURN_REG6 0xb130 -#define MIPI_READ_DATA_RETURN_REG7 0xb134 -#define MIPI_READ_DATA_VALID_REG 0xb138 - -/* DBI COMMANDS */ -#define soft_reset 0x01 -/* - * The display module performs a software reset. - * Registers are written with their SW Reset default values. - */ -#define get_power_mode 0x0a -/* - * The display module returns the current power mode - */ -#define get_address_mode 0x0b -/* - * The display module returns the current status. - */ -#define get_pixel_format 0x0c -/* - * This command gets the pixel format for the RGB image data - * used by the interface. - */ -#define get_display_mode 0x0d -/* - * The display module returns the Display Image Mode status. - */ -#define get_signal_mode 0x0e -/* - * The display module returns the Display Signal Mode. - */ -#define get_diagnostic_result 0x0f -/* - * The display module returns the self-diagnostic results following - * a Sleep Out command. - */ -#define enter_sleep_mode 0x10 -/* - * This command causes the display module to enter the Sleep mode. - * In this mode, all unnecessary blocks inside the display module are - * disabled except interface communication. This is the lowest power - * mode the display module supports. - */ -#define exit_sleep_mode 0x11 -/* - * This command causes the display module to exit Sleep mode. - * All blocks inside the display module are enabled. - */ -#define enter_partial_mode 0x12 -/* - * This command causes the display module to enter the Partial Display - * Mode. The Partial Display Mode window is described by the - * set_partial_area command. - */ -#define enter_normal_mode 0x13 -/* - * This command causes the display module to enter the Normal mode. - * Normal Mode is defined as Partial Display mode and Scroll mode are off - */ -#define exit_invert_mode 0x20 -/* - * This command causes the display module to stop inverting the image - * data on the display device. The frame memory contents remain unchanged. - * No status bits are changed. - */ -#define enter_invert_mode 0x21 -/* - * This command causes the display module to invert the image data only on - * the display device. The frame memory contents remain unchanged. - * No status bits are changed. - */ -#define set_gamma_curve 0x26 -/* - * This command selects the desired gamma curve for the display device. - * Four fixed gamma curves are defined in section DCS spec. - */ -#define set_display_off 0x28 -/* ************************************************************************* *\ -This command causes the display module to stop displaying the image data -on the display device. The frame memory contents remain unchanged. -No status bits are changed. -\* ************************************************************************* */ -#define set_display_on 0x29 -/* ************************************************************************* *\ -This command causes the display module to start displaying the image data -on the display device. The frame memory contents remain unchanged. -No status bits are changed. -\* ************************************************************************* */ -#define set_column_address 0x2a -/* - * This command defines the column extent of the frame memory accessed by - * the hostprocessor with the read_memory_continue and - * write_memory_continue commands. - * No status bits are changed. - */ -#define set_page_addr 0x2b -/* - * This command defines the page extent of the frame memory accessed by - * the host processor with the write_memory_continue and - * read_memory_continue command. - * No status bits are changed. - */ -#define write_mem_start 0x2c -/* - * This command transfers image data from the host processor to the - * display modules frame memory starting at the pixel location specified - * by preceding set_column_address and set_page_address commands. - */ -#define set_partial_area 0x30 -/* - * This command defines the Partial Display mode s display area. - * There are two parameters associated with this command, the first - * defines the Start Row (SR) and the second the End Row (ER). SR and ER - * refer to the Frame Memory Line Pointer. - */ -#define set_scroll_area 0x33 -/* - * This command defines the display modules Vertical Scrolling Area. - */ -#define set_tear_off 0x34 -/* - * This command turns off the display modules Tearing Effect output - * signal on the TE signal line. - */ -#define set_tear_on 0x35 -/* - * This command turns on the display modules Tearing Effect output signal - * on the TE signal line. - */ -#define set_address_mode 0x36 -/* - * This command sets the data order for transfers from the host processor - * to display modules frame memory,bits B[7:5] and B3, and from the - * display modules frame memory to the display device, bits B[2:0] and B4. - */ -#define set_scroll_start 0x37 -/* - * This command sets the start of the vertical scrolling area in the frame - * memory. The vertical scrolling area is fully defined when this command - * is used with the set_scroll_area command The set_scroll_start command - * has one parameter, the Vertical Scroll Pointer. The VSP defines the - * line in the frame memory that is written to the display device as the - * first line of the vertical scroll area. - */ -#define exit_idle_mode 0x38 -/* - * This command causes the display module to exit Idle mode. - */ -#define enter_idle_mode 0x39 -/* - * This command causes the display module to enter Idle Mode. - * In Idle Mode, color expression is reduced. Colors are shown on the - * display device using the MSB of each of the R, G and B color - * components in the frame memory - */ -#define set_pixel_format 0x3a -/* - * This command sets the pixel format for the RGB image data used by the - * interface. - * Bits D[6:4] DPI Pixel Format Definition - * Bits D[2:0] DBI Pixel Format Definition - * Bits D7 and D3 are not used. - */ -#define DCS_PIXEL_FORMAT_3bpp 0x1 -#define DCS_PIXEL_FORMAT_8bpp 0x2 -#define DCS_PIXEL_FORMAT_12bpp 0x3 -#define DCS_PIXEL_FORMAT_16bpp 0x5 -#define DCS_PIXEL_FORMAT_18bpp 0x6 -#define DCS_PIXEL_FORMAT_24bpp 0x7 - -#define write_mem_cont 0x3c - -/* - * This command transfers image data from the host processor to the - * display module's frame memory continuing from the pixel location - * following the previous write_memory_continue or write_memory_start - * command. - */ -#define set_tear_scanline 0x44 -/* - * This command turns on the display modules Tearing Effect output signal - * on the TE signal line when the display module reaches line N. - */ -#define get_scanline 0x45 -/* - * The display module returns the current scanline, N, used to update the - * display device. The total number of scanlines on a display device is - * defined as VSYNC + VBP + VACT + VFP.The first scanline is defined as - * the first line of V Sync and is denoted as Line 0. - * When in Sleep Mode, the value returned by get_scanline is undefined. - */ - -/* MCS or Generic COMMANDS */ -/* MCS/generic data type */ -#define GEN_SHORT_WRITE_0 0x03 /* generic short write, no parameters */ -#define GEN_SHORT_WRITE_1 0x13 /* generic short write, 1 parameters */ -#define GEN_SHORT_WRITE_2 0x23 /* generic short write, 2 parameters */ -#define GEN_READ_0 0x04 /* generic read, no parameters */ -#define GEN_READ_1 0x14 /* generic read, 1 parameters */ -#define GEN_READ_2 0x24 /* generic read, 2 parameters */ -#define GEN_LONG_WRITE 0x29 /* generic long write */ -#define MCS_SHORT_WRITE_0 0x05 /* MCS short write, no parameters */ -#define MCS_SHORT_WRITE_1 0x15 /* MCS short write, 1 parameters */ -#define MCS_READ 0x06 /* MCS read, no parameters */ -#define MCS_LONG_WRITE 0x39 /* MCS long write */ -/* MCS/generic commands */ -/* TPO MCS */ -#define write_display_profile 0x50 -#define write_display_brightness 0x51 -#define write_ctrl_display 0x53 -#define write_ctrl_cabc 0x55 - #define UI_IMAGE 0x01 - #define STILL_IMAGE 0x02 - #define MOVING_IMAGE 0x03 -#define write_hysteresis 0x57 -#define write_gamma_setting 0x58 -#define write_cabc_min_bright 0x5e -#define write_kbbc_profile 0x60 -/* TMD MCS */ -#define tmd_write_display_brightness 0x8c - -/* - * This command is used to control ambient light, panel backlight - * brightness and gamma settings. - */ -#define BRIGHT_CNTL_BLOCK_ON (1 << 5) -#define AMBIENT_LIGHT_SENSE_ON (1 << 4) -#define DISPLAY_DIMMING_ON (1 << 3) -#define BACKLIGHT_ON (1 << 2) -#define DISPLAY_BRIGHTNESS_AUTO (1 << 1) -#define GAMMA_AUTO (1 << 0) - -/* DCS Interface Pixel Formats */ -#define DCS_PIXEL_FORMAT_3BPP 0x1 -#define DCS_PIXEL_FORMAT_8BPP 0x2 -#define DCS_PIXEL_FORMAT_12BPP 0x3 -#define DCS_PIXEL_FORMAT_16BPP 0x5 -#define DCS_PIXEL_FORMAT_18BPP 0x6 -#define DCS_PIXEL_FORMAT_24BPP 0x7 -/* ONE PARAMETER READ DATA */ -#define addr_mode_data 0xfc -#define diag_res_data 0x00 -#define disp_mode_data 0x23 -#define pxl_fmt_data 0x77 -#define pwr_mode_data 0x74 -#define sig_mode_data 0x00 -/* TWO PARAMETERS READ DATA */ -#define scanline_data1 0xff -#define scanline_data2 0xff -#define NON_BURST_MODE_SYNC_PULSE 0x01 /* Non Burst Mode - * with Sync Pulse - */ -#define NON_BURST_MODE_SYNC_EVENTS 0x02 /* Non Burst Mode - * with Sync events - */ -#define BURST_MODE 0x03 /* Burst Mode */ -#define DBI_COMMAND_BUFFER_SIZE 0x240 /* 0x32 */ /* 0x120 */ - /* Allocate at least - * 0x100 Byte with 32 - * byte alignment - */ -#define DBI_DATA_BUFFER_SIZE 0x120 /* Allocate at least - * 0x100 Byte with 32 - * byte alignment - */ -#define DBI_CB_TIME_OUT 0xFFFF - -#define GEN_FB_TIME_OUT 2000 - -#define SKU_83 0x01 -#define SKU_100 0x02 -#define SKU_100L 0x04 -#define SKU_BYPASS 0x08 - -/* Some handy macros for playing with bitfields. */ -#define PSB_MASK(high, low) (((1<<((high)-(low)+1))-1)<<(low)) -#define SET_FIELD(value, field) (((value) << field ## _SHIFT) & field ## _MASK) -#define GET_FIELD(word, field) (((word) & field ## _MASK) >> field ## _SHIFT) - -#define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a))) - -/* PCI config space */ - -#define SB_PCKT 0x02100 /* cedarview */ -# define SB_OPCODE_MASK PSB_MASK(31, 16) -# define SB_OPCODE_SHIFT 16 -# define SB_OPCODE_READ 0 -# define SB_OPCODE_WRITE 1 -# define SB_DEST_MASK PSB_MASK(15, 8) -# define SB_DEST_SHIFT 8 -# define SB_DEST_DPLL 0x88 -# define SB_BYTE_ENABLE_MASK PSB_MASK(7, 4) -# define SB_BYTE_ENABLE_SHIFT 4 -# define SB_BUSY (1 << 0) - - -/* 32-bit value read/written from the DPIO reg. */ -#define SB_DATA 0x02104 /* cedarview */ -/* 32-bit address of the DPIO reg to be read/written. */ -#define SB_ADDR 0x02108 /* cedarview */ -#define DPIO_CFG 0x02110 /* cedarview */ -# define DPIO_MODE_SELECT_1 (1 << 3) -# define DPIO_MODE_SELECT_0 (1 << 2) -# define DPIO_SFR_BYPASS (1 << 1) -/* reset is active low */ -# define DPIO_CMN_RESET_N (1 << 0) - -/* Cedarview sideband registers */ -#define _SB_M_A 0x8008 -#define _SB_M_B 0x8028 -#define SB_M(pipe) _PIPE(pipe, _SB_M_A, _SB_M_B) -# define SB_M_DIVIDER_MASK (0xFF << 24) -# define SB_M_DIVIDER_SHIFT 24 - -#define _SB_N_VCO_A 0x8014 -#define _SB_N_VCO_B 0x8034 -#define SB_N_VCO(pipe) _PIPE(pipe, _SB_N_VCO_A, _SB_N_VCO_B) -#define SB_N_VCO_SEL_MASK PSB_MASK(31, 30) -#define SB_N_VCO_SEL_SHIFT 30 -#define SB_N_DIVIDER_MASK PSB_MASK(29, 26) -#define SB_N_DIVIDER_SHIFT 26 -#define SB_N_CB_TUNE_MASK PSB_MASK(25, 24) -#define SB_N_CB_TUNE_SHIFT 24 - -#define _SB_REF_A 0x8018 -#define _SB_REF_B 0x8038 -#define SB_REF_SFR(pipe) _PIPE(pipe, _SB_REF_A, _SB_REF_B) - -#define _SB_P_A 0x801c -#define _SB_P_B 0x803c -#define SB_P(pipe) _PIPE(pipe, _SB_P_A, _SB_P_B) -#define SB_P2_DIVIDER_MASK PSB_MASK(31, 30) -#define SB_P2_DIVIDER_SHIFT 30 -#define SB_P2_10 0 /* HDMI, DP, DAC */ -#define SB_P2_5 1 /* DAC */ -#define SB_P2_14 2 /* LVDS single */ -#define SB_P2_7 3 /* LVDS double */ -#define SB_P1_DIVIDER_MASK PSB_MASK(15, 12) -#define SB_P1_DIVIDER_SHIFT 12 - -#define PSB_LANE0 0x120 -#define PSB_LANE1 0x220 -#define PSB_LANE2 0x2320 -#define PSB_LANE3 0x2420 - -#define LANE_PLL_MASK (0x7 << 20) -#define LANE_PLL_ENABLE (0x3 << 20) - - -#endif diff --git a/drivers/staging/gma500/psb_intel_sdvo.c b/drivers/staging/gma500/psb_intel_sdvo.c deleted file mode 100644 index a4bad1a..0000000 --- a/drivers/staging/gma500/psb_intel_sdvo.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* - * Copyright (c) 2006-2007 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - */ - -#include -#include -/* #include */ -#include -#include "psb_drv.h" -#include "psb_intel_drv.h" -#include "psb_intel_reg.h" -#include "psb_intel_sdvo_regs.h" - -struct psb_intel_sdvo_priv { - struct psb_intel_i2c_chan *i2c_bus; - int slaveaddr; - int output_device; - - u16 active_outputs; - - struct psb_intel_sdvo_caps caps; - int pixel_clock_min, pixel_clock_max; - - int save_sdvo_mult; - u16 save_active_outputs; - struct psb_intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; - struct psb_intel_sdvo_dtd save_output_dtd[16]; - u32 save_SDVOX; - u8 in_out_map[4]; - - u8 by_input_wiring; - u32 active_device; -}; - -/** - * Writes the SDVOB or SDVOC with the given value, but always writes both - * SDVOB and SDVOC to work around apparent hardware issues (according to - * comments in the BIOS). - */ -void psb_intel_sdvo_write_sdvox(struct psb_intel_output *psb_intel_output, - u32 val) -{ - struct drm_device *dev = psb_intel_output->base.dev; - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - u32 bval = val, cval = val; - int i; - - if (sdvo_priv->output_device == SDVOB) - cval = REG_READ(SDVOC); - else - bval = REG_READ(SDVOB); - /* - * Write the registers twice for luck. Sometimes, - * writing them only once doesn't appear to 'stick'. - * The BIOS does this too. Yay, magic - */ - for (i = 0; i < 2; i++) { - REG_WRITE(SDVOB, bval); - REG_READ(SDVOB); - REG_WRITE(SDVOC, cval); - REG_READ(SDVOC); - } -} - -static bool psb_intel_sdvo_read_byte( - struct psb_intel_output *psb_intel_output, - u8 addr, u8 *ch) -{ - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - u8 out_buf[2]; - u8 buf[2]; - int ret; - - struct i2c_msg msgs[] = { - { - .addr = sdvo_priv->i2c_bus->slave_addr, - .flags = 0, - .len = 1, - .buf = out_buf, - }, - { - .addr = sdvo_priv->i2c_bus->slave_addr, - .flags = I2C_M_RD, - .len = 1, - .buf = buf, - } - }; - - out_buf[0] = addr; - out_buf[1] = 0; - - ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2); - if (ret == 2) { - *ch = buf[0]; - return true; - } - - return false; -} - -static bool psb_intel_sdvo_write_byte( - struct psb_intel_output *psb_intel_output, - int addr, u8 ch) -{ - u8 out_buf[2]; - struct i2c_msg msgs[] = { - { - .addr = psb_intel_output->i2c_bus->slave_addr, - .flags = 0, - .len = 2, - .buf = out_buf, - } - }; - - out_buf[0] = addr; - out_buf[1] = ch; - - if (i2c_transfer(&psb_intel_output->i2c_bus->adapter, msgs, 1) == 1) - return true; - return false; -} - -#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} -/** Mapping of command numbers to names, for debug output */ -static const struct _sdvo_cmd_name { - u8 cmd; - char *name; -} sdvo_cmd_names[] = { -SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY - (SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),}; - -#define SDVO_NAME(dev_priv) \ - ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") -#define SDVO_PRIV(output) ((struct psb_intel_sdvo_priv *) (output)->dev_priv) - -static void psb_intel_sdvo_write_cmd(struct psb_intel_output *psb_intel_output, - u8 cmd, - void *args, - int args_len) -{ - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - int i; - - if (0) { - printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); - for (i = 0; i < args_len; i++) - printk(KERN_CONT "%02X ", ((u8 *) args)[i]); - for (; i < 8; i++) - printk(KERN_CONT " "); - for (i = 0; - i < - sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); - i++) { - if (cmd == sdvo_cmd_names[i].cmd) { - printk(KERN_CONT - "(%s)", sdvo_cmd_names[i].name); - break; - } - } - if (i == - sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0])) - printk(KERN_CONT "(%02X)", cmd); - printk(KERN_CONT "\n"); - } - - for (i = 0; i < args_len; i++) { - psb_intel_sdvo_write_byte(psb_intel_output, - SDVO_I2C_ARG_0 - i, - ((u8 *) args)[i]); - } - - psb_intel_sdvo_write_byte(psb_intel_output, SDVO_I2C_OPCODE, cmd); -} - -static const char *const cmd_status_names[] = { - "Power on", - "Success", - "Not supported", - "Invalid arg", - "Pending", - "Target not specified", - "Scaling not supported" -}; - -static u8 psb_intel_sdvo_read_response( - struct psb_intel_output *psb_intel_output, - void *response, int response_len) -{ - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - int i; - u8 status; - u8 retry = 50; - - while (retry--) { - /* Read the command response */ - for (i = 0; i < response_len; i++) { - psb_intel_sdvo_read_byte(psb_intel_output, - SDVO_I2C_RETURN_0 + i, - &((u8 *) response)[i]); - } - - /* read the return status */ - psb_intel_sdvo_read_byte(psb_intel_output, - SDVO_I2C_CMD_STATUS, - &status); - - if (0) { - pr_debug("%s: R: ", SDVO_NAME(sdvo_priv)); - for (i = 0; i < response_len; i++) - printk(KERN_CONT "%02X ", ((u8 *) response)[i]); - for (; i < 8; i++) - printk(" "); - if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) - printk(KERN_CONT "(%s)", - cmd_status_names[status]); - else - printk(KERN_CONT "(??? %d)", status); - printk(KERN_CONT "\n"); - } - - if (status != SDVO_CMD_STATUS_PENDING) - return status; - - mdelay(50); - } - - return status; -} - -int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) -{ - if (mode->clock >= 100000) - return 1; - else if (mode->clock >= 50000) - return 2; - else - return 4; -} - -/** - * Don't check status code from this as it switches the bus back to the - * SDVO chips which defeats the purpose of doing a bus switch in the first - * place. - */ -void psb_intel_sdvo_set_control_bus_switch( - struct psb_intel_output *psb_intel_output, - u8 target) -{ - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_CONTROL_BUS_SWITCH, - &target, - 1); -} - -static bool psb_intel_sdvo_set_target_input( - struct psb_intel_output *psb_intel_output, - bool target_0, bool target_1) -{ - struct psb_intel_sdvo_set_target_input_args targets = { 0 }; - u8 status; - - if (target_0 && target_1) - return SDVO_CMD_STATUS_NOTSUPP; - - if (target_1) - targets.target_1 = 1; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_INPUT, - &targets, sizeof(targets)); - - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - - return status == SDVO_CMD_STATUS_SUCCESS; -} - -/** - * Return whether each input is trained. - * - * This function is making an assumption about the layout of the response, - * which should be checked against the docs. - */ -static bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_output - *psb_intel_output, bool *input_1, - bool *input_2) -{ - struct psb_intel_sdvo_get_trained_inputs_response response; - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_TRAINED_INPUTS, - NULL, 0); - status = - psb_intel_sdvo_read_response(psb_intel_output, &response, - sizeof(response)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - *input_1 = response.input0_trained; - *input_2 = response.input1_trained; - return true; -} - -static bool psb_intel_sdvo_get_active_outputs(struct psb_intel_output - *psb_intel_output, u16 *outputs) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, - NULL, 0); - status = - psb_intel_sdvo_read_response(psb_intel_output, outputs, - sizeof(*outputs)); - - return status == SDVO_CMD_STATUS_SUCCESS; -} - -static bool psb_intel_sdvo_set_active_outputs(struct psb_intel_output - *psb_intel_output, u16 outputs) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, - &outputs, sizeof(outputs)); - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - return status == SDVO_CMD_STATUS_SUCCESS; -} - -static bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_output - *psb_intel_output, int mode) -{ - u8 status, state = SDVO_ENCODER_STATE_ON; - - switch (mode) { - case DRM_MODE_DPMS_ON: - state = SDVO_ENCODER_STATE_ON; - break; - case DRM_MODE_DPMS_STANDBY: - state = SDVO_ENCODER_STATE_STANDBY; - break; - case DRM_MODE_DPMS_SUSPEND: - state = SDVO_ENCODER_STATE_SUSPEND; - break; - case DRM_MODE_DPMS_OFF: - state = SDVO_ENCODER_STATE_OFF; - break; - } - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_ENCODER_POWER_STATE, &state, - sizeof(state)); - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - - return status == SDVO_CMD_STATUS_SUCCESS; -} - -static bool psb_intel_sdvo_get_input_pixel_clock_range(struct psb_intel_output - *psb_intel_output, - int *clock_min, - int *clock_max) -{ - struct psb_intel_sdvo_pixel_clock_range clocks; - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, NULL, - 0); - - status = - psb_intel_sdvo_read_response(psb_intel_output, &clocks, - sizeof(clocks)); - - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - /* Convert the values from units of 10 kHz to kHz. */ - *clock_min = clocks.min * 10; - *clock_max = clocks.max * 10; - - return true; -} - -static bool psb_intel_sdvo_set_target_output( - struct psb_intel_output *psb_intel_output, - u16 outputs) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_OUTPUT, - &outputs, sizeof(outputs)); - - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - return status == SDVO_CMD_STATUS_SUCCESS; -} - -static bool psb_intel_sdvo_get_timing(struct psb_intel_output *psb_intel_output, - u8 cmd, struct psb_intel_sdvo_dtd *dtd) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, cmd, NULL, 0); - status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part1, - sizeof(dtd->part1)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, NULL, 0); - status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part2, - sizeof(dtd->part2)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - return true; -} - -static bool psb_intel_sdvo_get_input_timing( - struct psb_intel_output *psb_intel_output, - struct psb_intel_sdvo_dtd *dtd) -{ - return psb_intel_sdvo_get_timing(psb_intel_output, - SDVO_CMD_GET_INPUT_TIMINGS_PART1, - dtd); -} - -static bool psb_intel_sdvo_set_timing( - struct psb_intel_output *psb_intel_output, - u8 cmd, - struct psb_intel_sdvo_dtd *dtd) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, cmd, &dtd->part1, - sizeof(dtd->part1)); - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, &dtd->part2, - sizeof(dtd->part2)); - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - return true; -} - -static bool psb_intel_sdvo_set_input_timing( - struct psb_intel_output *psb_intel_output, - struct psb_intel_sdvo_dtd *dtd) -{ - return psb_intel_sdvo_set_timing(psb_intel_output, - SDVO_CMD_SET_INPUT_TIMINGS_PART1, - dtd); -} - -static bool psb_intel_sdvo_set_output_timing( - struct psb_intel_output *psb_intel_output, - struct psb_intel_sdvo_dtd *dtd) -{ - return psb_intel_sdvo_set_timing(psb_intel_output, - SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, - dtd); -} - -static int psb_intel_sdvo_get_clock_rate_mult(struct psb_intel_output - *psb_intel_output) -{ - u8 response, status; - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_CLOCK_RATE_MULT, - NULL, - 0); - - status = psb_intel_sdvo_read_response(psb_intel_output, &response, 1); - - if (status != SDVO_CMD_STATUS_SUCCESS) { - DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n"); - return SDVO_CLOCK_RATE_MULT_1X; - } else { - DRM_DEBUG("Current clock rate multiplier: %d\n", response); - } - - return response; -} - -static bool psb_intel_sdvo_set_clock_rate_mult(struct psb_intel_output - *psb_intel_output, u8 val) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_CLOCK_RATE_MULT, - &val, - 1); - - status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - return true; -} - -static bool psb_sdvo_set_current_inoutmap(struct psb_intel_output *output, - u32 in0outputmask, - u32 in1outputmask) -{ - u8 byArgs[4]; - u8 status; - int i; - struct psb_intel_sdvo_priv *sdvo_priv = output->dev_priv; - - /* Make all fields of the args/ret to zero */ - memset(byArgs, 0, sizeof(byArgs)); - - /* Fill up the argument values; */ - byArgs[0] = (u8) (in0outputmask & 0xFF); - byArgs[1] = (u8) ((in0outputmask >> 8) & 0xFF); - byArgs[2] = (u8) (in1outputmask & 0xFF); - byArgs[3] = (u8) ((in1outputmask >> 8) & 0xFF); - - - /*save inoutmap arg here*/ - for (i = 0; i < 4; i++) - sdvo_priv->in_out_map[i] = byArgs[0]; - - psb_intel_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP, byArgs, 4); - status = psb_intel_sdvo_read_response(output, NULL, 0); - - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - return true; -} - - -static void psb_intel_sdvo_set_iomap(struct psb_intel_output *output) -{ - u32 dwCurrentSDVOIn0 = 0; - u32 dwCurrentSDVOIn1 = 0; - u32 dwDevMask = 0; - - - struct psb_intel_sdvo_priv *sdvo_priv = output->dev_priv; - - /* Please DO NOT change the following code. */ - /* SDVOB_IN0 or SDVOB_IN1 ==> sdvo_in0 */ - /* SDVOC_IN0 or SDVOC_IN1 ==> sdvo_in1 */ - if (sdvo_priv->by_input_wiring & (SDVOB_IN0 | SDVOC_IN0)) { - switch (sdvo_priv->active_device) { - case SDVO_DEVICE_LVDS: - dwDevMask = SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1; - break; - case SDVO_DEVICE_TMDS: - dwDevMask = SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1; - break; - case SDVO_DEVICE_TV: - dwDevMask = - SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_SVID0 | - SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB1 | - SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_CVBS1 | - SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1; - break; - case SDVO_DEVICE_CRT: - dwDevMask = SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1; - break; - } - dwCurrentSDVOIn0 = (sdvo_priv->active_outputs & dwDevMask); - } else if (sdvo_priv->by_input_wiring & (SDVOB_IN1 | SDVOC_IN1)) { - switch (sdvo_priv->active_device) { - case SDVO_DEVICE_LVDS: - dwDevMask = SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1; - break; - case SDVO_DEVICE_TMDS: - dwDevMask = SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1; - break; - case SDVO_DEVICE_TV: - dwDevMask = - SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_SVID0 | - SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB1 | - SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_CVBS1 | - SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1; - break; - case SDVO_DEVICE_CRT: - dwDevMask = SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1; - break; - } - dwCurrentSDVOIn1 = (sdvo_priv->active_outputs & dwDevMask); - } - - psb_sdvo_set_current_inoutmap(output, dwCurrentSDVOIn0, - dwCurrentSDVOIn1); -} - - -static bool psb_intel_sdvo_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO - * device will be told of the multiplier during mode_set. - */ - adjusted_mode->clock *= psb_intel_sdvo_get_pixel_multiplier(mode); - return true; -} - -static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_crtc *crtc = encoder->crtc; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_output *psb_intel_output = - enc_to_psb_intel_output(encoder); - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - u16 width, height; - u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; - u16 h_sync_offset, v_sync_offset; - u32 sdvox; - struct psb_intel_sdvo_dtd output_dtd; - int sdvo_pixel_multiply; - - if (!mode) - return; - - psb_intel_sdvo_set_target_output(psb_intel_output, 0); - - width = mode->crtc_hdisplay; - height = mode->crtc_vdisplay; - - /* do some mode translations */ - h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; - h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; - - v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; - v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; - - h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; - v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; - - output_dtd.part1.clock = mode->clock / 10; - output_dtd.part1.h_active = width & 0xff; - output_dtd.part1.h_blank = h_blank_len & 0xff; - output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) | - ((h_blank_len >> 8) & 0xf); - output_dtd.part1.v_active = height & 0xff; - output_dtd.part1.v_blank = v_blank_len & 0xff; - output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) | - ((v_blank_len >> 8) & 0xf); - - output_dtd.part2.h_sync_off = h_sync_offset; - output_dtd.part2.h_sync_width = h_sync_len & 0xff; - output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | - (v_sync_len & 0xf); - output_dtd.part2.sync_off_width_high = - ((h_sync_offset & 0x300) >> 2) | ((h_sync_len & 0x300) >> 4) | - ((v_sync_offset & 0x30) >> 2) | ((v_sync_len & 0x30) >> 4); - - output_dtd.part2.dtd_flags = 0x18; - if (mode->flags & DRM_MODE_FLAG_PHSYNC) - output_dtd.part2.dtd_flags |= 0x2; - if (mode->flags & DRM_MODE_FLAG_PVSYNC) - output_dtd.part2.dtd_flags |= 0x4; - - output_dtd.part2.sdvo_flags = 0; - output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0; - output_dtd.part2.reserved = 0; - - /* Set the output timing to the screen */ - psb_intel_sdvo_set_target_output(psb_intel_output, - sdvo_priv->active_outputs); - - /* Set the input timing to the screen. Assume always input 0. */ - psb_intel_sdvo_set_target_input(psb_intel_output, true, false); - - psb_intel_sdvo_set_output_timing(psb_intel_output, &output_dtd); - - /* We would like to use i830_sdvo_create_preferred_input_timing() to - * provide the device with a timing it can support, if it supports that - * feature. However, presumably we would need to adjust the CRTC to - * output the preferred timing, and we don't support that currently. - */ - psb_intel_sdvo_set_input_timing(psb_intel_output, &output_dtd); - - switch (psb_intel_sdvo_get_pixel_multiplier(mode)) { - case 1: - psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, - SDVO_CLOCK_RATE_MULT_1X); - break; - case 2: - psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, - SDVO_CLOCK_RATE_MULT_2X); - break; - case 4: - psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, - SDVO_CLOCK_RATE_MULT_4X); - break; - } - - /* Set the SDVO control regs. */ - sdvox = REG_READ(sdvo_priv->output_device); - switch (sdvo_priv->output_device) { - case SDVOB: - sdvox &= SDVOB_PRESERVE_MASK; - break; - case SDVOC: - sdvox &= SDVOC_PRESERVE_MASK; - break; - } - sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; - if (psb_intel_crtc->pipe == 1) - sdvox |= SDVO_PIPE_B_SELECT; - - sdvo_pixel_multiply = psb_intel_sdvo_get_pixel_multiplier(mode); - - psb_intel_sdvo_write_sdvox(psb_intel_output, sdvox); - - psb_intel_sdvo_set_iomap(psb_intel_output); -} - -static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct psb_intel_output *psb_intel_output = - enc_to_psb_intel_output(encoder); - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - u32 temp; - - if (mode != DRM_MODE_DPMS_ON) { - psb_intel_sdvo_set_active_outputs(psb_intel_output, 0); - if (0) - psb_intel_sdvo_set_encoder_power_state( - psb_intel_output, - mode); - - if (mode == DRM_MODE_DPMS_OFF) { - temp = REG_READ(sdvo_priv->output_device); - if ((temp & SDVO_ENABLE) != 0) { - psb_intel_sdvo_write_sdvox(psb_intel_output, - temp & - ~SDVO_ENABLE); - } - } - } else { - bool input1, input2; - int i; - u8 status; - - temp = REG_READ(sdvo_priv->output_device); - if ((temp & SDVO_ENABLE) == 0) - psb_intel_sdvo_write_sdvox(psb_intel_output, - temp | SDVO_ENABLE); - for (i = 0; i < 2; i++) - psb_intel_wait_for_vblank(dev); - - status = - psb_intel_sdvo_get_trained_inputs(psb_intel_output, - &input1, - &input2); - - - /* Warn if the device reported failure to sync. - * A lot of SDVO devices fail to notify of sync, but it's - * a given it the status is a success, we succeeded. - */ - if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { - DRM_DEBUG - ("First %s output reported failure to sync\n", - SDVO_NAME(sdvo_priv)); - } - - if (0) - psb_intel_sdvo_set_encoder_power_state( - psb_intel_output, - mode); - psb_intel_sdvo_set_active_outputs(psb_intel_output, - sdvo_priv->active_outputs); - } - return; -} - -static void psb_intel_sdvo_save(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - /*int o;*/ - - sdvo_priv->save_sdvo_mult = - psb_intel_sdvo_get_clock_rate_mult(psb_intel_output); - psb_intel_sdvo_get_active_outputs(psb_intel_output, - &sdvo_priv->save_active_outputs); - - if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { - psb_intel_sdvo_set_target_input(psb_intel_output, - true, - false); - psb_intel_sdvo_get_input_timing(psb_intel_output, - &sdvo_priv->save_input_dtd_1); - } - - if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { - psb_intel_sdvo_set_target_input(psb_intel_output, - false, - true); - psb_intel_sdvo_get_input_timing(psb_intel_output, - &sdvo_priv->save_input_dtd_2); - } - sdvo_priv->save_SDVOX = REG_READ(sdvo_priv->output_device); - - /*TODO: save the in_out_map state*/ -} - -static void psb_intel_sdvo_restore(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - /*int o;*/ - int i; - bool input1, input2; - u8 status; - - psb_intel_sdvo_set_active_outputs(psb_intel_output, 0); - - if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { - psb_intel_sdvo_set_target_input(psb_intel_output, true, false); - psb_intel_sdvo_set_input_timing(psb_intel_output, - &sdvo_priv->save_input_dtd_1); - } - - if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { - psb_intel_sdvo_set_target_input(psb_intel_output, false, true); - psb_intel_sdvo_set_input_timing(psb_intel_output, - &sdvo_priv->save_input_dtd_2); - } - - psb_intel_sdvo_set_clock_rate_mult(psb_intel_output, - sdvo_priv->save_sdvo_mult); - - REG_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX); - - if (sdvo_priv->save_SDVOX & SDVO_ENABLE) { - for (i = 0; i < 2; i++) - psb_intel_wait_for_vblank(dev); - status = - psb_intel_sdvo_get_trained_inputs(psb_intel_output, - &input1, - &input2); - if (status == SDVO_CMD_STATUS_SUCCESS && !input1) - DRM_DEBUG - ("First %s output reported failure to sync\n", - SDVO_NAME(sdvo_priv)); - } - - psb_intel_sdvo_set_active_outputs(psb_intel_output, - sdvo_priv->save_active_outputs); - - /*TODO: restore in_out_map*/ - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_IN_OUT_MAP, - sdvo_priv->in_out_map, - 4); - - psb_intel_sdvo_read_response(psb_intel_output, NULL, 0); -} - -static int psb_intel_sdvo_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv; - - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - if (sdvo_priv->pixel_clock_min > mode->clock) - return MODE_CLOCK_LOW; - - if (sdvo_priv->pixel_clock_max < mode->clock) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - -static bool psb_intel_sdvo_get_capabilities( - struct psb_intel_output *psb_intel_output, - struct psb_intel_sdvo_caps *caps) -{ - u8 status; - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_DEVICE_CAPS, - NULL, - 0); - status = psb_intel_sdvo_read_response(psb_intel_output, - caps, - sizeof(*caps)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return false; - - return true; -} - -struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, int sdvoB) -{ - struct drm_connector *connector = NULL; - struct psb_intel_output *iout = NULL; - struct psb_intel_sdvo_priv *sdvo; - - /* find the sdvo connector */ - list_for_each_entry(connector, &dev->mode_config.connector_list, - head) { - iout = to_psb_intel_output(connector); - - if (iout->type != INTEL_OUTPUT_SDVO) - continue; - - sdvo = iout->dev_priv; - - if (sdvo->output_device == SDVOB && sdvoB) - return connector; - - if (sdvo->output_device == SDVOC && !sdvoB) - return connector; - - } - - return NULL; -} - -int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector) -{ - u8 response[2]; - u8 status; - struct psb_intel_output *psb_intel_output; - - if (!connector) - return 0; - - psb_intel_output = to_psb_intel_output(connector); - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_HOT_PLUG_SUPPORT, - NULL, - 0); - status = psb_intel_sdvo_read_response(psb_intel_output, - &response, - 2); - - if (response[0] != 0) - return 1; - - return 0; -} - -void psb_intel_sdvo_set_hotplug(struct drm_connector *connector, int on) -{ - u8 response[2]; - u8 status; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_ACTIVE_HOT_PLUG, - NULL, - 0); - psb_intel_sdvo_read_response(psb_intel_output, &response, 2); - - if (on) { - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, - 0); - status = psb_intel_sdvo_read_response(psb_intel_output, - &response, - 2); - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_ACTIVE_HOT_PLUG, - &response, 2); - } else { - response[0] = 0; - response[1] = 0; - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_SET_ACTIVE_HOT_PLUG, - &response, 2); - } - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_ACTIVE_HOT_PLUG, - NULL, - 0); - psb_intel_sdvo_read_response(psb_intel_output, &response, 2); -} - -static enum drm_connector_status psb_intel_sdvo_detect(struct drm_connector - *connector, bool force) -{ - u8 response[2]; - u8 status; - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - psb_intel_sdvo_write_cmd(psb_intel_output, - SDVO_CMD_GET_ATTACHED_DISPLAYS, - NULL, - 0); - status = psb_intel_sdvo_read_response(psb_intel_output, &response, 2); - - DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); - if ((response[0] != 0) || (response[1] != 0)) - return connector_status_connected; - else - return connector_status_disconnected; -} - -static int psb_intel_sdvo_get_modes(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - /* set the bus switch and get the modes */ - psb_intel_sdvo_set_control_bus_switch(psb_intel_output, - SDVO_CONTROL_BUS_DDC2); - psb_intel_ddc_get_modes(psb_intel_output); - - if (list_empty(&connector->probed_modes)) - return 0; - return 1; -} - -static void psb_intel_sdvo_destroy(struct drm_connector *connector) -{ - struct psb_intel_output *psb_intel_output = - to_psb_intel_output(connector); - - if (psb_intel_output->i2c_bus) - psb_intel_i2c_destroy(psb_intel_output->i2c_bus); - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(psb_intel_output); -} - -static const struct drm_encoder_helper_funcs psb_intel_sdvo_helper_funcs = { - .dpms = psb_intel_sdvo_dpms, - .mode_fixup = psb_intel_sdvo_mode_fixup, - .prepare = psb_intel_encoder_prepare, - .mode_set = psb_intel_sdvo_mode_set, - .commit = psb_intel_encoder_commit, -}; - -static const struct drm_connector_funcs psb_intel_sdvo_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .save = psb_intel_sdvo_save, - .restore = psb_intel_sdvo_restore, - .detect = psb_intel_sdvo_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = psb_intel_sdvo_destroy, -}; - -static const struct drm_connector_helper_funcs - psb_intel_sdvo_connector_helper_funcs = { - .get_modes = psb_intel_sdvo_get_modes, - .mode_valid = psb_intel_sdvo_mode_valid, - .best_encoder = psb_intel_best_encoder, -}; - -void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - -static const struct drm_encoder_funcs psb_intel_sdvo_enc_funcs = { - .destroy = psb_intel_sdvo_enc_destroy, -}; - - -void psb_intel_sdvo_init(struct drm_device *dev, int output_device) -{ - struct drm_connector *connector; - struct psb_intel_output *psb_intel_output; - struct psb_intel_sdvo_priv *sdvo_priv; - struct psb_intel_i2c_chan *i2cbus = NULL; - int connector_type; - u8 ch[0x40]; - int i; - int encoder_type, output_id; - - psb_intel_output = - kcalloc(sizeof(struct psb_intel_output) + - sizeof(struct psb_intel_sdvo_priv), 1, GFP_KERNEL); - if (!psb_intel_output) - return; - - connector = &psb_intel_output->base; - - drm_connector_init(dev, connector, &psb_intel_sdvo_connector_funcs, - DRM_MODE_CONNECTOR_Unknown); - drm_connector_helper_add(connector, - &psb_intel_sdvo_connector_helper_funcs); - sdvo_priv = (struct psb_intel_sdvo_priv *) (psb_intel_output + 1); - psb_intel_output->type = INTEL_OUTPUT_SDVO; - - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - - /* setup the DDC bus. */ - if (output_device == SDVOB) - i2cbus = - psb_intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB"); - else - i2cbus = - psb_intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC"); - - if (!i2cbus) - goto err_connector; - - sdvo_priv->i2c_bus = i2cbus; - - if (output_device == SDVOB) { - output_id = 1; - sdvo_priv->by_input_wiring = SDVOB_IN0; - sdvo_priv->i2c_bus->slave_addr = 0x38; - } else { - output_id = 2; - sdvo_priv->i2c_bus->slave_addr = 0x39; - } - - sdvo_priv->output_device = output_device; - psb_intel_output->i2c_bus = i2cbus; - psb_intel_output->dev_priv = sdvo_priv; - - - /* Read the regs to test if we can talk to the device */ - for (i = 0; i < 0x40; i++) { - if (!psb_intel_sdvo_read_byte(psb_intel_output, i, &ch[i])) { - dev_dbg(dev->dev, "No SDVO device found on SDVO%c\n", - output_device == SDVOB ? 'B' : 'C'); - goto err_i2c; - } - } - - psb_intel_sdvo_get_capabilities(psb_intel_output, &sdvo_priv->caps); - - memset(&sdvo_priv->active_outputs, 0, - sizeof(sdvo_priv->active_outputs)); - - /* TODO, CVBS, SVID, YPRPB & SCART outputs. */ - if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) { - sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0; - sdvo_priv->active_device = SDVO_DEVICE_CRT; - connector->display_info.subpixel_order = - SubPixelHorizontalRGB; - encoder_type = DRM_MODE_ENCODER_DAC; - connector_type = DRM_MODE_CONNECTOR_VGA; - } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) { - sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; - sdvo_priv->active_outputs = SDVO_DEVICE_CRT; - connector->display_info.subpixel_order = - SubPixelHorizontalRGB; - encoder_type = DRM_MODE_ENCODER_DAC; - connector_type = DRM_MODE_CONNECTOR_VGA; - } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) { - sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; - sdvo_priv->active_device = SDVO_DEVICE_TMDS; - connector->display_info.subpixel_order = - SubPixelHorizontalRGB; - encoder_type = DRM_MODE_ENCODER_TMDS; - connector_type = DRM_MODE_CONNECTOR_DVID; - } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) { - sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; - sdvo_priv->active_device = SDVO_DEVICE_TMDS; - connector->display_info.subpixel_order = - SubPixelHorizontalRGB; - encoder_type = DRM_MODE_ENCODER_TMDS; - connector_type = DRM_MODE_CONNECTOR_DVID; - } else { - unsigned char bytes[2]; - - memcpy(bytes, &sdvo_priv->caps.output_flags, 2); - dev_dbg(dev->dev, "%s: No active RGB or TMDS outputs (0x%02x%02x)\n", - SDVO_NAME(sdvo_priv), bytes[0], bytes[1]); - goto err_i2c; - } - - drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_sdvo_enc_funcs, - encoder_type); - drm_encoder_helper_add(&psb_intel_output->enc, - &psb_intel_sdvo_helper_funcs); - connector->connector_type = connector_type; - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); - drm_sysfs_connector_add(connector); - - /* Set the input timing to the screen. Assume always input 0. */ - psb_intel_sdvo_set_target_input(psb_intel_output, true, false); - - psb_intel_sdvo_get_input_pixel_clock_range(psb_intel_output, - &sdvo_priv->pixel_clock_min, - &sdvo_priv-> - pixel_clock_max); - - - dev_dbg(dev->dev, "%s device VID/DID: %02X:%02X.%02X, " - "clock range %dMHz - %dMHz, " - "input 1: %c, input 2: %c, " - "output 1: %c, output 2: %c\n", - SDVO_NAME(sdvo_priv), - sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id, - sdvo_priv->caps.device_rev_id, - sdvo_priv->pixel_clock_min / 1000, - sdvo_priv->pixel_clock_max / 1000, - (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N', - (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N', - /* check currently supported outputs */ - sdvo_priv->caps.output_flags & - (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N', - sdvo_priv->caps.output_flags & - (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); - - psb_intel_output->ddc_bus = i2cbus; - - return; - -err_i2c: - psb_intel_i2c_destroy(psb_intel_output->i2c_bus); -err_connector: - drm_connector_cleanup(connector); - kfree(psb_intel_output); - - return; -} diff --git a/drivers/staging/gma500/psb_intel_sdvo_regs.h b/drivers/staging/gma500/psb_intel_sdvo_regs.h deleted file mode 100644 index 96862ea..0000000 --- a/drivers/staging/gma500/psb_intel_sdvo_regs.h +++ /dev/null @@ -1,338 +0,0 @@ -/* - * SDVO command definitions and structures. - * - * Copyright (c) 2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Eric Anholt - */ - -#define SDVO_OUTPUT_FIRST (0) -#define SDVO_OUTPUT_TMDS0 (1 << 0) -#define SDVO_OUTPUT_RGB0 (1 << 1) -#define SDVO_OUTPUT_CVBS0 (1 << 2) -#define SDVO_OUTPUT_SVID0 (1 << 3) -#define SDVO_OUTPUT_YPRPB0 (1 << 4) -#define SDVO_OUTPUT_SCART0 (1 << 5) -#define SDVO_OUTPUT_LVDS0 (1 << 6) -#define SDVO_OUTPUT_TMDS1 (1 << 8) -#define SDVO_OUTPUT_RGB1 (1 << 9) -#define SDVO_OUTPUT_CVBS1 (1 << 10) -#define SDVO_OUTPUT_SVID1 (1 << 11) -#define SDVO_OUTPUT_YPRPB1 (1 << 12) -#define SDVO_OUTPUT_SCART1 (1 << 13) -#define SDVO_OUTPUT_LVDS1 (1 << 14) -#define SDVO_OUTPUT_LAST (14) - -struct psb_intel_sdvo_caps { - u8 vendor_id; - u8 device_id; - u8 device_rev_id; - u8 sdvo_version_major; - u8 sdvo_version_minor; - unsigned int sdvo_inputs_mask:2; - unsigned int smooth_scaling:1; - unsigned int sharp_scaling:1; - unsigned int up_scaling:1; - unsigned int down_scaling:1; - unsigned int stall_support:1; - unsigned int pad:1; - u16 output_flags; -} __packed; - -/** This matches the EDID DTD structure, more or less */ -struct psb_intel_sdvo_dtd { - struct { - u16 clock; /**< pixel clock, in 10kHz units */ - u8 h_active; /**< lower 8 bits (pixels) */ - u8 h_blank; /**< lower 8 bits (pixels) */ - u8 h_high; /**< upper 4 bits each h_active, h_blank */ - u8 v_active; /**< lower 8 bits (lines) */ - u8 v_blank; /**< lower 8 bits (lines) */ - u8 v_high; /**< upper 4 bits each v_active, v_blank */ - } part1; - - struct { - u8 h_sync_off; - /**< lower 8 bits, from hblank start */ - u8 h_sync_width;/**< lower 8 bits (pixels) */ - /** lower 4 bits each vsync offset, vsync width */ - u8 v_sync_off_width; - /** - * 2 high bits of hsync offset, 2 high bits of hsync width, - * bits 4-5 of vsync offset, and 2 high bits of vsync width. - */ - u8 sync_off_width_high; - u8 dtd_flags; - u8 sdvo_flags; - /** bits 6-7 of vsync offset at bits 6-7 */ - u8 v_sync_off_high; - u8 reserved; - } part2; -} __packed; - -struct psb_intel_sdvo_pixel_clock_range { - u16 min; /**< pixel clock, in 10kHz units */ - u16 max; /**< pixel clock, in 10kHz units */ -} __packed; - -struct psb_intel_sdvo_preferred_input_timing_args { - u16 clock; - u16 width; - u16 height; -} __packed; - -/* I2C registers for SDVO */ -#define SDVO_I2C_ARG_0 0x07 -#define SDVO_I2C_ARG_1 0x06 -#define SDVO_I2C_ARG_2 0x05 -#define SDVO_I2C_ARG_3 0x04 -#define SDVO_I2C_ARG_4 0x03 -#define SDVO_I2C_ARG_5 0x02 -#define SDVO_I2C_ARG_6 0x01 -#define SDVO_I2C_ARG_7 0x00 -#define SDVO_I2C_OPCODE 0x08 -#define SDVO_I2C_CMD_STATUS 0x09 -#define SDVO_I2C_RETURN_0 0x0a -#define SDVO_I2C_RETURN_1 0x0b -#define SDVO_I2C_RETURN_2 0x0c -#define SDVO_I2C_RETURN_3 0x0d -#define SDVO_I2C_RETURN_4 0x0e -#define SDVO_I2C_RETURN_5 0x0f -#define SDVO_I2C_RETURN_6 0x10 -#define SDVO_I2C_RETURN_7 0x11 -#define SDVO_I2C_VENDOR_BEGIN 0x20 - -/* Status results */ -#define SDVO_CMD_STATUS_POWER_ON 0x0 -#define SDVO_CMD_STATUS_SUCCESS 0x1 -#define SDVO_CMD_STATUS_NOTSUPP 0x2 -#define SDVO_CMD_STATUS_INVALID_ARG 0x3 -#define SDVO_CMD_STATUS_PENDING 0x4 -#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5 -#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6 - -/* SDVO commands, argument/result registers */ - -#define SDVO_CMD_RESET 0x01 - -/** Returns a struct psb_intel_sdvo_caps */ -#define SDVO_CMD_GET_DEVICE_CAPS 0x02 - -#define SDVO_CMD_GET_FIRMWARE_REV 0x86 -# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0 -# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1 -# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2 - -/** - * Reports which inputs are trained (managed to sync). - * - * Devices must have trained within 2 vsyncs of a mode change. - */ -#define SDVO_CMD_GET_TRAINED_INPUTS 0x03 -struct psb_intel_sdvo_get_trained_inputs_response { - unsigned int input0_trained:1; - unsigned int input1_trained:1; - unsigned int pad:6; -} __packed; - -/** Returns a struct psb_intel_sdvo_output_flags of active outputs. */ -#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04 - -/** - * Sets the current set of active outputs. - * - * Takes a struct psb_intel_sdvo_output_flags. - * Must be preceded by a SET_IN_OUT_MAP - * on multi-output devices. - */ -#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05 - -/** - * Returns the current mapping of SDVO inputs to outputs on the device. - * - * Returns two struct psb_intel_sdvo_output_flags structures. - */ -#define SDVO_CMD_GET_IN_OUT_MAP 0x06 - -/** - * Sets the current mapping of SDVO inputs to outputs on the device. - * - * Takes two struct i380_sdvo_output_flags structures. - */ -#define SDVO_CMD_SET_IN_OUT_MAP 0x07 - -/** - * Returns a struct psb_intel_sdvo_output_flags of attached displays. - */ -#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b - -/** - * Returns a struct psb_intel_sdvo_ouptut_flags of displays supporting hot plugging. - */ -#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c - -/** - * Takes a struct psb_intel_sdvo_output_flags. - */ -#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d - -/** - * Returns a struct psb_intel_sdvo_output_flags of displays with hot plug - * interrupts enabled. - */ -#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e - -#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f -struct psb_intel_sdvo_get_interrupt_event_source_response { - u16 interrupt_status; - unsigned int ambient_light_interrupt:1; - unsigned int pad:7; -} __packed; - -/** - * Selects which input is affected by future input commands. - * - * Commands affected include SET_INPUT_TIMINGS_PART[12], - * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12], - * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS. - */ -#define SDVO_CMD_SET_TARGET_INPUT 0x10 -struct psb_intel_sdvo_set_target_input_args { - unsigned int target_1:1; - unsigned int pad:7; -} __packed; - -/** - * Takes a struct psb_intel_sdvo_output_flags of which outputs are targeted by - * future output commands. - * - * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12], - * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE. - */ -#define SDVO_CMD_SET_TARGET_OUTPUT 0x11 - -#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12 -#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13 -#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14 -#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15 -#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16 -#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17 -#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18 -#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19 -/* Part 1 */ -# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0 -# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1 -# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2 -# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3 -# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4 -# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5 -# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6 -# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7 -/* Part 2 */ -# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0 -# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1 -# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2 -# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3 -# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4 -# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7) -# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5) -# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3) -# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1) -# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5 -# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7) -# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6) -# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6) -# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4) -# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4) -# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4) -# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4) -# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6 - -/** - * Generates a DTD based on the given width, height, and flags. - * - * This will be supported by any device supporting scaling or interlaced - * modes. - */ -#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a -# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0 -# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1 -# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2 -# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3 -# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4 -# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5 -# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6 -# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0) -# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1) - -#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b -#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c - -/** Returns a struct psb_intel_sdvo_pixel_clock_range */ -#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d -/** Returns a struct psb_intel_sdvo_pixel_clock_range */ -#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e - -/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */ -#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f - -/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ -#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20 -/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ -#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21 -# define SDVO_CLOCK_RATE_MULT_1X (1 << 0) -# define SDVO_CLOCK_RATE_MULT_2X (1 << 1) -# define SDVO_CLOCK_RATE_MULT_4X (1 << 3) - -#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 - -#define SDVO_CMD_GET_TV_FORMAT 0x28 - -#define SDVO_CMD_SET_TV_FORMAT 0x29 - -#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a -#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b -#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c -# define SDVO_ENCODER_STATE_ON (1 << 0) -# define SDVO_ENCODER_STATE_STANDBY (1 << 1) -# define SDVO_ENCODER_STATE_SUSPEND (1 << 2) -# define SDVO_ENCODER_STATE_OFF (1 << 3) - -#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93 - -#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a -# define SDVO_CONTROL_BUS_PROM 0x0 -# define SDVO_CONTROL_BUS_DDC1 0x1 -# define SDVO_CONTROL_BUS_DDC2 0x2 -# define SDVO_CONTROL_BUS_DDC3 0x3 - -/* SDVO Bus & SDVO Inputs wiring details*/ -/* Bit 0: Is SDVOB connected to In0 (1 = yes, 0 = no*/ -/* Bit 1: Is SDVOB connected to In1 (1 = yes, 0 = no*/ -/* Bit 2: Is SDVOC connected to In0 (1 = yes, 0 = no*/ -/* Bit 3: Is SDVOC connected to In1 (1 = yes, 0 = no*/ -#define SDVOB_IN0 0x01 -#define SDVOB_IN1 0x02 -#define SDVOC_IN0 0x04 -#define SDVOC_IN1 0x08 - -#define SDVO_DEVICE_NONE 0x00 -#define SDVO_DEVICE_CRT 0x01 -#define SDVO_DEVICE_TV 0x02 -#define SDVO_DEVICE_LVDS 0x04 -#define SDVO_DEVICE_TMDS 0x08 - diff --git a/drivers/staging/gma500/psb_irq.c b/drivers/staging/gma500/psb_irq.c deleted file mode 100644 index 36dd630..0000000 --- a/drivers/staging/gma500/psb_irq.c +++ /dev/null @@ -1,627 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to - * develop this driver. - * - **************************************************************************/ -/* - */ - -#include -#include "psb_drv.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include "power.h" -#include "mdfld_output.h" - -/* - * inline functions - */ - -static inline u32 -psb_pipestat(int pipe) -{ - if (pipe == 0) - return PIPEASTAT; - if (pipe == 1) - return PIPEBSTAT; - if (pipe == 2) - return PIPECSTAT; - BUG(); -} - -static inline u32 -mid_pipe_event(int pipe) -{ - if (pipe == 0) - return _PSB_PIPEA_EVENT_FLAG; - if (pipe == 1) - return _MDFLD_PIPEB_EVENT_FLAG; - if (pipe == 2) - return _MDFLD_PIPEC_EVENT_FLAG; - BUG(); -} - -static inline u32 -mid_pipe_vsync(int pipe) -{ - if (pipe == 0) - return _PSB_VSYNC_PIPEA_FLAG; - if (pipe == 1) - return _PSB_VSYNC_PIPEB_FLAG; - if (pipe == 2) - return _MDFLD_PIPEC_VBLANK_FLAG; - BUG(); -} - -static inline u32 -mid_pipeconf(int pipe) -{ - if (pipe == 0) - return PIPEACONF; - if (pipe == 1) - return PIPEBCONF; - if (pipe == 2) - return PIPECCONF; - BUG(); -} - -void -psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) -{ - if ((dev_priv->pipestat[pipe] & mask) != mask) { - u32 reg = psb_pipestat(pipe); - dev_priv->pipestat[pipe] |= mask; - /* Enable the interrupt, clear any pending status */ - if (gma_power_begin(dev_priv->dev, false)) { - u32 writeVal = PSB_RVDC32(reg); - writeVal |= (mask | (mask >> 16)); - PSB_WVDC32(writeVal, reg); - (void) PSB_RVDC32(reg); - gma_power_end(dev_priv->dev); - } - } -} - -void -psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) -{ - if ((dev_priv->pipestat[pipe] & mask) != 0) { - u32 reg = psb_pipestat(pipe); - dev_priv->pipestat[pipe] &= ~mask; - if (gma_power_begin(dev_priv->dev, false)) { - u32 writeVal = PSB_RVDC32(reg); - writeVal &= ~mask; - PSB_WVDC32(writeVal, reg); - (void) PSB_RVDC32(reg); - gma_power_end(dev_priv->dev); - } - } -} - -void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe) -{ - if (gma_power_begin(dev_priv->dev, false)) { - u32 pipe_event = mid_pipe_event(pipe); - dev_priv->vdc_irq_mask |= pipe_event; - PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); - PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - gma_power_end(dev_priv->dev); - } -} - -void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe) -{ - if (dev_priv->pipestat[pipe] == 0) { - if (gma_power_begin(dev_priv->dev, false)) { - u32 pipe_event = mid_pipe_event(pipe); - dev_priv->vdc_irq_mask &= ~pipe_event; - PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); - PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - gma_power_end(dev_priv->dev); - } - } -} - -/** - * Display controller interrupt handler for pipe event. - * - */ -static void mid_pipe_event_handler(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - - uint32_t pipe_stat_val = 0; - uint32_t pipe_stat_reg = psb_pipestat(pipe); - uint32_t pipe_enable = dev_priv->pipestat[pipe]; - uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16; - uint32_t pipe_clear; - uint32_t i = 0; - - spin_lock(&dev_priv->irqmask_lock); - - pipe_stat_val = PSB_RVDC32(pipe_stat_reg); - pipe_stat_val &= pipe_enable | pipe_status; - pipe_stat_val &= pipe_stat_val >> 16; - - spin_unlock(&dev_priv->irqmask_lock); - - /* Clear the 2nd level interrupt status bits - * Sometimes the bits are very sticky so we repeat until they unstick */ - for (i = 0; i < 0xffff; i++) { - PSB_WVDC32(PSB_RVDC32(pipe_stat_reg), pipe_stat_reg); - pipe_clear = PSB_RVDC32(pipe_stat_reg) & pipe_status; - - if (pipe_clear == 0) - break; - } - - if (pipe_clear) - dev_err(dev->dev, - "%s, can't clear status bits for pipe %d, its value = 0x%x.\n", - __func__, pipe, PSB_RVDC32(pipe_stat_reg)); - - if (pipe_stat_val & PIPE_VBLANK_STATUS) - drm_handle_vblank(dev, pipe); - - if (pipe_stat_val & PIPE_TE_STATUS) - drm_handle_vblank(dev, pipe); -} - -/* - * Display controller interrupt handler. - */ -static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) -{ - if (vdc_stat & _PSB_VSYNC_PIPEA_FLAG) - mid_pipe_event_handler(dev, 0); - - if (vdc_stat & _PSB_VSYNC_PIPEB_FLAG) - mid_pipe_event_handler(dev, 1); -} - -irqreturn_t psb_irq_handler(DRM_IRQ_ARGS) -{ - struct drm_device *dev = (struct drm_device *) arg; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - - uint32_t vdc_stat, dsp_int = 0, sgx_int = 0; - int handled = 0; - - spin_lock(&dev_priv->irqmask_lock); - - vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R); - - if (vdc_stat & _PSB_PIPE_EVENT_FLAG) - dsp_int = 1; - - /* FIXME: Handle Medfield - if (vdc_stat & _MDFLD_DISP_ALL_IRQ_FLAG) - dsp_int = 1; - */ - - if (vdc_stat & _PSB_IRQ_SGX_FLAG) - sgx_int = 1; - - vdc_stat &= dev_priv->vdc_irq_mask; - spin_unlock(&dev_priv->irqmask_lock); - - if (dsp_int && gma_power_is_on(dev)) { - psb_vdc_interrupt(dev, vdc_stat); - handled = 1; - } - - if (sgx_int) { - /* Not expected - we have it masked, shut it up */ - u32 s, s2; - s = PSB_RSGX32(PSB_CR_EVENT_STATUS); - s2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); - PSB_WSGX32(s, PSB_CR_EVENT_HOST_CLEAR); - PSB_WSGX32(s2, PSB_CR_EVENT_HOST_CLEAR2); - /* if s & _PSB_CE_TWOD_COMPLETE we have 2D done but - we may as well poll even if we add that ! */ - handled = 1; - } - - PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R); - (void) PSB_RVDC32(PSB_INT_IDENTITY_R); - DRM_READMEMORYBARRIER(); - - if (!handled) - return IRQ_NONE; - - return IRQ_HANDLED; -} - -void psb_irq_preinstall(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - if (gma_power_is_on(dev)) - PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - if (dev->vblank_enabled[0]) - dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; - if (dev->vblank_enabled[1]) - dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; - - /* FIXME: Handle Medfield irq mask - if (dev->vblank_enabled[1]) - dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG; - if (dev->vblank_enabled[2]) - dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG; - */ - - /* This register is safe even if display island is off */ - PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); -} - -int psb_irq_postinstall(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - /* This register is safe even if display island is off */ - PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - - if (dev->vblank_enabled[0]) - psb_enable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); - else - psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); - - if (dev->vblank_enabled[1]) - psb_enable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); - else - psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); - - if (dev->vblank_enabled[2]) - psb_enable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); - else - psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - return 0; -} - -void psb_irq_uninstall(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - - if (dev->vblank_enabled[0]) - psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); - - if (dev->vblank_enabled[1]) - psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); - - if (dev->vblank_enabled[2]) - psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); - - dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG | - _PSB_IRQ_MSVDX_FLAG | - _LNC_IRQ_TOPAZ_FLAG; - - /* These two registers are safe even if display island is off */ - PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); - PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - - wmb(); - - /* This register is safe even if display island is off */ - PSB_WVDC32(PSB_RVDC32(PSB_INT_IDENTITY_R), PSB_INT_IDENTITY_R); - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); -} - -void psb_irq_turn_on_dpst(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - u32 hist_reg; - u32 pwm_reg; - - if (gma_power_begin(dev, false)) { - PSB_WVDC32(1 << 31, HISTOGRAM_LOGIC_CONTROL); - hist_reg = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); - PSB_WVDC32(1 << 31, HISTOGRAM_INT_CONTROL); - hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); - - PSB_WVDC32(0x80010100, PWM_CONTROL_LOGIC); - pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); - PSB_WVDC32(pwm_reg | PWM_PHASEIN_ENABLE - | PWM_PHASEIN_INT_ENABLE, - PWM_CONTROL_LOGIC); - pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); - - psb_enable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); - - hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); - PSB_WVDC32(hist_reg | HISTOGRAM_INT_CTRL_CLEAR, - HISTOGRAM_INT_CONTROL); - pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); - PSB_WVDC32(pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE, - PWM_CONTROL_LOGIC); - - gma_power_end(dev); - } -} - -int psb_irq_enable_dpst(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - /* enable DPST */ - mid_enable_pipe_event(dev_priv, 0); - psb_irq_turn_on_dpst(dev); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - return 0; -} - -void psb_irq_turn_off_dpst(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - u32 hist_reg; - u32 pwm_reg; - - if (gma_power_begin(dev, false)) { - PSB_WVDC32(0x00000000, HISTOGRAM_INT_CONTROL); - hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL); - - psb_disable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); - - pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); - PSB_WVDC32(pwm_reg & !(PWM_PHASEIN_INT_ENABLE), - PWM_CONTROL_LOGIC); - pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); - - gma_power_end(dev); - } -} - -int psb_irq_disable_dpst(struct drm_device *dev) -{ - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; - unsigned long irqflags; - - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - mid_disable_pipe_event(dev_priv, 0); - psb_irq_turn_off_dpst(dev); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - - return 0; -} - -#ifdef PSB_FIXME -static int psb_vblank_do_wait(struct drm_device *dev, - unsigned int *sequence, atomic_t *counter) -{ - unsigned int cur_vblank; - int ret = 0; - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(counter)) - - *sequence) <= (1 << 23))); - *sequence = cur_vblank; - - return ret; -} -#endif - -/* - * It is used to enable VBLANK interrupt - */ -int psb_enable_vblank(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long irqflags; - uint32_t reg_val = 0; - uint32_t pipeconf_reg = mid_pipeconf(pipe); - -#if defined(CONFIG_DRM_PSB_MFLD) - /* Medfield is different - we should perhaps extract out vblank - and blacklight etc ops */ - if (IS_MFLD(dev) && !mdfld_panel_dpi(dev)) - return mdfld_enable_te(dev, pipe); -#endif - if (gma_power_begin(dev, false)) { - reg_val = REG_READ(pipeconf_reg); - gma_power_end(dev); - } - - if (!(reg_val & PIPEACONF_ENABLE)) - return -EINVAL; - - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - if (pipe == 0) - dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; - else if (pipe == 1) - dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; - - PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); - PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - psb_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - - return 0; -} - -/* - * It is used to disable VBLANK interrupt - */ -void psb_disable_vblank(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long irqflags; - -#if defined(CONFIG_DRM_PSB_MFLD) - if (IS_MFLD(dev) && !mdfld_panel_dpi(dev)) - mdfld_disable_te(dev, pipe); -#endif - spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - - if (pipe == 0) - dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEA_FLAG; - else if (pipe == 1) - dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEB_FLAG; - - PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); - PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); - psb_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); -} - -/** - * mdfld_enable_te - enable TE events - * @dev: our DRM device - * @pipe: which pipe to work on - * - * Enable TE events on a Medfield display pipe. Medfield specific. - */ -int mdfld_enable_te(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long flags; - uint32_t reg_val = 0; - uint32_t pipeconf_reg = mid_pipeconf(pipe); - - if (gma_power_begin(dev, false)) { - reg_val = REG_READ(pipeconf_reg); - gma_power_end(dev); - } - - if (!(reg_val & PIPEACONF_ENABLE)) - return -EINVAL; - - spin_lock_irqsave(&dev_priv->irqmask_lock, flags); - - mid_enable_pipe_event(dev_priv, pipe); - psb_enable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, flags); - - return 0; -} - -/** - * mdfld_disable_te - disable TE events - * @dev: our DRM device - * @pipe: which pipe to work on - * - * Disable TE events on a Medfield display pipe. Medfield specific. - */ -void mdfld_disable_te(struct drm_device *dev, int pipe) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long flags; - - spin_lock_irqsave(&dev_priv->irqmask_lock, flags); - - mid_disable_pipe_event(dev_priv, pipe); - psb_disable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); - - spin_unlock_irqrestore(&dev_priv->irqmask_lock, flags); -} - -/* Called from drm generic code, passed a 'crtc', which - * we use as a pipe index - */ -u32 psb_get_vblank_counter(struct drm_device *dev, int pipe) -{ - uint32_t high_frame = PIPEAFRAMEHIGH; - uint32_t low_frame = PIPEAFRAMEPIXEL; - uint32_t pipeconf_reg = PIPEACONF; - uint32_t reg_val = 0; - uint32_t high1 = 0, high2 = 0, low = 0, count = 0; - - switch (pipe) { - case 0: - break; - case 1: - high_frame = PIPEBFRAMEHIGH; - low_frame = PIPEBFRAMEPIXEL; - pipeconf_reg = PIPEBCONF; - break; - case 2: - high_frame = PIPECFRAMEHIGH; - low_frame = PIPECFRAMEPIXEL; - pipeconf_reg = PIPECCONF; - break; - default: - dev_err(dev->dev, "%s, invalid pipe.\n", __func__); - return 0; - } - - if (!gma_power_begin(dev, false)) - return 0; - - reg_val = REG_READ(pipeconf_reg); - - if (!(reg_val & PIPEACONF_ENABLE)) { - dev_err(dev->dev, "trying to get vblank count for disabled pipe %d\n", - pipe); - goto psb_get_vblank_counter_exit; - } - - /* - * High & low register fields aren't synchronized, so make sure - * we get a low value that's stable across two reads of the high - * register. - */ - do { - high1 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> - PIPE_FRAME_HIGH_SHIFT); - low = ((REG_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> - PIPE_FRAME_LOW_SHIFT); - high2 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> - PIPE_FRAME_HIGH_SHIFT); - } while (high1 != high2); - - count = (high1 << 8) | low; - -psb_get_vblank_counter_exit: - - gma_power_end(dev); - - return count; -} - diff --git a/drivers/staging/gma500/psb_irq.h b/drivers/staging/gma500/psb_irq.h deleted file mode 100644 index 216fda3..0000000 --- a/drivers/staging/gma500/psb_irq.h +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************** - * Copyright (c) 2009-2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: - * Benjamin Defnet - * Rajesh Poornachandran - * - **************************************************************************/ - -#ifndef _SYSIRQ_H_ -#define _SYSIRQ_H_ - -#include - -bool sysirq_init(struct drm_device *dev); -void sysirq_uninit(struct drm_device *dev); - -void psb_irq_preinstall(struct drm_device *dev); -int psb_irq_postinstall(struct drm_device *dev); -void psb_irq_uninstall(struct drm_device *dev); -irqreturn_t psb_irq_handler(DRM_IRQ_ARGS); - -int psb_irq_enable_dpst(struct drm_device *dev); -int psb_irq_disable_dpst(struct drm_device *dev); -void psb_irq_turn_on_dpst(struct drm_device *dev); -void psb_irq_turn_off_dpst(struct drm_device *dev); -int psb_enable_vblank(struct drm_device *dev, int pipe); -void psb_disable_vblank(struct drm_device *dev, int pipe); -u32 psb_get_vblank_counter(struct drm_device *dev, int pipe); - -#endif /* _SYSIRQ_H_ */ diff --git a/drivers/staging/gma500/psb_lid.c b/drivers/staging/gma500/psb_lid.c deleted file mode 100644 index b867aabe..0000000 --- a/drivers/staging/gma500/psb_lid.c +++ /dev/null @@ -1,88 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Authors: Thomas Hellstrom - **************************************************************************/ - -#include -#include "psb_drv.h" -#include "psb_reg.h" -#include "psb_intel_reg.h" -#include - -static void psb_lid_timer_func(unsigned long data) -{ - struct drm_psb_private * dev_priv = (struct drm_psb_private *)data; - struct drm_device *dev = (struct drm_device *)dev_priv->dev; - struct timer_list *lid_timer = &dev_priv->lid_timer; - unsigned long irq_flags; - u32 *lid_state = dev_priv->lid_state; - u32 pp_status; - - if (readl(lid_state) == dev_priv->lid_last_state) - goto lid_timer_schedule; - - if ((readl(lid_state)) & 0x01) { - /*lid state is open*/ - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); - - /*FIXME: should be backlight level before*/ - psb_intel_lvds_set_brightness(dev, 100); - } else { - psb_intel_lvds_set_brightness(dev, 0); - - REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & ~POWER_TARGET_ON); - do { - pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); - } - dev_priv->lid_last_state = readl(lid_state); - -lid_timer_schedule: - spin_lock_irqsave(&dev_priv->lid_lock, irq_flags); - if (!timer_pending(lid_timer)) { - lid_timer->expires = jiffies + PSB_LID_DELAY; - add_timer(lid_timer); - } - spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags); -} - -void psb_lid_timer_init(struct drm_psb_private *dev_priv) -{ - struct timer_list *lid_timer = &dev_priv->lid_timer; - unsigned long irq_flags; - - spin_lock_init(&dev_priv->lid_lock); - spin_lock_irqsave(&dev_priv->lid_lock, irq_flags); - - init_timer(lid_timer); - - lid_timer->data = (unsigned long)dev_priv; - lid_timer->function = psb_lid_timer_func; - lid_timer->expires = jiffies + PSB_LID_DELAY; - - add_timer(lid_timer); - spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags); -} - -void psb_lid_timer_takedown(struct drm_psb_private *dev_priv) -{ - del_timer_sync(&dev_priv->lid_timer); -} - diff --git a/drivers/staging/gma500/psb_reg.h b/drivers/staging/gma500/psb_reg.h deleted file mode 100644 index b81c7c1..0000000 --- a/drivers/staging/gma500/psb_reg.h +++ /dev/null @@ -1,582 +0,0 @@ -/************************************************************************** - * - * Copyright (c) (2005-2007) Imagination Technologies Limited. - * Copyright (c) 2007, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.. - * - **************************************************************************/ - -#ifndef _PSB_REG_H_ -#define _PSB_REG_H_ - -#define PSB_CR_CLKGATECTL 0x0000 -#define _PSB_C_CLKGATECTL_AUTO_MAN_REG (1 << 24) -#define _PSB_C_CLKGATECTL_USE_CLKG_SHIFT (20) -#define _PSB_C_CLKGATECTL_USE_CLKG_MASK (0x3 << 20) -#define _PSB_C_CLKGATECTL_DPM_CLKG_SHIFT (16) -#define _PSB_C_CLKGATECTL_DPM_CLKG_MASK (0x3 << 16) -#define _PSB_C_CLKGATECTL_TA_CLKG_SHIFT (12) -#define _PSB_C_CLKGATECTL_TA_CLKG_MASK (0x3 << 12) -#define _PSB_C_CLKGATECTL_TSP_CLKG_SHIFT (8) -#define _PSB_C_CLKGATECTL_TSP_CLKG_MASK (0x3 << 8) -#define _PSB_C_CLKGATECTL_ISP_CLKG_SHIFT (4) -#define _PSB_C_CLKGATECTL_ISP_CLKG_MASK (0x3 << 4) -#define _PSB_C_CLKGATECTL_2D_CLKG_SHIFT (0) -#define _PSB_C_CLKGATECTL_2D_CLKG_MASK (0x3 << 0) -#define _PSB_C_CLKGATECTL_CLKG_ENABLED (0) -#define _PSB_C_CLKGATECTL_CLKG_DISABLED (1) -#define _PSB_C_CLKGATECTL_CLKG_AUTO (2) - -#define PSB_CR_CORE_ID 0x0010 -#define _PSB_CC_ID_ID_SHIFT (16) -#define _PSB_CC_ID_ID_MASK (0xFFFF << 16) -#define _PSB_CC_ID_CONFIG_SHIFT (0) -#define _PSB_CC_ID_CONFIG_MASK (0xFFFF << 0) - -#define PSB_CR_CORE_REVISION 0x0014 -#define _PSB_CC_REVISION_DESIGNER_SHIFT (24) -#define _PSB_CC_REVISION_DESIGNER_MASK (0xFF << 24) -#define _PSB_CC_REVISION_MAJOR_SHIFT (16) -#define _PSB_CC_REVISION_MAJOR_MASK (0xFF << 16) -#define _PSB_CC_REVISION_MINOR_SHIFT (8) -#define _PSB_CC_REVISION_MINOR_MASK (0xFF << 8) -#define _PSB_CC_REVISION_MAINTENANCE_SHIFT (0) -#define _PSB_CC_REVISION_MAINTENANCE_MASK (0xFF << 0) - -#define PSB_CR_DESIGNER_REV_FIELD1 0x0018 - -#define PSB_CR_SOFT_RESET 0x0080 -#define _PSB_CS_RESET_TSP_RESET (1 << 6) -#define _PSB_CS_RESET_ISP_RESET (1 << 5) -#define _PSB_CS_RESET_USE_RESET (1 << 4) -#define _PSB_CS_RESET_TA_RESET (1 << 3) -#define _PSB_CS_RESET_DPM_RESET (1 << 2) -#define _PSB_CS_RESET_TWOD_RESET (1 << 1) -#define _PSB_CS_RESET_BIF_RESET (1 << 0) - -#define PSB_CR_DESIGNER_REV_FIELD2 0x001C - -#define PSB_CR_EVENT_HOST_ENABLE2 0x0110 - -#define PSB_CR_EVENT_STATUS2 0x0118 - -#define PSB_CR_EVENT_HOST_CLEAR2 0x0114 -#define _PSB_CE2_BIF_REQUESTER_FAULT (1 << 4) - -#define PSB_CR_EVENT_STATUS 0x012C - -#define PSB_CR_EVENT_HOST_ENABLE 0x0130 - -#define PSB_CR_EVENT_HOST_CLEAR 0x0134 -#define _PSB_CE_MASTER_INTERRUPT (1 << 31) -#define _PSB_CE_TA_DPM_FAULT (1 << 28) -#define _PSB_CE_TWOD_COMPLETE (1 << 27) -#define _PSB_CE_DPM_OUT_OF_MEMORY_ZLS (1 << 25) -#define _PSB_CE_DPM_TA_MEM_FREE (1 << 24) -#define _PSB_CE_PIXELBE_END_RENDER (1 << 18) -#define _PSB_CE_SW_EVENT (1 << 14) -#define _PSB_CE_TA_FINISHED (1 << 13) -#define _PSB_CE_TA_TERMINATE (1 << 12) -#define _PSB_CE_DPM_REACHED_MEM_THRESH (1 << 3) -#define _PSB_CE_DPM_OUT_OF_MEMORY_GBL (1 << 2) -#define _PSB_CE_DPM_OUT_OF_MEMORY_MT (1 << 1) -#define _PSB_CE_DPM_3D_MEM_FREE (1 << 0) - - -#define PSB_USE_OFFSET_MASK 0x0007FFFF -#define PSB_USE_OFFSET_SIZE (PSB_USE_OFFSET_MASK + 1) -#define PSB_CR_USE_CODE_BASE0 0x0A0C -#define PSB_CR_USE_CODE_BASE1 0x0A10 -#define PSB_CR_USE_CODE_BASE2 0x0A14 -#define PSB_CR_USE_CODE_BASE3 0x0A18 -#define PSB_CR_USE_CODE_BASE4 0x0A1C -#define PSB_CR_USE_CODE_BASE5 0x0A20 -#define PSB_CR_USE_CODE_BASE6 0x0A24 -#define PSB_CR_USE_CODE_BASE7 0x0A28 -#define PSB_CR_USE_CODE_BASE8 0x0A2C -#define PSB_CR_USE_CODE_BASE9 0x0A30 -#define PSB_CR_USE_CODE_BASE10 0x0A34 -#define PSB_CR_USE_CODE_BASE11 0x0A38 -#define PSB_CR_USE_CODE_BASE12 0x0A3C -#define PSB_CR_USE_CODE_BASE13 0x0A40 -#define PSB_CR_USE_CODE_BASE14 0x0A44 -#define PSB_CR_USE_CODE_BASE15 0x0A48 -#define PSB_CR_USE_CODE_BASE(_i) (0x0A0C + ((_i) << 2)) -#define _PSB_CUC_BASE_DM_SHIFT (25) -#define _PSB_CUC_BASE_DM_MASK (0x3 << 25) -#define _PSB_CUC_BASE_ADDR_SHIFT (0) /* 1024-bit aligned address? */ -#define _PSB_CUC_BASE_ADDR_ALIGNSHIFT (7) -#define _PSB_CUC_BASE_ADDR_MASK (0x1FFFFFF << 0) -#define _PSB_CUC_DM_VERTEX (0) -#define _PSB_CUC_DM_PIXEL (1) -#define _PSB_CUC_DM_RESERVED (2) -#define _PSB_CUC_DM_EDM (3) - -#define PSB_CR_PDS_EXEC_BASE 0x0AB8 -#define _PSB_CR_PDS_EXEC_BASE_ADDR_SHIFT (20) /* 1MB aligned address */ -#define _PSB_CR_PDS_EXEC_BASE_ADDR_ALIGNSHIFT (20) - -#define PSB_CR_EVENT_KICKER 0x0AC4 -#define _PSB_CE_KICKER_ADDRESS_SHIFT (4) /* 128-bit aligned address */ - -#define PSB_CR_EVENT_KICK 0x0AC8 -#define _PSB_CE_KICK_NOW (1 << 0) - -#define PSB_CR_BIF_DIR_LIST_BASE1 0x0C38 - -#define PSB_CR_BIF_CTRL 0x0C00 -#define _PSB_CB_CTRL_CLEAR_FAULT (1 << 4) -#define _PSB_CB_CTRL_INVALDC (1 << 3) -#define _PSB_CB_CTRL_FLUSH (1 << 2) - -#define PSB_CR_BIF_INT_STAT 0x0C04 - -#define PSB_CR_BIF_FAULT 0x0C08 -#define _PSB_CBI_STAT_PF_N_RW (1 << 14) -#define _PSB_CBI_STAT_FAULT_SHIFT (0) -#define _PSB_CBI_STAT_FAULT_MASK (0x3FFF << 0) -#define _PSB_CBI_STAT_FAULT_CACHE (1 << 1) -#define _PSB_CBI_STAT_FAULT_TA (1 << 2) -#define _PSB_CBI_STAT_FAULT_VDM (1 << 3) -#define _PSB_CBI_STAT_FAULT_2D (1 << 4) -#define _PSB_CBI_STAT_FAULT_PBE (1 << 5) -#define _PSB_CBI_STAT_FAULT_TSP (1 << 6) -#define _PSB_CBI_STAT_FAULT_ISP (1 << 7) -#define _PSB_CBI_STAT_FAULT_USSEPDS (1 << 8) -#define _PSB_CBI_STAT_FAULT_HOST (1 << 9) - -#define PSB_CR_BIF_BANK0 0x0C78 -#define PSB_CR_BIF_BANK1 0x0C7C -#define PSB_CR_BIF_DIR_LIST_BASE0 0x0C84 -#define PSB_CR_BIF_TWOD_REQ_BASE 0x0C88 -#define PSB_CR_BIF_3D_REQ_BASE 0x0CAC - -#define PSB_CR_2D_SOCIF 0x0E18 -#define _PSB_C2_SOCIF_FREESPACE_SHIFT (0) -#define _PSB_C2_SOCIF_FREESPACE_MASK (0xFF << 0) -#define _PSB_C2_SOCIF_EMPTY (0x80 << 0) - -#define PSB_CR_2D_BLIT_STATUS 0x0E04 -#define _PSB_C2B_STATUS_BUSY (1 << 24) -#define _PSB_C2B_STATUS_COMPLETE_SHIFT (0) -#define _PSB_C2B_STATUS_COMPLETE_MASK (0xFFFFFF << 0) - -/* - * 2D defs. - */ - -/* - * 2D Slave Port Data : Block Header's Object Type - */ - -#define PSB_2D_CLIP_BH (0x00000000) -#define PSB_2D_PAT_BH (0x10000000) -#define PSB_2D_CTRL_BH (0x20000000) -#define PSB_2D_SRC_OFF_BH (0x30000000) -#define PSB_2D_MASK_OFF_BH (0x40000000) -#define PSB_2D_RESERVED1_BH (0x50000000) -#define PSB_2D_RESERVED2_BH (0x60000000) -#define PSB_2D_FENCE_BH (0x70000000) -#define PSB_2D_BLIT_BH (0x80000000) -#define PSB_2D_SRC_SURF_BH (0x90000000) -#define PSB_2D_DST_SURF_BH (0xA0000000) -#define PSB_2D_PAT_SURF_BH (0xB0000000) -#define PSB_2D_SRC_PAL_BH (0xC0000000) -#define PSB_2D_PAT_PAL_BH (0xD0000000) -#define PSB_2D_MASK_SURF_BH (0xE0000000) -#define PSB_2D_FLUSH_BH (0xF0000000) - -/* - * Clip Definition block (PSB_2D_CLIP_BH) - */ -#define PSB_2D_CLIPCOUNT_MAX (1) -#define PSB_2D_CLIPCOUNT_MASK (0x00000000) -#define PSB_2D_CLIPCOUNT_CLRMASK (0xFFFFFFFF) -#define PSB_2D_CLIPCOUNT_SHIFT (0) -/* clip rectangle min & max */ -#define PSB_2D_CLIP_XMAX_MASK (0x00FFF000) -#define PSB_2D_CLIP_XMAX_CLRMASK (0xFF000FFF) -#define PSB_2D_CLIP_XMAX_SHIFT (12) -#define PSB_2D_CLIP_XMIN_MASK (0x00000FFF) -#define PSB_2D_CLIP_XMIN_CLRMASK (0x00FFF000) -#define PSB_2D_CLIP_XMIN_SHIFT (0) -/* clip rectangle offset */ -#define PSB_2D_CLIP_YMAX_MASK (0x00FFF000) -#define PSB_2D_CLIP_YMAX_CLRMASK (0xFF000FFF) -#define PSB_2D_CLIP_YMAX_SHIFT (12) -#define PSB_2D_CLIP_YMIN_MASK (0x00000FFF) -#define PSB_2D_CLIP_YMIN_CLRMASK (0x00FFF000) -#define PSB_2D_CLIP_YMIN_SHIFT (0) - -/* - * Pattern Control (PSB_2D_PAT_BH) - */ -#define PSB_2D_PAT_HEIGHT_MASK (0x0000001F) -#define PSB_2D_PAT_HEIGHT_SHIFT (0) -#define PSB_2D_PAT_WIDTH_MASK (0x000003E0) -#define PSB_2D_PAT_WIDTH_SHIFT (5) -#define PSB_2D_PAT_YSTART_MASK (0x00007C00) -#define PSB_2D_PAT_YSTART_SHIFT (10) -#define PSB_2D_PAT_XSTART_MASK (0x000F8000) -#define PSB_2D_PAT_XSTART_SHIFT (15) - -/* - * 2D Control block (PSB_2D_CTRL_BH) - */ -/* Present Flags */ -#define PSB_2D_SRCCK_CTRL (0x00000001) -#define PSB_2D_DSTCK_CTRL (0x00000002) -#define PSB_2D_ALPHA_CTRL (0x00000004) -/* Colour Key Colour (SRC/DST)*/ -#define PSB_2D_CK_COL_MASK (0xFFFFFFFF) -#define PSB_2D_CK_COL_CLRMASK (0x00000000) -#define PSB_2D_CK_COL_SHIFT (0) -/* Colour Key Mask (SRC/DST)*/ -#define PSB_2D_CK_MASK_MASK (0xFFFFFFFF) -#define PSB_2D_CK_MASK_CLRMASK (0x00000000) -#define PSB_2D_CK_MASK_SHIFT (0) -/* Alpha Control (Alpha/RGB)*/ -#define PSB_2D_GBLALPHA_MASK (0x000FF000) -#define PSB_2D_GBLALPHA_CLRMASK (0xFFF00FFF) -#define PSB_2D_GBLALPHA_SHIFT (12) -#define PSB_2D_SRCALPHA_OP_MASK (0x00700000) -#define PSB_2D_SRCALPHA_OP_CLRMASK (0xFF8FFFFF) -#define PSB_2D_SRCALPHA_OP_SHIFT (20) -#define PSB_2D_SRCALPHA_OP_ONE (0x00000000) -#define PSB_2D_SRCALPHA_OP_SRC (0x00100000) -#define PSB_2D_SRCALPHA_OP_DST (0x00200000) -#define PSB_2D_SRCALPHA_OP_SG (0x00300000) -#define PSB_2D_SRCALPHA_OP_DG (0x00400000) -#define PSB_2D_SRCALPHA_OP_GBL (0x00500000) -#define PSB_2D_SRCALPHA_OP_ZERO (0x00600000) -#define PSB_2D_SRCALPHA_INVERT (0x00800000) -#define PSB_2D_SRCALPHA_INVERT_CLR (0xFF7FFFFF) -#define PSB_2D_DSTALPHA_OP_MASK (0x07000000) -#define PSB_2D_DSTALPHA_OP_CLRMASK (0xF8FFFFFF) -#define PSB_2D_DSTALPHA_OP_SHIFT (24) -#define PSB_2D_DSTALPHA_OP_ONE (0x00000000) -#define PSB_2D_DSTALPHA_OP_SRC (0x01000000) -#define PSB_2D_DSTALPHA_OP_DST (0x02000000) -#define PSB_2D_DSTALPHA_OP_SG (0x03000000) -#define PSB_2D_DSTALPHA_OP_DG (0x04000000) -#define PSB_2D_DSTALPHA_OP_GBL (0x05000000) -#define PSB_2D_DSTALPHA_OP_ZERO (0x06000000) -#define PSB_2D_DSTALPHA_INVERT (0x08000000) -#define PSB_2D_DSTALPHA_INVERT_CLR (0xF7FFFFFF) - -#define PSB_2D_PRE_MULTIPLICATION_ENABLE (0x10000000) -#define PSB_2D_PRE_MULTIPLICATION_CLRMASK (0xEFFFFFFF) -#define PSB_2D_ZERO_SOURCE_ALPHA_ENABLE (0x20000000) -#define PSB_2D_ZERO_SOURCE_ALPHA_CLRMASK (0xDFFFFFFF) - -/* - *Source Offset (PSB_2D_SRC_OFF_BH) - */ -#define PSB_2D_SRCOFF_XSTART_MASK ((0x00000FFF) << 12) -#define PSB_2D_SRCOFF_XSTART_SHIFT (12) -#define PSB_2D_SRCOFF_YSTART_MASK (0x00000FFF) -#define PSB_2D_SRCOFF_YSTART_SHIFT (0) - -/* - * Mask Offset (PSB_2D_MASK_OFF_BH) - */ -#define PSB_2D_MASKOFF_XSTART_MASK ((0x00000FFF) << 12) -#define PSB_2D_MASKOFF_XSTART_SHIFT (12) -#define PSB_2D_MASKOFF_YSTART_MASK (0x00000FFF) -#define PSB_2D_MASKOFF_YSTART_SHIFT (0) - -/* - * 2D Fence (see PSB_2D_FENCE_BH): bits 0:27 are ignored - */ - -/* - *Blit Rectangle (PSB_2D_BLIT_BH) - */ - -#define PSB_2D_ROT_MASK (3 << 25) -#define PSB_2D_ROT_CLRMASK (~PSB_2D_ROT_MASK) -#define PSB_2D_ROT_NONE (0 << 25) -#define PSB_2D_ROT_90DEGS (1 << 25) -#define PSB_2D_ROT_180DEGS (2 << 25) -#define PSB_2D_ROT_270DEGS (3 << 25) - -#define PSB_2D_COPYORDER_MASK (3 << 23) -#define PSB_2D_COPYORDER_CLRMASK (~PSB_2D_COPYORDER_MASK) -#define PSB_2D_COPYORDER_TL2BR (0 << 23) -#define PSB_2D_COPYORDER_BR2TL (1 << 23) -#define PSB_2D_COPYORDER_TR2BL (2 << 23) -#define PSB_2D_COPYORDER_BL2TR (3 << 23) - -#define PSB_2D_DSTCK_CLRMASK (0xFF9FFFFF) -#define PSB_2D_DSTCK_DISABLE (0x00000000) -#define PSB_2D_DSTCK_PASS (0x00200000) -#define PSB_2D_DSTCK_REJECT (0x00400000) - -#define PSB_2D_SRCCK_CLRMASK (0xFFE7FFFF) -#define PSB_2D_SRCCK_DISABLE (0x00000000) -#define PSB_2D_SRCCK_PASS (0x00080000) -#define PSB_2D_SRCCK_REJECT (0x00100000) - -#define PSB_2D_CLIP_ENABLE (0x00040000) - -#define PSB_2D_ALPHA_ENABLE (0x00020000) - -#define PSB_2D_PAT_CLRMASK (0xFFFEFFFF) -#define PSB_2D_PAT_MASK (0x00010000) -#define PSB_2D_USE_PAT (0x00010000) -#define PSB_2D_USE_FILL (0x00000000) -/* - * Tungsten Graphics note on rop codes: If rop A and rop B are - * identical, the mask surface will not be read and need not be - * set up. - */ - -#define PSB_2D_ROP3B_MASK (0x0000FF00) -#define PSB_2D_ROP3B_CLRMASK (0xFFFF00FF) -#define PSB_2D_ROP3B_SHIFT (8) -/* rop code A */ -#define PSB_2D_ROP3A_MASK (0x000000FF) -#define PSB_2D_ROP3A_CLRMASK (0xFFFFFF00) -#define PSB_2D_ROP3A_SHIFT (0) - -#define PSB_2D_ROP4_MASK (0x0000FFFF) -/* - * DWORD0: (Only pass if Pattern control == Use Fill Colour) - * Fill Colour RGBA8888 - */ -#define PSB_2D_FILLCOLOUR_MASK (0xFFFFFFFF) -#define PSB_2D_FILLCOLOUR_SHIFT (0) -/* - * DWORD1: (Always Present) - * X Start (Dest) - * Y Start (Dest) - */ -#define PSB_2D_DST_XSTART_MASK (0x00FFF000) -#define PSB_2D_DST_XSTART_CLRMASK (0xFF000FFF) -#define PSB_2D_DST_XSTART_SHIFT (12) -#define PSB_2D_DST_YSTART_MASK (0x00000FFF) -#define PSB_2D_DST_YSTART_CLRMASK (0xFFFFF000) -#define PSB_2D_DST_YSTART_SHIFT (0) -/* - * DWORD2: (Always Present) - * X Size (Dest) - * Y Size (Dest) - */ -#define PSB_2D_DST_XSIZE_MASK (0x00FFF000) -#define PSB_2D_DST_XSIZE_CLRMASK (0xFF000FFF) -#define PSB_2D_DST_XSIZE_SHIFT (12) -#define PSB_2D_DST_YSIZE_MASK (0x00000FFF) -#define PSB_2D_DST_YSIZE_CLRMASK (0xFFFFF000) -#define PSB_2D_DST_YSIZE_SHIFT (0) - -/* - * Source Surface (PSB_2D_SRC_SURF_BH) - */ -/* - * WORD 0 - */ - -#define PSB_2D_SRC_FORMAT_MASK (0x00078000) -#define PSB_2D_SRC_1_PAL (0x00000000) -#define PSB_2D_SRC_2_PAL (0x00008000) -#define PSB_2D_SRC_4_PAL (0x00010000) -#define PSB_2D_SRC_8_PAL (0x00018000) -#define PSB_2D_SRC_8_ALPHA (0x00020000) -#define PSB_2D_SRC_4_ALPHA (0x00028000) -#define PSB_2D_SRC_332RGB (0x00030000) -#define PSB_2D_SRC_4444ARGB (0x00038000) -#define PSB_2D_SRC_555RGB (0x00040000) -#define PSB_2D_SRC_1555ARGB (0x00048000) -#define PSB_2D_SRC_565RGB (0x00050000) -#define PSB_2D_SRC_0888ARGB (0x00058000) -#define PSB_2D_SRC_8888ARGB (0x00060000) -#define PSB_2D_SRC_8888UYVY (0x00068000) -#define PSB_2D_SRC_RESERVED (0x00070000) -#define PSB_2D_SRC_1555ARGB_LOOKUP (0x00078000) - - -#define PSB_2D_SRC_STRIDE_MASK (0x00007FFF) -#define PSB_2D_SRC_STRIDE_CLRMASK (0xFFFF8000) -#define PSB_2D_SRC_STRIDE_SHIFT (0) -/* - * WORD 1 - Base Address - */ -#define PSB_2D_SRC_ADDR_MASK (0x0FFFFFFC) -#define PSB_2D_SRC_ADDR_CLRMASK (0x00000003) -#define PSB_2D_SRC_ADDR_SHIFT (2) -#define PSB_2D_SRC_ADDR_ALIGNSHIFT (2) - -/* - * Pattern Surface (PSB_2D_PAT_SURF_BH) - */ -/* - * WORD 0 - */ - -#define PSB_2D_PAT_FORMAT_MASK (0x00078000) -#define PSB_2D_PAT_1_PAL (0x00000000) -#define PSB_2D_PAT_2_PAL (0x00008000) -#define PSB_2D_PAT_4_PAL (0x00010000) -#define PSB_2D_PAT_8_PAL (0x00018000) -#define PSB_2D_PAT_8_ALPHA (0x00020000) -#define PSB_2D_PAT_4_ALPHA (0x00028000) -#define PSB_2D_PAT_332RGB (0x00030000) -#define PSB_2D_PAT_4444ARGB (0x00038000) -#define PSB_2D_PAT_555RGB (0x00040000) -#define PSB_2D_PAT_1555ARGB (0x00048000) -#define PSB_2D_PAT_565RGB (0x00050000) -#define PSB_2D_PAT_0888ARGB (0x00058000) -#define PSB_2D_PAT_8888ARGB (0x00060000) - -#define PSB_2D_PAT_STRIDE_MASK (0x00007FFF) -#define PSB_2D_PAT_STRIDE_CLRMASK (0xFFFF8000) -#define PSB_2D_PAT_STRIDE_SHIFT (0) -/* - * WORD 1 - Base Address - */ -#define PSB_2D_PAT_ADDR_MASK (0x0FFFFFFC) -#define PSB_2D_PAT_ADDR_CLRMASK (0x00000003) -#define PSB_2D_PAT_ADDR_SHIFT (2) -#define PSB_2D_PAT_ADDR_ALIGNSHIFT (2) - -/* - * Destination Surface (PSB_2D_DST_SURF_BH) - */ -/* - * WORD 0 - */ - -#define PSB_2D_DST_FORMAT_MASK (0x00078000) -#define PSB_2D_DST_332RGB (0x00030000) -#define PSB_2D_DST_4444ARGB (0x00038000) -#define PSB_2D_DST_555RGB (0x00040000) -#define PSB_2D_DST_1555ARGB (0x00048000) -#define PSB_2D_DST_565RGB (0x00050000) -#define PSB_2D_DST_0888ARGB (0x00058000) -#define PSB_2D_DST_8888ARGB (0x00060000) -#define PSB_2D_DST_8888AYUV (0x00070000) - -#define PSB_2D_DST_STRIDE_MASK (0x00007FFF) -#define PSB_2D_DST_STRIDE_CLRMASK (0xFFFF8000) -#define PSB_2D_DST_STRIDE_SHIFT (0) -/* - * WORD 1 - Base Address - */ -#define PSB_2D_DST_ADDR_MASK (0x0FFFFFFC) -#define PSB_2D_DST_ADDR_CLRMASK (0x00000003) -#define PSB_2D_DST_ADDR_SHIFT (2) -#define PSB_2D_DST_ADDR_ALIGNSHIFT (2) - -/* - * Mask Surface (PSB_2D_MASK_SURF_BH) - */ -/* - * WORD 0 - */ -#define PSB_2D_MASK_STRIDE_MASK (0x00007FFF) -#define PSB_2D_MASK_STRIDE_CLRMASK (0xFFFF8000) -#define PSB_2D_MASK_STRIDE_SHIFT (0) -/* - * WORD 1 - Base Address - */ -#define PSB_2D_MASK_ADDR_MASK (0x0FFFFFFC) -#define PSB_2D_MASK_ADDR_CLRMASK (0x00000003) -#define PSB_2D_MASK_ADDR_SHIFT (2) -#define PSB_2D_MASK_ADDR_ALIGNSHIFT (2) - -/* - * Source Palette (PSB_2D_SRC_PAL_BH) - */ - -#define PSB_2D_SRCPAL_ADDR_SHIFT (0) -#define PSB_2D_SRCPAL_ADDR_CLRMASK (0xF0000007) -#define PSB_2D_SRCPAL_ADDR_MASK (0x0FFFFFF8) -#define PSB_2D_SRCPAL_BYTEALIGN (1024) - -/* - * Pattern Palette (PSB_2D_PAT_PAL_BH) - */ - -#define PSB_2D_PATPAL_ADDR_SHIFT (0) -#define PSB_2D_PATPAL_ADDR_CLRMASK (0xF0000007) -#define PSB_2D_PATPAL_ADDR_MASK (0x0FFFFFF8) -#define PSB_2D_PATPAL_BYTEALIGN (1024) - -/* - * Rop3 Codes (2 LS bytes) - */ - -#define PSB_2D_ROP3_SRCCOPY (0xCCCC) -#define PSB_2D_ROP3_PATCOPY (0xF0F0) -#define PSB_2D_ROP3_WHITENESS (0xFFFF) -#define PSB_2D_ROP3_BLACKNESS (0x0000) -#define PSB_2D_ROP3_SRC (0xCC) -#define PSB_2D_ROP3_PAT (0xF0) -#define PSB_2D_ROP3_DST (0xAA) - -/* - * Sizes. - */ - -#define PSB_SCENE_HW_COOKIE_SIZE 16 -#define PSB_TA_MEM_HW_COOKIE_SIZE 16 - -/* - * Scene stuff. - */ - -#define PSB_NUM_HW_SCENES 2 - -/* - * Scheduler completion actions. - */ - -#define PSB_RASTER_BLOCK 0 -#define PSB_RASTER 1 -#define PSB_RETURN 2 -#define PSB_TA 3 - -/* Power management */ -#define PSB_PUNIT_PORT 0x04 -#define PSB_OSPMBA 0x78 -#define PSB_APMBA 0x7a -#define PSB_APM_CMD 0x0 -#define PSB_APM_STS 0x04 -#define PSB_PWRGT_VID_ENC_MASK 0x30 -#define PSB_PWRGT_VID_DEC_MASK 0xc -#define PSB_PWRGT_GL3_MASK 0xc0 - -#define PSB_PM_SSC 0x20 -#define PSB_PM_SSS 0x30 -#define PSB_PWRGT_DISPLAY_MASK 0xc /*on a different BA than video/gfx*/ -#define MDFLD_PWRGT_DISPLAY_A_CNTR 0x0000000c -#define MDFLD_PWRGT_DISPLAY_B_CNTR 0x0000c000 -#define MDFLD_PWRGT_DISPLAY_C_CNTR 0x00030000 -#define MDFLD_PWRGT_DISP_MIPI_CNTR 0x000c0000 -#define MDFLD_PWRGT_DISPLAY_CNTR (MDFLD_PWRGT_DISPLAY_A_CNTR | MDFLD_PWRGT_DISPLAY_B_CNTR | MDFLD_PWRGT_DISPLAY_C_CNTR | MDFLD_PWRGT_DISP_MIPI_CNTR) /* 0x000fc00c */ -/* Display SSS register bits are different in A0 vs. B0 */ -#define PSB_PWRGT_GFX_MASK 0x3 -#define MDFLD_PWRGT_DISPLAY_A_STS 0x000000c0 -#define MDFLD_PWRGT_DISPLAY_B_STS 0x00000300 -#define MDFLD_PWRGT_DISPLAY_C_STS 0x00000c00 -#define PSB_PWRGT_GFX_MASK_B0 0xc3 -#define MDFLD_PWRGT_DISPLAY_A_STS_B0 0x0000000c -#define MDFLD_PWRGT_DISPLAY_B_STS_B0 0x0000c000 -#define MDFLD_PWRGT_DISPLAY_C_STS_B0 0x00030000 -#define MDFLD_PWRGT_DISP_MIPI_STS 0x000c0000 -#define MDFLD_PWRGT_DISPLAY_STS_A0 (MDFLD_PWRGT_DISPLAY_A_STS | MDFLD_PWRGT_DISPLAY_B_STS | MDFLD_PWRGT_DISPLAY_C_STS | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */ -#define MDFLD_PWRGT_DISPLAY_STS_B0 (MDFLD_PWRGT_DISPLAY_A_STS_B0 | MDFLD_PWRGT_DISPLAY_B_STS_B0 | MDFLD_PWRGT_DISPLAY_C_STS_B0 | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */ -#endif -- cgit v0.10.2 From ff4f38765d0e3bcc7d992d07cb27c37adb9b16ba Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Jan 2012 12:51:14 -0600 Subject: staging: drm/omap: drm API update: make fops struct const Update to reflect changes in: "Make the per-driver file_operations struct const" Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index 602aa2d..4db40f7 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -708,6 +708,18 @@ static struct vm_operations_struct omap_gem_vm_ops = { .close = drm_gem_vm_close, }; +static const struct file_operations omapdriver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, + .mmap = omap_gem_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, + .llseek = noop_llseek, +}; + static struct drm_driver omap_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM, @@ -738,17 +750,7 @@ static struct drm_driver omap_drm_driver = { .dumb_destroy = omap_gem_dumb_destroy, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .unlocked_ioctl = drm_ioctl, - .release = drm_release, - .mmap = omap_gem_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - .read = drm_read, - .llseek = noop_llseek, - }, + .fops = &omapdriver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, -- cgit v0.10.2 From ae43d7ca4047b126adedcf7028c1ff99ed18703c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Jan 2012 12:51:15 -0600 Subject: staging: drm/omap: drm API update: addfb2 Update to reflect changes in: "drm: add an fb creation ioctl that takes a pixel format v5" Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index 76c4251..4ad2ae5 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "omap_drm.h" #include "omap_priv.h" @@ -80,9 +81,9 @@ void omap_connector_flush(struct drm_connector *connector, int x, int y, int w, int h); struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); + struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd); struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, - struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo); + struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb); int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, void **vaddr, dma_addr_t *paddr, unsigned int *screen_width); @@ -132,4 +133,29 @@ static inline int align_pitch(int pitch, int width, int bpp) return ALIGN(pitch, 8 * bytespp); } +/* should these be made into common util helpers? + */ + +static inline int objects_lookup(struct drm_device *dev, + struct drm_file *filp, uint32_t pixel_format, + struct drm_gem_object **bos, uint32_t *handles) +{ + int i, n = drm_format_num_planes(pixel_format); + + for (i = 0; i < n; i++) { + bos[i] = drm_gem_object_lookup(dev, filp, handles[i]); + if (!bos[i]) { + goto fail; + } + } + + return 0; + +fail: + while (--i > 0) { + drm_gem_object_unreference_unlocked(bos[i]); + } + return -ENOENT; +} + #endif /* __OMAP_DRV_H__ */ diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 0b50c5b..805a18e 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c @@ -22,11 +22,43 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" - /* * framebuffer funcs */ +/* per-format info: */ +struct format { + enum omap_color_mode dss_format; + uint32_t pixel_format; + struct { + int stride_bpp; /* this times width is stride */ + int sub_y; /* sub-sample in y dimension */ + } planes[4]; + bool yuv; +}; + +static const struct format formats[] = { + /* 16bpp [A]RGB: */ + { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */ + { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */ + { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */ + { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */ + { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */ + { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */ + { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */ + /* 24bpp RGB: */ + { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */ + /* 32bpp [A]RGB: */ + { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */ + { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */ + { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */ + { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */ + /* YUV: */ + { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true }, + { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true }, + { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, +}; + #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) struct omap_framebuffer { @@ -34,6 +66,7 @@ struct omap_framebuffer { struct drm_gem_object *bo; int size; dma_addr_t paddr; + const struct format *format; }; static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, @@ -91,7 +124,7 @@ int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, int bpp = fb->bits_per_pixel / 8; unsigned long offset; - offset = (x * bpp) + (y * fb->pitch); + offset = (x * bpp) + (y * fb->pitches[0]); if (vaddr) { void *bo_vaddr = omap_gem_vaddr(omap_fb->bo); @@ -105,7 +138,7 @@ int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, } *paddr = omap_fb->paddr + offset; - *screen_width = fb->pitch / bpp; + *screen_width = fb->pitches[0] / bpp; return omap_fb->size - offset; } @@ -171,39 +204,61 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb, } struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, - struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd) + struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd) { - struct drm_gem_object *bo; + struct drm_gem_object *bos[4]; struct drm_framebuffer *fb; - bo = drm_gem_object_lookup(dev, file, mode_cmd->handle); - if (!bo) { - return ERR_PTR(-ENOENT); - } - fb = omap_framebuffer_init(dev, mode_cmd, bo); - if (!fb) { - return ERR_PTR(-ENOMEM); + int ret; + + ret = objects_lookup(dev, file, mode_cmd->pixel_format, + bos, mode_cmd->handles); + if (ret) + return ERR_PTR(ret); + + fb = omap_framebuffer_init(dev, mode_cmd, bos); + if (IS_ERR(fb)) { + int i, n = drm_format_num_planes(mode_cmd->pixel_format); + for (i = 0; i < n; i++) + drm_gem_object_unreference_unlocked(bos[i]); + return fb; } return fb; } struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, - struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo) + struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) { struct omap_framebuffer *omap_fb; struct drm_framebuffer *fb = NULL; - int size, ret; + const struct format *format = NULL; + int i, size, ret; - DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%d)", + DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, - mode_cmd->bpp); + (char *)&mode_cmd->pixel_format); + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].pixel_format == mode_cmd->pixel_format) { + format = &formats[i]; + break; + } + } + + if (!format) { + dev_err(dev->dev, "unsupported pixel format: %4.4s\n", + (char *)&mode_cmd->pixel_format); + ret = -EINVAL; + goto fail; + } /* in case someone tries to feed us a completely bogus stride: */ - mode_cmd->pitch = align_pitch(mode_cmd->pitch, - mode_cmd->width, mode_cmd->bpp); + mode_cmd->pitches[0] = align_pitch(mode_cmd->pitches[0], + mode_cmd->width, format->planes[0].stride_bpp); omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); if (!omap_fb) { dev_err(dev->dev, "could not allocate fb\n"); + ret = -ENOMEM; goto fail; } @@ -216,17 +271,19 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, DBG("create: FB ID: %d (%p)", fb->base.id, fb); - size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height); + size = PAGE_ALIGN(mode_cmd->pitches[0] * mode_cmd->height); - if (size > bo->size) { + if (size > bos[0]->size) { dev_err(dev->dev, "provided buffer object is too small!\n"); + ret = -EINVAL; goto fail; } - omap_fb->bo = bo; + omap_fb->bo = bos[0]; omap_fb->size = size; - if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) { + ret = omap_gem_get_paddr(bos[0], &omap_fb->paddr, true); + if (ret) { dev_err(dev->dev, "could not map (paddr)!\n"); goto fail; } @@ -239,5 +296,5 @@ fail: if (fb) { omap_framebuffer_destroy(fb); } - return NULL; + return ERR_PTR(ret); } diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 093ae2f..ba45306 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -129,10 +129,8 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, struct drm_framebuffer *fb = NULL; union omap_gem_size gsize; struct fb_info *fbi = NULL; - struct drm_mode_fb_cmd mode_cmd = {0}; + struct drm_mode_fb_cmd2 mode_cmd = {0}; dma_addr_t paddr; - void __iomem *vaddr; - int size, screen_width; int ret; /* only doing ARGB32 since this is what is needed to alpha-blend @@ -145,36 +143,56 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, sizes->surface_height, sizes->surface_bpp, sizes->fb_width, sizes->fb_height); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; - mode_cmd.bpp = sizes->surface_bpp; - mode_cmd.depth = sizes->surface_depth; - - mode_cmd.pitch = align_pitch( - mode_cmd.width * ((mode_cmd.bpp + 7) / 8), - mode_cmd.width, mode_cmd.bpp); + mode_cmd.pitches[0] = align_pitch( + mode_cmd.width * ((sizes->surface_bpp + 7) / 8), + mode_cmd.width, sizes->surface_bpp); fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled; if (fbdev->ywrap_enabled) { /* need to align pitch to page size if using DMM scrolling */ - mode_cmd.pitch = ALIGN(mode_cmd.pitch, PAGE_SIZE); + mode_cmd.pitches[0] = ALIGN(mode_cmd.pitches[0], PAGE_SIZE); } /* allocate backing bo */ gsize = (union omap_gem_size){ - .bytes = PAGE_ALIGN(mode_cmd.pitch * mode_cmd.height), + .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height), }; DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index); fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); if (!fbdev->bo) { dev_err(dev->dev, "failed to allocate buffer object\n"); + ret = -ENOMEM; goto fail; } - fb = omap_framebuffer_init(dev, &mode_cmd, fbdev->bo); - if (!fb) { + fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo); + if (IS_ERR(fb)) { dev_err(dev->dev, "failed to allocate fb\n"); + /* note: if fb creation failed, we can't rely on fb destroy + * to unref the bo: + */ + drm_gem_object_unreference(fbdev->bo); + ret = PTR_ERR(fb); + goto fail; + } + + /* note: this keeps the bo pinned.. which is perhaps not ideal, + * but is needed as long as we use fb_mmap() to mmap to userspace + * (since this happens using fix.smem_start). Possibly we could + * implement our own mmap using GEM mmap support to avoid this + * (non-tiled buffer doesn't need to be pinned for fbcon to write + * to it). Then we just need to be sure that we are able to re- + * pin it in case of an opps. + */ + ret = omap_gem_get_paddr(fbdev->bo, &paddr, true); + if (ret) { + dev_err(dev->dev, "could not map (paddr)!\n"); ret = -ENOMEM; goto fail; } @@ -206,18 +224,15 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, goto fail_unlock; } - drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); - size = omap_framebuffer_get_buffer(fb, 0, 0, - &vaddr, &paddr, &screen_width); - dev->mode_config.fb_base = paddr; - fbi->screen_base = vaddr; - fbi->screen_size = size; + fbi->screen_base = omap_gem_vaddr(fbdev->bo); + fbi->screen_size = fbdev->bo->size; fbi->fix.smem_start = paddr; - fbi->fix.smem_len = size; + fbi->fix.smem_len = fbdev->bo->size; /* if we have DMM, then we can use it for scrolling by just * shuffling pages around in DMM rather than doing sw blit. -- cgit v0.10.2 From bb5c2d9aaaa26a55e684c175c431df95aa178233 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Jan 2012 12:51:16 -0600 Subject: staging: drm/omap: add drm_plane support Because framebuffer layer and overlay scanout video pipes are basically thing in OMAP display subsystem (the only difference being that the first video pipe does not support scaling or YUV formats), much of the CRTC code is pulled into the plane implementation, and a private plane object is used by the CRTC object. This avoids code duplication between the plane and CRTC. Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile index 592cf69..d9cdc12 100644 --- a/drivers/staging/omapdrm/Makefile +++ b/drivers/staging/omapdrm/Makefile @@ -7,6 +7,7 @@ ccflags-y := -Iinclude/drm -Werror omapdrm-y := omap_drv.o \ omap_debugfs.o \ omap_crtc.o \ + omap_plane.o \ omap_encoder.o \ omap_connector.o \ omap_fb.o \ diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index cffdf5e..a91c788 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -27,196 +27,95 @@ struct omap_crtc { struct drm_crtc base; - struct omap_overlay *ovl; - struct omap_overlay_info info; + struct drm_plane *plane; + const char *name; int id; - /* if there is a pending flip, this will be non-null: */ + /* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event; + struct drm_framebuffer *old_fb; }; -/* push changes down to dss2 */ -static int commit(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct omap_overlay *ovl = omap_crtc->ovl; - struct omap_overlay_info *info = &omap_crtc->info; - int ret; - - DBG("%s", omap_crtc->ovl->name); - DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, - info->out_height, info->screen_width); - DBG("%d,%d %08x", info->pos_x, info->pos_y, info->paddr); - - /* NOTE: do we want to do this at all here, or just wait - * for dpms(ON) since other CRTC's may not have their mode - * set yet, so fb dimensions may still change.. - */ - ret = ovl->set_overlay_info(ovl, info); - if (ret) { - dev_err(dev->dev, "could not set overlay info\n"); - return ret; - } - - /* our encoder doesn't necessarily get a commit() after this, in - * particular in the dpms() and mode_set_base() cases, so force the - * manager to update: - * - * could this be in the encoder somehow? - */ - if (ovl->manager) { - ret = ovl->manager->apply(ovl->manager); - if (ret) { - dev_err(dev->dev, "could not apply settings\n"); - return ret; - } - } - - if (info->enabled) { - omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, - crtc->fb->width, crtc->fb->height); - } - - return 0; -} - -/* update parameters that are dependent on the framebuffer dimensions and - * position within the fb that this crtc scans out from. This is called - * when framebuffer dimensions or x,y base may have changed, either due - * to our mode, or a change in another crtc that is scanning out of the - * same fb. - */ -static void update_scanout(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - dma_addr_t paddr; - unsigned int screen_width; - - omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, - NULL, &paddr, &screen_width); - - DBG("%s: %d,%d: %08x (%d)", omap_crtc->ovl->name, - crtc->x, crtc->y, (u32)paddr, screen_width); - - omap_crtc->info.paddr = paddr; - omap_crtc->info.screen_width = screen_width; -} - static void omap_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size) { - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->ovl->name); + /* not supported.. at least not yet */ } static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->ovl->name); + omap_crtc->plane->funcs->destroy(omap_crtc->plane); drm_crtc_cleanup(crtc); kfree(omap_crtc); } static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) { + struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + int i; - DBG("%s: %d", omap_crtc->ovl->name, mode); + WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); - if (mode == DRM_MODE_DPMS_ON) { - update_scanout(crtc); - omap_crtc->info.enabled = true; - } else { - omap_crtc->info.enabled = false; + for (i = 0; i < priv->num_planes; i++) { + struct drm_plane *plane = priv->planes[i]; + if (plane->crtc == crtc) + WARN_ON(omap_plane_dpms(plane, mode)); } - - WARN_ON(commit(crtc)); } static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->ovl->name); return true; } static int omap_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_plane *plane = omap_crtc->plane; - DBG("%s: %d,%d: %dx%d", omap_crtc->ovl->name, x, y, - mode->hdisplay, mode->vdisplay); - - /* just use adjusted mode */ - mode = adjusted_mode; - - omap_crtc->info.width = mode->hdisplay; - omap_crtc->info.height = mode->vdisplay; - omap_crtc->info.out_width = mode->hdisplay; - omap_crtc->info.out_height = mode->vdisplay; - omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U; - omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; - omap_crtc->info.rotation = OMAP_DSS_ROT_0; - omap_crtc->info.global_alpha = 0xff; - omap_crtc->info.mirror = 0; - omap_crtc->info.mirror = 0; - omap_crtc->info.pos_x = 0; - omap_crtc->info.pos_y = 0; -#if 0 /* re-enable when these are available in DSS2 driver */ - omap_crtc->info.zorder = 3; /* GUI in the front, video behind */ - omap_crtc->info.min_x_decim = 1; - omap_crtc->info.max_x_decim = 1; - omap_crtc->info.min_y_decim = 1; - omap_crtc->info.max_y_decim = 1; -#endif - - update_scanout(crtc); - - return 0; + return plane->funcs->update_plane(plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + x << 16, y << 16, + mode->hdisplay << 16, mode->vdisplay << 16); } static void omap_crtc_prepare(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct omap_overlay *ovl = omap_crtc->ovl; - - DBG("%s", omap_crtc->ovl->name); - - ovl->get_overlay_info(ovl, &omap_crtc->info); - + DBG("%s", omap_crtc->name); omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); } static void omap_crtc_commit(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->ovl->name); + DBG("%s", omap_crtc->name); omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); } static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) + struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_plane *plane = omap_crtc->plane; + struct drm_display_mode *mode = &crtc->mode; - DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb); - - update_scanout(crtc); - - return commit(crtc); + return plane->funcs->update_plane(plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + x << 16, y << 16, + mode->hdisplay << 16, mode->vdisplay << 16); } static void omap_crtc_load_lut(struct drm_crtc *crtc) { - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - DBG("%s", omap_crtc->ovl->name); } static void page_flip_cb(void *arg) @@ -225,15 +124,16 @@ static void page_flip_cb(void *arg) struct drm_device *dev = crtc->dev; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_pending_vblank_event *event = omap_crtc->event; + struct drm_framebuffer *old_fb = omap_crtc->old_fb; struct timeval now; unsigned long flags; WARN_ON(!event); omap_crtc->event = NULL; + omap_crtc->old_fb = NULL; - update_scanout(crtc); - WARN_ON(commit(crtc)); + omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); /* wakeup userspace */ /* TODO: this should happen *after* flip in vsync IRQ handler */ @@ -264,8 +164,9 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, return -EINVAL; } - crtc->fb = fb; + omap_crtc->old_fb = crtc->fb; omap_crtc->event = event; + crtc->fb = fb; omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, page_flip_cb, crtc); @@ -290,12 +191,6 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { .load_lut = omap_crtc_load_lut, }; -struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - return omap_crtc->ovl; -} - /* initialize crtc */ struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct omap_overlay *ovl, int id) @@ -310,9 +205,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, goto fail; } - omap_crtc->ovl = ovl; - omap_crtc->id = id; crtc = &omap_crtc->base; + + omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true); + omap_crtc->plane->crtc = crtc; + omap_crtc->name = ovl->name; + omap_crtc->id = id; + drm_crtc_init(dev, crtc, &omap_crtc_funcs); drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index 4db40f7..2de99ff 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -204,12 +204,6 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, struct omap_overlay_manager *mgr = NULL; struct drm_crtc *crtc; - if (ovl->manager) { - DBG("disconnecting %s from %s", ovl->name, - ovl->manager->name); - ovl->unset_manager(ovl); - } - /* find next best connector, ones with detected connection first */ while (*j < priv->num_connectors && !mgr) { @@ -245,11 +239,6 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, (*j)++; } - if (mgr) { - DBG("connecting %s to %s", ovl->name, mgr->name); - ovl->set_manager(ovl, mgr); - } - crtc = omap_crtc_init(dev, ovl, priv->num_crtcs); if (!crtc) { @@ -265,6 +254,26 @@ static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, return 0; } +static int create_plane(struct drm_device *dev, struct omap_overlay *ovl, + unsigned int possible_crtcs) +{ + struct omap_drm_private *priv = dev->dev_private; + struct drm_plane *plane = + omap_plane_init(dev, ovl, possible_crtcs, false); + + if (!plane) { + dev_err(dev->dev, "could not create plane: %s\n", + ovl->name); + return -ENOMEM; + } + + BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); + + priv->planes[priv->num_planes++] = plane; + + return 0; +} + static int match_dev_name(struct omap_dss_device *dssdev, void *data) { return !strcmp(dssdev->name, data); @@ -332,6 +341,12 @@ static int omap_modeset_init(struct drm_device *dev) omap_dss_get_overlay(kms_pdata->ovl_ids[i]); create_crtc(dev, ovl, &j, connected_connectors); } + + for (i = 0; i < kms_pdata->pln_cnt; i++) { + struct omap_overlay *ovl = + omap_dss_get_overlay(kms_pdata->pln_ids[i]); + create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); + } } else { /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try * to make educated guesses about everything else @@ -353,6 +368,12 @@ static int omap_modeset_init(struct drm_device *dev) create_crtc(dev, omap_dss_get_overlay(i), &j, connected_connectors); } + + /* use any remaining overlays as drm planes */ + for (; i < omap_dss_get_num_overlays(); i++) { + struct omap_overlay *ovl = omap_dss_get_overlay(i); + create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); + } } /* for now keep the mapping of CRTCs and encoders static.. */ @@ -361,15 +382,7 @@ static int omap_modeset_init(struct drm_device *dev) struct omap_overlay_manager *mgr = omap_encoder_get_manager(encoder); - encoder->possible_crtcs = 0; - - for (j = 0; j < priv->num_crtcs; j++) { - struct omap_overlay *ovl = - omap_crtc_get_overlay(priv->crtcs[j]); - if (ovl->manager == mgr) { - encoder->possible_crtcs |= (1 << j); - } - } + encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; DBG("%s: possible_crtcs=%08x", mgr->name, encoder->possible_crtcs); diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index 4ad2ae5..e2b1b55 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -42,6 +42,8 @@ struct omap_drm_private { unsigned int num_crtcs; struct drm_crtc *crtcs[8]; + unsigned int num_planes; + struct drm_plane *planes[8]; unsigned int num_encoders; struct drm_encoder *encoders[8]; unsigned int num_connectors; @@ -62,7 +64,11 @@ void omap_fbdev_free(struct drm_device *dev); struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct omap_overlay *ovl, int id); -struct omap_overlay *omap_crtc_get_overlay(struct drm_crtc *crtc); + +struct drm_plane *omap_plane_init(struct drm_device *dev, + struct omap_overlay *ovl, unsigned int possible_crtcs, + bool priv); +int omap_plane_dpms(struct drm_plane *plane, int mode); struct drm_encoder *omap_encoder_init(struct drm_device *dev, struct omap_overlay_manager *mgr); diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c new file mode 100644 index 0000000..a1d948c --- /dev/null +++ b/drivers/staging/omapdrm/omap_plane.c @@ -0,0 +1,308 @@ +/* + * drivers/staging/omapdrm/omap_plane.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" + +/* some hackery because omapdss has an 'enum omap_plane' (which would be + * better named omap_plane_id).. and compiler seems unhappy about having + * both a 'struct omap_plane' and 'enum omap_plane' + */ +#define omap_plane _omap_plane + +/* + * plane funcs + */ + +#define to_omap_plane(x) container_of(x, struct omap_plane, base) + +struct omap_plane { + struct drm_plane base; + struct omap_overlay *ovl; + struct omap_overlay_info info; + + /* Source values, converted to integers because we don't support + * fractional positions: + */ + unsigned int src_x, src_y; +}; + + +/* push changes down to dss2 */ +static int commit(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_overlay *ovl = omap_plane->ovl; + struct omap_overlay_info *info = &omap_plane->info; + int ret; + + DBG("%s", ovl->name); + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, + info->out_height, info->screen_width); + DBG("%d,%d %08x", info->pos_x, info->pos_y, info->paddr); + + /* NOTE: do we want to do this at all here, or just wait + * for dpms(ON) since other CRTC's may not have their mode + * set yet, so fb dimensions may still change.. + */ + ret = ovl->set_overlay_info(ovl, info); + if (ret) { + dev_err(dev->dev, "could not set overlay info\n"); + return ret; + } + + /* our encoder doesn't necessarily get a commit() after this, in + * particular in the dpms() and mode_set_base() cases, so force the + * manager to update: + * + * could this be in the encoder somehow? + */ + if (ovl->manager) { + ret = ovl->manager->apply(ovl->manager); + if (ret) { + dev_err(dev->dev, "could not apply settings\n"); + return ret; + } + } + + if (info->enabled) { + omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, + info->out_width, info->out_height); + } + + return 0; +} + +/* when CRTC that we are attached to has potentially changed, this checks + * if we are attached to proper manager, and if necessary updates. + */ +static void update_manager(struct drm_plane *plane) +{ + struct omap_drm_private *priv = plane->dev->dev_private; + struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_overlay *ovl = omap_plane->ovl; + struct omap_overlay_manager *mgr = NULL; + int i; + + if (plane->crtc) { + for (i = 0; i < priv->num_encoders; i++) { + struct drm_encoder *encoder = priv->encoders[i]; + if (encoder->crtc == plane->crtc) { + mgr = omap_encoder_get_manager(encoder); + break; + } + } + } + + if (ovl->manager != mgr) { + bool enabled = omap_plane->info.enabled; + + /* don't switch things around with enabled overlays: */ + if (enabled) + omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); + + if (ovl->manager) { + DBG("disconnecting %s from %s", ovl->name, + ovl->manager->name); + ovl->unset_manager(ovl); + } + + if (mgr) { + DBG("connecting %s to %s", ovl->name, mgr->name); + ovl->set_manager(ovl, mgr); + } + + if (enabled && mgr) + omap_plane_dpms(plane, DRM_MODE_DPMS_ON); + } +} + +/* update parameters that are dependent on the framebuffer dimensions and + * position within the fb that this plane scans out from. This is called + * when framebuffer or x,y base may have changed. + */ +static void update_scanout(struct drm_plane *plane) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + unsigned int screen_width; /* really means "pitch" */ + dma_addr_t paddr; + + omap_framebuffer_get_buffer(plane->fb, + omap_plane->src_x, omap_plane->src_y, + NULL, &paddr, &screen_width); + + DBG("%s: %d,%d: %08x (%d)", omap_plane->ovl->name, + omap_plane->src_x, omap_plane->src_y, + (u32)paddr, screen_width); + + omap_plane->info.paddr = paddr; + omap_plane->info.screen_width = screen_width; +} + +static int omap_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + + /* src values are in Q16 fixed point, convert to integer: */ + src_x = src_x >> 16; + src_y = src_y >> 16; + src_w = src_w >> 16; + src_h = src_h >> 16; + + omap_plane->info.enabled = true; + omap_plane->info.pos_x = crtc_x; + omap_plane->info.pos_y = crtc_y; + omap_plane->info.out_width = crtc_w; + omap_plane->info.out_height = crtc_h; + omap_plane->info.width = src_w; + omap_plane->info.height = src_h; + omap_plane->src_x = src_x; + omap_plane->src_y = src_y; + + /* note: this is done after this fxn returns.. but if we need + * to do a commit/update_scanout, etc before this returns we + * need the current value. + */ + plane->fb = fb; + plane->crtc = crtc; + + update_scanout(plane); + update_manager(plane); + commit(plane); + + return 0; +} + +static int omap_plane_disable(struct drm_plane *plane) +{ + return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); +} + +static void omap_plane_destroy(struct drm_plane *plane) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + DBG("%s", omap_plane->ovl->name); + omap_plane_disable(plane); + drm_plane_cleanup(plane); + kfree(omap_plane); +} + +int omap_plane_dpms(struct drm_plane *plane, int mode) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + + DBG("%s: %d", omap_plane->ovl->name, mode); + + if (mode == DRM_MODE_DPMS_ON) { + update_scanout(plane); + omap_plane->info.enabled = true; + } else { + omap_plane->info.enabled = false; + } + + return commit(plane); +} + +static const struct drm_plane_funcs omap_plane_funcs = { + .update_plane = omap_plane_update, + .disable_plane = omap_plane_disable, + .destroy = omap_plane_destroy, +}; + +static const uint32_t formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_ABGR4444, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_NV12, + DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, +}; + +/* initialize plane */ +struct drm_plane *omap_plane_init(struct drm_device *dev, + struct omap_overlay *ovl, unsigned int possible_crtcs, + bool priv) +{ + struct drm_plane *plane = NULL; + struct omap_plane *omap_plane; + + DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name, + possible_crtcs, priv); + + omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); + if (!omap_plane) { + dev_err(dev->dev, "could not allocate plane\n"); + goto fail; + } + + omap_plane->ovl = ovl; + plane = &omap_plane->base; + + drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, + formats, ARRAY_SIZE(formats), priv); + + /* get our starting configuration, set defaults for parameters + * we don't currently use, etc: + */ + ovl->get_overlay_info(ovl, &omap_plane->info); + omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA; + omap_plane->info.rotation = OMAP_DSS_ROT_0; + omap_plane->info.global_alpha = 0xff; + omap_plane->info.mirror = 0; + omap_plane->info.mirror = 0; + + /* Set defaults depending on whether we are a CRTC or overlay + * layer. + * TODO add ioctl to give userspace an API to change this.. this + * will come in a subsequent patch. + */ + if (priv) + omap_plane->info.zorder = 0; + else + omap_plane->info.zorder = 1; + + /* TODO color mode should come from fb.. this will come in a + * subsequent patch + */ + omap_plane->info.color_mode = OMAP_DSS_COLOR_RGB24U; + + update_manager(plane); + + return plane; + +fail: + if (plane) { + omap_plane_destroy(plane); + } + return NULL; +} diff --git a/drivers/staging/omapdrm/omap_priv.h b/drivers/staging/omapdrm/omap_priv.h index c324709..ef64414 100644 --- a/drivers/staging/omapdrm/omap_priv.h +++ b/drivers/staging/omapdrm/omap_priv.h @@ -27,14 +27,22 @@ * pipes/overlays/CRTCs are used.. if this is not provided, then instead the * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to * one manager, with priority given to managers that are connected to - * detected devices. This should be a good default behavior for most cases, - * but yet there still might be times when you wish to do something different. + * detected devices. Remaining overlays are used as video planes. This + * should be a good default behavior for most cases, but yet there still + * might be times when you wish to do something different. */ struct omap_kms_platform_data { + /* overlays to use as CRTCs: */ int ovl_cnt; const int *ovl_ids; + + /* overlays to use as video planes: */ + int pln_cnt; + const int *pln_ids; + int mgr_cnt; const int *mgr_ids; + int dev_cnt; const char **dev_names; }; -- cgit v0.10.2 From 9a0774e0991c82a95ef5e5848b318bd15aba0dcd Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Jan 2012 12:51:17 -0600 Subject: staging: drm/omap: multiplanar and YUV support Add support in framebuffer objects for other color formats and multi- planar YUV (NV12). Since this requires changing the API between the plane and fb for getting scanout information (paddr, etc), take advantage of the opportunity and put in place a way to allow fb's to be unpinned when they are not being scanned out. Now, before start of scanout the plane calls omap_framebuffer_pin() which takes care to pin all the backing bo's, then omap_framebuffer_update_scanout() however many times to update the scanout address(es), etc, and then when finished omap_framebuffer_unpin(). Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index a91c788..3cee04e 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -168,7 +168,7 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, omap_crtc->event = event; crtc->fb = fb; - omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, + omap_gem_op_async(omap_framebuffer_bo(fb, 0), OMAP_GEM_READ, page_flip_cb, crtc); return 0; diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index e2b1b55..48f6fce 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -90,9 +90,11 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_file *file, struct drm_mode_fb_cmd2 *mode_cmd); struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); -struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb); -int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, - void **vaddr, dma_addr_t *paddr, unsigned int *screen_width); +struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p); +int omap_framebuffer_pin(struct drm_framebuffer *fb); +void omap_framebuffer_unpin(struct drm_framebuffer *fb); +void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, + struct omap_overlay_info *info); struct drm_connector *omap_framebuffer_get_next_connector( struct drm_framebuffer *fb, struct drm_connector *from); void omap_framebuffer_flush(struct drm_framebuffer *fb, diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 805a18e..d021a7e 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c @@ -59,14 +59,20 @@ static const struct format formats[] = { { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, }; +/* per-plane info for the fb: */ +struct plane { + struct drm_gem_object *bo; + uint32_t pitch; + uint32_t offset; + dma_addr_t paddr; +}; + #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) struct omap_framebuffer { struct drm_framebuffer base; - struct drm_gem_object *bo; - int size; - dma_addr_t paddr; const struct format *format; + struct plane planes[4]; }; static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, @@ -74,22 +80,23 @@ static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, unsigned int *handle) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - return drm_gem_handle_create(file_priv, omap_fb->bo, handle); + return drm_gem_handle_create(file_priv, + omap_fb->planes[0].bo, handle); } static void omap_framebuffer_destroy(struct drm_framebuffer *fb) { - struct drm_device *dev = fb->dev; struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + int i, n = drm_format_num_planes(omap_fb->format->pixel_format); DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); drm_framebuffer_cleanup(fb); - if (omap_fb->bo) { - if (omap_fb->paddr && omap_gem_put_paddr(omap_fb->bo)) - dev_err(dev->dev, "could not unmap!\n"); - drm_gem_object_unreference_unlocked(omap_fb->bo); + for (i = 0; i < n; i++) { + struct plane *plane = &omap_fb->planes[i]; + if (plane->bo) + drm_gem_object_unreference_unlocked(plane->bo); } kfree(omap_fb); @@ -116,37 +123,76 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { .dirty = omap_framebuffer_dirty, }; -/* returns the buffer size */ -int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, - void **vaddr, dma_addr_t *paddr, unsigned int *screen_width) +/* pins buffer in preparation for scanout */ +int omap_framebuffer_pin(struct drm_framebuffer *fb) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int bpp = fb->bits_per_pixel / 8; - unsigned long offset; - - offset = (x * bpp) + (y * fb->pitches[0]); - - if (vaddr) { - void *bo_vaddr = omap_gem_vaddr(omap_fb->bo); - /* note: we can only count on having a vaddr for buffers that - * are allocated physically contiguously to begin with (ie. - * dma_alloc_coherent()). But this should be ok because it - * is only used by legacy fbdev - */ - BUG_ON(IS_ERR_OR_NULL(bo_vaddr)); - *vaddr = bo_vaddr + offset; + int ret, i, n = drm_format_num_planes(omap_fb->format->pixel_format); + + for (i = 0; i < n; i++) { + struct plane *plane = &omap_fb->planes[i]; + ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true); + if (ret) + goto fail; } - *paddr = omap_fb->paddr + offset; - *screen_width = fb->pitches[0] / bpp; + return 0; - return omap_fb->size - offset; +fail: + while (--i > 0) { + struct plane *plane = &omap_fb->planes[i]; + omap_gem_put_paddr(plane->bo); + } + return ret; } -struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb) +/* releases buffer when done with scanout */ +void omap_framebuffer_unpin(struct drm_framebuffer *fb) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - return omap_fb->bo; + int i, n = drm_format_num_planes(omap_fb->format->pixel_format); + + for (i = 0; i < n; i++) { + struct plane *plane = &omap_fb->planes[i]; + omap_gem_put_paddr(plane->bo); + } +} + +/* update ovl info for scanout, handles cases of multi-planar fb's, etc. + */ +void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y, + struct omap_overlay_info *info) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + const struct format *format = omap_fb->format; + struct plane *plane = &omap_fb->planes[0]; + unsigned int offset; + + offset = plane->offset + + (x * format->planes[0].stride_bpp) + + (y * plane->pitch / format->planes[0].sub_y); + + info->color_mode = format->dss_format; + info->paddr = plane->paddr + offset; + info->screen_width = plane->pitch / format->planes[0].stride_bpp; + + if (format->dss_format == OMAP_DSS_COLOR_NV12) { + plane = &omap_fb->planes[1]; + offset = plane->offset + + (x * format->planes[1].stride_bpp) + + (y * plane->pitch / format->planes[1].sub_y); + info->p_uv_addr = plane->paddr + offset; + } else { + info->p_uv_addr = 0; + } +} + +struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + if (p >= drm_format_num_planes(omap_fb->format->pixel_format)) + return NULL; + return omap_fb->planes[p].bo; } /* iterate thru all the connectors, returning ones that are attached @@ -231,7 +277,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, struct omap_framebuffer *omap_fb; struct drm_framebuffer *fb = NULL; const struct format *format = NULL; - int i, size, ret; + int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, @@ -251,10 +297,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, goto fail; } - /* in case someone tries to feed us a completely bogus stride: */ - mode_cmd->pitches[0] = align_pitch(mode_cmd->pitches[0], - mode_cmd->width, format->planes[0].stride_bpp); - omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL); if (!omap_fb) { dev_err(dev->dev, "could not allocate fb\n"); @@ -271,21 +313,32 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, DBG("create: FB ID: %d (%p)", fb->base.id, fb); - size = PAGE_ALIGN(mode_cmd->pitches[0] * mode_cmd->height); + omap_fb->format = format; - if (size > bos[0]->size) { - dev_err(dev->dev, "provided buffer object is too small!\n"); - ret = -EINVAL; - goto fail; - } + for (i = 0; i < n; i++) { + struct plane *plane = &omap_fb->planes[i]; + int size, pitch = mode_cmd->pitches[i]; - omap_fb->bo = bos[0]; - omap_fb->size = size; + if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) { + dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n", + pitch, mode_cmd->width * format->planes[i].stride_bpp); + ret = -EINVAL; + goto fail; + } - ret = omap_gem_get_paddr(bos[0], &omap_fb->paddr, true); - if (ret) { - dev_err(dev->dev, "could not map (paddr)!\n"); - goto fail; + size = pitch * mode_cmd->height / format->planes[i].sub_y; + + if (size > (bos[i]->size - mode_cmd->offsets[i])) { + dev_err(dev->dev, "provided buffer object is too small! %d < %d\n", + bos[i]->size - mode_cmd->offsets[i], size); + ret = -EINVAL; + goto fail; + } + + plane->bo = bos[i]; + plane->offset = mode_cmd->offsets[i]; + plane->pitch = mode_cmd->pitches[i]; + plane->paddr = pitch; } drm_helper_mode_fill_fb_struct(fb, mode_cmd); diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index e0ebd1d..ae1ad35 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -1034,6 +1034,11 @@ void omap_gem_free_object(struct drm_gem_object *obj) drm_gem_free_mmap_offset(obj); } + /* this means the object is still pinned.. which really should + * not happen. I think.. + */ + WARN_ON(omap_obj->paddr_cnt > 0); + /* don't free externally allocated backing memory */ if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) { if (omap_obj->pages) { diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c index a1d948c..66eed75 100644 --- a/drivers/staging/omapdrm/omap_plane.c +++ b/drivers/staging/omapdrm/omap_plane.c @@ -40,6 +40,9 @@ struct omap_plane { * fractional positions: */ unsigned int src_x, src_y; + + /* last fb that we pinned: */ + struct drm_framebuffer *pinned_fb; }; @@ -55,7 +58,8 @@ static int commit(struct drm_plane *plane) DBG("%s", ovl->name); DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, info->out_height, info->screen_width); - DBG("%d,%d %08x", info->pos_x, info->pos_y, info->paddr); + DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, + info->paddr, info->p_uv_addr); /* NOTE: do we want to do this at all here, or just wait * for dpms(ON) since other CRTC's may not have their mode @@ -133,6 +137,23 @@ static void update_manager(struct drm_plane *plane) } } +/* update which fb (if any) is pinned for scanout */ +static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + int ret = 0; + + if (omap_plane->pinned_fb != fb) { + if (omap_plane->pinned_fb) + omap_framebuffer_unpin(omap_plane->pinned_fb); + omap_plane->pinned_fb = fb; + if (fb) + ret = omap_framebuffer_pin(fb); + } + + return ret; +} + /* update parameters that are dependent on the framebuffer dimensions and * position within the fb that this plane scans out from. This is called * when framebuffer or x,y base may have changed. @@ -140,19 +161,23 @@ static void update_manager(struct drm_plane *plane) static void update_scanout(struct drm_plane *plane) { struct omap_plane *omap_plane = to_omap_plane(plane); - unsigned int screen_width; /* really means "pitch" */ - dma_addr_t paddr; + struct omap_overlay_info *info = &omap_plane->info; + int ret; - omap_framebuffer_get_buffer(plane->fb, - omap_plane->src_x, omap_plane->src_y, - NULL, &paddr, &screen_width); + ret = update_pin(plane, plane->fb); + if (ret) { + dev_err(plane->dev->dev, + "could not pin fb: %d\n", ret); + omap_plane->info.enabled = false; + } - DBG("%s: %d,%d: %08x (%d)", omap_plane->ovl->name, - omap_plane->src_x, omap_plane->src_y, - (u32)paddr, screen_width); + omap_framebuffer_update_scanout(plane->fb, + omap_plane->src_x, omap_plane->src_y, info); - omap_plane->info.paddr = paddr; - omap_plane->info.screen_width = screen_width; + DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name, + omap_plane->src_x, omap_plane->src_y, + (u32)info->paddr, (u32)info->p_uv_addr, + info->screen_width); } static int omap_plane_update(struct drm_plane *plane, @@ -219,6 +244,7 @@ int omap_plane_dpms(struct drm_plane *plane, int mode) omap_plane->info.enabled = true; } else { omap_plane->info.enabled = false; + update_pin(plane, NULL); } return commit(plane); @@ -291,11 +317,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, else omap_plane->info.zorder = 1; - /* TODO color mode should come from fb.. this will come in a - * subsequent patch - */ - omap_plane->info.color_mode = OMAP_DSS_COLOR_RGB24U; - update_manager(plane); return plane; -- cgit v0.10.2 From 2f53700df17bf52332056c1e85b23bcf64c77e57 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Jan 2012 12:51:18 -0600 Subject: staging: drm/omap: updates for DSS fifomerge API changes Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index 3cee04e..17ca163 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -81,7 +81,7 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc, struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_plane *plane = omap_crtc->plane; - return plane->funcs->update_plane(plane, crtc, crtc->fb, + return omap_plane_mode_set(plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, x << 16, y << 16, mode->hdisplay << 16, mode->vdisplay << 16); diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index 48f6fce..61fe022 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -69,6 +69,12 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, struct omap_overlay *ovl, unsigned int possible_crtcs, bool priv); int omap_plane_dpms(struct drm_plane *plane, int mode); +int omap_plane_mode_set(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); struct drm_encoder *omap_encoder_init(struct drm_device *dev, struct omap_overlay_manager *mgr); diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c index 66eed75..9790912 100644 --- a/drivers/staging/omapdrm/omap_plane.c +++ b/drivers/staging/omapdrm/omap_plane.c @@ -85,7 +85,7 @@ static int commit(struct drm_plane *plane) } } - if (info->enabled) { + if (ovl->is_enabled(ovl)) { omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, info->out_width, info->out_height); } @@ -115,7 +115,7 @@ static void update_manager(struct drm_plane *plane) } if (ovl->manager != mgr) { - bool enabled = omap_plane->info.enabled; + bool enabled = ovl->is_enabled(ovl); /* don't switch things around with enabled overlays: */ if (enabled) @@ -168,7 +168,8 @@ static void update_scanout(struct drm_plane *plane) if (ret) { dev_err(plane->dev->dev, "could not pin fb: %d\n", ret); - omap_plane->info.enabled = false; + omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); + return; } omap_framebuffer_update_scanout(plane->fb, @@ -180,7 +181,7 @@ static void update_scanout(struct drm_plane *plane) info->screen_width); } -static int omap_plane_update(struct drm_plane *plane, +int omap_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, @@ -195,7 +196,6 @@ static int omap_plane_update(struct drm_plane *plane, src_w = src_w >> 16; src_h = src_h >> 16; - omap_plane->info.enabled = true; omap_plane->info.pos_x = crtc_x; omap_plane->info.pos_y = crtc_y; omap_plane->info.out_width = crtc_w; @@ -214,11 +214,22 @@ static int omap_plane_update(struct drm_plane *plane, update_scanout(plane); update_manager(plane); - commit(plane); return 0; } +static int omap_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + return omap_plane_dpms(plane, DRM_MODE_DPMS_ON); +} + static int omap_plane_disable(struct drm_plane *plane) { return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); @@ -236,18 +247,22 @@ static void omap_plane_destroy(struct drm_plane *plane) int omap_plane_dpms(struct drm_plane *plane, int mode) { struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_overlay *ovl = omap_plane->ovl; + int r; DBG("%s: %d", omap_plane->ovl->name, mode); if (mode == DRM_MODE_DPMS_ON) { update_scanout(plane); - omap_plane->info.enabled = true; + r = commit(plane); + if (!r) + r = ovl->enable(ovl); } else { - omap_plane->info.enabled = false; + r = ovl->disable(ovl); update_pin(plane, NULL); } - return commit(plane); + return r; } static const struct drm_plane_funcs omap_plane_funcs = { -- cgit v0.10.2 From 6b8ca4cf53e04f377944228b66ddc7d5d593e705 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sun, 8 Jan 2012 19:37:37 -0600 Subject: staging: drm/omap: fix minimum width/height Fix minimum width/height so planes could be used to implement hw mouse cursor. Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index 2de99ff..3bbea9a 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -390,8 +390,8 @@ static int omap_modeset_init(struct drm_device *dev) dump_video_chains(); - dev->mode_config.min_width = 256; - dev->mode_config.min_height = 256; + dev->mode_config.min_width = 32; + dev->mode_config.min_height = 32; /* note: eventually will need some cpu_is_omapXYZ() type stuff here * to fill in these limits properly on different OMAP generations.. -- cgit v0.10.2 From a9e8d70c1ac6c9ccf6852c91e082e28249564e6e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 9 Jan 2012 08:51:03 +0100 Subject: drivers/staging/omapdrm/omap_fbdev.c: move free after uses Move the free after the final uses. The semantic patch that makes this report is available in scripts/coccinelle/free/kfree.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index ba45306..96940bb 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -377,11 +377,11 @@ void omap_fbdev_free(struct drm_device *dev) fbdev = to_omap_fbdev(priv->fbdev); - kfree(fbdev); - /* this will free the backing object */ if (fbdev->fb) fbdev->fb->funcs->destroy(fbdev->fb); + kfree(fbdev); + priv->fbdev = NULL; } -- cgit v0.10.2 From c5b1247bd1c3ab6722acfa95213be9a16bfb664c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 18 Jan 2012 18:33:02 -0600 Subject: staging: drm/omap: fix locking issue The create/free mmap offset code must be synchronized. Yet only some callers of omap_gem_mmap_offset() held struct_mutex. Leading to various crashes around drm_mm_insert_helper_range(). (In the free-object path, which is currently the only place we drm_gem_free_mmap_offset(), struct_mutex is already held.) Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index ae1ad35..b7d6f88 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -116,6 +116,9 @@ struct omap_gem_object { } *sync; }; +static int get_pages(struct drm_gem_object *obj, struct page ***pages); +static uint64_t mmap_offset(struct drm_gem_object *obj); + /* To deal with userspace mmap'ings of 2d tiled buffers, which (a) are * not necessarily pinned in TILER all the time, and (b) when they are * they are not necessarily page aligned, we reserve one or more small @@ -149,7 +152,7 @@ static void evict_entry(struct drm_gem_object *obj, { if (obj->dev->dev_mapping) { size_t size = PAGE_SIZE * usergart[fmt].height; - loff_t off = omap_gem_mmap_offset(obj) + + loff_t off = mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT); unmap_mapping_range(obj->dev->dev_mapping, off, size, 1); } @@ -189,8 +192,6 @@ static inline bool is_shmem(struct drm_gem_object *obj) return obj->filp != NULL; } -static int get_pages(struct drm_gem_object *obj, struct page ***pages); - static DEFINE_SPINLOCK(sync_lock); /** ensure backing pages are allocated */ @@ -251,7 +252,7 @@ static void omap_gem_detach_pages(struct drm_gem_object *obj) } /** get mmap offset */ -uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) +static uint64_t mmap_offset(struct drm_gem_object *obj) { if (!obj->map_list.map) { /* Make it mmapable */ @@ -267,6 +268,15 @@ uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT; } +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) +{ + uint64_t offset; + mutex_lock(&obj->dev->struct_mutex); + offset = mmap_offset(obj); + mutex_unlock(&obj->dev->struct_mutex); + return offset; +} + /** get mmap size */ size_t omap_gem_mmap_size(struct drm_gem_object *obj) { -- cgit v0.10.2 From 9256a4789be3dae37d00924c03546ba7958ea5a3 Mon Sep 17 00:00:00 2001 From: Dan Magenheimer Date: Wed, 25 Jan 2012 14:32:51 -0800 Subject: zcache: fix deadlock condition I discovered this deadlock condition awhile ago working on RAMster but it affects zcache as well. The list spinlock must be locked prior to the page spinlock and released after. As a result, the page copy must also be done while the locks are held. Applies to 3.2. Konrad, please push (via GregKH?)... this is definitely a bug fix so need not be pushed during a -rc0 window. Signed-off-by: Dan Magenheimer Acked-by: Konrad Rzeszutek Wilk Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index 642840c..ae0ed82 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -358,8 +358,8 @@ static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id, if (unlikely(zbpg == NULL)) goto out; /* ok, have a page, now compress the data before taking locks */ - spin_lock(&zbpg->lock); spin_lock(&zbud_budlists_spinlock); + spin_lock(&zbpg->lock); list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list); zbud_unbuddied[nchunks].count++; zh = &zbpg->buddy[0]; @@ -389,12 +389,11 @@ init_zh: zh->oid = *oid; zh->pool_id = pool_id; zh->client_id = client_id; - /* can wait to copy the data until the list locks are dropped */ - spin_unlock(&zbud_budlists_spinlock); - to = zbud_data(zh, size); memcpy(to, cdata, size); spin_unlock(&zbpg->lock); + spin_unlock(&zbud_budlists_spinlock); + zbud_cumul_chunk_counts[nchunks]++; atomic_inc(&zcache_zbud_curr_zpages); zcache_zbud_cumul_zpages++; -- cgit v0.10.2 From e8b4553457e78bcff90f70a31212a40a8fd4f0db Mon Sep 17 00:00:00 2001 From: Dan Magenheimer Date: Mon, 23 Jan 2012 16:52:20 -0500 Subject: zcache: Set SWIZ_BITS to 8 to reduce tmem bucket lock contention. SWIZ_BITS > 8 results in a much larger number of "tmem_obj" allocations, likely one per page-placed-in-frontswap. The tmem_obj is not huge (roughly 100 bytes), but it is large enough to add a not-insignificant memory overhead to zcache. The SWIZ_BITS=8 will get roughly the same lock contention without the space wastage. The effect of SWIZ_BITS can be thought of as "2^SWIZ_BITS is the number of unique oids that be generated" (This concept is limited to frontswap's use of tmem). Acked-by: Seth Jennings Signed-off-by: Konrad Rzeszutek Wilk Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index ae0ed82..ce07087 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -1781,9 +1781,9 @@ static int zcache_frontswap_poolid = -1; * Swizzling increases objects per swaptype, increasing tmem concurrency * for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS * Setting SWIZ_BITS to 27 basically reconstructs the swap entry from - * frontswap_get_page() + * frontswap_get_page(), but has side-effects. Hence using 8. */ -#define SWIZ_BITS 27 +#define SWIZ_BITS 8 #define SWIZ_MASK ((1 << SWIZ_BITS) - 1) #define _oswiz(_type, _ind) ((_type << SWIZ_BITS) | (_ind & SWIZ_MASK)) #define iswiz(_ind) (_ind >> SWIZ_BITS) -- cgit v0.10.2 From 1793bf1deddc8ce25dc41925d5dbe64536c841b6 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 7 Jan 2012 10:07:03 -0600 Subject: staging: r8712u: Add new Sitecom UsB ID Add USB ID for SITECOM WLA-1000 V1 001 WLAN Reported-and-tested-by: Roland Gruber Reported-and-tested-by: Dario Lucia Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c index 5385da2..8de0c80 100644 --- a/drivers/staging/rtl8712/usb_intf.c +++ b/drivers/staging/rtl8712/usb_intf.c @@ -89,6 +89,7 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = { {USB_DEVICE(0x0DF6, 0x0045)}, {USB_DEVICE(0x0DF6, 0x0059)}, /* 11n mode disable */ {USB_DEVICE(0x0DF6, 0x004B)}, + {USB_DEVICE(0x0DF6, 0x005B)}, {USB_DEVICE(0x0DF6, 0x005D)}, {USB_DEVICE(0x0DF6, 0x0063)}, /* Sweex */ -- cgit v0.10.2 From 3c1b86f17068cf6476fb2d022b9c8b44dedea2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 20 Jan 2012 19:56:21 -0800 Subject: Staging: android: binder: Don't call dump_stack in binder_vma_open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If user-space partially unmaps the driver, binder_vma_open would dump the kernel stack. This is not a kernel bug however and will be treated as if the whole area was unmapped once binder_vma_close gets called. Signed-off-by: Arve HjønnevÃ¥g Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 7491801..48cf27c 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2759,7 +2759,6 @@ static void binder_vma_open(struct vm_area_struct *vma) proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); - dump_stack(); } static void binder_vma_close(struct vm_area_struct *vma) -- cgit v0.10.2 From 635032cb397b396241372fa0ff36ae758e658b23 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Sun, 22 Jan 2012 16:33:46 +0200 Subject: Staging: asus_oled: fix image processing Programming an image was broken, because odev->buf_offs was not advanced for val == 0 in append_values(). This regression was introduced in: commit 1ff12a4aa354bed093a0240d5e6347b1e27601bc Author: Kevin A. Granade Date: Sat Sep 5 01:03:39 2009 -0500 Staging: asus_oled: Cleaned up checkpatch issues. Fix the image processing by special-casing val == 0. I have tested this change on an Asus G50V laptop only. Cc: Jakub Schmidtke Cc: Kevin A. Granade Signed-off-by: Pekka Paalanen Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/asus_oled/asus_oled.c b/drivers/staging/asus_oled/asus_oled.c index e77e4e0..2fdc5db 100644 --- a/drivers/staging/asus_oled/asus_oled.c +++ b/drivers/staging/asus_oled/asus_oled.c @@ -355,7 +355,14 @@ static void send_data(struct asus_oled_dev *odev) static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count) { - while (count-- > 0 && val) { + odev->last_val = val; + + if (val == 0) { + odev->buf_offs += count; + return 0; + } + + while (count-- > 0) { size_t x = odev->buf_offs % odev->width; size_t y = odev->buf_offs / odev->width; size_t i; @@ -406,7 +413,6 @@ static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count) ; } - odev->last_val = val; odev->buf_offs++; } -- cgit v0.10.2 From 3589e74595a4332ebf77b5ed006f3c6686071ecd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Sun, 22 Jan 2012 16:33:47 +0200 Subject: Staging: asus_oled: fix NULL-ptr crash on unloading Asus_oled triggers the following bug on module unloading: usbcore: deregistering interface driver asus-oled BUG: unable to handle kernel NULL pointer dereference at 0000000000000038 IP: [] sysfs_delete_link+0x30/0x66 Call Trace: [] device_remove_class_symlinks+0x6b/0x70 [] device_del+0x9f/0x1ab [] device_unregister+0x11/0x1e [] asus_oled_disconnect+0x4f/0x9e [asus_oled] [] usb_unbind_interface+0x54/0x103 [] __device_release_driver+0xa2/0xeb [] driver_detach+0x87/0xad [] bus_remove_driver+0x91/0xc1 [] driver_unregister+0x66/0x6e [] usb_deregister+0xbb/0xc4 [] asus_oled_exit+0x2f/0x31 [asus_oled] [] sys_delete_module+0x1b8/0x21b [] ? do_munmap+0x2ef/0x313 [] system_call_fastpath+0x16/0x1b This is due to an incorrect destruction sequence in asus_oled_exit(). Fix the order, fixes the bug. Tested on an Asus G50V laptop only. Cc: Jakub Schmidtke Signed-off-by: Pekka Paalanen Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/asus_oled/asus_oled.c b/drivers/staging/asus_oled/asus_oled.c index 2fdc5db..1df9586 100644 --- a/drivers/staging/asus_oled/asus_oled.c +++ b/drivers/staging/asus_oled/asus_oled.c @@ -811,10 +811,9 @@ error: static void __exit asus_oled_exit(void) { + usb_deregister(&oled_driver); class_remove_file(oled_class, &class_attr_version.attr); class_destroy(oled_class); - - usb_deregister(&oled_driver); } module_init(asus_oled_init); -- cgit v0.10.2 From b0d017e80e9f4e6b37e699b9a944646e64deb473 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Fri, 27 Jan 2012 11:40:10 -0700 Subject: Staging: android: Remove pmem driver Addroid pmem driver is no longer used in any of the Android products. This patch removes pmem driver from Android staging area Reference: https://lkml.org/lkml/2012/1/23/183 Signed-off-by: Shuah Khan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index becf711..94cb2ac 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -99,10 +99,6 @@ config ANDROID_LOW_MEMORY_KILLER ---help--- Register processes to be killed when memory is low -config ANDROID_PMEM - bool "Android pmem allocator" - depends on ARM - source "drivers/staging/android/switch/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index eaed1ff..5fcc24f 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -5,5 +5,4 @@ obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o -obj-$(CONFIG_ANDROID_PMEM) += pmem.o obj-$(CONFIG_ANDROID_SWITCH) += switch/ diff --git a/drivers/staging/android/android_pmem.h b/drivers/staging/android/android_pmem.h deleted file mode 100644 index f633621..0000000 --- a/drivers/staging/android/android_pmem.h +++ /dev/null @@ -1,93 +0,0 @@ -/* include/linux/android_pmem.h - * - * Copyright (C) 2007 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef _ANDROID_PMEM_H_ -#define _ANDROID_PMEM_H_ - -#define PMEM_IOCTL_MAGIC 'p' -#define PMEM_GET_PHYS _IOW(PMEM_IOCTL_MAGIC, 1, unsigned int) -#define PMEM_MAP _IOW(PMEM_IOCTL_MAGIC, 2, unsigned int) -#define PMEM_GET_SIZE _IOW(PMEM_IOCTL_MAGIC, 3, unsigned int) -#define PMEM_UNMAP _IOW(PMEM_IOCTL_MAGIC, 4, unsigned int) -/* This ioctl will allocate pmem space, backing the file, it will fail - * if the file already has an allocation, pass it the len as the argument - * to the ioctl */ -#define PMEM_ALLOCATE _IOW(PMEM_IOCTL_MAGIC, 5, unsigned int) -/* This will connect a one pmem file to another, pass the file that is already - * backed in memory as the argument to the ioctl - */ -#define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int) -/* Returns the total size of the pmem region it is sent to as a pmem_region - * struct (with offset set to 0). - */ -#define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int) -#define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) - -struct android_pmem_platform_data -{ - const char* name; - /* starting physical address of memory region */ - unsigned long start; - /* size of memory region */ - unsigned long size; - /* set to indicate the region should not be managed with an allocator */ - unsigned no_allocator; - /* set to indicate maps of this region should be cached, if a mix of - * cached and uncached is desired, set this and open the device with - * O_SYNC to get an uncached region */ - unsigned cached; - /* The MSM7k has bits to enable a write buffer in the bus controller*/ - unsigned buffered; -}; - -struct pmem_region { - unsigned long offset; - unsigned long len; -}; - -#ifdef CONFIG_ANDROID_PMEM -int is_pmem_file(struct file *file); -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *end, struct file **filp); -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end); -void put_pmem_file(struct file* file); -void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); -int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)); -int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation); - -#else -static inline int is_pmem_file(struct file *file) { return 0; } -static inline int get_pmem_file(int fd, unsigned long *start, - unsigned long *vstart, unsigned long *end, - struct file **filp) { return -ENOSYS; } -static inline int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end) { return -ENOSYS; } -static inline void put_pmem_file(struct file* file) { return; } -static inline void flush_pmem_file(struct file *file, unsigned long start, - unsigned long len) { return; } -static inline int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) { return -ENOSYS; } - -static inline int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) { return -ENOSYS; } -#endif - -#endif //_ANDROID_PPP_H_ - diff --git a/drivers/staging/android/pmem.c b/drivers/staging/android/pmem.c deleted file mode 100644 index 7d97032..0000000 --- a/drivers/staging/android/pmem.c +++ /dev/null @@ -1,1345 +0,0 @@ -/* pmem.c - * - * Copyright (C) 2007 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "android_pmem.h" - -#define PMEM_MAX_DEVICES 10 -#define PMEM_MAX_ORDER 128 -#define PMEM_MIN_ALLOC PAGE_SIZE - -#define PMEM_DEBUG 1 - -/* indicates that a refernce to this file has been taken via get_pmem_file, - * the file should not be released until put_pmem_file is called */ -#define PMEM_FLAGS_BUSY 0x1 -/* indicates that this is a suballocation of a larger master range */ -#define PMEM_FLAGS_CONNECTED 0x1 << 1 -/* indicates this is a master and not a sub allocation and that it is mmaped */ -#define PMEM_FLAGS_MASTERMAP 0x1 << 2 -/* submap and unsubmap flags indicate: - * 00: subregion has never been mmaped - * 10: subregion has been mmaped, reference to the mm was taken - * 11: subretion has ben released, refernece to the mm still held - * 01: subretion has been released, reference to the mm has been released - */ -#define PMEM_FLAGS_SUBMAP 0x1 << 3 -#define PMEM_FLAGS_UNSUBMAP 0x1 << 4 - - -struct pmem_data { - /* in alloc mode: an index into the bitmap - * in no_alloc mode: the size of the allocation */ - int index; - /* see flags above for descriptions */ - unsigned int flags; - /* protects this data field, if the mm_mmap sem will be held at the - * same time as this sem, the mm sem must be taken first (as this is - * the order for vma_open and vma_close ops */ - struct rw_semaphore sem; - /* info about the mmaping process */ - struct vm_area_struct *vma; - /* task struct of the mapping process */ - struct task_struct *task; - /* process id of teh mapping process */ - pid_t pid; - /* file descriptor of the master */ - int master_fd; - /* file struct of the master */ - struct file *master_file; - /* a list of currently available regions if this is a suballocation */ - struct list_head region_list; - /* a linked list of data so we can access them for debugging */ - struct list_head list; -#if PMEM_DEBUG - int ref; -#endif -}; - -struct pmem_bits { - unsigned allocated:1; /* 1 if allocated, 0 if free */ - unsigned order:7; /* size of the region in pmem space */ -}; - -struct pmem_region_node { - struct pmem_region region; - struct list_head list; -}; - -#define PMEM_DEBUG_MSGS 0 -#if PMEM_DEBUG_MSGS -#define DLOG(fmt,args...) \ - do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ - ##args); } \ - while (0) -#else -#define DLOG(x...) do {} while (0) -#endif - -struct pmem_info { - struct miscdevice dev; - /* physical start address of the remaped pmem space */ - unsigned long base; - /* vitual start address of the remaped pmem space */ - unsigned char __iomem *vbase; - /* total size of the pmem space */ - unsigned long size; - /* number of entries in the pmem space */ - unsigned long num_entries; - /* pfn of the garbage page in memory */ - unsigned long garbage_pfn; - /* index of the garbage page in the pmem space */ - int garbage_index; - /* the bitmap for the region indicating which entries are allocated - * and which are free */ - struct pmem_bits *bitmap; - /* indicates the region should not be managed with an allocator */ - unsigned no_allocator; - /* indicates maps of this region should be cached, if a mix of - * cached and uncached is desired, set this and open the device with - * O_SYNC to get an uncached region */ - unsigned cached; - unsigned buffered; - /* in no_allocator mode the first mapper gets the whole space and sets - * this flag */ - unsigned allocated; - /* for debugging, creates a list of pmem file structs, the - * data_list_lock should be taken before pmem_data->sem if both are - * needed */ - struct mutex data_list_lock; - struct list_head data_list; - /* pmem_sem protects the bitmap array - * a write lock should be held when modifying entries in bitmap - * a read lock should be held when reading data from bits or - * dereferencing a pointer into bitmap - * - * pmem_data->sem protects the pmem data of a particular file - * Many of the function that require the pmem_data->sem have a non- - * locking version for when the caller is already holding that sem. - * - * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER: - * down(pmem_data->sem) => down(bitmap_sem) - */ - struct rw_semaphore bitmap_sem; - - long (*ioctl)(struct file *, unsigned int, unsigned long); - int (*release)(struct inode *, struct file *); -}; - -static struct pmem_info pmem[PMEM_MAX_DEVICES]; -static int id_count; - -#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated) -#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order -#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index))) -#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index))) -#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC) -#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base) -#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC) -#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase) -#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED) -#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) -#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \ - (!(data->flags & PMEM_FLAGS_UNSUBMAP))) - -static int pmem_release(struct inode *, struct file *); -static int pmem_mmap(struct file *, struct vm_area_struct *); -static int pmem_open(struct inode *, struct file *); -static long pmem_ioctl(struct file *, unsigned int, unsigned long); - -struct file_operations pmem_fops = { - .release = pmem_release, - .mmap = pmem_mmap, - .open = pmem_open, - .unlocked_ioctl = pmem_ioctl, -}; - -static int get_id(struct file *file) -{ - return MINOR(file->f_dentry->d_inode->i_rdev); -} - -int is_pmem_file(struct file *file) -{ - int id; - - if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode)) - return 0; - id = get_id(file); - if (unlikely(id >= PMEM_MAX_DEVICES)) - return 0; - if (unlikely(file->f_dentry->d_inode->i_rdev != - MKDEV(MISC_MAJOR, pmem[id].dev.minor))) - return 0; - return 1; -} - -static int has_allocation(struct file *file) -{ - struct pmem_data *data; - /* check is_pmem_file first if not accessed via pmem_file_ops */ - - if (unlikely(!file->private_data)) - return 0; - data = (struct pmem_data *)file->private_data; - if (unlikely(data->index < 0)) - return 0; - return 1; -} - -static int is_master_owner(struct file *file) -{ - struct file *master_file; - struct pmem_data *data; - int put_needed, ret = 0; - - if (!is_pmem_file(file) || !has_allocation(file)) - return 0; - data = (struct pmem_data *)file->private_data; - if (PMEM_FLAGS_MASTERMAP & data->flags) - return 1; - master_file = fget_light(data->master_fd, &put_needed); - if (master_file && data->master_file == master_file) - ret = 1; - fput_light(master_file, put_needed); - return ret; -} - -static int pmem_free(int id, int index) -{ - /* caller should hold the write lock on pmem_sem! */ - int buddy, curr = index; - DLOG("index %d\n", index); - - if (pmem[id].no_allocator) { - pmem[id].allocated = 0; - return 0; - } - /* clean up the bitmap, merging any buddies */ - pmem[id].bitmap[curr].allocated = 0; - /* find a slots buddy Buddy# = Slot# ^ (1 << order) - * if the buddy is also free merge them - * repeat until the buddy is not free or end of the bitmap is reached - */ - do { - buddy = PMEM_BUDDY_INDEX(id, curr); - if (PMEM_IS_FREE(id, buddy) && - PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) { - PMEM_ORDER(id, buddy)++; - PMEM_ORDER(id, curr)++; - curr = min(buddy, curr); - } else { - break; - } - } while (curr < pmem[id].num_entries); - - return 0; -} - -static void pmem_revoke(struct file *file, struct pmem_data *data); - -static int pmem_release(struct inode *inode, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_region_node *region_node; - struct list_head *elt, *elt2; - int id = get_id(file), ret = 0; - - - mutex_lock(&pmem[id].data_list_lock); - /* if this file is a master, revoke all the memory in the connected - * files */ - if (PMEM_FLAGS_MASTERMAP & data->flags) { - struct pmem_data *sub_data; - list_for_each(elt, &pmem[id].data_list) { - sub_data = list_entry(elt, struct pmem_data, list); - down_read(&sub_data->sem); - if (PMEM_IS_SUBMAP(sub_data) && - file == sub_data->master_file) { - up_read(&sub_data->sem); - pmem_revoke(file, sub_data); - } else - up_read(&sub_data->sem); - } - } - list_del(&data->list); - mutex_unlock(&pmem[id].data_list_lock); - - - down_write(&data->sem); - - /* if its not a conencted file and it has an allocation, free it */ - if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) { - down_write(&pmem[id].bitmap_sem); - ret = pmem_free(id, data->index); - up_write(&pmem[id].bitmap_sem); - } - - /* if this file is a submap (mapped, connected file), downref the - * task struct */ - if (PMEM_FLAGS_SUBMAP & data->flags) - if (data->task) { - put_task_struct(data->task); - data->task = NULL; - } - - file->private_data = NULL; - - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, list); - list_del(elt); - kfree(region_node); - } - BUG_ON(!list_empty(&data->region_list)); - - up_write(&data->sem); - kfree(data); - if (pmem[id].release) - ret = pmem[id].release(inode, file); - - return ret; -} - -static int pmem_open(struct inode *inode, struct file *file) -{ - struct pmem_data *data; - int id = get_id(file); - int ret = 0; - - DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file)); - /* setup file->private_data to indicate its unmapped */ - /* you can only open a pmem device one time */ - if (file->private_data != NULL) - return -1; - data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); - if (!data) { - printk("pmem: unable to allocate memory for pmem metadata."); - return -1; - } - data->flags = 0; - data->index = -1; - data->task = NULL; - data->vma = NULL; - data->pid = 0; - data->master_file = NULL; -#if PMEM_DEBUG - data->ref = 0; -#endif - INIT_LIST_HEAD(&data->region_list); - init_rwsem(&data->sem); - - file->private_data = data; - INIT_LIST_HEAD(&data->list); - - mutex_lock(&pmem[id].data_list_lock); - list_add(&data->list, &pmem[id].data_list); - mutex_unlock(&pmem[id].data_list_lock); - return ret; -} - -static unsigned long pmem_order(unsigned long len) -{ - int i; - - len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC; - len--; - for (i = 0; i < sizeof(len)*8; i++) - if (len >> i == 0) - break; - return i; -} - -static int pmem_allocate(int id, unsigned long len) -{ - /* caller should hold the write lock on pmem_sem! */ - /* return the corresponding pdata[] entry */ - int curr = 0; - int end = pmem[id].num_entries; - int best_fit = -1; - unsigned long order = pmem_order(len); - - if (pmem[id].no_allocator) { - DLOG("no allocator"); - if ((len > pmem[id].size) || pmem[id].allocated) - return -1; - pmem[id].allocated = 1; - return len; - } - - if (order > PMEM_MAX_ORDER) - return -1; - DLOG("order %lx\n", order); - - /* look through the bitmap: - * if you find a free slot of the correct order use it - * otherwise, use the best fit (smallest with size > order) slot - */ - while (curr < end) { - if (PMEM_IS_FREE(id, curr)) { - if (PMEM_ORDER(id, curr) == (unsigned char)order) { - /* set the not free bit and clear others */ - best_fit = curr; - break; - } - if (PMEM_ORDER(id, curr) > (unsigned char)order && - (best_fit < 0 || - PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit))) - best_fit = curr; - } - curr = PMEM_NEXT_INDEX(id, curr); - } - - /* if best_fit < 0, there are no suitable slots, - * return an error - */ - if (best_fit < 0) { - printk("pmem: no space left to allocate!\n"); - return -1; - } - - /* now partition the best fit: - * split the slot into 2 buddies of order - 1 - * repeat until the slot is of the correct order - */ - while (PMEM_ORDER(id, best_fit) > (unsigned char)order) { - int buddy; - PMEM_ORDER(id, best_fit) -= 1; - buddy = PMEM_BUDDY_INDEX(id, best_fit); - PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit); - } - pmem[id].bitmap[best_fit].allocated = 1; - return best_fit; -} - -static pgprot_t pmem_access_prot(struct file *file, pgprot_t vma_prot) -{ - int id = get_id(file); -#ifdef pgprot_noncached - if (pmem[id].cached == 0 || file->f_flags & O_SYNC) - return pgprot_noncached(vma_prot); -#endif -#ifdef pgprot_ext_buffered - else if (pmem[id].buffered) - return pgprot_ext_buffered(vma_prot); -#endif - return vma_prot; -} - -static unsigned long pmem_start_addr(int id, struct pmem_data *data) -{ - if (pmem[id].no_allocator) - return PMEM_START_ADDR(id, 0); - else - return PMEM_START_ADDR(id, data->index); - -} - -static void *pmem_start_vaddr(int id, struct pmem_data *data) -{ - return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase; -} - -static unsigned long pmem_len(int id, struct pmem_data *data) -{ - if (pmem[id].no_allocator) - return data->index; - else - return PMEM_LEN(id, data->index); -} - -static int pmem_map_garbage(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - int i, garbage_pages = len >> PAGE_SHIFT; - - vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE; - for (i = 0; i < garbage_pages; i++) { - if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE), - pmem[id].garbage_pfn)) - return -EAGAIN; - } - return 0; -} - -static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - int garbage_pages; - DLOG("unmap offset %lx len %lx\n", offset, len); - - BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); - - garbage_pages = len >> PAGE_SHIFT; - zap_page_range(vma, vma->vm_start + offset, len, NULL); - pmem_map_garbage(id, vma, data, offset, len); - return 0; -} - -static int pmem_map_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - DLOG("map offset %lx len %lx\n", offset, len); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset)); - - if (io_remap_pfn_range(vma, vma->vm_start + offset, - (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT, - len, vma->vm_page_prot)) { - return -EAGAIN; - } - return 0; -} - -static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - /* hold the mm semp for the vma you are modifying when you call this */ - BUG_ON(!vma); - zap_page_range(vma, vma->vm_start + offset, len, NULL); - return pmem_map_pfn_range(id, vma, data, offset, len); -} - -static void pmem_vma_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct pmem_data *data = file->private_data; - int id = get_id(file); - /* this should never be called as we don't support copying pmem - * ranges via fork */ - BUG_ON(!has_allocation(file)); - down_write(&data->sem); - /* remap the garbage pages, forkers don't get access to the data */ - pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end); - up_write(&data->sem); -} - -static void pmem_vma_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct pmem_data *data = file->private_data; - - DLOG("current %u ppid %u file %p count %d\n", current->pid, - current->parent->pid, file, file_count(file)); - if (unlikely(!is_pmem_file(file) || !has_allocation(file))) { - printk(KERN_WARNING "pmem: something is very wrong, you are " - "closing a vm backing an allocation that doesn't " - "exist!\n"); - return; - } - down_write(&data->sem); - if (data->vma == vma) { - data->vma = NULL; - if ((data->flags & PMEM_FLAGS_CONNECTED) && - (data->flags & PMEM_FLAGS_SUBMAP)) - data->flags |= PMEM_FLAGS_UNSUBMAP; - } - /* the kernel is going to free this vma now anyway */ - up_write(&data->sem); -} - -static struct vm_operations_struct vm_ops = { - .open = pmem_vma_open, - .close = pmem_vma_close, -}; - -static int pmem_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct pmem_data *data; - int index; - unsigned long vma_size = vma->vm_end - vma->vm_start; - int ret = 0, id = get_id(file); - - if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) { -#if PMEM_DEBUG - printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned" - " and a multiple of pages_size.\n"); -#endif - return -EINVAL; - } - - data = (struct pmem_data *)file->private_data; - down_write(&data->sem); - /* check this file isn't already mmaped, for submaps check this file - * has never been mmaped */ - if ((data->flags & PMEM_FLAGS_SUBMAP) || - (data->flags & PMEM_FLAGS_UNSUBMAP)) { -#if PMEM_DEBUG - printk(KERN_ERR "pmem: you can only mmap a pmem file once, " - "this file is already mmaped. %x\n", data->flags); -#endif - ret = -EINVAL; - goto error; - } - /* if file->private_data == unalloced, alloc*/ - if (data && data->index == -1) { - down_write(&pmem[id].bitmap_sem); - index = pmem_allocate(id, vma->vm_end - vma->vm_start); - up_write(&pmem[id].bitmap_sem); - data->index = index; - } - /* either no space was available or an error occured */ - if (!has_allocation(file)) { - ret = -EINVAL; - printk("pmem: could not find allocation for map.\n"); - goto error; - } - - if (pmem_len(id, data) < vma_size) { -#if PMEM_DEBUG - printk(KERN_WARNING "pmem: mmap size [%lu] does not match" - "size of backing region [%lu].\n", vma_size, - pmem_len(id, data)); -#endif - ret = -EINVAL; - goto error; - } - - vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT; - vma->vm_page_prot = pmem_access_prot(file, vma->vm_page_prot); - - if (data->flags & PMEM_FLAGS_CONNECTED) { - struct pmem_region_node *region_node; - struct list_head *elt; - if (pmem_map_garbage(id, vma, data, 0, vma_size)) { - printk("pmem: mmap failed in kernel!\n"); - ret = -EAGAIN; - goto error; - } - list_for_each(elt, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - DLOG("remapping file: %p %lx %lx\n", file, - region_node->region.offset, - region_node->region.len); - if (pmem_remap_pfn_range(id, vma, data, - region_node->region.offset, - region_node->region.len)) { - ret = -EAGAIN; - goto error; - } - } - data->flags |= PMEM_FLAGS_SUBMAP; - get_task_struct(current->group_leader); - data->task = current->group_leader; - data->vma = vma; -#if PMEM_DEBUG - data->pid = current->pid; -#endif - DLOG("submmapped file %p vma %p pid %u\n", file, vma, - current->pid); - } else { - if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) { - printk(KERN_INFO "pmem: mmap failed in kernel!\n"); - ret = -EAGAIN; - goto error; - } - data->flags |= PMEM_FLAGS_MASTERMAP; - data->pid = current->pid; - } - vma->vm_ops = &vm_ops; -error: - up_write(&data->sem); - return ret; -} - -/* the following are the api for accessing pmem regions by other drivers - * from inside the kernel */ -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *len) -{ - struct pmem_data *data; - if (!is_pmem_file(file) || !has_allocation(file)) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from invalid" - "file.\n"); -#endif - return -1; - } - data = (struct pmem_data *)file->private_data; - down_read(&data->sem); - if (data->vma) { - *start = data->vma->vm_start; - *len = data->vma->vm_end - data->vma->vm_start; - } else { - *start = 0; - *len = 0; - } - up_read(&data->sem); - return 0; -} - -int get_pmem_addr(struct file *file, unsigned long *start, - unsigned long *vstart, unsigned long *len) -{ - struct pmem_data *data; - int id; - - if (!is_pmem_file(file) || !has_allocation(file)) { - return -1; - } - - data = (struct pmem_data *)file->private_data; - if (data->index == -1) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from file with no " - "allocation.\n"); - return -1; -#endif - } - id = get_id(file); - - down_read(&data->sem); - *start = pmem_start_addr(id, data); - *len = pmem_len(id, data); - *vstart = (unsigned long)pmem_start_vaddr(id, data); - up_read(&data->sem); -#if PMEM_DEBUG - down_write(&data->sem); - data->ref++; - up_write(&data->sem); -#endif - return 0; -} - -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *len, struct file **filp) -{ - struct file *file; - - file = fget(fd); - if (unlikely(file == NULL)) { - printk(KERN_INFO "pmem: requested data from file descriptor " - "that doesn't exist."); - return -1; - } - - if (get_pmem_addr(file, start, vstart, len)) - goto end; - - if (filp) - *filp = file; - return 0; -end: - fput(file); - return -1; -} - -void put_pmem_file(struct file *file) -{ - struct pmem_data *data; - int id; - - if (!is_pmem_file(file)) - return; - id = get_id(file); - data = (struct pmem_data *)file->private_data; -#if PMEM_DEBUG - down_write(&data->sem); - if (data->ref == 0) { - printk("pmem: pmem_put > pmem_get %s (pid %d)\n", - pmem[id].dev.name, data->pid); - BUG(); - } - data->ref--; - up_write(&data->sem); -#endif - fput(file); -} - -void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) -{ - struct pmem_data *data; - int id; - void *vaddr; - struct pmem_region_node *region_node; - struct list_head *elt; - void *flush_start, *flush_end; - - if (!is_pmem_file(file) || !has_allocation(file)) { - return; - } - - id = get_id(file); - data = (struct pmem_data *)file->private_data; - if (!pmem[id].cached || file->f_flags & O_SYNC) - return; - - down_read(&data->sem); - vaddr = pmem_start_vaddr(id, data); - /* if this isn't a submmapped file, flush the whole thing */ - if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { - dmac_flush_range(vaddr, vaddr + pmem_len(id, data)); - goto end; - } - /* otherwise, flush the region of the file we are drawing */ - list_for_each(elt, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, list); - if ((offset >= region_node->region.offset) && - ((offset + len) <= (region_node->region.offset + - region_node->region.len))) { - flush_start = vaddr + region_node->region.offset; - flush_end = flush_start + region_node->region.len; - dmac_flush_range(flush_start, flush_end); - break; - } - } -end: - up_read(&data->sem); -} - -static int pmem_connect(unsigned long connect, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_data *src_data; - struct file *src_file; - int ret = 0, put_needed; - - down_write(&data->sem); - /* retrieve the src file and check it is a pmem file with an alloc */ - src_file = fget_light(connect, &put_needed); - DLOG("connect %p to %p\n", file, src_file); - if (!src_file) { - printk("pmem: src file not found!\n"); - ret = -EINVAL; - goto err_no_file; - } - if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) { - printk(KERN_INFO "pmem: src file is not a pmem file or has no " - "alloc!\n"); - ret = -EINVAL; - goto err_bad_file; - } - src_data = (struct pmem_data *)src_file->private_data; - - if (has_allocation(file) && (data->index != src_data->index)) { - printk("pmem: file is already mapped but doesn't match this" - " src_file!\n"); - ret = -EINVAL; - goto err_bad_file; - } - data->index = src_data->index; - data->flags |= PMEM_FLAGS_CONNECTED; - data->master_fd = connect; - data->master_file = src_file; - -err_bad_file: - fput_light(src_file, put_needed); -err_no_file: - up_write(&data->sem); - return ret; -} - -static void pmem_unlock_data_and_mm(struct pmem_data *data, - struct mm_struct *mm) -{ - up_write(&data->sem); - if (mm != NULL) { - up_write(&mm->mmap_sem); - mmput(mm); - } -} - -static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, - struct mm_struct **locked_mm) -{ - int ret = 0; - struct mm_struct *mm = NULL; - *locked_mm = NULL; -lock_mm: - down_read(&data->sem); - if (PMEM_IS_SUBMAP(data)) { - mm = get_task_mm(data->task); - if (!mm) { -#if PMEM_DEBUG - printk("pmem: can't remap task is gone!\n"); -#endif - up_read(&data->sem); - return -1; - } - } - up_read(&data->sem); - - if (mm) - down_write(&mm->mmap_sem); - - down_write(&data->sem); - /* check that the file didn't get mmaped before we could take the - * data sem, this should be safe b/c you can only submap each file - * once */ - if (PMEM_IS_SUBMAP(data) && !mm) { - pmem_unlock_data_and_mm(data, mm); - up_write(&data->sem); - goto lock_mm; - } - /* now check that vma.mm is still there, it could have been - * deleted by vma_close before we could get the data->sem */ - if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) { - /* might as well release this */ - if (data->flags & PMEM_FLAGS_SUBMAP) { - put_task_struct(data->task); - data->task = NULL; - /* lower the submap flag to show the mm is gone */ - data->flags &= ~(PMEM_FLAGS_SUBMAP); - } - pmem_unlock_data_and_mm(data, mm); - return -1; - } - *locked_mm = mm; - return ret; -} - -int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) -{ - int ret; - struct pmem_region_node *region_node; - struct mm_struct *mm = NULL; - struct list_head *elt, *elt2; - int id = get_id(file); - struct pmem_data *data = (struct pmem_data *)file->private_data; - - /* pmem region must be aligned on a page boundry */ - if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) || - !PMEM_IS_PAGE_ALIGNED(region->len))) { -#if PMEM_DEBUG - printk("pmem: request for unaligned pmem suballocation " - "%lx %lx\n", region->offset, region->len); -#endif - return -EINVAL; - } - - /* if userspace requests a region of len 0, there's nothing to do */ - if (region->len == 0) - return 0; - - /* lock the mm and data */ - ret = pmem_lock_data_and_mm(file, data, &mm); - if (ret) - return 0; - - /* only the owner of the master file can remap the client fds - * that back in it */ - if (!is_master_owner(file)) { -#if PMEM_DEBUG - printk("pmem: remap requested from non-master process\n"); -#endif - ret = -EINVAL; - goto err; - } - - /* check that the requested range is within the src allocation */ - if (unlikely((region->offset > pmem_len(id, data)) || - (region->len > pmem_len(id, data)) || - (region->offset + region->len > pmem_len(id, data)))) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n"); -#endif - ret = -EINVAL; - goto err; - } - - if (operation == PMEM_MAP) { - region_node = kmalloc(sizeof(struct pmem_region_node), - GFP_KERNEL); - if (!region_node) { - ret = -ENOMEM; -#if PMEM_DEBUG - printk(KERN_INFO "No space to allocate metadata!"); -#endif - goto err; - } - region_node->region = *region; - list_add(®ion_node->list, &data->region_list); - } else if (operation == PMEM_UNMAP) { - int found = 0; - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - if (region->len == 0 || - (region_node->region.offset == region->offset && - region_node->region.len == region->len)) { - list_del(elt); - kfree(region_node); - found = 1; - } - } - if (!found) { -#if PMEM_DEBUG - printk("pmem: Unmap region does not map any mapped " - "region!"); -#endif - ret = -EINVAL; - goto err; - } - } - - if (data->vma && PMEM_IS_SUBMAP(data)) { - if (operation == PMEM_MAP) - ret = pmem_remap_pfn_range(id, data->vma, data, - region->offset, region->len); - else if (operation == PMEM_UNMAP) - ret = pmem_unmap_pfn_range(id, data->vma, data, - region->offset, region->len); - } - -err: - pmem_unlock_data_and_mm(data, mm); - return ret; -} - -static void pmem_revoke(struct file *file, struct pmem_data *data) -{ - struct pmem_region_node *region_node; - struct list_head *elt, *elt2; - struct mm_struct *mm = NULL; - int id = get_id(file); - int ret = 0; - - data->master_file = NULL; - ret = pmem_lock_data_and_mm(file, data, &mm); - /* if lock_data_and_mm fails either the task that mapped the fd, or - * the vma that mapped it have already gone away, nothing more - * needs to be done */ - if (ret) - return; - /* unmap everything */ - /* delete the regions and region list nothing is mapped any more */ - if (data->vma) - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - pmem_unmap_pfn_range(id, data->vma, data, - region_node->region.offset, - region_node->region.len); - list_del(elt); - kfree(region_node); - } - /* delete the master file */ - pmem_unlock_data_and_mm(data, mm); -} - -static void pmem_get_size(struct pmem_region *region, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - int id = get_id(file); - - if (!has_allocation(file)) { - region->offset = 0; - region->len = 0; - return; - } else { - region->offset = pmem_start_addr(id, data); - region->len = pmem_len(id, data); - } - DLOG("offset %lx len %lx\n", region->offset, region->len); -} - - -static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct pmem_data *data; - int id = get_id(file); - - switch (cmd) { - case PMEM_GET_PHYS: - { - struct pmem_region region; - DLOG("get_phys\n"); - if (!has_allocation(file)) { - region.offset = 0; - region.len = 0; - } else { - data = (struct pmem_data *)file->private_data; - region.offset = pmem_start_addr(id, data); - region.len = pmem_len(id, data); - } - printk(KERN_INFO "pmem: request for physical address of pmem region " - "from process %d.\n", current->pid); - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_MAP: - { - struct pmem_region region; - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - data = (struct pmem_data *)file->private_data; - return pmem_remap(®ion, file, PMEM_MAP); - } - break; - case PMEM_UNMAP: - { - struct pmem_region region; - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - data = (struct pmem_data *)file->private_data; - return pmem_remap(®ion, file, PMEM_UNMAP); - break; - } - case PMEM_GET_SIZE: - { - struct pmem_region region; - DLOG("get_size\n"); - pmem_get_size(®ion, file); - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_GET_TOTAL_SIZE: - { - struct pmem_region region; - DLOG("get total size\n"); - region.offset = 0; - get_id(file); - region.len = pmem[id].size; - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_ALLOCATE: - { - if (has_allocation(file)) - return -EINVAL; - data = (struct pmem_data *)file->private_data; - data->index = pmem_allocate(id, arg); - break; - } - case PMEM_CONNECT: - DLOG("connect\n"); - return pmem_connect(arg, file); - break; - case PMEM_CACHE_FLUSH: - { - struct pmem_region region; - DLOG("flush\n"); - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - flush_pmem_file(file, region.offset, region.len); - break; - } - default: - if (pmem[id].ioctl) - return pmem[id].ioctl(file, cmd, arg); - return -EINVAL; - } - return 0; -} - -#if PMEM_DEBUG -static ssize_t debug_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t debug_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct list_head *elt, *elt2; - struct pmem_data *data; - struct pmem_region_node *region_node; - int id = (int)file->private_data; - const int debug_bufmax = 4096; - static char buffer[4096]; - int n = 0; - - DLOG("debug open\n"); - n = scnprintf(buffer, debug_bufmax, - "pid #: mapped regions (offset, len) (offset,len)...\n"); - - mutex_lock(&pmem[id].data_list_lock); - list_for_each(elt, &pmem[id].data_list) { - data = list_entry(elt, struct pmem_data, list); - down_read(&data->sem); - n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:", - data->pid); - list_for_each(elt2, &data->region_list) { - region_node = list_entry(elt2, struct pmem_region_node, - list); - n += scnprintf(buffer + n, debug_bufmax - n, - "(%lx,%lx) ", - region_node->region.offset, - region_node->region.len); - } - n += scnprintf(buffer + n, debug_bufmax - n, "\n"); - up_read(&data->sem); - } - mutex_unlock(&pmem[id].data_list_lock); - - n++; - buffer[n] = 0; - return simple_read_from_buffer(buf, count, ppos, buffer, n); -} - -static struct file_operations debug_fops = { - .read = debug_read, - .open = debug_open, -}; -#endif - -#if 0 -static struct miscdevice pmem_dev = { - .name = "pmem", - .fops = &pmem_fops, -}; -#endif - -int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) -{ - int err = 0; - int i, index = 0; - int id = id_count; - id_count++; - - pmem[id].no_allocator = pdata->no_allocator; - pmem[id].cached = pdata->cached; - pmem[id].buffered = pdata->buffered; - pmem[id].base = pdata->start; - pmem[id].size = pdata->size; - pmem[id].ioctl = ioctl; - pmem[id].release = release; - init_rwsem(&pmem[id].bitmap_sem); - mutex_init(&pmem[id].data_list_lock); - INIT_LIST_HEAD(&pmem[id].data_list); - pmem[id].dev.name = pdata->name; - pmem[id].dev.minor = id; - pmem[id].dev.fops = &pmem_fops; - printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached); - - err = misc_register(&pmem[id].dev); - if (err) { - printk(KERN_ALERT "Unable to register pmem driver!\n"); - goto err_cant_register_device; - } - pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC; - - pmem[id].bitmap = kmalloc(pmem[id].num_entries * - sizeof(struct pmem_bits), GFP_KERNEL); - if (!pmem[id].bitmap) - goto err_no_mem_for_metadata; - - memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) * - pmem[id].num_entries); - - for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) { - if ((pmem[id].num_entries) & 1<name, S_IFREG | S_IRUGO, NULL, (void *)id, - &debug_fops); -#endif - return 0; -error_cant_remap: - kfree(pmem[id].bitmap); -err_no_mem_for_metadata: - misc_deregister(&pmem[id].dev); -err_cant_register_device: - return -1; -} - -static int pmem_probe(struct platform_device *pdev) -{ - struct android_pmem_platform_data *pdata; - - if (!pdev || !pdev->dev.platform_data) { - printk(KERN_ALERT "Unable to probe pmem!\n"); - return -1; - } - pdata = pdev->dev.platform_data; - return pmem_setup(pdata, NULL, NULL); -} - - -static int pmem_remove(struct platform_device *pdev) -{ - int id = pdev->id; - __free_page(pfn_to_page(pmem[id].garbage_pfn)); - misc_deregister(&pmem[id].dev); - return 0; -} - -static struct platform_driver pmem_driver = { - .probe = pmem_probe, - .remove = pmem_remove, - .driver = { .name = "android_pmem" } -}; - - -static int __init pmem_init(void) -{ - return platform_driver_register(&pmem_driver); -} - -static void __exit pmem_exit(void) -{ - platform_driver_unregister(&pmem_driver); -} - -module_init(pmem_init); -module_exit(pmem_exit); - -- cgit v0.10.2 From bd1eff9741af27378b241b347041c724bb28e857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 1 Feb 2012 15:29:13 -0800 Subject: Staging: android: binder: Fix crashes when sharing a binder file between processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opening the binder driver and sharing the file returned with other processes (e.g. by calling fork) can crash the kernel. Prevent these crashes with the following changes: - Add a mutex to protect against two processes mmapping the same binder_proc. - After locking mmap_sem, check that the vma we want to access (still) points to the same mm_struct. - Use proc->tsk instead of current to get the files struct since this is where we get the rlimit from. Signed-off-by: Arve HjønnevÃ¥g Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 48cf27c..f0b7e66 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -38,6 +38,7 @@ static DEFINE_MUTEX(binder_lock); static DEFINE_MUTEX(binder_deferred_lock); +static DEFINE_MUTEX(binder_mmap_lock); static HLIST_HEAD(binder_procs); static HLIST_HEAD(binder_deferred_list); @@ -632,6 +633,11 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; + if (vma && mm != vma->vm_mm) { + pr_err("binder: %d: vma mm and task mm mismatch\n", + proc->pid); + vma = NULL; + } } if (allocate == 0) @@ -2802,6 +2808,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; + mutex_lock(&binder_mmap_lock); if (proc->buffer) { ret = -EBUSY; failure_string = "already mapped"; @@ -2816,6 +2823,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } proc->buffer = area->addr; proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; + mutex_unlock(&binder_mmap_lock); #ifdef CONFIG_CPU_CACHE_VIPT if (cache_is_vipt_aliasing()) { @@ -2848,7 +2856,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) binder_insert_free_buffer(proc, buffer); proc->free_async_space = proc->buffer_size / 2; barrier(); - proc->files = get_files_struct(current); + proc->files = get_files_struct(proc->tsk); proc->vma = vma; /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", @@ -2859,10 +2867,12 @@ err_alloc_small_buf_failed: kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: + mutex_lock(&binder_mmap_lock); vfree(proc->buffer); proc->buffer = NULL; err_get_vm_area_failed: err_already_mapped: + mutex_unlock(&binder_mmap_lock); err_bad_arg: printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); -- cgit v0.10.2 From a9d3c9e3c575ee09c905e07ae7cc1d52e2548d05 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 1 Feb 2012 19:04:02 -0800 Subject: staging: fix go7007-usb license Add MODULE_LICENSE() for this source module to fix build warning: WARNING: modpost: missing MODULE_LICENSE() in drivers/staging/media/go7007/go7007-usb.o see include/linux/module.h for more information Signed-off-by: Randy Dunlap Cc: Ross Cohen Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 70e006b..5443e25 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1279,3 +1279,4 @@ static struct usb_driver go7007_usb_driver = { }; module_usb_driver(go7007_usb_driver); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 2a4830110b90deb4ee99b1ab8c8ebb120f27c0c8 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Fri, 30 Dec 2011 10:42:15 -0600 Subject: staging: zcache: fix serialization bug in zv stats In a multithreaded workload, the zv_curr_dist_counts and zv_cumul_dist_counts statistics are being corrupted because the increments and decrements in zv_create and zv_free are not atomic. This patch converts these statistics and their corresponding increments/decrements/reads to atomic operations. Signed-off-by: Seth Jennings Acked-by: Dan Magenheimer Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c index ce07087..ef7c52b 100644 --- a/drivers/staging/zcache/zcache-main.c +++ b/drivers/staging/zcache/zcache-main.c @@ -654,8 +654,8 @@ static unsigned int zv_max_zsize = (PAGE_SIZE / 8) * 7; */ static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5; -static unsigned long zv_curr_dist_counts[NCHUNKS]; -static unsigned long zv_cumul_dist_counts[NCHUNKS]; +static atomic_t zv_curr_dist_counts[NCHUNKS]; +static atomic_t zv_cumul_dist_counts[NCHUNKS]; static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, struct tmem_oid *oid, uint32_t index, @@ -674,8 +674,8 @@ static struct zv_hdr *zv_create(struct xv_pool *xvpool, uint32_t pool_id, &page, &offset, ZCACHE_GFP_MASK); if (unlikely(ret)) goto out; - zv_curr_dist_counts[chunks]++; - zv_cumul_dist_counts[chunks]++; + atomic_inc(&zv_curr_dist_counts[chunks]); + atomic_inc(&zv_cumul_dist_counts[chunks]); zv = kmap_atomic(page, KM_USER0) + offset; zv->index = index; zv->oid = *oid; @@ -697,7 +697,7 @@ static void zv_free(struct xv_pool *xvpool, struct zv_hdr *zv) ASSERT_SENTINEL(zv, ZVH); BUG_ON(chunks >= NCHUNKS); - zv_curr_dist_counts[chunks]--; + atomic_dec(&zv_curr_dist_counts[chunks]); size -= sizeof(*zv); BUG_ON(size == 0); INVERT_SENTINEL(zv, ZVH); @@ -737,7 +737,7 @@ static int zv_curr_dist_counts_show(char *buf) char *p = buf; for (i = 0; i < NCHUNKS; i++) { - n = zv_curr_dist_counts[i]; + n = atomic_read(&zv_curr_dist_counts[i]); p += sprintf(p, "%lu ", n); chunks += n; sum_total_chunks += i * n; @@ -753,7 +753,7 @@ static int zv_cumul_dist_counts_show(char *buf) char *p = buf; for (i = 0; i < NCHUNKS; i++) { - n = zv_cumul_dist_counts[i]; + n = atomic_read(&zv_cumul_dist_counts[i]); p += sprintf(p, "%lu ", n); chunks += n; sum_total_chunks += i * n; -- cgit v0.10.2 From 9545f86e3a3b6fac508dd43638d633d1a1567c26 Mon Sep 17 00:00:00 2001 From: Ameya Palande Date: Tue, 10 Jan 2012 09:00:58 -0800 Subject: MAINTAINERS: staging: iio: add iio information Signed-off-by: Ameya Palande Signed-off-by: Greg Kroah-Hartman diff --git a/MAINTAINERS b/MAINTAINERS index a1fce9a..3146731 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3324,6 +3324,12 @@ S: Maintained F: net/ieee802154/ F: drivers/ieee802154/ +IIO SUBSYSTEM AND DRIVERS +M: Jonathan Cameron +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/staging/iio/ + IKANOS/ADI EAGLE ADSL USB DRIVER M: Matthieu Castet M: Stanislaw Gruszka -- cgit v0.10.2 From e5d7965f88a3755b2d0c54768a17032ab3a72819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 13 Jan 2012 22:21:25 +0400 Subject: staging: android: lowmemorykiller: Don't wait more than one second for a process to die MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a process forked and the child process was killed by the lowmemorykiller, the lowmemory killer would be disabled until the parent process reaped the child or it died itself. Signed-off-by: Arve HjønnevÃ¥g Signed-off-by: Anton Vorontsov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 2d8d2b7..efc7dc1 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -54,6 +54,7 @@ static size_t lowmem_minfree[6] = { static int lowmem_minfree_size = 4; static struct task_struct *lowmem_deathpending; +static unsigned long lowmem_deathpending_timeout; #define lowmem_print(level, x...) \ do { \ @@ -103,7 +104,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) * Note: Currently you need CONFIG_PROFILING * for this to work correctly. */ - if (lowmem_deathpending) + if (lowmem_deathpending && + time_before_eq(jiffies, lowmem_deathpending_timeout)) return 0; if (lowmem_adj_size < array_size) @@ -178,6 +180,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) */ #ifdef CONFIG_PROFILING lowmem_deathpending = selected; + lowmem_deathpending_timeout = jiffies + HZ; task_handoff_register(&task_nb); #endif force_sig(SIGKILL, selected); -- cgit v0.10.2 From 5a63177a6967698df58c6c93d0db3a3586e0b028 Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Mon, 30 Jan 2012 19:20:17 -0600 Subject: staging: tidspbridge: fix bridge_open memory leaks There are two members of pr_ctxt allocated during bridge_open that are never freed resulting in memory leaks, these are stream_id and node_id, they are now freed on release of the handle (bridge_release) right before freeing pr_ctxt. Error path for bridge_open was also fixed since the same variables could result in memory leaking due to missing handling of failure scenarios. While at it, the indentation changes were introduced to avoid interleaved goto statements inside big if blocks. Signed-off-by: Omar Ramirez Luna Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/tidspbridge/rmgr/drv_interface.c b/drivers/staging/tidspbridge/rmgr/drv_interface.c index 76cfc6e..8bac511 100644 --- a/drivers/staging/tidspbridge/rmgr/drv_interface.c +++ b/drivers/staging/tidspbridge/rmgr/drv_interface.c @@ -500,35 +500,42 @@ static int bridge_open(struct inode *ip, struct file *filp) } #endif pr_ctxt = kzalloc(sizeof(struct process_context), GFP_KERNEL); - if (pr_ctxt) { - pr_ctxt->res_state = PROC_RES_ALLOCATED; - spin_lock_init(&pr_ctxt->dmm_map_lock); - INIT_LIST_HEAD(&pr_ctxt->dmm_map_list); - spin_lock_init(&pr_ctxt->dmm_rsv_lock); - INIT_LIST_HEAD(&pr_ctxt->dmm_rsv_list); - - pr_ctxt->node_id = kzalloc(sizeof(struct idr), GFP_KERNEL); - if (pr_ctxt->node_id) { - idr_init(pr_ctxt->node_id); - } else { - status = -ENOMEM; - goto err; - } + if (!pr_ctxt) + return -ENOMEM; + + pr_ctxt->res_state = PROC_RES_ALLOCATED; + spin_lock_init(&pr_ctxt->dmm_map_lock); + INIT_LIST_HEAD(&pr_ctxt->dmm_map_list); + spin_lock_init(&pr_ctxt->dmm_rsv_lock); + INIT_LIST_HEAD(&pr_ctxt->dmm_rsv_list); - pr_ctxt->stream_id = kzalloc(sizeof(struct idr), GFP_KERNEL); - if (pr_ctxt->stream_id) - idr_init(pr_ctxt->stream_id); - else - status = -ENOMEM; - } else { + pr_ctxt->node_id = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (!pr_ctxt->node_id) { status = -ENOMEM; + goto err1; } -err: + + idr_init(pr_ctxt->node_id); + + pr_ctxt->stream_id = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (!pr_ctxt->stream_id) { + status = -ENOMEM; + goto err2; + } + + idr_init(pr_ctxt->stream_id); + filp->private_data = pr_ctxt; + #ifdef CONFIG_TIDSPBRIDGE_RECOVERY - if (!status) - atomic_inc(&bridge_cref); + atomic_inc(&bridge_cref); #endif + return 0; + +err2: + kfree(pr_ctxt->node_id); +err1: + kfree(pr_ctxt); return status; } @@ -550,6 +557,8 @@ static int bridge_release(struct inode *ip, struct file *filp) flush_signals(current); drv_remove_all_resources(pr_ctxt); proc_detach(pr_ctxt); + kfree(pr_ctxt->node_id); + kfree(pr_ctxt->stream_id); kfree(pr_ctxt); filp->private_data = NULL; -- cgit v0.10.2 From 44c5435064b5e6182167b40a7f259969dbcaf1b8 Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Mon, 30 Jan 2012 19:20:18 -0600 Subject: staging: tidspbridge: fix incorrect free to drv_datap This structure is still used after it has been freed, since it is being allocated in probe, calls to free it have been moved to module's remove routine. This should fix the follwoing messages when attempting to remove the module: drv_get_first_dev_extension: Failed to retrieve the object handle drv_get_first_dev_extension: Failed to retrieve the object handle drv_destroy: Failed to store DRV object mgr_destroy: Failed to store MGR object Signed-off-by: Omar Ramirez Luna Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c index e1c4492..dde559d 100644 --- a/drivers/staging/tidspbridge/core/tiomap3430.c +++ b/drivers/staging/tidspbridge/core/tiomap3430.c @@ -1046,8 +1046,6 @@ static int bridge_dev_destroy(struct bridge_dev_context *dev_ctxt) /* Free the driver's device context: */ kfree(drv_datap->base_img); - kfree(drv_datap); - dev_set_drvdata(bridge, NULL); kfree((void *)dev_ctxt); return status; } diff --git a/drivers/staging/tidspbridge/rmgr/drv_interface.c b/drivers/staging/tidspbridge/rmgr/drv_interface.c index 8bac511..385740b 100644 --- a/drivers/staging/tidspbridge/rmgr/drv_interface.c +++ b/drivers/staging/tidspbridge/rmgr/drv_interface.c @@ -410,6 +410,9 @@ static int __devexit omap34_xx_bridge_remove(struct platform_device *pdev) DBC_ASSERT(ret == true); } + kfree(drv_datap); + dev_set_drvdata(bridge, NULL); + func_cont: mem_ext_phys_pool_release(); -- cgit v0.10.2 From 4c0029f01f4009d6ab149230026c9716bde96283 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Fri, 3 Feb 2012 16:05:13 -0600 Subject: staging: r8712u: Fix problem when CONFIG_R8712_AP is set When this driver was upgraded to the vendor 20100831 version in commit 93c55dda092c7 et al,, one listhead initialization was missed. This broke complete operation of the driver whenever AP mode was enabled. This patch fixes https://bugs.archlinux.org/task/27996. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/rtl8712/rtl871x_sta_mgt.c b/drivers/staging/rtl8712/rtl871x_sta_mgt.c index 64f5696..81bde80 100644 --- a/drivers/staging/rtl8712/rtl871x_sta_mgt.c +++ b/drivers/staging/rtl8712/rtl871x_sta_mgt.c @@ -43,6 +43,7 @@ static void _init_stainfo(struct sta_info *psta) _r8712_init_sta_xmit_priv(&psta->sta_xmitpriv); _r8712_init_sta_recv_priv(&psta->sta_recvpriv); #ifdef CONFIG_R8712_AP + _init_listhead(&psta->asoc_list); _init_listhead(&psta->auth_list); #endif } -- cgit v0.10.2 From 737912e11bf5bd4874acc771d8511a6eab891fc5 Mon Sep 17 00:00:00 2001 From: Bart Westgeest Date: Wed, 25 Jan 2012 13:46:32 -0500 Subject: staging: usbip: fix to prevent potentially using uninitialized spinlock The stub_probe function can be called as soon as the stub_driver is registered. This can lead to the busid_table_lock being used before it is initialized. Moved calling the init_busid_table function (which initalizes this spinlock) to be called earlier in the init function to prevent this from happening. Signed-off-by: Bart Westgeest Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/usbip/stub_main.c b/drivers/staging/usbip/stub_main.c index 2d63178..705a9e5 100644 --- a/drivers/staging/usbip/stub_main.c +++ b/drivers/staging/usbip/stub_main.c @@ -246,8 +246,9 @@ static int __init usbip_host_init(void) { int ret; - stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); + init_busid_table(); + stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); if (!stub_priv_cache) { pr_err("kmem_cache_create failed\n"); return -ENOMEM; @@ -266,7 +267,6 @@ static int __init usbip_host_init(void) goto err_create_file; } - init_busid_table(); pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); return ret; -- cgit v0.10.2 From 8c213fa59199f9673d66970d6940fa093186642f Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 5 Feb 2012 21:12:26 -0600 Subject: staging: r8712u: Use asynchronous firmware loading In https://bugs.archlinux.org/task/27996, failure of driver r8712u is reported, with a timeout during module loading due to synchronous loading of the firmware. The code now uses request_firmware_nowait(). Signed-off-by: Larry Finger Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/rtl8712/drv_types.h b/drivers/staging/rtl8712/drv_types.h index 9b5d771..ed85b44 100644 --- a/drivers/staging/rtl8712/drv_types.h +++ b/drivers/staging/rtl8712/drv_types.h @@ -37,6 +37,8 @@ struct _adapter; #include "wlan_bssdef.h" #include "rtl8712_spec.h" #include "rtl8712_hal.h" +#include +#include enum _NIC_VERSION { RTL8711_NIC, @@ -168,6 +170,7 @@ struct _adapter { s32 bSurpriseRemoved; u32 IsrContent; u32 ImrContent; + bool fw_found; u8 EepromAddressSize; u8 hw_init_completed; struct task_struct *cmdThread; @@ -184,6 +187,10 @@ struct _adapter { _workitem wkFilterRxFF0; u8 blnEnableRxFF0Filter; spinlock_t lockRxFF0Filter; + const struct firmware *fw; + struct usb_interface *pusb_intf; + struct mutex mutex_start; + struct completion rtl8712_fw_ready; }; static inline u8 *myid(struct eeprom_priv *peepriv) diff --git a/drivers/staging/rtl8712/hal_init.c b/drivers/staging/rtl8712/hal_init.c index d0029aa..cc893c0 100644 --- a/drivers/staging/rtl8712/hal_init.c +++ b/drivers/staging/rtl8712/hal_init.c @@ -42,29 +42,56 @@ #define FWBUFF_ALIGN_SZ 512 #define MAX_DUMP_FWSZ 49152 /*default = 49152 (48k)*/ -static u32 rtl871x_open_fw(struct _adapter *padapter, void **pphfwfile_hdl, - const u8 **ppmappedfw) +static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context) { + struct _adapter *padapter = context; + + complete(&padapter->rtl8712_fw_ready); + if (!firmware) { + struct usb_device *udev = padapter->dvobjpriv.pusbdev; + struct usb_interface *pusb_intf = padapter->pusb_intf; + printk(KERN_ERR "r8712u: Firmware request failed\n"); + padapter->fw_found = false; + usb_put_dev(udev); + usb_set_intfdata(pusb_intf, NULL); + return; + } + padapter->fw = firmware; + padapter->fw_found = true; + /* firmware available - start netdev */ + register_netdev(padapter->pnetdev); +} + +static const char firmware_file[] = "rtlwifi/rtl8712u.bin"; + +int rtl871x_load_fw(struct _adapter *padapter) +{ + struct device *dev = &padapter->dvobjpriv.pusbdev->dev; int rc; - const char firmware_file[] = "rtlwifi/rtl8712u.bin"; - const struct firmware **praw = (const struct firmware **) - (pphfwfile_hdl); - struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *) - (&padapter->dvobjpriv); - struct usb_device *pusbdev = pdvobjpriv->pusbdev; + init_completion(&padapter->rtl8712_fw_ready); printk(KERN_INFO "r8712u: Loading firmware from \"%s\"\n", firmware_file); - rc = request_firmware(praw, firmware_file, &pusbdev->dev); - if (rc < 0) { - printk(KERN_ERR "r8712u: Unable to load firmware\n"); - printk(KERN_ERR "r8712u: Install latest linux-firmware\n"); + rc = request_firmware_nowait(THIS_MODULE, 1, firmware_file, dev, + GFP_KERNEL, padapter, rtl871x_load_fw_cb); + if (rc) + printk(KERN_ERR "r8712u: Firmware request error %d\n", rc); + return rc; +} +MODULE_FIRMWARE("rtlwifi/rtl8712u.bin"); + +static u32 rtl871x_open_fw(struct _adapter *padapter, const u8 **ppmappedfw) +{ + const struct firmware **praw = &padapter->fw; + + if (padapter->fw->size > 200000) { + printk(KERN_ERR "r8172u: Badfw->size of %d\n", + (int)padapter->fw->size); return 0; } *ppmappedfw = (u8 *)((*praw)->data); return (*praw)->size; } -MODULE_FIRMWARE("rtlwifi/rtl8712u.bin"); static void fill_fwpriv(struct _adapter *padapter, struct fw_priv *pfwpriv) { @@ -142,18 +169,17 @@ static u8 rtl8712_dl_fw(struct _adapter *padapter) uint dump_imem_sz, imem_sz, dump_emem_sz, emem_sz; /* max = 49152; */ struct fw_hdr fwhdr; u32 ulfilelength; /* FW file size */ - void *phfwfile_hdl = NULL; const u8 *pmappedfw = NULL; u8 *ptmpchar = NULL, *ppayload, *ptr; struct tx_desc *ptx_desc; u32 txdscp_sz = sizeof(struct tx_desc); u8 ret = _FAIL; - ulfilelength = rtl871x_open_fw(padapter, &phfwfile_hdl, &pmappedfw); + ulfilelength = rtl871x_open_fw(padapter, &pmappedfw); if (pmappedfw && (ulfilelength > 0)) { update_fwhdr(&fwhdr, pmappedfw); if (chk_fwhdr(&fwhdr, ulfilelength) == _FAIL) - goto firmware_rel; + return ret; fill_fwpriv(padapter, &fwhdr.fwpriv); /* firmware check ok */ maxlen = (fwhdr.img_IMEM_size > fwhdr.img_SRAM_size) ? @@ -161,7 +187,7 @@ static u8 rtl8712_dl_fw(struct _adapter *padapter) maxlen += txdscp_sz; ptmpchar = _malloc(maxlen + FWBUFF_ALIGN_SZ); if (ptmpchar == NULL) - goto firmware_rel; + return ret; ptx_desc = (struct tx_desc *)(ptmpchar + FWBUFF_ALIGN_SZ - ((addr_t)(ptmpchar) & (FWBUFF_ALIGN_SZ - 1))); @@ -297,8 +323,6 @@ static u8 rtl8712_dl_fw(struct _adapter *padapter) exit_fail: kfree(ptmpchar); -firmware_rel: - release_firmware((struct firmware *)phfwfile_hdl); return ret; } diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c index 9a75c6d..98a3d68 100644 --- a/drivers/staging/rtl8712/os_intfs.c +++ b/drivers/staging/rtl8712/os_intfs.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "osdep_service.h" #include "drv_types.h" #include "xmit_osdep.h" @@ -264,12 +265,12 @@ static void start_drv_timers(struct _adapter *padapter) void r8712_stop_drv_timers(struct _adapter *padapter) { _cancel_timer_ex(&padapter->mlmepriv.assoc_timer); - _cancel_timer_ex(&padapter->mlmepriv.sitesurveyctrl. - sitesurvey_ctrl_timer); _cancel_timer_ex(&padapter->securitypriv.tkip_timer); _cancel_timer_ex(&padapter->mlmepriv.scan_to_timer); _cancel_timer_ex(&padapter->mlmepriv.dhcp_timer); _cancel_timer_ex(&padapter->mlmepriv.wdg_timer); + _cancel_timer_ex(&padapter->mlmepriv.sitesurveyctrl. + sitesurvey_ctrl_timer); } static u8 init_default_value(struct _adapter *padapter) @@ -347,7 +348,8 @@ u8 r8712_free_drv_sw(struct _adapter *padapter) r8712_free_mlme_priv(&padapter->mlmepriv); r8712_free_io_queue(padapter); _free_xmit_priv(&padapter->xmitpriv); - _r8712_free_sta_priv(&padapter->stapriv); + if (padapter->fw_found) + _r8712_free_sta_priv(&padapter->stapriv); _r8712_free_recv_priv(&padapter->recvpriv); mp871xdeinit(padapter); if (pnetdev) @@ -388,6 +390,7 @@ static int netdev_open(struct net_device *pnetdev) { struct _adapter *padapter = (struct _adapter *)netdev_priv(pnetdev); + mutex_lock(&padapter->mutex_start); if (padapter->bup == false) { padapter->bDriverStopped = false; padapter->bSurpriseRemoved = false; @@ -435,11 +438,13 @@ static int netdev_open(struct net_device *pnetdev) /* start driver mlme relation timer */ start_drv_timers(padapter); padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK); + mutex_unlock(&padapter->mutex_start); return 0; netdev_open_error: padapter->bup = false; netif_carrier_off(pnetdev); netif_stop_queue(pnetdev); + mutex_unlock(&padapter->mutex_start); return -1; } @@ -473,6 +478,9 @@ static int netdev_close(struct net_device *pnetdev) r8712_free_network_queue(padapter); /* The interface is no longer Up: */ padapter->bup = false; + release_firmware(padapter->fw); + /* never exit with a firmware callback pending */ + wait_for_completion(&padapter->rtl8712_fw_ready); return 0; } diff --git a/drivers/staging/rtl8712/rtl8712_hal.h b/drivers/staging/rtl8712/rtl8712_hal.h index 665e718..d19865a 100644 --- a/drivers/staging/rtl8712/rtl8712_hal.h +++ b/drivers/staging/rtl8712/rtl8712_hal.h @@ -145,5 +145,6 @@ struct hal_priv { }; uint rtl8712_hal_init(struct _adapter *padapter); +int rtl871x_load_fw(struct _adapter *padapter); #endif diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c index 8de0c80..9bade18 100644 --- a/drivers/staging/rtl8712/usb_intf.c +++ b/drivers/staging/rtl8712/usb_intf.c @@ -390,6 +390,7 @@ static int r871xu_drv_init(struct usb_interface *pusb_intf, pdvobjpriv = &padapter->dvobjpriv; pdvobjpriv->padapter = padapter; padapter->dvobjpriv.pusbdev = udev; + padapter->pusb_intf = pusb_intf; usb_set_intfdata(pusb_intf, pnetdev); SET_NETDEV_DEV(pnetdev, &pusb_intf->dev); /* step 2. */ @@ -596,10 +597,11 @@ static int r871xu_drv_init(struct usb_interface *pusb_intf, "%pM\n", mac); memcpy(pnetdev->dev_addr, mac, ETH_ALEN); } - /* step 6. Tell the network stack we exist */ - if (register_netdev(pnetdev) != 0) + /* step 6. Load the firmware asynchronously */ + if (rtl871x_load_fw(padapter)) goto error; spin_lock_init(&padapter->lockRxFF0Filter); + mutex_init(&padapter->mutex_start); return 0; error: usb_put_dev(udev); @@ -630,7 +632,8 @@ static void r871xu_dev_remove(struct usb_interface *pusb_intf) flush_scheduled_work(); udelay(1); /*Stop driver mlme relation timer */ - r8712_stop_drv_timers(padapter); + if (padapter->fw_found) + r8712_stop_drv_timers(padapter); r871x_dev_unload(padapter); r8712_free_drv_sw(padapter); } -- cgit v0.10.2 From 203209ef77e4d5f0ee729557b09770bce0c2d251 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 7 Feb 2012 09:13:27 +0400 Subject: staging: android/ram_console: Don't build on arches w/o ioremap This patch fixes UML build: CC drivers/staging/android/ram_console.o drivers/staging/android/ram_console.c: In function 'ram_console_driver_probe': drivers/staging/android/ram_console.c:358:2: error: implicit declaration of function 'ioremap' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors make[3]: *** [drivers/staging/android/ram_console.o] Error 1 Signed-off-by: Anton Vorontsov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 94cb2ac..fef3580 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -27,6 +27,7 @@ config ANDROID_LOGGER config ANDROID_RAM_CONSOLE bool "Android RAM buffer console" + depends on !S390 && !UML default n config ANDROID_RAM_CONSOLE_ENABLE_VERBOSE -- cgit v0.10.2 From c8a64268d187b2ace478d2147ce5c79658aca2d2 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 8 Feb 2012 09:05:42 -0500 Subject: m32r: relocate drivers back out of 8250 dir Commit 9bef3d4197379a995fa80f81950bbbf8d32e9e8b "serial: group all the 8250 related code together" inadvertently swept up the m32r driver in the move, because it had comments mentioning 8250 registers within it. However these are only there by nature of the driver being based off the 8250 source code -- the hardware itself does not actually have any relation to the original 8250 style UARTs. Reported-by: Jiri Slaby Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/8250/m32r_sio.c b/drivers/tty/serial/8250/m32r_sio.c deleted file mode 100644 index 94a6792..0000000 --- a/drivers/tty/serial/8250/m32r_sio.c +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * m32r_sio.c - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.c. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata - * - * 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. - */ - -/* - * A note about mapbase / membase - * - * mapbase is the physical address of the IO port. Currently, we don't - * support this very well, and it may well be dropped from this driver - * in future. As such, mapbase should be NULL. - * - * membase is an 'ioremapped' cookie. This is compatible with the old - * serial.c driver, and is currently the preferred form. - */ - -#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define PORT_M32R_BASE PORT_M32R_SIO -#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) -#define BAUD_RATE 115200 - -#include -#include "m32r_sio.h" -#include "m32r_sio_reg.h" - -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - -#define PASS_LIMIT 256 - -/* - * We default to IRQ0 for the "no irq" hack. Some - * machine types want others as well - they're free - * to redefine this in their header file. - */ -#define is_real_interrupt(irq) ((irq) != 0) - -#define BASE_BAUD 115200 - -/* Standard COM flags */ -#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) - -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ -#if defined(CONFIG_PLAT_USRV) - -#define SERIAL_PORT_DFNS \ - /* UART CLK PORT IRQ FLAGS */ \ - { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ - -#else /* !CONFIG_PLAT_USRV */ - -#if defined(CONFIG_SERIAL_M32R_PLDSIO) -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ - STD_COM_FLAGS }, /* ttyS0 */ -#else -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ - STD_COM_FLAGS }, /* ttyS0 */ -#endif - -#endif /* !CONFIG_PLAT_USRV */ - -static struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS -}; - -#define UART_NR ARRAY_SIZE(old_serial_port) - -struct uart_sio_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short rev; - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char lsr_break_flag; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); -}; - -struct irq_info { - spinlock_t lock; - struct list_head *head; -}; - -static struct irq_info irq_lists[NR_IRQS]; - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial_uart_config uart_config[] = { - [PORT_UNKNOWN] = { - .name = "unknown", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, - [PORT_INDEX(PORT_M32R_SIO)] = { - .name = "M32RSIO", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, -}; - -#ifdef CONFIG_SERIAL_M32R_PLDSIO - -#define __sio_in(x) inw((unsigned long)(x)) -#define __sio_out(v,x) outw((v),(unsigned long)(x)) - -static inline void sio_set_baud_rate(unsigned long baud) -{ - unsigned short sbaud; - sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1; - __sio_out(sbaud, PLD_ESIO0BAUR); -} - -static void sio_reset(void) -{ - unsigned short tmp; - - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0CR); - sio_set_baud_rate(BAUD_RATE); - __sio_out(0x0300, PLD_ESIO0CR); - __sio_out(0x0003, PLD_ESIO0CR); -} - -static void sio_init(void) -{ - unsigned short tmp; - - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0CR); - __sio_out(0x0300, PLD_ESIO0CR); - __sio_out(0x0003, PLD_ESIO0CR); -} - -static void sio_error(int *status) -{ - printk("SIO0 error[%04x]\n", *status); - do { - sio_init(); - } while ((*status = __sio_in(PLD_ESIO0CR)) != 3); -} - -#else /* not CONFIG_SERIAL_M32R_PLDSIO */ - -#define __sio_in(x) inl(x) -#define __sio_out(v,x) outl((v),(x)) - -static inline void sio_set_baud_rate(unsigned long baud) -{ - unsigned long i, j; - - i = boot_cpu_data.bus_clock / (baud * 16); - j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud; - i -= 1; - j = (j + 1) >> 1; - - __sio_out(i, M32R_SIO0_BAUR_PORTL); - __sio_out(j, M32R_SIO0_RBAUR_PORTL); -} - -static void sio_reset(void) -{ - __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */ - __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */ - __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */ - sio_set_baud_rate(BAUD_RATE); - __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL); - __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */ -} - -static void sio_init(void) -{ - unsigned int tmp; - - tmp = __sio_in(M32R_SIO0_RXB_PORTL); - tmp = __sio_in(M32R_SIO0_RXB_PORTL); - tmp = __sio_in(M32R_SIO0_STS_PORTL); - __sio_out(0x00000003, M32R_SIO0_CR_PORTL); -} - -static void sio_error(int *status) -{ - printk("SIO0 error[%04x]\n", *status); - do { - sio_init(); - } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3); -} - -#endif /* CONFIG_SERIAL_M32R_PLDSIO */ - -static unsigned int sio_in(struct uart_sio_port *up, int offset) -{ - return __sio_in(up->port.iobase + offset); -} - -static void sio_out(struct uart_sio_port *up, int offset, int value) -{ - __sio_out(value, up->port.iobase + offset); -} - -static unsigned int serial_in(struct uart_sio_port *up, int offset) -{ - if (!offset) - return 0; - - return __sio_in(offset); -} - -static void serial_out(struct uart_sio_port *up, int offset, int value) -{ - if (!offset) - return; - - __sio_out(value, offset); -} - -static void m32r_sio_stop_tx(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void m32r_sio_start_tx(struct uart_port *port) -{ -#ifdef CONFIG_SERIAL_M32R_PLDSIO - struct uart_sio_port *up = (struct uart_sio_port *)port; - struct circ_buf *xmit = &up->port.state->xmit; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - } - while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); -#else - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -#endif -} - -static void m32r_sio_stop_rx(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void m32r_sio_enable_ms(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void receive_chars(struct uart_sio_port *up, int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch; - unsigned char flag; - int max_count = 256; - - do { - ch = sio_in(up, SIORXB); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ingored. - */ - *status &= up->port.read_status_mask; - - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } - - if (*status & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); - - if (*status & UART_LSR_OE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - ignore_char: - *status = serial_in(up, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); -} - -static void transmit_chars(struct uart_sio_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { -#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */ - serial_out(up, UART_TX, up->port.x_char); -#endif - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - m32r_sio_stop_tx(&up->port); - return; - } - - count = up->port.fifosize; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - while (!(serial_in(up, UART_LSR) & UART_LSR_THRE)); - - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - DEBUG_INTR("THRE..."); - - if (uart_circ_empty(xmit)) - m32r_sio_stop_tx(&up->port); -} - -/* - * This handles the interrupt from one port. - */ -static inline void m32r_sio_handle_port(struct uart_sio_port *up, - unsigned int status) -{ - DEBUG_INTR("status = %x...", status); - - if (status & 0x04) - receive_chars(up, &status); - if (status & 0x01) - transmit_chars(up); -} - -/* - * This is the serial driver's interrupt routine. - * - * Arjan thinks the old way was overly complex, so it got simplified. - * Alan disagrees, saying that need the complexity to handle the weird - * nature of ISA shared interrupts. (This is a special exception.) - * - * In order to handle ISA shared interrupts properly, we need to check - * that all ports have been serviced, and therefore the ISA interrupt - * line has been de-asserted. - * - * This means we need to loop through all ports. checking that they - * don't have an interrupt pending. - */ -static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) -{ - struct irq_info *i = dev_id; - struct list_head *l, *end = NULL; - int pass_counter = 0; - - DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); - -#ifdef CONFIG_SERIAL_M32R_PLDSIO -// if (irq == PLD_IRQ_SIO0_SND) -// irq = PLD_IRQ_SIO0_RCV; -#else - if (irq == M32R_IRQ_SIO0_S) - irq = M32R_IRQ_SIO0_R; -#endif - - spin_lock(&i->lock); - - l = i->head; - do { - struct uart_sio_port *up; - unsigned int sts; - - up = list_entry(l, struct uart_sio_port, list); - - sts = sio_in(up, SIOSTS); - if (sts & 0x5) { - spin_lock(&up->port.lock); - m32r_sio_handle_port(up, sts); - spin_unlock(&up->port.lock); - - end = NULL; - } else if (end == NULL) - end = l; - - l = l->next; - - if (l == i->head && pass_counter++ > PASS_LIMIT) { - if (sts & 0xe0) - sio_error(&sts); - break; - } - } while (l != end); - - spin_unlock(&i->lock); - - DEBUG_INTR("end.\n"); - - return IRQ_HANDLED; -} - -/* - * To support ISA shared interrupts, we need to have one interrupt - * handler that ensures that the IRQ line has been deasserted - * before returning. Failing to do this will result in the IRQ - * line being stuck active, and, since ISA irqs are edge triggered, - * no more IRQs will be seen. - */ -static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up) -{ - spin_lock_irq(&i->lock); - - if (!list_empty(i->head)) { - if (i->head == &up->list) - i->head = i->head->next; - list_del(&up->list); - } else { - BUG_ON(i->head != &up->list); - i->head = NULL; - } - - spin_unlock_irq(&i->lock); -} - -static int serial_link_irq_chain(struct uart_sio_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - int ret, irq_flags = 0; - - spin_lock_irq(&i->lock); - - if (i->head) { - list_add(&up->list, i->head); - spin_unlock_irq(&i->lock); - - ret = 0; - } else { - INIT_LIST_HEAD(&up->list); - i->head = &up->list; - spin_unlock_irq(&i->lock); - - ret = request_irq(up->port.irq, m32r_sio_interrupt, - irq_flags, "SIO0-RX", i); - ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt, - irq_flags, "SIO0-TX", i); - if (ret < 0) - serial_do_unlink(i, up); - } - - return ret; -} - -static void serial_unlink_irq_chain(struct uart_sio_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - - BUG_ON(i->head == NULL); - - if (list_empty(i->head)) { - free_irq(up->port.irq, i); - free_irq(up->port.irq + 1, i); - } - - serial_do_unlink(i, up); -} - -/* - * This function is used to handle ports that do not have an interrupt. - */ -static void m32r_sio_timeout(unsigned long data) -{ - struct uart_sio_port *up = (struct uart_sio_port *)data; - unsigned int timeout; - unsigned int sts; - - sts = sio_in(up, SIOSTS); - if (sts & 0x5) { - spin_lock(&up->port.lock); - m32r_sio_handle_port(up, sts); - spin_unlock(&up->port.lock); - } - - timeout = up->port.timeout; - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - mod_timer(&up->timer, jiffies + timeout); -} - -static unsigned int m32r_sio_tx_empty(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int m32r_sio_get_mctrl(struct uart_port *port) -{ - return 0; -} - -static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - -} - -static void m32r_sio_break_ctl(struct uart_port *port, int break_state) -{ - -} - -static int m32r_sio_startup(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - int retval; - - sio_init(); - - /* - * If the "interrupt" for this port doesn't correspond with any - * hardware interrupt, we use a timer-based system. The original - * driver used to do this with IRQ0. - */ - if (!is_real_interrupt(up->port.irq)) { - unsigned int timeout = up->port.timeout; - - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + timeout); - } else { - retval = serial_link_irq_chain(up); - if (retval) - return retval; - } - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - * - M32R_SIO: 0x0c - * - M32R_PLDSIO: 0x04 - */ - up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - sio_out(up, SIOTRCR, up->ier); - - /* - * And clear the interrupt registers again for luck. - */ - sio_reset(); - - return 0; -} - -static void m32r_sio_shutdown(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - sio_out(up, SIOTRCR, 0); - - /* - * Disable break condition and FIFOs - */ - - sio_init(); - - if (!is_real_interrupt(up->port.irq)) - del_timer_sync(&up->timer); - else - serial_unlink_irq_chain(up); -} - -static unsigned int m32r_sio_get_divisor(struct uart_port *port, - unsigned int baud) -{ - return uart_get_divisor(port, baud); -} - -static void m32r_sio_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned char cval = 0; - unsigned long flags; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (termios->c_cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* - * Ask the core to calculate the divisor for us. - */ -#ifdef CONFIG_SERIAL_M32R_PLDSIO - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); -#else - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); -#endif - quot = m32r_sio_get_divisor(port, baud); - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - sio_set_baud_rate(baud); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - up->lcr = cval; /* Save LCR */ - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void m32r_sio_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (up->pm) - up->pm(port, state, oldstate); -} - -/* - * Resource handling. This is complicated by the fact that resources - * depend on the port type. Maybe we should be claiming the standard - * 8250 ports, and then trying to get other resources as necessary? - */ -static int -m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) -{ - unsigned int size = 8 << up->port.regshift; -#ifndef CONFIG_SERIAL_M32R_PLDSIO - unsigned long start; -#endif - int ret = 0; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { -#ifdef CONFIG_SERIAL_M32R_PLDSIO - *res = request_mem_region(up->port.mapbase, size, "serial"); -#else - start = up->port.mapbase; - *res = request_mem_region(start, size, "serial"); -#endif - if (!*res) - ret = -EBUSY; - } - break; - - case UPIO_PORT: - *res = request_region(up->port.iobase, size, "serial"); - if (!*res) - ret = -EBUSY; - break; - } - return ret; -} - -static void m32r_sio_release_port(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long start, offset = 0, size = 0; - - size <<= up->port.regshift; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { - /* - * Unmap the area. - */ - iounmap(up->port.membase); - up->port.membase = NULL; - - start = up->port.mapbase; - - if (size) - release_mem_region(start + offset, size); - release_mem_region(start, 8 << up->port.regshift); - } - break; - - case UPIO_PORT: - start = up->port.iobase; - - if (size) - release_region(start + offset, size); - release_region(start + offset, 8 << up->port.regshift); - break; - - default: - break; - } -} - -static int m32r_sio_request_port(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - struct resource *res = NULL; - int ret = 0; - - ret = m32r_sio_request_std_resource(up, &res); - - /* - * If we have a mapbase, then request that as well. - */ - if (ret == 0 && up->port.flags & UPF_IOREMAP) { - int size = resource_size(res); - - up->port.membase = ioremap(up->port.mapbase, size); - if (!up->port.membase) - ret = -ENOMEM; - } - - if (ret < 0) { - if (res) - release_resource(res); - } - - return ret; -} - -static void m32r_sio_config_port(struct uart_port *port, int unused) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int -m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if (ser->irq >= nr_irqs || ser->irq < 0 || - ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || - ser->type >= ARRAY_SIZE(uart_config)) - return -EINVAL; - return 0; -} - -static const char * -m32r_sio_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops m32r_sio_pops = { - .tx_empty = m32r_sio_tx_empty, - .set_mctrl = m32r_sio_set_mctrl, - .get_mctrl = m32r_sio_get_mctrl, - .stop_tx = m32r_sio_stop_tx, - .start_tx = m32r_sio_start_tx, - .stop_rx = m32r_sio_stop_rx, - .enable_ms = m32r_sio_enable_ms, - .break_ctl = m32r_sio_break_ctl, - .startup = m32r_sio_startup, - .shutdown = m32r_sio_shutdown, - .set_termios = m32r_sio_set_termios, - .pm = m32r_sio_pm, - .type = m32r_sio_type, - .release_port = m32r_sio_release_port, - .request_port = m32r_sio_request_port, - .config_port = m32r_sio_config_port, - .verify_port = m32r_sio_verify_port, -}; - -static struct uart_sio_port m32r_sio_ports[UART_NR]; - -static void __init m32r_sio_init_ports(void) -{ - struct uart_sio_port *up; - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); - i++, up++) { - up->port.iobase = old_serial_port[i].port; - up->port.irq = irq_canonicalize(old_serial_port[i].irq); - up->port.uartclk = old_serial_port[i].baud_base * 16; - up->port.flags = old_serial_port[i].flags; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; - up->port.ops = &m32r_sio_pops; - } -} - -static void __init m32r_sio_register_ports(struct uart_driver *drv) -{ - int i; - - m32r_sio_init_ports(); - - for (i = 0; i < UART_NR; i++) { - struct uart_sio_port *up = &m32r_sio_ports[i]; - - up->port.line = i; - up->port.ops = &m32r_sio_pops; - init_timer(&up->timer); - up->timer.function = m32r_sio_timeout; - - up->mcr_mask = ~0; - up->mcr_force = 0; - - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_sio_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = sio_in(up, SIOSTS); - - if (--tmout == 0) - break; - udelay(1); - } while ((status & UART_EMPTY) != UART_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout) - udelay(1); - } -} - -static void m32r_sio_console_putchar(struct uart_port *port, int ch) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - wait_for_xmitr(up); - sio_out(up, SIOTXB, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console_lock must be held when we get here. - */ -static void m32r_sio_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_sio_port *up = &m32r_sio_ports[co->index]; - unsigned int ier; - - /* - * First save the UER then disable the interrupts - */ - ier = sio_in(up, SIOTRCR); - sio_out(up, SIOTRCR, 0); - - uart_console_write(&up->port, s, count, m32r_sio_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - sio_out(up, SIOTRCR, ier); -} - -static int __init m32r_sio_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= UART_NR) - co->index = 0; - port = &m32r_sio_ports[co->index].port; - - /* - * Temporary fix. - */ - spin_lock_init(&port->lock); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver m32r_sio_reg; -static struct console m32r_sio_console = { - .name = "ttyS", - .write = m32r_sio_console_write, - .device = uart_console_device, - .setup = m32r_sio_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &m32r_sio_reg, -}; - -static int __init m32r_sio_console_init(void) -{ - sio_reset(); - sio_init(); - m32r_sio_init_ports(); - register_console(&m32r_sio_console); - return 0; -} -console_initcall(m32r_sio_console_init); - -#define M32R_SIO_CONSOLE &m32r_sio_console -#else -#define M32R_SIO_CONSOLE NULL -#endif - -static struct uart_driver m32r_sio_reg = { - .owner = THIS_MODULE, - .driver_name = "sio", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = UART_NR, - .cons = M32R_SIO_CONSOLE, -}; - -/** - * m32r_sio_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -void m32r_sio_suspend_port(int line) -{ - uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -/** - * m32r_sio_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -void m32r_sio_resume_port(int line) -{ - uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -static int __init m32r_sio_init(void) -{ - int ret, i; - - printk(KERN_INFO "Serial: M32R SIO driver\n"); - - for (i = 0; i < nr_irqs; i++) - spin_lock_init(&irq_lists[i].lock); - - ret = uart_register_driver(&m32r_sio_reg); - if (ret >= 0) - m32r_sio_register_ports(&m32r_sio_reg); - - return ret; -} - -static void __exit m32r_sio_exit(void) -{ - int i; - - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port); - - uart_unregister_driver(&m32r_sio_reg); -} - -module_init(m32r_sio_init); -module_exit(m32r_sio_exit); - -EXPORT_SYMBOL(m32r_sio_suspend_port); -EXPORT_SYMBOL(m32r_sio_resume_port); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/tty/serial/8250/m32r_sio.h b/drivers/tty/serial/8250/m32r_sio.h deleted file mode 100644 index e9b7e11..0000000 --- a/drivers/tty/serial/8250/m32r_sio.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * m32r_sio.h - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.h. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata - * - * 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. - */ - - -struct m32r_sio_probe { - struct module *owner; - int (*pci_init_one)(struct pci_dev *dev); - void (*pci_remove_one)(struct pci_dev *dev); - void (*pnp_init)(void); -}; - -int m32r_sio_register_probe(struct m32r_sio_probe *probe); -void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); -void m32r_sio_get_irq_map(unsigned int *map); -void m32r_sio_suspend_port(int line); -void m32r_sio_resume_port(int line); - -struct old_serial_port { - unsigned int uart; - unsigned int baud_base; - unsigned int port; - unsigned int irq; - unsigned int flags; - unsigned char io_type; - unsigned char __iomem *iomem_base; - unsigned short iomem_reg_shift; -}; - -#define _INLINE_ inline - -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/tty/serial/8250/m32r_sio_reg.h b/drivers/tty/serial/8250/m32r_sio_reg.h deleted file mode 100644 index 4671473..0000000 --- a/drivers/tty/serial/8250/m32r_sio_reg.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * m32r_sio_reg.h - * - * Copyright (C) 1992, 1994 by Theodore Ts'o. - * Copyright (C) 2004 Hirokazu Takata - * - * Redistribution of this file is permitted under the terms of the GNU - * Public License (GPL) - * - * These are the UART port assignments, expressed as offsets from the base - * register. These assignments should hold for any serial port based on - * a 8250, 16450, or 16550(A). - */ - -#ifndef _M32R_SIO_REG_H -#define _M32R_SIO_REG_H - - -#ifdef CONFIG_SERIAL_M32R_PLDSIO - -#define SIOCR 0x000 -#define SIOMOD0 0x002 -#define SIOMOD1 0x004 -#define SIOSTS 0x006 -#define SIOTRCR 0x008 -#define SIOBAUR 0x00a -// #define SIORBAUR 0x018 -#define SIOTXB 0x00c -#define SIORXB 0x00e - -#define UART_RX ((unsigned long) PLD_ESIO0RXB) - /* In: Receive buffer (DLAB=0) */ -#define UART_TX ((unsigned long) PLD_ESIO0TXB) - /* Out: Transmit buffer (DLAB=0) */ -#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ -#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx - * In: Fifo count - * Out: Fifo custom trigger levels - * XR16C85x only */ - -#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ -#define UART_IER ((unsigned long) PLD_ESIO0INTCR) - /* Out: Interrupt Enable Register */ -#define UART_FCTR 0 /* (LCR=BF) Feature Control Register - * XR16C85x only */ - -#define UART_IIR 0 /* In: Interrupt ID Register */ -#define UART_FCR 0 /* Out: FIFO Control Register */ -#define UART_EFR 0 /* I/O: Extended Features Register */ - /* (DLAB=1, 16C660 only) */ - -#define UART_LCR 0 /* Out: Line Control Register */ -#define UART_MCR 0 /* Out: Modem Control Register */ -#define UART_LSR ((unsigned long) PLD_ESIO0STS) - /* In: Line Status Register */ -#define UART_MSR 0 /* In: Modem Status Register */ -#define UART_SCR 0 /* I/O: Scratch Register */ -#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register - * FCTR bit 6 selects SCR or EMSR - * XR16c85x only */ - -#else /* not CONFIG_SERIAL_M32R_PLDSIO */ - -#define SIOCR 0x000 -#define SIOMOD0 0x004 -#define SIOMOD1 0x008 -#define SIOSTS 0x00c -#define SIOTRCR 0x010 -#define SIOBAUR 0x014 -#define SIORBAUR 0x018 -#define SIOTXB 0x01c -#define SIORXB 0x020 - -#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */ -#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */ -#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ -#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx - * In: Fifo count - * Out: Fifo custom trigger levels - * XR16C85x only */ - -#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ -#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */ -#define UART_FCTR 0 /* (LCR=BF) Feature Control Register - * XR16C85x only */ - -#define UART_IIR 0 /* In: Interrupt ID Register */ -#define UART_FCR 0 /* Out: FIFO Control Register */ -#define UART_EFR 0 /* I/O: Extended Features Register */ - /* (DLAB=1, 16C660 only) */ - -#define UART_LCR 0 /* Out: Line Control Register */ -#define UART_MCR 0 /* Out: Modem Control Register */ -#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */ -#define UART_MSR 0 /* In: Modem Status Register */ -#define UART_SCR 0 /* I/O: Scratch Register */ -#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register - * FCTR bit 6 selects SCR or EMSR - * XR16c85x only */ - -#endif /* CONFIG_SERIAL_M32R_PLDSIO */ - -#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * These are the definitions for the Line Control Register - * - * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting - * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. - */ -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ -#define UART_LCR_SBC 0x40 /* Set break control */ -#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ -#define UART_LCR_EPAR 0x10 /* Even parity select */ -#define UART_LCR_PARITY 0x08 /* Parity Enable */ -#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ -#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ -#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ -#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ -#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ - -/* - * These are the definitions for the Line Status Register - */ -#define UART_LSR_TEMT 0x02 /* Transmitter empty */ -#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x00 /* Break interrupt indicator */ -#define UART_LSR_FE 0x80 /* Frame error indicator */ -#define UART_LSR_PE 0x40 /* Parity error indicator */ -#define UART_LSR_OE 0x20 /* Overrun error indicator */ -#define UART_LSR_DR 0x04 /* Receiver data ready */ - -/* - * These are the definitions for the Interrupt Identification Register - */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ - -#define UART_IIR_MSI 0x00 /* Modem status interrupt */ -#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ -#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ - -/* - * These are the definitions for the Interrupt Enable Register - */ -#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */ -#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */ -#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */ -#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */ - -#endif /* _M32R_SIO_REG_H */ diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c new file mode 100644 index 0000000..94a6792 --- /dev/null +++ b/drivers/tty/serial/m32r_sio.c @@ -0,0 +1,1191 @@ +/* + * m32r_sio.c + * + * Driver for M32R serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.c. + * + * Copyright (C) 2001 Russell King. + * Copyright (C) 2004 Hirokazu Takata + * + * 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. + */ + +/* + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. Currently, we don't + * support this very well, and it may well be dropped from this driver + * in future. As such, mapbase should be NULL. + * + * membase is an 'ioremapped' cookie. This is compatible with the old + * serial.c driver, and is currently the preferred form. + */ + +#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PORT_M32R_BASE PORT_M32R_SIO +#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) +#define BAUD_RATE 115200 + +#include +#include "m32r_sio.h" +#include "m32r_sio_reg.h" + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 256 + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +#define BASE_BAUD 115200 + +/* Standard COM flags */ +#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) + +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#if defined(CONFIG_PLAT_USRV) + +#define SERIAL_PORT_DFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ + +#else /* !CONFIG_PLAT_USRV */ + +#if defined(CONFIG_SERIAL_M32R_PLDSIO) +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ + STD_COM_FLAGS }, /* ttyS0 */ +#else +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ + STD_COM_FLAGS }, /* ttyS0 */ +#endif + +#endif /* !CONFIG_PLAT_USRV */ + +static struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS +}; + +#define UART_NR ARRAY_SIZE(old_serial_port) + +struct uart_sio_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short rev; + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char lsr_break_flag; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +static struct irq_info irq_lists[NR_IRQS]; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uart_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .dfl_xmit_fifo_size = 1, + .flags = 0, + }, + [PORT_INDEX(PORT_M32R_SIO)] = { + .name = "M32RSIO", + .dfl_xmit_fifo_size = 1, + .flags = 0, + }, +}; + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + +#define __sio_in(x) inw((unsigned long)(x)) +#define __sio_out(v,x) outw((v),(unsigned long)(x)) + +static inline void sio_set_baud_rate(unsigned long baud) +{ + unsigned short sbaud; + sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1; + __sio_out(sbaud, PLD_ESIO0BAUR); +} + +static void sio_reset(void) +{ + unsigned short tmp; + + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0CR); + sio_set_baud_rate(BAUD_RATE); + __sio_out(0x0300, PLD_ESIO0CR); + __sio_out(0x0003, PLD_ESIO0CR); +} + +static void sio_init(void) +{ + unsigned short tmp; + + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0CR); + __sio_out(0x0300, PLD_ESIO0CR); + __sio_out(0x0003, PLD_ESIO0CR); +} + +static void sio_error(int *status) +{ + printk("SIO0 error[%04x]\n", *status); + do { + sio_init(); + } while ((*status = __sio_in(PLD_ESIO0CR)) != 3); +} + +#else /* not CONFIG_SERIAL_M32R_PLDSIO */ + +#define __sio_in(x) inl(x) +#define __sio_out(v,x) outl((v),(x)) + +static inline void sio_set_baud_rate(unsigned long baud) +{ + unsigned long i, j; + + i = boot_cpu_data.bus_clock / (baud * 16); + j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud; + i -= 1; + j = (j + 1) >> 1; + + __sio_out(i, M32R_SIO0_BAUR_PORTL); + __sio_out(j, M32R_SIO0_RBAUR_PORTL); +} + +static void sio_reset(void) +{ + __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */ + __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */ + __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */ + sio_set_baud_rate(BAUD_RATE); + __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL); + __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */ +} + +static void sio_init(void) +{ + unsigned int tmp; + + tmp = __sio_in(M32R_SIO0_RXB_PORTL); + tmp = __sio_in(M32R_SIO0_RXB_PORTL); + tmp = __sio_in(M32R_SIO0_STS_PORTL); + __sio_out(0x00000003, M32R_SIO0_CR_PORTL); +} + +static void sio_error(int *status) +{ + printk("SIO0 error[%04x]\n", *status); + do { + sio_init(); + } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3); +} + +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +static unsigned int sio_in(struct uart_sio_port *up, int offset) +{ + return __sio_in(up->port.iobase + offset); +} + +static void sio_out(struct uart_sio_port *up, int offset, int value) +{ + __sio_out(value, up->port.iobase + offset); +} + +static unsigned int serial_in(struct uart_sio_port *up, int offset) +{ + if (!offset) + return 0; + + return __sio_in(offset); +} + +static void serial_out(struct uart_sio_port *up, int offset, int value) +{ + if (!offset) + return; + + __sio_out(value, offset); +} + +static void m32r_sio_stop_tx(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void m32r_sio_start_tx(struct uart_port *port) +{ +#ifdef CONFIG_SERIAL_M32R_PLDSIO + struct uart_sio_port *up = (struct uart_sio_port *)port; + struct circ_buf *xmit = &up->port.state->xmit; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + } + while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); +#else + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +#endif +} + +static void m32r_sio_stop_rx(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void m32r_sio_enable_ms(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void receive_chars(struct uart_sio_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch; + unsigned char flag; + int max_count = 256; + + do { + ch = sio_in(up, SIORXB); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= up->port.read_status_mask; + + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } + + if (*status & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) + tty_insert_flip_char(tty, ch, flag); + + if (*status & UART_LSR_OE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_sio_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { +#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */ + serial_out(up, UART_TX, up->port.x_char); +#endif + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + m32r_sio_stop_tx(&up->port); + return; + } + + count = up->port.fifosize; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + while (!(serial_in(up, UART_LSR) & UART_LSR_THRE)); + + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + m32r_sio_stop_tx(&up->port); +} + +/* + * This handles the interrupt from one port. + */ +static inline void m32r_sio_handle_port(struct uart_sio_port *up, + unsigned int status) +{ + DEBUG_INTR("status = %x...", status); + + if (status & 0x04) + receive_chars(up, &status); + if (status & 0x01) + transmit_chars(up); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0; + + DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO +// if (irq == PLD_IRQ_SIO0_SND) +// irq = PLD_IRQ_SIO0_RCV; +#else + if (irq == M32R_IRQ_SIO0_S) + irq = M32R_IRQ_SIO0_R; +#endif + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_sio_port *up; + unsigned int sts; + + up = list_entry(l, struct uart_sio_port, list); + + sts = sio_in(up, SIOSTS); + if (sts & 0x5) { + spin_lock(&up->port.lock); + m32r_sio_handle_port(up, sts); + spin_unlock(&up->port.lock); + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + if (sts & 0xe0) + sio_error(&sts); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_HANDLED; +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct uart_sio_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + int ret, irq_flags = 0; + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(up->port.irq, m32r_sio_interrupt, + irq_flags, "SIO0-RX", i); + ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt, + irq_flags, "SIO0-TX", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_sio_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) { + free_irq(up->port.irq, i); + free_irq(up->port.irq + 1, i); + } + + serial_do_unlink(i, up); +} + +/* + * This function is used to handle ports that do not have an interrupt. + */ +static void m32r_sio_timeout(unsigned long data) +{ + struct uart_sio_port *up = (struct uart_sio_port *)data; + unsigned int timeout; + unsigned int sts; + + sts = sio_in(up, SIOSTS); + if (sts & 0x5) { + spin_lock(&up->port.lock); + m32r_sio_handle_port(up, sts); + spin_unlock(&up->port.lock); + } + + timeout = up->port.timeout; + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + mod_timer(&up->timer, jiffies + timeout); +} + +static unsigned int m32r_sio_tx_empty(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int m32r_sio_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + +} + +static void m32r_sio_break_ctl(struct uart_port *port, int break_state) +{ + +} + +static int m32r_sio_startup(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + int retval; + + sio_init(); + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + * - M32R_SIO: 0x0c + * - M32R_PLDSIO: 0x04 + */ + up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + sio_out(up, SIOTRCR, up->ier); + + /* + * And clear the interrupt registers again for luck. + */ + sio_reset(); + + return 0; +} + +static void m32r_sio_shutdown(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + sio_out(up, SIOTRCR, 0); + + /* + * Disable break condition and FIFOs + */ + + sio_init(); + + if (!is_real_interrupt(up->port.irq)) + del_timer_sync(&up->timer); + else + serial_unlink_irq_chain(up); +} + +static unsigned int m32r_sio_get_divisor(struct uart_port *port, + unsigned int baud) +{ + return uart_get_divisor(port, baud); +} + +static void m32r_sio_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned char cval = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Ask the core to calculate the divisor for us. + */ +#ifdef CONFIG_SERIAL_M32R_PLDSIO + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); +#else + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); +#endif + quot = m32r_sio_get_divisor(port, baud); + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + sio_set_baud_rate(baud); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + up->lcr = cval; /* Save LCR */ + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void m32r_sio_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (up->pm) + up->pm(port, state, oldstate); +} + +/* + * Resource handling. This is complicated by the fact that resources + * depend on the port type. Maybe we should be claiming the standard + * 8250 ports, and then trying to get other resources as necessary? + */ +static int +m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; +#ifndef CONFIG_SERIAL_M32R_PLDSIO + unsigned long start; +#endif + int ret = 0; + + switch (up->port.iotype) { + case UPIO_MEM: + if (up->port.mapbase) { +#ifdef CONFIG_SERIAL_M32R_PLDSIO + *res = request_mem_region(up->port.mapbase, size, "serial"); +#else + start = up->port.mapbase; + *res = request_mem_region(start, size, "serial"); +#endif + if (!*res) + ret = -EBUSY; + } + break; + + case UPIO_PORT: + *res = request_region(up->port.iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static void m32r_sio_release_port(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long start, offset = 0, size = 0; + + size <<= up->port.regshift; + + switch (up->port.iotype) { + case UPIO_MEM: + if (up->port.mapbase) { + /* + * Unmap the area. + */ + iounmap(up->port.membase); + up->port.membase = NULL; + + start = up->port.mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << up->port.regshift); + } + break; + + case UPIO_PORT: + start = up->port.iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << up->port.regshift); + break; + + default: + break; + } +} + +static int m32r_sio_request_port(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + struct resource *res = NULL; + int ret = 0; + + ret = m32r_sio_request_std_resource(up, &res); + + /* + * If we have a mapbase, then request that as well. + */ + if (ret == 0 && up->port.flags & UPF_IOREMAP) { + int size = resource_size(res); + + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) + ret = -ENOMEM; + } + + if (ret < 0) { + if (res) + release_resource(res); + } + + return ret; +} + +static void m32r_sio_config_port(struct uart_port *port, int unused) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); + up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int +m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= nr_irqs || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type >= ARRAY_SIZE(uart_config)) + return -EINVAL; + return 0; +} + +static const char * +m32r_sio_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops m32r_sio_pops = { + .tx_empty = m32r_sio_tx_empty, + .set_mctrl = m32r_sio_set_mctrl, + .get_mctrl = m32r_sio_get_mctrl, + .stop_tx = m32r_sio_stop_tx, + .start_tx = m32r_sio_start_tx, + .stop_rx = m32r_sio_stop_rx, + .enable_ms = m32r_sio_enable_ms, + .break_ctl = m32r_sio_break_ctl, + .startup = m32r_sio_startup, + .shutdown = m32r_sio_shutdown, + .set_termios = m32r_sio_set_termios, + .pm = m32r_sio_pm, + .type = m32r_sio_type, + .release_port = m32r_sio_release_port, + .request_port = m32r_sio_request_port, + .config_port = m32r_sio_config_port, + .verify_port = m32r_sio_verify_port, +}; + +static struct uart_sio_port m32r_sio_ports[UART_NR]; + +static void __init m32r_sio_init_ports(void) +{ + struct uart_sio_port *up; + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.ops = &m32r_sio_pops; + } +} + +static void __init m32r_sio_register_ports(struct uart_driver *drv) +{ + int i; + + m32r_sio_init_ports(); + + for (i = 0; i < UART_NR; i++) { + struct uart_sio_port *up = &m32r_sio_ports[i]; + + up->port.line = i; + up->port.ops = &m32r_sio_pops; + init_timer(&up->timer); + up->timer.function = m32r_sio_timeout; + + up->mcr_mask = ~0; + up->mcr_force = 0; + + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_sio_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = sio_in(up, SIOSTS); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & UART_EMPTY) != UART_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) + udelay(1); + } +} + +static void m32r_sio_console_putchar(struct uart_port *port, int ch) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + wait_for_xmitr(up); + sio_out(up, SIOTXB, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void m32r_sio_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_sio_port *up = &m32r_sio_ports[co->index]; + unsigned int ier; + + /* + * First save the UER then disable the interrupts + */ + ier = sio_in(up, SIOTRCR); + sio_out(up, SIOTRCR, 0); + + uart_console_write(&up->port, s, count, m32r_sio_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, SIOTRCR, ier); +} + +static int __init m32r_sio_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &m32r_sio_ports[co->index].port; + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver m32r_sio_reg; +static struct console m32r_sio_console = { + .name = "ttyS", + .write = m32r_sio_console_write, + .device = uart_console_device, + .setup = m32r_sio_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &m32r_sio_reg, +}; + +static int __init m32r_sio_console_init(void) +{ + sio_reset(); + sio_init(); + m32r_sio_init_ports(); + register_console(&m32r_sio_console); + return 0; +} +console_initcall(m32r_sio_console_init); + +#define M32R_SIO_CONSOLE &m32r_sio_console +#else +#define M32R_SIO_CONSOLE NULL +#endif + +static struct uart_driver m32r_sio_reg = { + .owner = THIS_MODULE, + .driver_name = "sio", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = UART_NR, + .cons = M32R_SIO_CONSOLE, +}; + +/** + * m32r_sio_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void m32r_sio_suspend_port(int line) +{ + uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); +} + +/** + * m32r_sio_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void m32r_sio_resume_port(int line) +{ + uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); +} + +static int __init m32r_sio_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: M32R SIO driver\n"); + + for (i = 0; i < nr_irqs; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = uart_register_driver(&m32r_sio_reg); + if (ret >= 0) + m32r_sio_register_ports(&m32r_sio_reg); + + return ret; +} + +static void __exit m32r_sio_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port); + + uart_unregister_driver(&m32r_sio_reg); +} + +module_init(m32r_sio_init); +module_exit(m32r_sio_exit); + +EXPORT_SYMBOL(m32r_sio_suspend_port); +EXPORT_SYMBOL(m32r_sio_resume_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/tty/serial/m32r_sio.h b/drivers/tty/serial/m32r_sio.h new file mode 100644 index 0000000..e9b7e11 --- /dev/null +++ b/drivers/tty/serial/m32r_sio.h @@ -0,0 +1,48 @@ +/* + * m32r_sio.h + * + * Driver for M32R serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.h. + * + * Copyright (C) 2001 Russell King. + * Copyright (C) 2004 Hirokazu Takata + * + * 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. + */ + + +struct m32r_sio_probe { + struct module *owner; + int (*pci_init_one)(struct pci_dev *dev); + void (*pci_remove_one)(struct pci_dev *dev); + void (*pnp_init)(void); +}; + +int m32r_sio_register_probe(struct m32r_sio_probe *probe); +void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); +void m32r_sio_get_irq_map(unsigned int *map); +void m32r_sio_suspend_port(int line); +void m32r_sio_resume_port(int line); + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char io_type; + unsigned char __iomem *iomem_base; + unsigned short iomem_reg_shift; +}; + +#define _INLINE_ inline + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/tty/serial/m32r_sio_reg.h b/drivers/tty/serial/m32r_sio_reg.h new file mode 100644 index 0000000..4671473 --- /dev/null +++ b/drivers/tty/serial/m32r_sio_reg.h @@ -0,0 +1,152 @@ +/* + * m32r_sio_reg.h + * + * Copyright (C) 1992, 1994 by Theodore Ts'o. + * Copyright (C) 2004 Hirokazu Takata + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + * + * These are the UART port assignments, expressed as offsets from the base + * register. These assignments should hold for any serial port based on + * a 8250, 16450, or 16550(A). + */ + +#ifndef _M32R_SIO_REG_H +#define _M32R_SIO_REG_H + + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + +#define SIOCR 0x000 +#define SIOMOD0 0x002 +#define SIOMOD1 0x004 +#define SIOSTS 0x006 +#define SIOTRCR 0x008 +#define SIOBAUR 0x00a +// #define SIORBAUR 0x018 +#define SIOTXB 0x00c +#define SIORXB 0x00e + +#define UART_RX ((unsigned long) PLD_ESIO0RXB) + /* In: Receive buffer (DLAB=0) */ +#define UART_TX ((unsigned long) PLD_ESIO0TXB) + /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER ((unsigned long) PLD_ESIO0INTCR) + /* Out: Interrupt Enable Register */ +#define UART_FCTR 0 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 0 /* In: Interrupt ID Register */ +#define UART_FCR 0 /* Out: FIFO Control Register */ +#define UART_EFR 0 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 0 /* Out: Line Control Register */ +#define UART_MCR 0 /* Out: Modem Control Register */ +#define UART_LSR ((unsigned long) PLD_ESIO0STS) + /* In: Line Status Register */ +#define UART_MSR 0 /* In: Modem Status Register */ +#define UART_SCR 0 /* I/O: Scratch Register */ +#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +#else /* not CONFIG_SERIAL_M32R_PLDSIO */ + +#define SIOCR 0x000 +#define SIOMOD0 0x004 +#define SIOMOD1 0x008 +#define SIOSTS 0x00c +#define SIOTRCR 0x010 +#define SIOBAUR 0x014 +#define SIORBAUR 0x018 +#define SIOTXB 0x01c +#define SIORXB 0x020 + +#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */ +#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */ +#define UART_FCTR 0 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 0 /* In: Interrupt ID Register */ +#define UART_FCR 0 /* Out: FIFO Control Register */ +#define UART_EFR 0 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 0 /* Out: Line Control Register */ +#define UART_MCR 0 /* Out: Modem Control Register */ +#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */ +#define UART_MSR 0 /* In: Modem Status Register */ +#define UART_SCR 0 /* I/O: Scratch Register */ +#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * These are the definitions for the Line Control Register + * + * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting + * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +/* + * These are the definitions for the Line Status Register + */ +#define UART_LSR_TEMT 0x02 /* Transmitter empty */ +#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x00 /* Break interrupt indicator */ +#define UART_LSR_FE 0x80 /* Frame error indicator */ +#define UART_LSR_PE 0x40 /* Parity error indicator */ +#define UART_LSR_OE 0x20 /* Overrun error indicator */ +#define UART_LSR_DR 0x04 /* Receiver data ready */ + +/* + * These are the definitions for the Interrupt Identification Register + */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ + +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +/* + * These are the definitions for the Interrupt Enable Register + */ +#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */ + +#endif /* _M32R_SIO_REG_H */ -- cgit v0.10.2 From 15f51136719699d7204eeeccd66849a4d90ae426 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 7 Feb 2012 08:20:37 -0800 Subject: ACPI: remove duplicated lines of merging problems with acpi_processor_add Those lines have two copies. Not sure how it could happen, looks like git problem. Signed-off-by: Yinghai Lu Cc: Linus Torvalds Cc: Thomas Renninger Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 2b805d7..8ae05ce 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -586,13 +586,6 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) if (pr->flags.need_hotplug_init) return 0; - /* - * Do not start hotplugged CPUs now, but when they - * are onlined the first time - */ - if (pr->flags.need_hotplug_init) - return 0; - result = acpi_processor_start(pr); if (result) goto err_remove_sysfs; -- cgit v0.10.2 From 29bb5d4fd3140a7d5d02d858118c74a45f15c296 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Feb 2012 15:11:17 -0800 Subject: driver-core: cpu: fix kobject warning when hotplugging a cpu Due to the sysdev conversion to struct device, the cpu objects get reused when adding a cpu after offlining it, which causes a big warning that the kobject portion is not properly initialized. So clear out the object before we register it again, so all is quiet. Reported-by: Konrad Rzeszutek Wilk Tested-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 23f2c4c..4dabf50 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -240,6 +240,7 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) int error; cpu->node_id = cpu_to_node(num); + memset(&cpu->dev, 0x00, sizeof(struct device)); cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; cpu->dev.release = cpu_device_release; -- cgit v0.10.2 From 936d7de3d736e0737542641269436f4b5968e9ef Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 7 Feb 2012 14:51:21 +0000 Subject: IPoIB: Stop lying about hard_header_len and use skb->cb to stash LL addresses Commit a0417fa3a18a ("net: Make qdisc_skb_cb upper size bound explicit.") made it possible for a netdev driver to use skb->cb between its header_ops.create method and its .ndo_start_xmit method. Use this in ipoib_hard_header() to stash away the LL address (GID + QPN), instead of the "ipoib_pseudoheader" hack. This allows IPoIB to stop lying about its hard_header_len, which will let us fix the L2 check for GRO. Signed-off-by: Roland Dreier Signed-off-by: David S. Miller diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index b3cc1e0..86df632 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -44,6 +44,7 @@ #include #include +#include #include @@ -117,8 +118,9 @@ struct ipoib_header { u16 reserved; }; -struct ipoib_pseudoheader { - u8 hwaddr[INFINIBAND_ALEN]; +struct ipoib_cb { + struct qdisc_skb_cb qdisc_cb; + u8 hwaddr[INFINIBAND_ALEN]; }; /* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 3514ca0..3974c29 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -653,7 +653,7 @@ static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct n } static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, - struct ipoib_pseudoheader *phdr) + struct ipoib_cb *cb) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_path *path; @@ -661,17 +661,15 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, spin_lock_irqsave(&priv->lock, flags); - path = __path_find(dev, phdr->hwaddr + 4); + path = __path_find(dev, cb->hwaddr + 4); if (!path || !path->valid) { int new_path = 0; if (!path) { - path = path_rec_create(dev, phdr->hwaddr + 4); + path = path_rec_create(dev, cb->hwaddr + 4); new_path = 1; } if (path) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); if (!path->query && path_rec_start(dev, path)) { @@ -695,12 +693,10 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, be16_to_cpu(path->pathrec.dlid)); spin_unlock_irqrestore(&priv->lock, flags); - ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr)); + ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr)); return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -774,16 +770,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb_any(skb); } } else { - struct ipoib_pseudoheader *phdr = - (struct ipoib_pseudoheader *) skb->data; - skb_pull(skb, sizeof *phdr); + struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; - if (phdr->hwaddr[4] == 0xff) { + if (cb->hwaddr[4] == 0xff) { /* Add in the P_Key for multicast*/ - phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff; - phdr->hwaddr[9] = priv->pkey & 0xff; + cb->hwaddr[8] = (priv->pkey >> 8) & 0xff; + cb->hwaddr[9] = priv->pkey & 0xff; - ipoib_mcast_send(dev, phdr->hwaddr + 4, skb); + ipoib_mcast_send(dev, cb->hwaddr + 4, skb); } else { /* unicast GID -- should be ARP or RARP reply */ @@ -792,14 +786,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n", skb_dst(skb) ? "neigh" : "dst", be16_to_cpup((__be16 *) skb->data), - IPOIB_QPN(phdr->hwaddr), - phdr->hwaddr + 4); + IPOIB_QPN(cb->hwaddr), + cb->hwaddr + 4); dev_kfree_skb_any(skb); ++dev->stats.tx_dropped; goto unlock; } - unicast_arp_send(skb, dev, phdr); + unicast_arp_send(skb, dev, cb); } } unlock: @@ -825,8 +819,6 @@ static int ipoib_hard_header(struct sk_buff *skb, const void *daddr, const void *saddr, unsigned len) { struct ipoib_header *header; - struct dst_entry *dst; - struct neighbour *n; header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -834,18 +826,13 @@ static int ipoib_hard_header(struct sk_buff *skb, header->reserved = 0; /* - * If we don't have a neighbour structure, stuff the - * destination address onto the front of the skb so we can - * figure out where to send the packet later. + * If we don't have a dst_entry structure, stuff the + * destination address into skb->cb so we can figure out where + * to send the packet later. */ - dst = skb_dst(skb); - n = NULL; - if (dst) - n = dst_get_neighbour_noref_raw(dst); - if ((!dst || !n) && daddr) { - struct ipoib_pseudoheader *phdr = - (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); - memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); + if (!skb_dst(skb)) { + struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb; + memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN); } return 0; @@ -1021,11 +1008,7 @@ static void ipoib_setup(struct net_device *dev) dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - /* - * We add in INFINIBAND_ALEN to allow for the destination - * address "pseudoheader" for skbs without neighbour struct. - */ - dev->hard_header_len = IPOIB_ENCAP_LEN + INFINIBAND_ALEN; + dev->hard_header_len = IPOIB_ENCAP_LEN; dev->addr_len = INFINIBAND_ALEN; dev->type = ARPHRD_INFINIBAND; dev->tx_queue_len = ipoib_sendq_size * 2; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index f7ff9dd..20ebc6f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -262,21 +262,13 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, netif_tx_lock_bh(dev); while (!skb_queue_empty(&mcast->pkt_queue)) { struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue); - struct dst_entry *dst = skb_dst(skb); - struct neighbour *n = NULL; netif_tx_unlock_bh(dev); skb->dev = dev; - if (dst) - n = dst_get_neighbour_noref_raw(dst); - if (!dst || !n) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof (struct ipoib_pseudoheader)); - } - if (dev_queue_xmit(skb)) ipoib_warn(priv, "dev_queue_xmit failed to requeue packet\n"); + netif_tx_lock_bh(dev); } netif_tx_unlock_bh(dev); -- cgit v0.10.2 From 5ca3b72c5da47d95b83857b768def6172fbc080a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Feb 2012 08:51:50 +0000 Subject: gro: more generic L2 header check Shlomo Pongratz reported GRO L2 header check was suited for Ethernet only, and failed on IB/ipoib traffic. He provided a patch faking a zeroed header to let GRO aggregates frames. Roland Dreier, Herbert Xu, and others suggested we change GRO L2 header check to be more generic, ie not assuming L2 header is 14 bytes, but taking into account hard_header_len. __napi_gro_receive() has special handling for the common case (Ethernet) to avoid a memcmp() call and use an inline optimized function instead. Signed-off-by: Eric Dumazet Reported-by: Shlomo Pongratz Cc: Roland Dreier Cc: Or Gerlitz Cc: Herbert Xu Tested-by: Sean Hefty Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 115dee1..6ca32f6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3500,14 +3500,20 @@ static inline gro_result_t __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff *p; + unsigned int maclen = skb->dev->hard_header_len; for (p = napi->gro_list; p; p = p->next) { unsigned long diffs; diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev; diffs |= p->vlan_tci ^ skb->vlan_tci; - diffs |= compare_ether_header(skb_mac_header(p), - skb_gro_mac_header(skb)); + if (maclen == ETH_HLEN) + diffs |= compare_ether_header(skb_mac_header(p), + skb_gro_mac_header(skb)); + else if (!diffs) + diffs = memcmp(skb_mac_header(p), + skb_gro_mac_header(skb), + maclen); NAPI_GRO_CB(p)->same_flow = !diffs; NAPI_GRO_CB(p)->flush = 0; } -- cgit v0.10.2 From 67435319866f89b55721fc88ca0dbda51629e3d6 Mon Sep 17 00:00:00 2001 From: Evgeniy Polyakov Date: Thu, 9 Feb 2012 02:44:50 +0300 Subject: staging: pohmelfs: remove drivers/staging/pohmelfs New pohmelfs is coming, and it is time to remove deadly old design https://lkml.org/lkml/2012/2/8/293 Signed-off-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index b31194c..9e63472 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -60,8 +60,6 @@ source "drivers/staging/rts5139/Kconfig" source "drivers/staging/frontier/Kconfig" -source "drivers/staging/pohmelfs/Kconfig" - source "drivers/staging/phison/Kconfig" source "drivers/staging/line6/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index b5813c4..943e148 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -22,7 +22,6 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_RTS_PSTOR) += rts_pstor/ obj-$(CONFIG_RTS5139) += rts5139/ obj-$(CONFIG_TRANZPORT) += frontier/ -obj-$(CONFIG_POHMELFS) += pohmelfs/ obj-$(CONFIG_IDE_PHISON) += phison/ obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/ diff --git a/drivers/staging/pohmelfs/Kconfig b/drivers/staging/pohmelfs/Kconfig deleted file mode 100644 index 8d53b1a..0000000 --- a/drivers/staging/pohmelfs/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -config POHMELFS - tristate "POHMELFS filesystem support" - depends on NET - select CONNECTOR - select CRYPTO - select CRYPTO_BLKCIPHER - select CRYPTO_HMAC - help - POHMELFS stands for Parallel Optimized Host Message Exchange Layered - File System. This is a network filesystem which supports coherent - caching of data and metadata on clients. - -config POHMELFS_DEBUG - bool "POHMELFS debugging" - depends on POHMELFS - default n - help - Turns on excessive POHMELFS debugging facilities. - You usually do not want to slow things down noticeably and get really - lots of kernel messages in syslog. diff --git a/drivers/staging/pohmelfs/Makefile b/drivers/staging/pohmelfs/Makefile deleted file mode 100644 index 196561c..0000000 --- a/drivers/staging/pohmelfs/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_POHMELFS) += pohmelfs.o - -pohmelfs-y := inode.o config.o dir.o net.o path_entry.o trans.o crypto.o lock.o mcache.o diff --git a/drivers/staging/pohmelfs/config.c b/drivers/staging/pohmelfs/config.c deleted file mode 100644 index b6c42cb..0000000 --- a/drivers/staging/pohmelfs/config.c +++ /dev/null @@ -1,611 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -/* - * Global configuration list. - * Each client can be asked to get one of them. - * - * Allows to provide remote server address (ipv4/v6/whatever), port - * and so on via kernel connector. - */ - -static struct cb_id pohmelfs_cn_id = {.idx = POHMELFS_CN_IDX, .val = POHMELFS_CN_VAL}; -static LIST_HEAD(pohmelfs_config_list); -static DEFINE_MUTEX(pohmelfs_config_lock); - -static inline int pohmelfs_config_eql(struct pohmelfs_ctl *sc, struct pohmelfs_ctl *ctl) -{ - if (sc->idx == ctl->idx && sc->type == ctl->type && - sc->proto == ctl->proto && - sc->addrlen == ctl->addrlen && - !memcmp(&sc->addr, &ctl->addr, ctl->addrlen)) - return 1; - - return 0; -} - -static struct pohmelfs_config_group *pohmelfs_find_config_group(unsigned int idx) -{ - struct pohmelfs_config_group *g, *group = NULL; - - list_for_each_entry(g, &pohmelfs_config_list, group_entry) { - if (g->idx == idx) { - group = g; - break; - } - } - - return group; -} - -static struct pohmelfs_config_group *pohmelfs_find_create_config_group(unsigned int idx) -{ - struct pohmelfs_config_group *g; - - g = pohmelfs_find_config_group(idx); - if (g) - return g; - - g = kzalloc(sizeof(struct pohmelfs_config_group), GFP_KERNEL); - if (!g) - return NULL; - - INIT_LIST_HEAD(&g->config_list); - g->idx = idx; - g->num_entry = 0; - - list_add_tail(&g->group_entry, &pohmelfs_config_list); - - return g; -} - -static inline void pohmelfs_insert_config_entry(struct pohmelfs_sb *psb, struct pohmelfs_config *dst) -{ - struct pohmelfs_config *tmp; - - INIT_LIST_HEAD(&dst->config_entry); - - list_for_each_entry(tmp, &psb->state_list, config_entry) { - if (dst->state.ctl.prio > tmp->state.ctl.prio) - list_add_tail(&dst->config_entry, &tmp->config_entry); - } - if (list_empty(&dst->config_entry)) - list_add_tail(&dst->config_entry, &psb->state_list); -} - -static int pohmelfs_move_config_entry(struct pohmelfs_sb *psb, - struct pohmelfs_config *dst, struct pohmelfs_config *new) -{ - if ((dst->state.ctl.prio == new->state.ctl.prio) && - (dst->state.ctl.perm == new->state.ctl.perm)) - return 0; - - dprintk("%s: dst: prio: %d, perm: %x, new: prio: %d, perm: %d.\n", - __func__, dst->state.ctl.prio, dst->state.ctl.perm, - new->state.ctl.prio, new->state.ctl.perm); - dst->state.ctl.prio = new->state.ctl.prio; - dst->state.ctl.perm = new->state.ctl.perm; - - list_del_init(&dst->config_entry); - pohmelfs_insert_config_entry(psb, dst); - return 0; -} - -/* - * pohmelfs_copy_config() is used to copy new state configs from the - * config group (controlled by the netlink messages) into the superblock. - * This happens either at startup time where no transactions can access - * the list of the configs (and thus list of the network states), or at - * run-time, where it is protected by the psb->state_lock. - */ -int pohmelfs_copy_config(struct pohmelfs_sb *psb) -{ - struct pohmelfs_config_group *g; - struct pohmelfs_config *c, *dst; - int err = -ENODEV; - - mutex_lock(&pohmelfs_config_lock); - - g = pohmelfs_find_config_group(psb->idx); - if (!g) - goto out_unlock; - - /* - * Run over all entries in given config group and try to create and - * initialize those, which do not exist in superblock list. - * Skip all existing entries. - */ - - list_for_each_entry(c, &g->config_list, config_entry) { - err = 0; - list_for_each_entry(dst, &psb->state_list, config_entry) { - if (pohmelfs_config_eql(&dst->state.ctl, &c->state.ctl)) { - err = pohmelfs_move_config_entry(psb, dst, c); - if (!err) - err = -EEXIST; - break; - } - } - - if (err) - continue; - - dst = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL); - if (!dst) { - err = -ENOMEM; - break; - } - - memcpy(&dst->state.ctl, &c->state.ctl, sizeof(struct pohmelfs_ctl)); - - pohmelfs_insert_config_entry(psb, dst); - - err = pohmelfs_state_init_one(psb, dst); - if (err) { - list_del(&dst->config_entry); - kfree(dst); - } - - err = 0; - } - -out_unlock: - mutex_unlock(&pohmelfs_config_lock); - - return err; -} - -int pohmelfs_copy_crypto(struct pohmelfs_sb *psb) -{ - struct pohmelfs_config_group *g; - int err = -ENOENT; - - mutex_lock(&pohmelfs_config_lock); - g = pohmelfs_find_config_group(psb->idx); - if (!g) - goto err_out_exit; - - if (g->hash_string) { - err = -ENOMEM; - psb->hash_string = kstrdup(g->hash_string, GFP_KERNEL); - if (!psb->hash_string) - goto err_out_exit; - psb->hash_strlen = g->hash_strlen; - } - - if (g->cipher_string) { - psb->cipher_string = kstrdup(g->cipher_string, GFP_KERNEL); - if (!psb->cipher_string) - goto err_out_free_hash_string; - psb->cipher_strlen = g->cipher_strlen; - } - - if (g->hash_keysize) { - psb->hash_key = kmemdup(g->hash_key, g->hash_keysize, - GFP_KERNEL); - if (!psb->hash_key) - goto err_out_free_cipher_string; - psb->hash_keysize = g->hash_keysize; - } - - if (g->cipher_keysize) { - psb->cipher_key = kmemdup(g->cipher_key, g->cipher_keysize, - GFP_KERNEL); - if (!psb->cipher_key) - goto err_out_free_hash; - psb->cipher_keysize = g->cipher_keysize; - } - - mutex_unlock(&pohmelfs_config_lock); - - return 0; - -err_out_free_hash: - kfree(psb->hash_key); -err_out_free_cipher_string: - kfree(psb->cipher_string); -err_out_free_hash_string: - kfree(psb->hash_string); -err_out_exit: - mutex_unlock(&pohmelfs_config_lock); - return err; -} - -static int pohmelfs_send_reply(int err, int msg_num, int action, struct cn_msg *msg, struct pohmelfs_ctl *ctl) -{ - struct pohmelfs_cn_ack *ack; - - ack = kzalloc(sizeof(struct pohmelfs_cn_ack), GFP_KERNEL); - if (!ack) - return -ENOMEM; - - memcpy(&ack->msg, msg, sizeof(struct cn_msg)); - - if (action == POHMELFS_CTLINFO_ACK) - memcpy(&ack->ctl, ctl, sizeof(struct pohmelfs_ctl)); - - ack->msg.len = sizeof(struct pohmelfs_cn_ack) - sizeof(struct cn_msg); - ack->msg.ack = msg->ack + 1; - ack->error = err; - ack->msg_num = msg_num; - - cn_netlink_send(&ack->msg, 0, GFP_KERNEL); - kfree(ack); - return 0; -} - -static int pohmelfs_cn_disp(struct cn_msg *msg) -{ - struct pohmelfs_config_group *g; - struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; - struct pohmelfs_config *c, *tmp; - int err = 0, i = 1; - - if (msg->len != sizeof(struct pohmelfs_ctl)) - return -EBADMSG; - - mutex_lock(&pohmelfs_config_lock); - - g = pohmelfs_find_config_group(ctl->idx); - if (!g) { - pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL); - goto out_unlock; - } - - list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { - struct pohmelfs_ctl *sc = &c->state.ctl; - if (pohmelfs_send_reply(err, g->num_entry - i, POHMELFS_CTLINFO_ACK, msg, sc)) { - err = -ENOMEM; - goto out_unlock; - } - i += 1; - } - - out_unlock: - mutex_unlock(&pohmelfs_config_lock); - return err; -} - -static int pohmelfs_cn_dump(struct cn_msg *msg) -{ - struct pohmelfs_config_group *g; - struct pohmelfs_config *c, *tmp; - int err = 0, i = 1; - int total_msg = 0; - - if (msg->len != sizeof(struct pohmelfs_ctl)) - return -EBADMSG; - - mutex_lock(&pohmelfs_config_lock); - - list_for_each_entry(g, &pohmelfs_config_list, group_entry) - total_msg += g->num_entry; - if (total_msg == 0) { - if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL)) - err = -ENOMEM; - goto out_unlock; - } - - list_for_each_entry(g, &pohmelfs_config_list, group_entry) { - list_for_each_entry_safe(c, tmp, &g->config_list, - config_entry) { - struct pohmelfs_ctl *sc = &c->state.ctl; - if (pohmelfs_send_reply(err, total_msg - i, - POHMELFS_CTLINFO_ACK, msg, - sc)) { - err = -ENOMEM; - goto out_unlock; - } - i += 1; - } - } - -out_unlock: - mutex_unlock(&pohmelfs_config_lock); - return err; -} - -static int pohmelfs_cn_flush(struct cn_msg *msg) -{ - struct pohmelfs_config_group *g; - struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; - struct pohmelfs_config *c, *tmp; - int err = 0; - - if (msg->len != sizeof(struct pohmelfs_ctl)) - return -EBADMSG; - - mutex_lock(&pohmelfs_config_lock); - - if (ctl->idx != POHMELFS_NULL_IDX) { - g = pohmelfs_find_config_group(ctl->idx); - - if (!g) - goto out_unlock; - - list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { - list_del(&c->config_entry); - g->num_entry--; - kfree(c); - } - } else { - list_for_each_entry(g, &pohmelfs_config_list, group_entry) { - list_for_each_entry_safe(c, tmp, &g->config_list, - config_entry) { - list_del(&c->config_entry); - g->num_entry--; - kfree(c); - } - } - } - -out_unlock: - mutex_unlock(&pohmelfs_config_lock); - pohmelfs_cn_dump(msg); - - return err; -} - -static int pohmelfs_modify_config(struct pohmelfs_ctl *old, struct pohmelfs_ctl *new) -{ - old->perm = new->perm; - old->prio = new->prio; - return 0; -} - -static int pohmelfs_cn_ctl(struct cn_msg *msg, int action) -{ - struct pohmelfs_config_group *g; - struct pohmelfs_ctl *ctl = (struct pohmelfs_ctl *)msg->data; - struct pohmelfs_config *c, *tmp; - int err = 0; - - if (msg->len != sizeof(struct pohmelfs_ctl)) - return -EBADMSG; - - mutex_lock(&pohmelfs_config_lock); - - g = pohmelfs_find_create_config_group(ctl->idx); - if (!g) { - err = -ENOMEM; - goto out_unlock; - } - - list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { - struct pohmelfs_ctl *sc = &c->state.ctl; - - if (pohmelfs_config_eql(sc, ctl)) { - if (action == POHMELFS_FLAGS_ADD) { - err = -EEXIST; - goto out_unlock; - } else if (action == POHMELFS_FLAGS_DEL) { - list_del(&c->config_entry); - g->num_entry--; - kfree(c); - goto out_unlock; - } else if (action == POHMELFS_FLAGS_MODIFY) { - err = pohmelfs_modify_config(sc, ctl); - goto out_unlock; - } else { - err = -EEXIST; - goto out_unlock; - } - } - } - if (action == POHMELFS_FLAGS_DEL) { - err = -EBADMSG; - goto out_unlock; - } - - c = kzalloc(sizeof(struct pohmelfs_config), GFP_KERNEL); - if (!c) { - err = -ENOMEM; - goto out_unlock; - } - memcpy(&c->state.ctl, ctl, sizeof(struct pohmelfs_ctl)); - g->num_entry++; - - list_add_tail(&c->config_entry, &g->config_list); - - out_unlock: - mutex_unlock(&pohmelfs_config_lock); - if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL)) - err = -ENOMEM; - - return err; -} - -static int pohmelfs_crypto_hash_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c) -{ - char *algo = (char *)c->data; - u8 *key = (u8 *)(algo + c->strlen); - - if (g->hash_string) - return -EEXIST; - - g->hash_string = kstrdup(algo, GFP_KERNEL); - if (!g->hash_string) - return -ENOMEM; - g->hash_strlen = c->strlen; - g->hash_keysize = c->keysize; - - g->hash_key = kmemdup(key, c->keysize, GFP_KERNEL); - if (!g->hash_key) { - kfree(g->hash_string); - return -ENOMEM; - } - - return 0; -} - -static int pohmelfs_crypto_cipher_init(struct pohmelfs_config_group *g, struct pohmelfs_crypto *c) -{ - char *algo = (char *)c->data; - u8 *key = (u8 *)(algo + c->strlen); - - if (g->cipher_string) - return -EEXIST; - - g->cipher_string = kstrdup(algo, GFP_KERNEL); - if (!g->cipher_string) - return -ENOMEM; - g->cipher_strlen = c->strlen; - g->cipher_keysize = c->keysize; - - g->cipher_key = kmemdup(key, c->keysize, GFP_KERNEL); - if (!g->cipher_key) { - kfree(g->cipher_string); - return -ENOMEM; - } - - return 0; -} - -static int pohmelfs_cn_crypto(struct cn_msg *msg) -{ - struct pohmelfs_crypto *crypto = (struct pohmelfs_crypto *)msg->data; - struct pohmelfs_config_group *g; - int err = 0; - - dprintk("%s: idx: %u, strlen: %u, type: %u, keysize: %u, algo: %s.\n", - __func__, crypto->idx, crypto->strlen, crypto->type, - crypto->keysize, (char *)crypto->data); - - mutex_lock(&pohmelfs_config_lock); - g = pohmelfs_find_create_config_group(crypto->idx); - if (!g) { - err = -ENOMEM; - goto out_unlock; - } - - switch (crypto->type) { - case POHMELFS_CRYPTO_HASH: - err = pohmelfs_crypto_hash_init(g, crypto); - break; - case POHMELFS_CRYPTO_CIPHER: - err = pohmelfs_crypto_cipher_init(g, crypto); - break; - default: - err = -ENOTSUPP; - break; - } - -out_unlock: - mutex_unlock(&pohmelfs_config_lock); - if (pohmelfs_send_reply(err, 0, POHMELFS_NOINFO_ACK, msg, NULL)) - err = -ENOMEM; - - return err; -} - -static void pohmelfs_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) -{ - int err; - - if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) - return; - - switch (msg->flags) { - case POHMELFS_FLAGS_ADD: - case POHMELFS_FLAGS_DEL: - case POHMELFS_FLAGS_MODIFY: - err = pohmelfs_cn_ctl(msg, msg->flags); - break; - case POHMELFS_FLAGS_FLUSH: - err = pohmelfs_cn_flush(msg); - break; - case POHMELFS_FLAGS_SHOW: - err = pohmelfs_cn_disp(msg); - break; - case POHMELFS_FLAGS_DUMP: - err = pohmelfs_cn_dump(msg); - break; - case POHMELFS_FLAGS_CRYPTO: - err = pohmelfs_cn_crypto(msg); - break; - default: - err = -ENOSYS; - break; - } -} - -int pohmelfs_config_check(struct pohmelfs_config *config, int idx) -{ - struct pohmelfs_ctl *ctl = &config->state.ctl; - struct pohmelfs_config *tmp; - int err = -ENOENT; - struct pohmelfs_ctl *sc; - struct pohmelfs_config_group *g; - - mutex_lock(&pohmelfs_config_lock); - - g = pohmelfs_find_config_group(ctl->idx); - if (g) { - list_for_each_entry(tmp, &g->config_list, config_entry) { - sc = &tmp->state.ctl; - - if (pohmelfs_config_eql(sc, ctl)) { - err = 0; - break; - } - } - } - - mutex_unlock(&pohmelfs_config_lock); - - return err; -} - -int __init pohmelfs_config_init(void) -{ - /* XXX remove (void *) cast when vanilla connector got synced */ - return cn_add_callback(&pohmelfs_cn_id, "pohmelfs", (void *)pohmelfs_cn_callback); -} - -void pohmelfs_config_exit(void) -{ - struct pohmelfs_config *c, *tmp; - struct pohmelfs_config_group *g, *gtmp; - - cn_del_callback(&pohmelfs_cn_id); - - mutex_lock(&pohmelfs_config_lock); - list_for_each_entry_safe(g, gtmp, &pohmelfs_config_list, group_entry) { - list_for_each_entry_safe(c, tmp, &g->config_list, config_entry) { - list_del(&c->config_entry); - kfree(c); - } - - list_del(&g->group_entry); - - kfree(g->hash_string); - - kfree(g->cipher_string); - - kfree(g); - } - mutex_unlock(&pohmelfs_config_lock); -} diff --git a/drivers/staging/pohmelfs/crypto.c b/drivers/staging/pohmelfs/crypto.c deleted file mode 100644 index ad92771..0000000 --- a/drivers/staging/pohmelfs/crypto.c +++ /dev/null @@ -1,878 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -static struct crypto_hash *pohmelfs_init_hash(struct pohmelfs_sb *psb) -{ - int err; - struct crypto_hash *hash; - - hash = crypto_alloc_hash(psb->hash_string, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hash)) { - err = PTR_ERR(hash); - dprintk("%s: idx: %u: failed to allocate hash '%s', err: %d.\n", - __func__, psb->idx, psb->hash_string, err); - goto err_out_exit; - } - - psb->crypto_attached_size = crypto_hash_digestsize(hash); - - if (!psb->hash_keysize) - return hash; - - err = crypto_hash_setkey(hash, psb->hash_key, psb->hash_keysize); - if (err) { - dprintk("%s: idx: %u: failed to set key for hash '%s', err: %d.\n", - __func__, psb->idx, psb->hash_string, err); - goto err_out_free; - } - - return hash; - -err_out_free: - crypto_free_hash(hash); -err_out_exit: - return ERR_PTR(err); -} - -static struct crypto_ablkcipher *pohmelfs_init_cipher(struct pohmelfs_sb *psb) -{ - int err = -EINVAL; - struct crypto_ablkcipher *cipher; - - if (!psb->cipher_keysize) - goto err_out_exit; - - cipher = crypto_alloc_ablkcipher(psb->cipher_string, 0, 0); - if (IS_ERR(cipher)) { - err = PTR_ERR(cipher); - dprintk("%s: idx: %u: failed to allocate cipher '%s', err: %d.\n", - __func__, psb->idx, psb->cipher_string, err); - goto err_out_exit; - } - - crypto_ablkcipher_clear_flags(cipher, ~0); - - err = crypto_ablkcipher_setkey(cipher, psb->cipher_key, psb->cipher_keysize); - if (err) { - dprintk("%s: idx: %u: failed to set key for cipher '%s', err: %d.\n", - __func__, psb->idx, psb->cipher_string, err); - goto err_out_free; - } - - return cipher; - -err_out_free: - crypto_free_ablkcipher(cipher); -err_out_exit: - return ERR_PTR(err); -} - -int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb) -{ - int err; - - e->page_num = 0; - - e->size = PAGE_SIZE; - e->data = kmalloc(e->size, GFP_KERNEL); - if (!e->data) { - err = -ENOMEM; - goto err_out_exit; - } - - if (psb->hash_string) { - e->hash = pohmelfs_init_hash(psb); - if (IS_ERR(e->hash)) { - err = PTR_ERR(e->hash); - e->hash = NULL; - goto err_out_free; - } - } - - if (psb->cipher_string) { - e->cipher = pohmelfs_init_cipher(psb); - if (IS_ERR(e->cipher)) { - err = PTR_ERR(e->cipher); - e->cipher = NULL; - goto err_out_free_hash; - } - } - - return 0; - -err_out_free_hash: - crypto_free_hash(e->hash); -err_out_free: - kfree(e->data); -err_out_exit: - return err; -} - -void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e) -{ - crypto_free_hash(e->hash); - crypto_free_ablkcipher(e->cipher); - kfree(e->data); -} - -static void pohmelfs_crypto_complete(struct crypto_async_request *req, int err) -{ - struct pohmelfs_crypto_completion *c = req->data; - - if (err == -EINPROGRESS) - return; - - dprintk("%s: req: %p, err: %d.\n", __func__, req, err); - c->error = err; - complete(&c->complete); -} - -static int pohmelfs_crypto_process(struct ablkcipher_request *req, - struct scatterlist *sg_dst, struct scatterlist *sg_src, - void *iv, int enc, unsigned long timeout) -{ - struct pohmelfs_crypto_completion complete; - int err; - - init_completion(&complete.complete); - complete.error = -EINPROGRESS; - - ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - pohmelfs_crypto_complete, &complete); - - ablkcipher_request_set_crypt(req, sg_src, sg_dst, sg_src->length, iv); - - if (enc) - err = crypto_ablkcipher_encrypt(req); - else - err = crypto_ablkcipher_decrypt(req); - - switch (err) { - case -EINPROGRESS: - case -EBUSY: - err = wait_for_completion_interruptible_timeout(&complete.complete, - timeout); - if (!err) - err = -ETIMEDOUT; - else if (err > 0) - err = complete.error; - break; - default: - break; - } - - return err; -} - -int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 cmd_iv, - void *data, struct page *page, unsigned int size) -{ - int err; - struct scatterlist sg; - - if (!e->cipher && !e->hash) - return 0; - - dprintk("%s: eng: %p, iv: %llx, data: %p, page: %p/%lu, size: %u.\n", - __func__, e, cmd_iv, data, page, (page) ? page->index : 0, size); - - if (data) { - sg_init_one(&sg, data, size); - } else { - sg_init_table(&sg, 1); - sg_set_page(&sg, page, size, 0); - } - - if (e->cipher) { - struct ablkcipher_request *req = e->data + crypto_hash_digestsize(e->hash); - u8 iv[32]; - - memset(iv, 0, sizeof(iv)); - memcpy(iv, &cmd_iv, sizeof(cmd_iv)); - - ablkcipher_request_set_tfm(req, e->cipher); - - err = pohmelfs_crypto_process(req, &sg, &sg, iv, 0, e->timeout); - if (err) - goto err_out_exit; - } - - if (e->hash) { - struct hash_desc desc; - void *dst = e->data + e->size/2; - - desc.tfm = e->hash; - desc.flags = 0; - - err = crypto_hash_init(&desc); - if (err) - goto err_out_exit; - - err = crypto_hash_update(&desc, &sg, size); - if (err) - goto err_out_exit; - - err = crypto_hash_final(&desc, dst); - if (err) - goto err_out_exit; - - err = !!memcmp(dst, e->data, crypto_hash_digestsize(e->hash)); - - if (err) { -#ifdef CONFIG_POHMELFS_DEBUG - unsigned int i; - unsigned char *recv = e->data, *calc = dst; - - dprintk("%s: eng: %p, hash: %p, cipher: %p: iv : %llx, hash mismatch (recv/calc): ", - __func__, e, e->hash, e->cipher, cmd_iv); - for (i = 0; i < crypto_hash_digestsize(e->hash); ++i) { -#if 0 - dprintka("%02x ", recv[i]); - if (recv[i] != calc[i]) { - dprintka("| calc byte: %02x.\n", calc[i]); - break; - } -#else - dprintka("%02x/%02x ", recv[i], calc[i]); -#endif - } - dprintk("\n"); -#endif - goto err_out_exit; - } else { - dprintk("%s: eng: %p, hash: %p, cipher: %p: hashes matched.\n", - __func__, e, e->hash, e->cipher); - } - } - - dprintk("%s: eng: %p, size: %u, hash: %p, cipher: %p: completed.\n", - __func__, e, e->size, e->hash, e->cipher); - - return 0; - -err_out_exit: - dprintk("%s: eng: %p, hash: %p, cipher: %p: err: %d.\n", - __func__, e, e->hash, e->cipher, err); - return err; -} - -static int pohmelfs_trans_iter(struct netfs_trans *t, struct pohmelfs_crypto_engine *e, - int (*iterator) (struct pohmelfs_crypto_engine *e, - struct scatterlist *dst, - struct scatterlist *src)) -{ - void *data = t->iovec.iov_base + sizeof(struct netfs_cmd) + t->psb->crypto_attached_size; - unsigned int size = t->iovec.iov_len - sizeof(struct netfs_cmd) - t->psb->crypto_attached_size; - struct netfs_cmd *cmd = data; - unsigned int sz, pages = t->attached_pages, i, csize, cmd_cmd, dpage_idx; - struct scatterlist sg_src, sg_dst; - int err; - - while (size) { - cmd = data; - cmd_cmd = __be16_to_cpu(cmd->cmd); - csize = __be32_to_cpu(cmd->size); - cmd->iv = __cpu_to_be64(e->iv); - - if (cmd_cmd == NETFS_READ_PAGES || cmd_cmd == NETFS_READ_PAGE) - csize = __be16_to_cpu(cmd->ext); - - sz = csize + __be16_to_cpu(cmd->cpad) + sizeof(struct netfs_cmd); - - dprintk("%s: size: %u, sz: %u, cmd_size: %u, cmd_cpad: %u.\n", - __func__, size, sz, __be32_to_cpu(cmd->size), __be16_to_cpu(cmd->cpad)); - - data += sz; - size -= sz; - - sg_init_one(&sg_src, cmd->data, sz - sizeof(struct netfs_cmd)); - sg_init_one(&sg_dst, cmd->data, sz - sizeof(struct netfs_cmd)); - - err = iterator(e, &sg_dst, &sg_src); - if (err) - return err; - } - - if (!pages) - return 0; - - dpage_idx = 0; - for (i = 0; i < t->page_num; ++i) { - struct page *page = t->pages[i]; - struct page *dpage = e->pages[dpage_idx]; - - if (!page) - continue; - - sg_init_table(&sg_src, 1); - sg_init_table(&sg_dst, 1); - sg_set_page(&sg_src, page, page_private(page), 0); - sg_set_page(&sg_dst, dpage, page_private(page), 0); - - err = iterator(e, &sg_dst, &sg_src); - if (err) - return err; - - pages--; - if (!pages) - break; - dpage_idx++; - } - - return 0; -} - -static int pohmelfs_encrypt_iterator(struct pohmelfs_crypto_engine *e, - struct scatterlist *sg_dst, struct scatterlist *sg_src) -{ - struct ablkcipher_request *req = e->data; - u8 iv[32]; - - memset(iv, 0, sizeof(iv)); - - memcpy(iv, &e->iv, sizeof(e->iv)); - - return pohmelfs_crypto_process(req, sg_dst, sg_src, iv, 1, e->timeout); -} - -static int pohmelfs_encrypt(struct pohmelfs_crypto_thread *tc) -{ - struct netfs_trans *t = tc->trans; - struct pohmelfs_crypto_engine *e = &tc->eng; - struct ablkcipher_request *req = e->data; - - memset(req, 0, sizeof(struct ablkcipher_request)); - ablkcipher_request_set_tfm(req, e->cipher); - - e->iv = pohmelfs_gen_iv(t); - - return pohmelfs_trans_iter(t, e, pohmelfs_encrypt_iterator); -} - -static int pohmelfs_hash_iterator(struct pohmelfs_crypto_engine *e, - struct scatterlist *sg_dst, struct scatterlist *sg_src) -{ - return crypto_hash_update(e->data, sg_src, sg_src->length); -} - -static int pohmelfs_hash(struct pohmelfs_crypto_thread *tc) -{ - struct pohmelfs_crypto_engine *e = &tc->eng; - struct hash_desc *desc = e->data; - unsigned char *dst = tc->trans->iovec.iov_base + sizeof(struct netfs_cmd); - int err; - - desc->tfm = e->hash; - desc->flags = 0; - - err = crypto_hash_init(desc); - if (err) - return err; - - err = pohmelfs_trans_iter(tc->trans, e, pohmelfs_hash_iterator); - if (err) - return err; - - err = crypto_hash_final(desc, dst); - if (err) - return err; - - { - unsigned int i; - dprintk("%s: ", __func__); - for (i = 0; i < tc->psb->crypto_attached_size; ++i) - dprintka("%02x ", dst[i]); - dprintka("\n"); - } - - return 0; -} - -static void pohmelfs_crypto_pages_free(struct pohmelfs_crypto_engine *e) -{ - unsigned int i; - - for (i = 0; i < e->page_num; ++i) - __free_page(e->pages[i]); - kfree(e->pages); -} - -static int pohmelfs_crypto_pages_alloc(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb) -{ - unsigned int i; - - e->pages = kmalloc(psb->trans_max_pages * sizeof(struct page *), GFP_KERNEL); - if (!e->pages) - return -ENOMEM; - - for (i = 0; i < psb->trans_max_pages; ++i) { - e->pages[i] = alloc_page(GFP_KERNEL); - if (!e->pages[i]) - break; - } - - e->page_num = i; - if (!e->page_num) - goto err_out_free; - - return 0; - -err_out_free: - kfree(e->pages); - return -ENOMEM; -} - -static void pohmelfs_sys_crypto_exit_one(struct pohmelfs_crypto_thread *t) -{ - struct pohmelfs_sb *psb = t->psb; - - if (t->thread) - kthread_stop(t->thread); - - mutex_lock(&psb->crypto_thread_lock); - list_del(&t->thread_entry); - psb->crypto_thread_num--; - mutex_unlock(&psb->crypto_thread_lock); - - pohmelfs_crypto_engine_exit(&t->eng); - pohmelfs_crypto_pages_free(&t->eng); - kfree(t); -} - -static int pohmelfs_crypto_finish(struct netfs_trans *t, struct pohmelfs_sb *psb, int err) -{ - struct netfs_cmd *cmd = t->iovec.iov_base; - netfs_convert_cmd(cmd); - - if (likely(!err)) - err = netfs_trans_finish_send(t, psb); - - t->result = err; - netfs_trans_put(t); - - return err; -} - -void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th) -{ - struct pohmelfs_sb *psb = th->psb; - - th->page = NULL; - th->trans = NULL; - - mutex_lock(&psb->crypto_thread_lock); - list_move_tail(&th->thread_entry, &psb->crypto_ready_list); - mutex_unlock(&psb->crypto_thread_lock); - wake_up(&psb->wait); -} - -static int pohmelfs_crypto_thread_trans(struct pohmelfs_crypto_thread *t) -{ - struct netfs_trans *trans; - int err = 0; - - trans = t->trans; - trans->eng = NULL; - - if (t->eng.hash) { - err = pohmelfs_hash(t); - if (err) - goto out_complete; - } - - if (t->eng.cipher) { - err = pohmelfs_encrypt(t); - if (err) - goto out_complete; - trans->eng = &t->eng; - } - -out_complete: - t->page = NULL; - t->trans = NULL; - - if (!trans->eng) - pohmelfs_crypto_thread_make_ready(t); - - pohmelfs_crypto_finish(trans, t->psb, err); - return err; -} - -static int pohmelfs_crypto_thread_page(struct pohmelfs_crypto_thread *t) -{ - struct pohmelfs_crypto_engine *e = &t->eng; - struct page *page = t->page; - int err; - - WARN_ON(!PageChecked(page)); - - err = pohmelfs_crypto_process_input_data(e, e->iv, NULL, page, t->size); - if (!err) - SetPageUptodate(page); - else - SetPageError(page); - unlock_page(page); - page_cache_release(page); - - pohmelfs_crypto_thread_make_ready(t); - - return err; -} - -static int pohmelfs_crypto_thread_func(void *data) -{ - struct pohmelfs_crypto_thread *t = data; - - while (!kthread_should_stop()) { - wait_event_interruptible(t->wait, kthread_should_stop() || - t->trans || t->page); - - if (kthread_should_stop()) - break; - - if (!t->trans && !t->page) - continue; - - dprintk("%s: thread: %p, trans: %p, page: %p.\n", - __func__, t, t->trans, t->page); - - if (t->trans) - pohmelfs_crypto_thread_trans(t); - else if (t->page) - pohmelfs_crypto_thread_page(t); - } - - return 0; -} - -static void pohmelfs_crypto_flush(struct pohmelfs_sb *psb, struct list_head *head) -{ - while (!list_empty(head)) { - struct pohmelfs_crypto_thread *t = NULL; - - mutex_lock(&psb->crypto_thread_lock); - if (!list_empty(head)) { - t = list_first_entry(head, struct pohmelfs_crypto_thread, thread_entry); - list_del_init(&t->thread_entry); - } - mutex_unlock(&psb->crypto_thread_lock); - - if (t) - pohmelfs_sys_crypto_exit_one(t); - } -} - -static void pohmelfs_sys_crypto_exit(struct pohmelfs_sb *psb) -{ - while (!list_empty(&psb->crypto_active_list) || !list_empty(&psb->crypto_ready_list)) { - dprintk("%s: crypto_thread_num: %u.\n", __func__, psb->crypto_thread_num); - pohmelfs_crypto_flush(psb, &psb->crypto_active_list); - pohmelfs_crypto_flush(psb, &psb->crypto_ready_list); - } -} - -static int pohmelfs_sys_crypto_init(struct pohmelfs_sb *psb) -{ - unsigned int i; - struct pohmelfs_crypto_thread *t; - struct pohmelfs_config *c; - struct netfs_state *st; - int err; - - list_for_each_entry(c, &psb->state_list, config_entry) { - st = &c->state; - - err = pohmelfs_crypto_engine_init(&st->eng, psb); - if (err) - goto err_out_exit; - - dprintk("%s: st: %p, eng: %p, hash: %p, cipher: %p.\n", - __func__, st, &st->eng, &st->eng.hash, &st->eng.cipher); - } - - for (i = 0; i < psb->crypto_thread_num; ++i) { - err = -ENOMEM; - t = kzalloc(sizeof(struct pohmelfs_crypto_thread), GFP_KERNEL); - if (!t) - goto err_out_free_state_engines; - - init_waitqueue_head(&t->wait); - - t->psb = psb; - t->trans = NULL; - t->eng.thread = t; - - err = pohmelfs_crypto_engine_init(&t->eng, psb); - if (err) - goto err_out_free_state_engines; - - err = pohmelfs_crypto_pages_alloc(&t->eng, psb); - if (err) - goto err_out_free; - - t->thread = kthread_run(pohmelfs_crypto_thread_func, t, - "pohmelfs-crypto-%d-%d", psb->idx, i); - if (IS_ERR(t->thread)) { - err = PTR_ERR(t->thread); - t->thread = NULL; - goto err_out_free; - } - - if (t->eng.cipher) - psb->crypto_align_size = crypto_ablkcipher_blocksize(t->eng.cipher); - - mutex_lock(&psb->crypto_thread_lock); - list_add_tail(&t->thread_entry, &psb->crypto_ready_list); - mutex_unlock(&psb->crypto_thread_lock); - } - - psb->crypto_thread_num = i; - return 0; - -err_out_free: - pohmelfs_sys_crypto_exit_one(t); -err_out_free_state_engines: - list_for_each_entry(c, &psb->state_list, config_entry) { - st = &c->state; - pohmelfs_crypto_engine_exit(&st->eng); - } -err_out_exit: - pohmelfs_sys_crypto_exit(psb); - return err; -} - -void pohmelfs_crypto_exit(struct pohmelfs_sb *psb) -{ - pohmelfs_sys_crypto_exit(psb); - - kfree(psb->hash_string); - kfree(psb->cipher_string); -} - -static int pohmelfs_crypt_init_complete(struct page **pages, unsigned int page_num, - void *private, int err) -{ - struct pohmelfs_sb *psb = private; - - psb->flags = -err; - dprintk("%s: err: %d.\n", __func__, err); - - wake_up(&psb->wait); - - return err; -} - -static int pohmelfs_crypto_init_handshake(struct pohmelfs_sb *psb) -{ - struct netfs_trans *t; - struct netfs_crypto_capabilities *cap; - struct netfs_cmd *cmd; - char *str; - int err = -ENOMEM, size; - - size = sizeof(struct netfs_crypto_capabilities) + - psb->cipher_strlen + psb->hash_strlen + 2; /* 0 bytes */ - - t = netfs_trans_alloc(psb, size, 0, 0); - if (!t) - goto err_out_exit; - - t->complete = pohmelfs_crypt_init_complete; - t->private = psb; - - cmd = netfs_trans_current(t); - cap = (struct netfs_crypto_capabilities *)(cmd + 1); - str = (char *)(cap + 1); - - cmd->cmd = NETFS_CAPABILITIES; - cmd->id = POHMELFS_CRYPTO_CAPABILITIES; - cmd->size = size; - cmd->start = 0; - cmd->ext = 0; - cmd->csize = 0; - - netfs_convert_cmd(cmd); - netfs_trans_update(cmd, t, size); - - cap->hash_strlen = psb->hash_strlen; - if (cap->hash_strlen) { - sprintf(str, "%s", psb->hash_string); - str += cap->hash_strlen; - } - - cap->cipher_strlen = psb->cipher_strlen; - cap->cipher_keysize = psb->cipher_keysize; - if (cap->cipher_strlen) - sprintf(str, "%s", psb->cipher_string); - - netfs_convert_crypto_capabilities(cap); - - psb->flags = ~0; - err = netfs_trans_finish(t, psb); - if (err) - goto err_out_exit; - - err = wait_event_interruptible_timeout(psb->wait, (psb->flags != ~0), - psb->wait_on_page_timeout); - if (!err) - err = -ETIMEDOUT; - else if (err > 0) - err = -psb->flags; - - if (!err) - psb->perform_crypto = 1; - psb->flags = 0; - - /* - * At this point NETFS_CAPABILITIES response command - * should setup superblock in a way, which is acceptable - * for both client and server, so if server refuses connection, - * it will send error in transaction response. - */ - - if (err) - goto err_out_exit; - - return 0; - -err_out_exit: - return err; -} - -int pohmelfs_crypto_init(struct pohmelfs_sb *psb) -{ - int err; - - if (!psb->cipher_string && !psb->hash_string) - return 0; - - err = pohmelfs_crypto_init_handshake(psb); - if (err) - return err; - - err = pohmelfs_sys_crypto_init(psb); - if (err) - return err; - - return 0; -} - -static int pohmelfs_crypto_thread_get(struct pohmelfs_sb *psb, - int (*action)(struct pohmelfs_crypto_thread *t, void *data), void *data) -{ - struct pohmelfs_crypto_thread *t = NULL; - int err; - - while (!t) { - err = wait_event_interruptible_timeout(psb->wait, - !list_empty(&psb->crypto_ready_list), - psb->wait_on_page_timeout); - - t = NULL; - err = 0; - mutex_lock(&psb->crypto_thread_lock); - if (!list_empty(&psb->crypto_ready_list)) { - t = list_entry(psb->crypto_ready_list.prev, - struct pohmelfs_crypto_thread, - thread_entry); - - list_move_tail(&t->thread_entry, - &psb->crypto_active_list); - - action(t, data); - wake_up(&t->wait); - - } - mutex_unlock(&psb->crypto_thread_lock); - } - - return err; -} - -static int pohmelfs_trans_crypt_action(struct pohmelfs_crypto_thread *t, void *data) -{ - struct netfs_trans *trans = data; - - netfs_trans_get(trans); - t->trans = trans; - - dprintk("%s: t: %p, gen: %u, thread: %p.\n", __func__, trans, trans->gen, t); - return 0; -} - -int pohmelfs_trans_crypt(struct netfs_trans *trans, struct pohmelfs_sb *psb) -{ - if ((!psb->hash_string && !psb->cipher_string) || !psb->perform_crypto) { - netfs_trans_get(trans); - return pohmelfs_crypto_finish(trans, psb, 0); - } - - return pohmelfs_crypto_thread_get(psb, pohmelfs_trans_crypt_action, trans); -} - -struct pohmelfs_crypto_input_action_data { - struct page *page; - struct pohmelfs_crypto_engine *e; - u64 iv; - unsigned int size; -}; - -static int pohmelfs_crypt_input_page_action(struct pohmelfs_crypto_thread *t, void *data) -{ - struct pohmelfs_crypto_input_action_data *act = data; - - memcpy(t->eng.data, act->e->data, t->psb->crypto_attached_size); - - t->size = act->size; - t->eng.iv = act->iv; - - t->page = act->page; - return 0; -} - -int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e, - struct page *page, unsigned int size, u64 iv) -{ - struct inode *inode = page->mapping->host; - struct pohmelfs_crypto_input_action_data act; - int err = -ENOENT; - - act.page = page; - act.e = e; - act.size = size; - act.iv = iv; - - err = pohmelfs_crypto_thread_get(POHMELFS_SB(inode->i_sb), - pohmelfs_crypt_input_page_action, &act); - if (err) - goto err_out_exit; - - return 0; - -err_out_exit: - SetPageUptodate(page); - page_cache_release(page); - - return err; -} diff --git a/drivers/staging/pohmelfs/dir.c b/drivers/staging/pohmelfs/dir.c deleted file mode 100644 index 2ee4491..0000000 --- a/drivers/staging/pohmelfs/dir.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -static int pohmelfs_cmp_hash(struct pohmelfs_name *n, u32 hash) -{ - if (n->hash > hash) - return -1; - if (n->hash < hash) - return 1; - - return 0; -} - -static struct pohmelfs_name *pohmelfs_search_hash_unprecise(struct pohmelfs_inode *pi, u32 hash) -{ - struct rb_node *n = pi->hash_root.rb_node; - struct pohmelfs_name *tmp = NULL; - int cmp; - - while (n) { - tmp = rb_entry(n, struct pohmelfs_name, hash_node); - - cmp = pohmelfs_cmp_hash(tmp, hash); - if (cmp < 0) - n = n->rb_left; - else if (cmp > 0) - n = n->rb_right; - else - break; - - } - - return tmp; -} - -struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash) -{ - struct pohmelfs_name *tmp; - - tmp = pohmelfs_search_hash_unprecise(pi, hash); - if (tmp && (tmp->hash == hash)) - return tmp; - - return NULL; -} - -static void __pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *node) -{ - rb_erase(&node->hash_node, &parent->hash_root); -} - -/* - * Remove name cache entry from its caches and free it. - */ -static void pohmelfs_name_free(struct pohmelfs_inode *parent, struct pohmelfs_name *node) -{ - __pohmelfs_name_del(parent, node); - list_del(&node->sync_create_entry); - kfree(node); -} - -static struct pohmelfs_name *pohmelfs_insert_hash(struct pohmelfs_inode *pi, - struct pohmelfs_name *new) -{ - struct rb_node **n = &pi->hash_root.rb_node, *parent = NULL; - struct pohmelfs_name *ret = NULL, *tmp; - int cmp; - - while (*n) { - parent = *n; - - tmp = rb_entry(parent, struct pohmelfs_name, hash_node); - - cmp = pohmelfs_cmp_hash(tmp, new->hash); - if (cmp < 0) - n = &parent->rb_left; - else if (cmp > 0) - n = &parent->rb_right; - else { - ret = tmp; - break; - } - } - - if (ret) { - printk("%s: exist: parent: %llu, ino: %llu, hash: %x, len: %u, data: '%s', " - "new: ino: %llu, hash: %x, len: %u, data: '%s'.\n", - __func__, pi->ino, - ret->ino, ret->hash, ret->len, ret->data, - new->ino, new->hash, new->len, new->data); - ret->ino = new->ino; - return ret; - } - - rb_link_node(&new->hash_node, parent, n); - rb_insert_color(&new->hash_node, &pi->hash_root); - - return NULL; -} - -/* - * Free name cache for given inode. - */ -void pohmelfs_free_names(struct pohmelfs_inode *parent) -{ - struct rb_node *rb_node; - struct pohmelfs_name *n; - - for (rb_node = rb_first(&parent->hash_root); rb_node;) { - n = rb_entry(rb_node, struct pohmelfs_name, hash_node); - rb_node = rb_next(rb_node); - - pohmelfs_name_free(parent, n); - } -} - -static void pohmelfs_fix_offset(struct pohmelfs_inode *parent, struct pohmelfs_name *node) -{ - parent->total_len -= node->len; -} - -/* - * Free name cache entry helper. - */ -void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *node) -{ - pohmelfs_fix_offset(parent, node); - pohmelfs_name_free(parent, node); -} - -/* - * Insert new name cache entry into all hash cache. - */ -static int pohmelfs_insert_name(struct pohmelfs_inode *parent, struct pohmelfs_name *n) -{ - struct pohmelfs_name *name; - - name = pohmelfs_insert_hash(parent, n); - if (name) - return -EEXIST; - - parent->total_len += n->len; - list_add_tail(&n->sync_create_entry, &parent->sync_create_list); - - return 0; -} - -/* - * Allocate new name cache entry. - */ -static struct pohmelfs_name *pohmelfs_name_alloc(unsigned int len) -{ - struct pohmelfs_name *n; - - n = kzalloc(sizeof(struct pohmelfs_name) + len, GFP_KERNEL); - if (!n) - return NULL; - - INIT_LIST_HEAD(&n->sync_create_entry); - - n->data = (char *)(n+1); - - return n; -} - -/* - * Add new name entry into directory's cache. - */ -static int pohmelfs_add_dir(struct pohmelfs_sb *psb, struct pohmelfs_inode *parent, - struct pohmelfs_inode *npi, struct qstr *str, unsigned int mode, int link) -{ - int err = -ENOMEM; - struct pohmelfs_name *n; - - n = pohmelfs_name_alloc(str->len + 1); - if (!n) - goto err_out_exit; - - n->ino = npi->ino; - n->mode = mode; - n->len = str->len; - n->hash = str->hash; - sprintf(n->data, "%s", str->name); - - mutex_lock(&parent->offset_lock); - err = pohmelfs_insert_name(parent, n); - mutex_unlock(&parent->offset_lock); - - if (err) { - if (err != -EEXIST) - goto err_out_free; - kfree(n); - } - - return 0; - -err_out_free: - kfree(n); -err_out_exit: - return err; -} - -/* - * Create new inode for given parameters (name, inode info, parent). - * This does not create object on the server, it will be synced there during writeback. - */ -struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb, - struct pohmelfs_inode *parent, struct qstr *str, - struct netfs_inode_info *info, int link) -{ - struct inode *new = NULL; - struct pohmelfs_inode *npi; - int err = -EEXIST; - - dprintk("%s: creating inode: parent: %llu, ino: %llu, str: %p.\n", - __func__, (parent) ? parent->ino : 0, info->ino, str); - - err = -ENOMEM; - new = iget_locked(psb->sb, info->ino); - if (!new) - goto err_out_exit; - - npi = POHMELFS_I(new); - npi->ino = info->ino; - err = 0; - - if (new->i_state & I_NEW) { - dprintk("%s: filling VFS inode: %lu/%llu.\n", - __func__, new->i_ino, info->ino); - pohmelfs_fill_inode(new, info); - - if (S_ISDIR(info->mode)) { - struct qstr s; - - s.name = "."; - s.len = 1; - s.hash = jhash(s.name, s.len, 0); - - err = pohmelfs_add_dir(psb, npi, npi, &s, info->mode, 0); - if (err) - goto err_out_put; - - s.name = ".."; - s.len = 2; - s.hash = jhash(s.name, s.len, 0); - - err = pohmelfs_add_dir(psb, npi, (parent) ? parent : npi, &s, - (parent) ? parent->vfs_inode.i_mode : npi->vfs_inode.i_mode, 0); - if (err) - goto err_out_put; - } - } - - if (str) { - if (parent) { - err = pohmelfs_add_dir(psb, parent, npi, str, info->mode, link); - - dprintk("%s: %s inserted name: '%s', new_offset: %llu, ino: %llu, parent: %llu.\n", - __func__, (err) ? "unsuccessfully" : "successfully", - str->name, parent->total_len, info->ino, parent->ino); - - if (err && err != -EEXIST) - goto err_out_put; - } - } - - if (new->i_state & I_NEW) { - if (parent) - mark_inode_dirty(&parent->vfs_inode); - mark_inode_dirty(new); - } - - set_bit(NETFS_INODE_OWNED, &npi->state); - npi->lock_type = POHMELFS_WRITE_LOCK; - unlock_new_inode(new); - - return npi; - -err_out_put: - printk("%s: putting inode: %p, npi: %p, error: %d.\n", __func__, new, npi, err); - iput(new); -err_out_exit: - return ERR_PTR(err); -} - -static int pohmelfs_remote_sync_complete(struct page **pages, unsigned int page_num, - void *private, int err) -{ - struct pohmelfs_inode *pi = private; - struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); - - dprintk("%s: ino: %llu, err: %d.\n", __func__, pi->ino, err); - - if (err) - pi->error = err; - wake_up(&psb->wait); - pohmelfs_put_inode(pi); - - return err; -} - -/* - * Receive directory content from the server. - * This should be only done for objects, which were not created locally, - * and which were not synced previously. - */ -static int pohmelfs_sync_remote_dir(struct pohmelfs_inode *pi) -{ - struct inode *inode = &pi->vfs_inode; - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - long ret = psb->wait_on_page_timeout; - int err; - - dprintk("%s: dir: %llu, state: %lx: remote_synced: %d.\n", - __func__, pi->ino, pi->state, test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)); - - if (test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state)) - return 0; - - if (!igrab(inode)) { - err = -ENOENT; - goto err_out_exit; - } - - err = pohmelfs_meta_command(pi, NETFS_READDIR, NETFS_TRANS_SINGLE_DST, - pohmelfs_remote_sync_complete, pi, 0); - if (err) - goto err_out_exit; - - pi->error = 0; - ret = wait_event_interruptible_timeout(psb->wait, - test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state) || pi->error, ret); - dprintk("%s: awake dir: %llu, ret: %ld, err: %d.\n", __func__, pi->ino, ret, pi->error); - if (ret <= 0) { - err = ret; - if (!err) - err = -ETIMEDOUT; - goto err_out_exit; - } - - if (pi->error) - return pi->error; - - return 0; - -err_out_exit: - clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); - - return err; -} - -static int pohmelfs_dir_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} - -/* - * VFS readdir callback. Syncs directory content from server if needed, - * and provides direntry info to the userspace. - */ -static int pohmelfs_readdir(struct file *file, void *dirent, filldir_t filldir) -{ - struct inode *inode = file->f_path.dentry->d_inode; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - struct pohmelfs_name *n; - struct rb_node *rb_node; - int err = 0, mode; - u64 len; - - dprintk("%s: parent: %llu, fpos: %llu, hash: %08lx.\n", - __func__, pi->ino, (u64)file->f_pos, - (unsigned long)file->private_data); -#if 0 - err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_READ_LOCK); - if (err) - return err; -#endif - err = pohmelfs_sync_remote_dir(pi); - if (err) - return err; - - if (file->private_data && (file->private_data == (void *)(unsigned long)file->f_pos)) - return 0; - - mutex_lock(&pi->offset_lock); - n = pohmelfs_search_hash_unprecise(pi, (unsigned long)file->private_data); - - while (n) { - mode = (n->mode >> 12) & 15; - - dprintk("%s: offset: %llu, parent ino: %llu, name: '%s', len: %u, ino: %llu, " - "mode: %o/%o, fpos: %llu, hash: %08x.\n", - __func__, file->f_pos, pi->ino, n->data, n->len, - n->ino, n->mode, mode, file->f_pos, n->hash); - - file->private_data = (void *)(unsigned long)n->hash; - - len = n->len; - err = filldir(dirent, n->data, n->len, file->f_pos, n->ino, mode); - - if (err < 0) { - dprintk("%s: err: %d.\n", __func__, err); - err = 0; - break; - } - - file->f_pos += len; - - rb_node = rb_next(&n->hash_node); - - if (!rb_node || (rb_node == &n->hash_node)) { - file->private_data = (void *)(unsigned long)file->f_pos; - break; - } - - n = rb_entry(rb_node, struct pohmelfs_name, hash_node); - } - mutex_unlock(&pi->offset_lock); - - return err; -} - -static loff_t pohmelfs_dir_lseek(struct file *file, loff_t offset, int origin) -{ - file->f_pos = offset; - file->private_data = NULL; - return offset; -} - -const struct file_operations pohmelfs_dir_fops = { - .open = pohmelfs_dir_open, - .read = generic_read_dir, - .llseek = pohmelfs_dir_lseek, - .readdir = pohmelfs_readdir, -}; - -/* - * Lookup single object on server. - */ -static int pohmelfs_lookup_single(struct pohmelfs_inode *parent, - struct qstr *str, u64 ino) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(parent->vfs_inode.i_sb); - long ret = msecs_to_jiffies(5000); - int err; - - set_bit(NETFS_COMMAND_PENDING, &parent->state); - err = pohmelfs_meta_command_data(parent, parent->ino, NETFS_LOOKUP, - (char *)str->name, NETFS_TRANS_SINGLE_DST, NULL, NULL, ino); - if (err) - goto err_out_exit; - - err = 0; - ret = wait_event_interruptible_timeout(psb->wait, - !test_bit(NETFS_COMMAND_PENDING, &parent->state), ret); - if (ret <= 0) { - err = ret; - if (!err) - err = -ETIMEDOUT; - } - - if (err) - goto err_out_exit; - - return 0; - -err_out_exit: - clear_bit(NETFS_COMMAND_PENDING, &parent->state); - - printk("%s: failed: parent: %llu, ino: %llu, name: '%s', err: %d.\n", - __func__, parent->ino, ino, str->name, err); - - return err; -} - -/* - * VFS lookup callback. - * We first try to get inode number from local name cache, if we have one, - * then inode can be found in inode cache. If there is no inode or no object in - * local cache, try to lookup it on server. This only should be done for directories, - * which were not created locally, otherwise remote server does not know about dir at all, - * so no need to try to know that. - */ -struct dentry *pohmelfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct pohmelfs_inode *parent = POHMELFS_I(dir); - struct pohmelfs_name *n; - struct inode *inode = NULL; - unsigned long ino = 0; - int err, lock_type = POHMELFS_READ_LOCK, need_lock = 1; - struct qstr str = dentry->d_name; - - if ((nd->intent.open.flags & O_ACCMODE) != O_RDONLY) - lock_type = POHMELFS_WRITE_LOCK; - - if (test_bit(NETFS_INODE_OWNED, &parent->state)) { - if (lock_type == parent->lock_type) - need_lock = 0; - if ((lock_type == POHMELFS_READ_LOCK) && (parent->lock_type == POHMELFS_WRITE_LOCK)) - need_lock = 0; - } - - if ((lock_type == POHMELFS_READ_LOCK) && !test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state)) - need_lock = 1; - - str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); - - mutex_lock(&parent->offset_lock); - n = pohmelfs_search_hash(parent, str.hash); - if (n) - ino = n->ino; - mutex_unlock(&parent->offset_lock); - - dprintk("%s: start ino: %lu, inode: %p, name: '%s', hash: %x, parent_state: %lx, need_lock: %d.\n", - __func__, ino, inode, str.name, str.hash, parent->state, need_lock); - - if (ino) { - inode = ilookup(dir->i_sb, ino); - if (inode) - goto out; - } - - dprintk("%s: no inode dir: %p, dir_ino: %llu, name: '%s', len: %u, dir_state: %lx, ino: %lu.\n", - __func__, dir, parent->ino, - str.name, str.len, parent->state, ino); - - if (!ino) { - if (!need_lock) - goto out; - } - - err = pohmelfs_data_lock(parent, 0, ~0, lock_type); - if (err) - goto out; - - err = pohmelfs_lookup_single(parent, &str, ino); - if (err) - goto out; - - if (!ino) { - mutex_lock(&parent->offset_lock); - n = pohmelfs_search_hash(parent, str.hash); - if (n) - ino = n->ino; - mutex_unlock(&parent->offset_lock); - } - - if (ino) { - inode = ilookup(dir->i_sb, ino); - dprintk("%s: second lookup ino: %lu, inode: %p, name: '%s', hash: %x.\n", - __func__, ino, inode, str.name, str.hash); - if (!inode) { - dprintk("%s: No inode for ino: %lu, name: '%s', hash: %x.\n", - __func__, ino, str.name, str.hash); - /* return NULL; */ - return ERR_PTR(-EACCES); - } - } else { - printk("%s: No inode number : name: '%s', hash: %x.\n", - __func__, str.name, str.hash); - } -out: - return d_splice_alias(inode, dentry); -} - -/* - * Create new object in local cache. Object will be synced to server - * during writeback for given inode. - */ -struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb, - struct pohmelfs_inode *parent, struct qstr *str, u64 start, umode_t mode) -{ - struct pohmelfs_inode *npi; - int err = -ENOMEM; - struct netfs_inode_info info; - - dprintk("%s: name: '%s', mode: %ho, start: %llu.\n", - __func__, str->name, mode, start); - - info.mode = mode; - info.ino = start; - - if (!start) - info.ino = pohmelfs_new_ino(psb); - - info.nlink = S_ISDIR(mode) ? 2 : 1; - info.uid = current_fsuid(); - info.gid = current_fsgid(); - info.size = 0; - info.blocksize = 512; - info.blocks = 0; - info.rdev = 0; - info.version = 0; - - npi = pohmelfs_new_inode(psb, parent, str, &info, !!start); - if (IS_ERR(npi)) { - err = PTR_ERR(npi); - goto err_out_unlock; - } - - return npi; - -err_out_unlock: - dprintk("%s: err: %d.\n", __func__, err); - return ERR_PTR(err); -} - -/* - * Create local object and bind it to dentry. - */ -static int pohmelfs_create_entry(struct inode *dir, struct dentry *dentry, - u64 start, umode_t mode) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(dir->i_sb); - struct pohmelfs_inode *npi, *parent; - struct qstr str = dentry->d_name; - int err; - - parent = POHMELFS_I(dir); - - err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); - if (err) - return err; - - str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); - - npi = pohmelfs_create_entry_local(psb, parent, &str, start, mode); - if (IS_ERR(npi)) - return PTR_ERR(npi); - - d_instantiate(dentry, &npi->vfs_inode); - - dprintk("%s: parent: %llu, inode: %llu, name: '%s', parent_nlink: %d, nlink: %d.\n", - __func__, parent->ino, npi->ino, dentry->d_name.name, - (signed)dir->i_nlink, (signed)npi->vfs_inode.i_nlink); - - return 0; -} - -/* - * VFS create and mkdir callbacks. - */ -static int pohmelfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - struct nameidata *nd) -{ - return pohmelfs_create_entry(dir, dentry, 0, mode); -} - -static int pohmelfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - int err; - - inode_inc_link_count(dir); - err = pohmelfs_create_entry(dir, dentry, 0, mode | S_IFDIR); - if (err) - inode_dec_link_count(dir); - - return err; -} - -static int pohmelfs_remove_entry(struct inode *dir, struct dentry *dentry) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(dir->i_sb); - struct inode *inode = dentry->d_inode; - struct pohmelfs_inode *parent = POHMELFS_I(dir), *pi = POHMELFS_I(inode); - struct pohmelfs_name *n; - int err = -ENOENT; - struct qstr str = dentry->d_name; - - err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); - if (err) - return err; - - str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); - - dprintk("%s: dir_ino: %llu, inode: %llu, name: '%s', nlink: %d.\n", - __func__, parent->ino, pi->ino, - str.name, (signed)inode->i_nlink); - - BUG_ON(!inode); - - mutex_lock(&parent->offset_lock); - n = pohmelfs_search_hash(parent, str.hash); - if (n) { - pohmelfs_fix_offset(parent, n); - if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) - pohmelfs_remove_child(pi, n); - - pohmelfs_name_free(parent, n); - err = 0; - } - mutex_unlock(&parent->offset_lock); - - if (!err) { - psb->avail_size += inode->i_size; - - pohmelfs_inode_del_inode(psb, pi); - - mark_inode_dirty(dir); - - inode->i_ctime = dir->i_ctime; - if (inode->i_nlink) - inode_dec_link_count(inode); - } - - return err; -} - -/* - * Unlink and rmdir VFS callbacks. - */ -static int pohmelfs_unlink(struct inode *dir, struct dentry *dentry) -{ - return pohmelfs_remove_entry(dir, dentry); -} - -static int pohmelfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - int err; - struct inode *inode = dentry->d_inode; - - dprintk("%s: parent: %llu, inode: %llu, name: '%s', parent_nlink: %d, nlink: %d.\n", - __func__, POHMELFS_I(dir)->ino, POHMELFS_I(inode)->ino, - dentry->d_name.name, (signed)dir->i_nlink, (signed)inode->i_nlink); - - err = pohmelfs_remove_entry(dir, dentry); - if (!err) { - inode_dec_link_count(dir); - inode_dec_link_count(inode); - } - - return err; -} - -/* - * Link creation is synchronous. - * I'm lazy. - * Earth is somewhat round. - */ -static int pohmelfs_create_link(struct pohmelfs_inode *parent, struct qstr *obj, - struct pohmelfs_inode *target, struct qstr *tstr) -{ - struct super_block *sb = parent->vfs_inode.i_sb; - struct pohmelfs_sb *psb = POHMELFS_SB(sb); - struct netfs_cmd *cmd; - struct netfs_trans *t; - void *data; - int err, parent_len, target_len = 0, cur_len, path_size = 0; - - err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); - if (err) - return err; - - err = sb->s_op->write_inode(&parent->vfs_inode, 0); - if (err) - goto err_out_exit; - - if (tstr) - target_len = tstr->len; - - parent_len = pohmelfs_path_length(parent); - if (target) - target_len += pohmelfs_path_length(target); - - if (parent_len < 0) { - err = parent_len; - goto err_out_exit; - } - - if (target_len < 0) { - err = target_len; - goto err_out_exit; - } - - t = netfs_trans_alloc(psb, parent_len + target_len + obj->len + 2, 0, 0); - if (!t) { - err = -ENOMEM; - goto err_out_exit; - } - cur_len = netfs_trans_cur_len(t); - - cmd = netfs_trans_current(t); - if (IS_ERR(cmd)) { - err = PTR_ERR(cmd); - goto err_out_free; - } - - data = (void *)(cmd + 1); - cur_len -= sizeof(struct netfs_cmd); - - err = pohmelfs_construct_path_string(parent, data, parent_len); - if (err > 0) { - /* Do not place null-byte before the slash */ - path_size = err - 1; - cur_len -= path_size; - - err = snprintf(data + path_size, cur_len, "/%s|", obj->name); - - path_size += err; - cur_len -= err; - - cmd->ext = path_size - 1; /* No | symbol */ - - if (target) { - err = pohmelfs_construct_path_string(target, data + path_size, target_len); - if (err > 0) { - path_size += err; - cur_len -= err; - } - } - } - - if (err < 0) - goto err_out_free; - - cmd->start = 0; - - if (!target && tstr) { - if (tstr->len > cur_len - 1) { - err = -ENAMETOOLONG; - goto err_out_free; - } - - err = snprintf(data + path_size, cur_len, "%s", tstr->name) + 1; /* 0-byte */ - path_size += err; - cur_len -= err; - cmd->start = 1; - } - - dprintk("%s: parent: %llu, obj: '%s', target_inode: %llu, target_str: '%s', full: '%s'.\n", - __func__, parent->ino, obj->name, (target) ? target->ino : 0, (tstr) ? tstr->name : NULL, - (char *)data); - - cmd->cmd = NETFS_LINK; - cmd->size = path_size; - cmd->id = parent->ino; - - netfs_convert_cmd(cmd); - - netfs_trans_update(cmd, t, path_size); - - err = netfs_trans_finish(t, psb); - if (err) - goto err_out_exit; - - return 0; - -err_out_free: - t->result = err; - netfs_trans_put(t); -err_out_exit: - return err; -} - -/* - * VFS hard and soft link callbacks. - */ -static int pohmelfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - int err; - struct qstr str = dentry->d_name; - - str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); - - err = inode->i_sb->s_op->write_inode(inode, 0); - if (err) - return err; - - err = pohmelfs_create_link(POHMELFS_I(dir), &str, pi, NULL); - if (err) - return err; - - return pohmelfs_create_entry(dir, dentry, pi->ino, inode->i_mode); -} - -static int pohmelfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - struct qstr sym_str; - struct qstr str = dentry->d_name; - struct inode *inode; - int err; - - str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); - - sym_str.name = symname; - sym_str.len = strlen(symname); - - err = pohmelfs_create_link(POHMELFS_I(dir), &str, NULL, &sym_str); - if (err) - goto err_out_exit; - - err = pohmelfs_create_entry(dir, dentry, 0, S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); - if (err) - goto err_out_exit; - - inode = dentry->d_inode; - - err = page_symlink(inode, symname, sym_str.len + 1); - if (err) - goto err_out_put; - - return 0; - -err_out_put: - iput(inode); -err_out_exit: - return err; -} - -static int pohmelfs_send_rename(struct pohmelfs_inode *pi, struct pohmelfs_inode *parent, - struct qstr *str) -{ - int path_len, err, total_len = 0, inode_len, parent_len; - char *path; - struct netfs_trans *t; - struct netfs_cmd *cmd; - struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); - - parent_len = pohmelfs_path_length(parent); - inode_len = pohmelfs_path_length(pi); - - if (parent_len < 0 || inode_len < 0) - return -EINVAL; - - path_len = parent_len + inode_len + str->len + 3; - - t = netfs_trans_alloc(psb, path_len, 0, 0); - if (!t) - return -ENOMEM; - - cmd = netfs_trans_current(t); - path = (char *)(cmd + 1); - - err = pohmelfs_construct_path_string(pi, path, inode_len); - if (err < 0) - goto err_out_unlock; - - cmd->ext = err; - - path += err; - total_len += err; - path_len -= err; - - *path = '|'; - path++; - total_len++; - path_len--; - - err = pohmelfs_construct_path_string(parent, path, parent_len); - if (err < 0) - goto err_out_unlock; - - /* - * Do not place a null-byte before the final slash and the name. - */ - err--; - path += err; - total_len += err; - path_len -= err; - - err = snprintf(path, path_len - 1, "/%s", str->name); - - total_len += err + 1; /* 0 symbol */ - path_len -= err + 1; - - cmd->cmd = NETFS_RENAME; - cmd->id = pi->ino; - cmd->start = parent->ino; - cmd->size = total_len; - - netfs_convert_cmd(cmd); - - netfs_trans_update(cmd, t, total_len); - - return netfs_trans_finish(t, psb); - -err_out_unlock: - netfs_trans_free(t); - return err; -} - -static int pohmelfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct inode *inode = old_dentry->d_inode; - struct pohmelfs_inode *old_parent, *pi, *new_parent; - struct qstr str = new_dentry->d_name; - struct pohmelfs_name *n; - unsigned int old_hash; - int err = -ENOENT; - - pi = POHMELFS_I(inode); - old_parent = POHMELFS_I(old_dir); - - if (new_dir) - new_dir->i_sb->s_op->write_inode(new_dir, 0); - - old_hash = jhash(old_dentry->d_name.name, old_dentry->d_name.len, 0); - str.hash = jhash(new_dentry->d_name.name, new_dentry->d_name.len, 0); - - str.len = new_dentry->d_name.len; - str.name = new_dentry->d_name.name; - str.hash = jhash(new_dentry->d_name.name, new_dentry->d_name.len, 0); - - if (new_dir) { - new_parent = POHMELFS_I(new_dir); - err = -ENOTEMPTY; - - if (S_ISDIR(inode->i_mode) && - new_parent->total_len <= 3) - goto err_out_exit; - } else { - new_parent = old_parent; - } - - dprintk("%s: ino: %llu, parent: %llu, name: '%s' -> parent: %llu, name: '%s', i_size: %llu.\n", - __func__, pi->ino, old_parent->ino, old_dentry->d_name.name, - new_parent->ino, new_dentry->d_name.name, inode->i_size); - - if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state) && - test_bit(NETFS_INODE_OWNED, &pi->state)) { - err = pohmelfs_send_rename(pi, new_parent, &str); - if (err) - goto err_out_exit; - } - - n = pohmelfs_name_alloc(str.len + 1); - if (!n) - goto err_out_exit; - - mutex_lock(&new_parent->offset_lock); - n->ino = pi->ino; - n->mode = inode->i_mode; - n->len = str.len; - n->hash = str.hash; - sprintf(n->data, "%s", str.name); - - err = pohmelfs_insert_name(new_parent, n); - mutex_unlock(&new_parent->offset_lock); - - if (err) - goto err_out_exit; - - mutex_lock(&old_parent->offset_lock); - n = pohmelfs_search_hash(old_parent, old_hash); - if (n) - pohmelfs_name_del(old_parent, n); - mutex_unlock(&old_parent->offset_lock); - - mark_inode_dirty(inode); - mark_inode_dirty(&new_parent->vfs_inode); - - WARN_ON_ONCE(list_empty(&inode->i_dentry)); - - return 0; - -err_out_exit: - - clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); - - return err; -} - -/* - * POHMELFS directory inode operations. - */ -const struct inode_operations pohmelfs_dir_inode_ops = { - .link = pohmelfs_link, - .symlink = pohmelfs_symlink, - .unlink = pohmelfs_unlink, - .mkdir = pohmelfs_mkdir, - .rmdir = pohmelfs_rmdir, - .create = pohmelfs_create, - .lookup = pohmelfs_lookup, - .setattr = pohmelfs_setattr, - .rename = pohmelfs_rename, -}; diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c deleted file mode 100644 index 807e3f3..0000000 --- a/drivers/staging/pohmelfs/inode.c +++ /dev/null @@ -1,2055 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -#define POHMELFS_MAGIC_NUM 0x504f482e - -static struct kmem_cache *pohmelfs_inode_cache; -static atomic_t psb_bdi_num = ATOMIC_INIT(0); - -/* - * Removes inode from all trees, drops local name cache and removes all queued - * requests for object removal. - */ -void pohmelfs_inode_del_inode(struct pohmelfs_sb *psb, struct pohmelfs_inode *pi) -{ - mutex_lock(&pi->offset_lock); - pohmelfs_free_names(pi); - mutex_unlock(&pi->offset_lock); - - dprintk("%s: deleted stuff in ino: %llu.\n", __func__, pi->ino); -} - -/* - * Sync inode to server. - * Returns zero in success and negative error value otherwise. - * It will gather path to root directory into structures containing - * creation mode, permissions and names, so that the whole path - * to given inode could be created using only single network command. - */ -int pohmelfs_write_inode_create(struct inode *inode, struct netfs_trans *trans) -{ - struct pohmelfs_inode *pi = POHMELFS_I(inode); - int err = -ENOMEM, size; - struct netfs_cmd *cmd; - void *data; - int cur_len = netfs_trans_cur_len(trans); - - if (unlikely(cur_len < 0)) - return -ETOOSMALL; - - cmd = netfs_trans_current(trans); - cur_len -= sizeof(struct netfs_cmd); - - data = (void *)(cmd + 1); - - err = pohmelfs_construct_path_string(pi, data, cur_len); - if (err < 0) - goto err_out_exit; - - size = err; - - cmd->start = i_size_read(inode); - cmd->cmd = NETFS_CREATE; - cmd->size = size; - cmd->id = pi->ino; - cmd->ext = inode->i_mode; - - netfs_convert_cmd(cmd); - - netfs_trans_update(cmd, trans, size); - - return 0; - -err_out_exit: - printk("%s: completed ino: %llu, err: %d.\n", __func__, pi->ino, err); - return err; -} - -static int pohmelfs_write_trans_complete(struct page **pages, unsigned int page_num, - void *private, int err) -{ - unsigned i; - - dprintk("%s: pages: %lu-%lu, page_num: %u, err: %d.\n", - __func__, pages[0]->index, pages[page_num-1]->index, - page_num, err); - - for (i = 0; i < page_num; i++) { - struct page *page = pages[i]; - - if (!page) - continue; - - end_page_writeback(page); - - if (err < 0) { - SetPageError(page); - set_page_dirty(page); - } - - unlock_page(page); - page_cache_release(page); - - /* dprintk("%s: %3u/%u: page: %p.\n", __func__, i, page_num, page); */ - } - return err; -} - -static int pohmelfs_inode_has_dirty_pages(struct address_space *mapping, pgoff_t index) -{ - int ret; - struct page *page; - - rcu_read_lock(); - ret = radix_tree_gang_lookup_tag(&mapping->page_tree, - (void **)&page, index, 1, PAGECACHE_TAG_DIRTY); - rcu_read_unlock(); - return ret; -} - -static int pohmelfs_writepages(struct address_space *mapping, struct writeback_control *wbc) -{ - struct inode *inode = mapping->host; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - int err = 0; - int done = 0; - int nr_pages; - pgoff_t index; - pgoff_t end; /* Inclusive */ - int scanned = 0; - int range_whole = 0; - - if (wbc->range_cyclic) { - index = mapping->writeback_index; /* Start from prev offset */ - end = -1; - } else { - index = wbc->range_start >> PAGE_CACHE_SHIFT; - end = wbc->range_end >> PAGE_CACHE_SHIFT; - if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) - range_whole = 1; - scanned = 1; - } -retry: - while (!done && (index <= end)) { - unsigned int i = min(end - index, (pgoff_t)psb->trans_max_pages); - int path_len; - struct netfs_trans *trans; - - err = pohmelfs_inode_has_dirty_pages(mapping, index); - if (!err) - break; - - err = pohmelfs_path_length(pi); - if (err < 0) - break; - - path_len = err; - - if (path_len <= 2) { - err = -ENOENT; - break; - } - - trans = netfs_trans_alloc(psb, path_len, 0, i); - if (!trans) { - err = -ENOMEM; - break; - } - trans->complete = &pohmelfs_write_trans_complete; - - trans->page_num = nr_pages = find_get_pages_tag(mapping, &index, - PAGECACHE_TAG_DIRTY, trans->page_num, - trans->pages); - - dprintk("%s: t: %p, nr_pages: %u, end: %lu, index: %lu, max: %u.\n", - __func__, trans, nr_pages, end, index, trans->page_num); - - if (!nr_pages) - goto err_out_reset; - - err = pohmelfs_write_inode_create(inode, trans); - if (err) - goto err_out_reset; - - err = 0; - scanned = 1; - - for (i = 0; i < trans->page_num; i++) { - struct page *page = trans->pages[i]; - - lock_page(page); - - if (unlikely(page->mapping != mapping)) - goto out_continue; - - if (!wbc->range_cyclic && page->index > end) { - done = 1; - goto out_continue; - } - - if (wbc->sync_mode != WB_SYNC_NONE) - wait_on_page_writeback(page); - - if (PageWriteback(page) || - !clear_page_dirty_for_io(page)) { - dprintk("%s: not clear for io page: %p, writeback: %d.\n", - __func__, page, PageWriteback(page)); - goto out_continue; - } - - set_page_writeback(page); - - trans->attached_size += page_private(page); - trans->attached_pages++; -#if 0 - dprintk("%s: %u/%u added trans: %p, gen: %u, page: %p, [High: %d], size: %lu, idx: %lu.\n", - __func__, i, trans->page_num, trans, trans->gen, page, - !!PageHighMem(page), page_private(page), page->index); -#endif - wbc->nr_to_write--; - - if (wbc->nr_to_write <= 0) - done = 1; - - continue; -out_continue: - unlock_page(page); - trans->pages[i] = NULL; - } - - err = netfs_trans_finish(trans, psb); - if (err) - break; - - continue; - -err_out_reset: - trans->result = err; - netfs_trans_reset(trans); - netfs_trans_put(trans); - break; - } - - if (!scanned && !done) { - /* - * We hit the last page and there is more work to be done: wrap - * back to the start of the file - */ - scanned = 1; - index = 0; - goto retry; - } - - if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) - mapping->writeback_index = index; - - return err; -} - -/* - * Inode writeback creation completion callback. - * Only invoked for just created inodes, which do not have pages attached, - * like dirs and empty files. - */ -static int pohmelfs_write_inode_complete(struct page **pages, unsigned int page_num, - void *private, int err) -{ - struct inode *inode = private; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - - if (inode) { - if (err) { - mark_inode_dirty(inode); - clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); - } else { - set_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); - } - - pohmelfs_put_inode(pi); - } - - return err; -} - -int pohmelfs_write_create_inode(struct pohmelfs_inode *pi) -{ - struct netfs_trans *t; - struct inode *inode = &pi->vfs_inode; - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - int err; - - if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) - return 0; - - dprintk("%s: started ino: %llu.\n", __func__, pi->ino); - - err = pohmelfs_path_length(pi); - if (err < 0) - goto err_out_exit; - - t = netfs_trans_alloc(psb, err + 1, 0, 0); - if (!t) { - err = -ENOMEM; - goto err_out_exit; - } - t->complete = pohmelfs_write_inode_complete; - t->private = igrab(inode); - if (!t->private) { - err = -ENOENT; - goto err_out_put; - } - - err = pohmelfs_write_inode_create(inode, t); - if (err) - goto err_out_put; - - netfs_trans_finish(t, POHMELFS_SB(inode->i_sb)); - - return 0; - -err_out_put: - t->result = err; - netfs_trans_put(t); -err_out_exit: - return err; -} - -/* - * Sync all not-yet-created children in given directory to the server. - */ -static int pohmelfs_write_inode_create_children(struct inode *inode) -{ - struct pohmelfs_inode *parent = POHMELFS_I(inode); - struct super_block *sb = inode->i_sb; - struct pohmelfs_name *n; - - while (!list_empty(&parent->sync_create_list)) { - n = NULL; - mutex_lock(&parent->offset_lock); - if (!list_empty(&parent->sync_create_list)) { - n = list_first_entry(&parent->sync_create_list, - struct pohmelfs_name, sync_create_entry); - list_del_init(&n->sync_create_entry); - } - mutex_unlock(&parent->offset_lock); - - if (!n) - break; - - inode = ilookup(sb, n->ino); - - dprintk("%s: parent: %llu, ino: %llu, inode: %p.\n", - __func__, parent->ino, n->ino, inode); - - if (inode && (inode->i_state & I_DIRTY)) { - struct pohmelfs_inode *pi = POHMELFS_I(inode); - pohmelfs_write_create_inode(pi); - /* pohmelfs_meta_command(pi, NETFS_INODE_INFO, 0, NULL, NULL, 0); */ - iput(inode); - } - } - - return 0; -} - -/* - * Removes given child from given inode on server. - */ -int pohmelfs_remove_child(struct pohmelfs_inode *pi, struct pohmelfs_name *n) -{ - return pohmelfs_meta_command_data(pi, pi->ino, NETFS_REMOVE, NULL, 0, NULL, NULL, 0); -} - -/* - * Writeback for given inode. - */ -static int pohmelfs_write_inode(struct inode *inode, - struct writeback_control *wbc) -{ - struct pohmelfs_inode *pi = POHMELFS_I(inode); - - pohmelfs_write_create_inode(pi); - pohmelfs_write_inode_create_children(inode); - - return 0; -} - -/* - * It is not exported, sorry... - */ -static inline wait_queue_head_t *page_waitqueue(struct page *page) -{ - const struct zone *zone = page_zone(page); - - return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)]; -} - -static int pohmelfs_wait_on_page_locked(struct page *page) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(page->mapping->host->i_sb); - long ret = psb->wait_on_page_timeout; - DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); - int err = 0; - - if (!PageLocked(page)) - return 0; - - for (;;) { - prepare_to_wait(page_waitqueue(page), - &wait.wait, TASK_INTERRUPTIBLE); - - dprintk("%s: page: %p, locked: %d, uptodate: %d, error: %d, flags: %lx.\n", - __func__, page, PageLocked(page), PageUptodate(page), - PageError(page), page->flags); - - if (!PageLocked(page)) - break; - - if (!signal_pending(current)) { - ret = schedule_timeout(ret); - if (!ret) - break; - continue; - } - ret = -ERESTARTSYS; - break; - } - finish_wait(page_waitqueue(page), &wait.wait); - - if (!ret) - err = -ETIMEDOUT; - - - if (!err) - SetPageUptodate(page); - - if (err) - printk("%s: page: %p, uptodate: %d, locked: %d, err: %d.\n", - __func__, page, PageUptodate(page), PageLocked(page), err); - - return err; -} - -static int pohmelfs_read_page_complete(struct page **pages, unsigned int page_num, - void *private, int err) -{ - struct page *page = private; - - if (PageChecked(page)) - return err; - - if (err < 0) { - dprintk("%s: page: %p, err: %d.\n", __func__, page, err); - SetPageError(page); - } - - unlock_page(page); - - return err; -} - -/* - * Read a page from remote server. - * Function will wait until page is unlocked. - */ -static int pohmelfs_readpage(struct file *file, struct page *page) -{ - struct inode *inode = page->mapping->host; - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - struct pohmelfs_inode *pi = POHMELFS_I(inode); - struct netfs_trans *t; - struct netfs_cmd *cmd; - int err, path_len; - void *data; - u64 isize; - - err = pohmelfs_data_lock(pi, page->index << PAGE_CACHE_SHIFT, - PAGE_SIZE, POHMELFS_READ_LOCK); - if (err) - goto err_out_exit; - - isize = i_size_read(inode); - if (isize <= page->index << PAGE_CACHE_SHIFT) { - SetPageUptodate(page); - unlock_page(page); - return 0; - } - - path_len = pohmelfs_path_length(pi); - if (path_len < 0) { - err = path_len; - goto err_out_exit; - } - - t = netfs_trans_alloc(psb, path_len, NETFS_TRANS_SINGLE_DST, 0); - if (!t) { - err = -ENOMEM; - goto err_out_exit; - } - - t->complete = pohmelfs_read_page_complete; - t->private = page; - - cmd = netfs_trans_current(t); - data = (void *)(cmd + 1); - - err = pohmelfs_construct_path_string(pi, data, path_len); - if (err < 0) - goto err_out_free; - - path_len = err; - - cmd->id = pi->ino; - cmd->start = page->index; - cmd->start <<= PAGE_CACHE_SHIFT; - cmd->size = PAGE_CACHE_SIZE + path_len; - cmd->cmd = NETFS_READ_PAGE; - cmd->ext = path_len; - - dprintk("%s: path: '%s', page: %p, ino: %llu, start: %llu, size: %lu.\n", - __func__, (char *)data, page, pi->ino, cmd->start, PAGE_CACHE_SIZE); - - netfs_convert_cmd(cmd); - netfs_trans_update(cmd, t, path_len); - - err = netfs_trans_finish(t, psb); - if (err) - goto err_out_return; - - return pohmelfs_wait_on_page_locked(page); - -err_out_free: - t->result = err; - netfs_trans_put(t); -err_out_exit: - SetPageError(page); - if (PageLocked(page)) - unlock_page(page); -err_out_return: - printk("%s: page: %p, start: %lu, size: %lu, err: %d.\n", - __func__, page, page->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE, err); - - return err; -} - -/* - * Write begin/end magic. - * Allocates a page and writes inode if it was not synced to server before. - */ -static int pohmelfs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - struct inode *inode = mapping->host; - struct page *page; - pgoff_t index; - unsigned start, end; - int err; - - *pagep = NULL; - - index = pos >> PAGE_CACHE_SHIFT; - start = pos & (PAGE_CACHE_SIZE - 1); - end = start + len; - - page = grab_cache_page(mapping, index); -#if 0 - dprintk("%s: page: %p pos: %llu, len: %u, index: %lu, start: %u, end: %u, uptodate: %d.\n", - __func__, page, pos, len, index, start, end, PageUptodate(page)); -#endif - if (!page) { - err = -ENOMEM; - goto err_out_exit; - } - - while (!PageUptodate(page)) { - if (start && test_bit(NETFS_INODE_REMOTE_SYNCED, &POHMELFS_I(inode)->state)) { - err = pohmelfs_readpage(file, page); - if (err) - goto err_out_exit; - - lock_page(page); - continue; - } - - if (len != PAGE_CACHE_SIZE) { - void *kaddr = kmap_atomic(page, KM_USER0); - - memset(kaddr + start, 0, PAGE_CACHE_SIZE - start); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - } - SetPageUptodate(page); - } - - set_page_private(page, end); - - *pagep = page; - - return 0; - -err_out_exit: - page_cache_release(page); - *pagep = NULL; - - return err; -} - -static int pohmelfs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - struct inode *inode = mapping->host; - - if (copied != len) { - unsigned from = pos & (PAGE_CACHE_SIZE - 1); - void *kaddr = kmap_atomic(page, KM_USER0); - - memset(kaddr + from + copied, 0, len - copied); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - } - - SetPageUptodate(page); - set_page_dirty(page); -#if 0 - dprintk("%s: page: %p [U: %d, D: %d, L: %d], pos: %llu, len: %u, copied: %u.\n", - __func__, page, - PageUptodate(page), PageDirty(page), PageLocked(page), - pos, len, copied); -#endif - flush_dcache_page(page); - - unlock_page(page); - page_cache_release(page); - - if (pos + copied > inode->i_size) { - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - - psb->avail_size -= pos + copied - inode->i_size; - - i_size_write(inode, pos + copied); - } - - return copied; -} - -static int pohmelfs_readpages_trans_complete(struct page **__pages, unsigned int page_num, - void *private, int err) -{ - struct pohmelfs_inode *pi = private; - unsigned int i, num; - struct page **pages, *page = (struct page *)__pages; - loff_t index = page->index; - - pages = kzalloc(sizeof(void *) * page_num, GFP_NOIO); - if (!pages) - return -ENOMEM; - - num = find_get_pages_contig(pi->vfs_inode.i_mapping, index, page_num, pages); - if (num <= 0) { - err = num; - goto err_out_free; - } - - for (i = 0; i < num; ++i) { - page = pages[i]; - - if (err) - printk("%s: %u/%u: page: %p, index: %lu, uptodate: %d, locked: %d, err: %d.\n", - __func__, i, num, page, page->index, - PageUptodate(page), PageLocked(page), err); - - if (!PageChecked(page)) { - if (err < 0) - SetPageError(page); - unlock_page(page); - } - page_cache_release(page); - page_cache_release(page); - } - -err_out_free: - kfree(pages); - return err; -} - -static int pohmelfs_send_readpages(struct pohmelfs_inode *pi, struct page *first, unsigned int num) -{ - struct netfs_trans *t; - struct netfs_cmd *cmd; - struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); - int err, path_len; - void *data; - - err = pohmelfs_data_lock(pi, first->index << PAGE_CACHE_SHIFT, - num * PAGE_SIZE, POHMELFS_READ_LOCK); - if (err) - goto err_out_exit; - - path_len = pohmelfs_path_length(pi); - if (path_len < 0) { - err = path_len; - goto err_out_exit; - } - - t = netfs_trans_alloc(psb, path_len, NETFS_TRANS_SINGLE_DST, 0); - if (!t) { - err = -ENOMEM; - goto err_out_exit; - } - - cmd = netfs_trans_current(t); - data = (void *)(cmd + 1); - - t->complete = pohmelfs_readpages_trans_complete; - t->private = pi; - t->page_num = num; - t->pages = (struct page **)first; - - err = pohmelfs_construct_path_string(pi, data, path_len); - if (err < 0) - goto err_out_put; - - path_len = err; - - cmd->cmd = NETFS_READ_PAGES; - cmd->start = first->index; - cmd->start <<= PAGE_CACHE_SHIFT; - cmd->size = (num << 8 | PAGE_CACHE_SHIFT); - cmd->id = pi->ino; - cmd->ext = path_len; - - dprintk("%s: t: %p, gen: %u, path: '%s', path_len: %u, " - "start: %lu, num: %u.\n", - __func__, t, t->gen, (char *)data, path_len, - first->index, num); - - netfs_convert_cmd(cmd); - netfs_trans_update(cmd, t, path_len); - - return netfs_trans_finish(t, psb); - -err_out_put: - netfs_trans_free(t); -err_out_exit: - pohmelfs_readpages_trans_complete((struct page **)first, num, pi, err); - return err; -} - -#define list_to_page(head) (list_entry((head)->prev, struct page, lru)) - -static int pohmelfs_readpages(struct file *file, struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - unsigned int page_idx, num = 0; - struct page *page = NULL, *first = NULL; - - for (page_idx = 0; page_idx < nr_pages; page_idx++) { - page = list_to_page(pages); - - prefetchw(&page->flags); - list_del(&page->lru); - - if (!add_to_page_cache_lru(page, mapping, - page->index, GFP_KERNEL)) { - - if (!num) { - num = 1; - first = page; - continue; - } - - dprintk("%s: added to lru page: %p, page_index: %lu, first_index: %lu.\n", - __func__, page, page->index, first->index); - - if (unlikely(first->index + num != page->index) || (num > 500)) { - pohmelfs_send_readpages(POHMELFS_I(mapping->host), - first, num); - first = page; - num = 0; - } - - num++; - } - } - pohmelfs_send_readpages(POHMELFS_I(mapping->host), first, num); - - /* - * This will be sync read, so when last page is processed, - * all previous are alerady unlocked and ready to be used. - */ - return 0; -} - -/* - * Small address space operations for POHMELFS. - */ -const struct address_space_operations pohmelfs_aops = { - .readpage = pohmelfs_readpage, - .readpages = pohmelfs_readpages, - .writepages = pohmelfs_writepages, - .write_begin = pohmelfs_write_begin, - .write_end = pohmelfs_write_end, - .set_page_dirty = __set_page_dirty_nobuffers, -}; - -static void pohmelfs_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(pohmelfs_inode_cache, POHMELFS_I(inode)); -} - -/* - * ->destroy_inode() callback. Deletes inode from the caches - * and frees private data. - */ -static void pohmelfs_destroy_inode(struct inode *inode) -{ - struct super_block *sb = inode->i_sb; - struct pohmelfs_sb *psb = POHMELFS_SB(sb); - struct pohmelfs_inode *pi = POHMELFS_I(inode); - - /* pohmelfs_data_unlock(pi, 0, inode->i_size, POHMELFS_READ_LOCK); */ - - pohmelfs_inode_del_inode(psb, pi); - - dprintk("%s: pi: %p, inode: %p, ino: %llu.\n", - __func__, pi, &pi->vfs_inode, pi->ino); - atomic_long_dec(&psb->total_inodes); - call_rcu(&inode->i_rcu, pohmelfs_i_callback); -} - -/* - * ->alloc_inode() callback. Allocates inode and initializes private data. - */ -static struct inode *pohmelfs_alloc_inode(struct super_block *sb) -{ - struct pohmelfs_inode *pi; - - pi = kmem_cache_alloc(pohmelfs_inode_cache, GFP_NOIO); - if (!pi) - return NULL; - - pi->hash_root = RB_ROOT; - mutex_init(&pi->offset_lock); - - INIT_LIST_HEAD(&pi->sync_create_list); - - INIT_LIST_HEAD(&pi->inode_entry); - - pi->lock_type = 0; - pi->state = 0; - pi->total_len = 0; - pi->drop_count = 0; - - dprintk("%s: pi: %p, inode: %p.\n", __func__, pi, &pi->vfs_inode); - - atomic_long_inc(&POHMELFS_SB(sb)->total_inodes); - - return &pi->vfs_inode; -} - -/* - * We want fsync() to work on POHMELFS. - */ -static int pohmelfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - struct inode *inode = file->f_mapping->host; - int err = filemap_write_and_wait_range(inode->i_mapping, start, end); - if (!err) { - mutex_lock(&inode->i_mutex); - err = sync_inode_metadata(inode, 1); - mutex_unlock(&inode->i_mutex); - } - return err; -} - -ssize_t pohmelfs_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; - struct kiocb kiocb; - ssize_t ret; - loff_t pos = *ppos; - - init_sync_kiocb(&kiocb, file); - kiocb.ki_pos = pos; - kiocb.ki_left = len; - - dprintk("%s: len: %zu, pos: %llu.\n", __func__, len, pos); - - mutex_lock(&inode->i_mutex); - ret = pohmelfs_data_lock(pi, pos, len, POHMELFS_WRITE_LOCK); - if (ret) - goto err_out_unlock; - - ret = __generic_file_aio_write(&kiocb, &iov, 1, &kiocb.ki_pos); - *ppos = kiocb.ki_pos; - - mutex_unlock(&inode->i_mutex); - WARN_ON(ret < 0); - - if (ret > 0) { - ssize_t err; - - err = generic_write_sync(file, pos, ret); - if (err < 0) - ret = err; - WARN_ON(ret < 0); - } - - return ret; - -err_out_unlock: - mutex_unlock(&inode->i_mutex); - return ret; -} - -static const struct file_operations pohmelfs_file_ops = { - .open = generic_file_open, - .fsync = pohmelfs_fsync, - - .llseek = generic_file_llseek, - - .read = do_sync_read, - .aio_read = generic_file_aio_read, - - .mmap = generic_file_mmap, - - .splice_read = generic_file_splice_read, - .splice_write = generic_file_splice_write, - - .write = pohmelfs_write, - .aio_write = generic_file_aio_write, -}; - -const struct inode_operations pohmelfs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -}; - -int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr) -{ - int err; - - err = inode_change_ok(inode, attr); - if (err) { - dprintk("%s: ino: %llu, inode changes are not allowed.\n", __func__, POHMELFS_I(inode)->ino); - goto err_out_exit; - } - - if ((attr->ia_valid & ATTR_SIZE) && - attr->ia_size != i_size_read(inode)) { - err = vmtruncate(inode, attr->ia_size); - if (err) { - dprintk("%s: ino: %llu, failed to set the attributes.\n", __func__, POHMELFS_I(inode)->ino); - goto err_out_exit; - } - } - - setattr_copy(inode, attr); - mark_inode_dirty(inode); - - dprintk("%s: ino: %llu, mode: %o -> %o, uid: %u -> %u, gid: %u -> %u, size: %llu -> %llu.\n", - __func__, POHMELFS_I(inode)->ino, inode->i_mode, attr->ia_mode, - inode->i_uid, attr->ia_uid, inode->i_gid, attr->ia_gid, inode->i_size, attr->ia_size); - - return 0; - -err_out_exit: - return err; -} - -int pohmelfs_setattr(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - int err; - - err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_WRITE_LOCK); - if (err) - goto err_out_exit; - - err = security_inode_setattr(dentry, attr); - if (err) - goto err_out_exit; - - err = pohmelfs_setattr_raw(inode, attr); - if (err) - goto err_out_exit; - - return 0; - -err_out_exit: - return err; -} - -static int pohmelfs_send_xattr_req(struct pohmelfs_inode *pi, u64 id, u64 start, - const char *name, const void *value, size_t attrsize, int command) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); - int err, path_len, namelen = strlen(name) + 1; /* 0-byte */ - struct netfs_trans *t; - struct netfs_cmd *cmd; - void *data; - - dprintk("%s: id: %llu, start: %llu, name: '%s', attrsize: %zu, cmd: %d.\n", - __func__, id, start, name, attrsize, command); - - path_len = pohmelfs_path_length(pi); - if (path_len < 0) { - err = path_len; - goto err_out_exit; - } - - t = netfs_trans_alloc(psb, namelen + path_len + attrsize, 0, 0); - if (!t) { - err = -ENOMEM; - goto err_out_exit; - } - - cmd = netfs_trans_current(t); - data = cmd + 1; - - path_len = pohmelfs_construct_path_string(pi, data, path_len); - if (path_len < 0) { - err = path_len; - goto err_out_put; - } - data += path_len; - - /* - * 'name' is a NUL-terminated string already and - * 'namelen' includes 0-byte. - */ - memcpy(data, name, namelen); - data += namelen; - - memcpy(data, value, attrsize); - - cmd->cmd = command; - cmd->id = id; - cmd->start = start; - cmd->size = attrsize + namelen + path_len; - cmd->ext = path_len; - cmd->csize = 0; - cmd->cpad = 0; - - netfs_convert_cmd(cmd); - netfs_trans_update(cmd, t, namelen + path_len + attrsize); - - return netfs_trans_finish(t, psb); - -err_out_put: - t->result = err; - netfs_trans_put(t); -err_out_exit: - return err; -} - -static int pohmelfs_setxattr(struct dentry *dentry, const char *name, - const void *value, size_t attrsize, int flags) -{ - struct inode *inode = dentry->d_inode; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - - if (!(psb->state_flags & POHMELFS_FLAGS_XATTR)) - return -EOPNOTSUPP; - - return pohmelfs_send_xattr_req(pi, flags, attrsize, name, - value, attrsize, NETFS_XATTR_SET); -} - -static ssize_t pohmelfs_getxattr(struct dentry *dentry, const char *name, - void *value, size_t attrsize) -{ - struct inode *inode = dentry->d_inode; - struct pohmelfs_inode *pi = POHMELFS_I(inode); - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - struct pohmelfs_mcache *m; - int err; - long timeout = psb->mcache_timeout; - - if (!(psb->state_flags & POHMELFS_FLAGS_XATTR)) - return -EOPNOTSUPP; - - m = pohmelfs_mcache_alloc(psb, 0, attrsize, value); - if (IS_ERR(m)) - return PTR_ERR(m); - - dprintk("%s: ino: %llu, name: '%s', size: %zu.\n", - __func__, pi->ino, name, attrsize); - - err = pohmelfs_send_xattr_req(pi, m->gen, attrsize, name, value, 0, NETFS_XATTR_GET); - if (err) - goto err_out_put; - - do { - err = wait_for_completion_timeout(&m->complete, timeout); - if (err) { - err = m->err; - break; - } - - /* - * This loop is a bit ugly, since it waits until reference counter - * hits 1 and then puts the object here. Main goal is to prevent race with - * the network thread, when it can start processing the given request, i.e. - * increase its reference counter but yet not complete it, while - * we will exit from ->getxattr() with timeout, and although request - * will not be freed (its reference counter was increased by network - * thread), data pointer provided by user may be released, so we will - * overwrite an already freed area in the network thread. - * - * Now after timeout we remove request from the cache, so it can not be - * found by network thread, and wait for its reference counter to hit 1, - * i.e. if network thread already started to process this request, we wait - * for it to finish, and then free object locally. If reference counter is - * already 1, i.e. request is not used by anyone else, we can free it without - * problem. - */ - err = -ETIMEDOUT; - timeout = HZ; - - pohmelfs_mcache_remove_locked(psb, m); - } while (atomic_read(&m->refcnt) != 1); - - pohmelfs_mcache_put(psb, m); - - dprintk("%s: ino: %llu, err: %d.\n", __func__, pi->ino, err); - - return err; - -err_out_put: - pohmelfs_mcache_put(psb, m); - return err; -} - -static int pohmelfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) -{ - struct inode *inode = dentry->d_inode; -#if 0 - struct pohmelfs_inode *pi = POHMELFS_I(inode); - int err; - - err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_READ_LOCK); - if (err) - return err; - dprintk("%s: ino: %llu, mode: %o, uid: %u, gid: %u, size: %llu.\n", - __func__, pi->ino, inode->i_mode, inode->i_uid, - inode->i_gid, inode->i_size); -#endif - - generic_fillattr(inode, stat); - return 0; -} - -const struct inode_operations pohmelfs_file_inode_operations = { - .setattr = pohmelfs_setattr, - .getattr = pohmelfs_getattr, - .setxattr = pohmelfs_setxattr, - .getxattr = pohmelfs_getxattr, -}; - -/* - * Fill inode data: mode, size, operation callbacks and so on... - */ -void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info) -{ - inode->i_mode = info->mode; - set_nlink(inode, info->nlink); - inode->i_uid = info->uid; - inode->i_gid = info->gid; - inode->i_blocks = info->blocks; - inode->i_rdev = info->rdev; - inode->i_size = info->size; - inode->i_version = info->version; - inode->i_blkbits = ffs(info->blocksize); - - dprintk("%s: inode: %p, num: %lu/%llu inode is regular: %d, dir: %d, link: %d, mode: %o, size: %llu.\n", - __func__, inode, inode->i_ino, info->ino, - S_ISREG(inode->i_mode), S_ISDIR(inode->i_mode), - S_ISLNK(inode->i_mode), inode->i_mode, inode->i_size); - - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; - - /* - * i_mapping is a pointer to i_data during inode initialization. - */ - inode->i_data.a_ops = &pohmelfs_aops; - - if (S_ISREG(inode->i_mode)) { - inode->i_fop = &pohmelfs_file_ops; - inode->i_op = &pohmelfs_file_inode_operations; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_fop = &pohmelfs_dir_fops; - inode->i_op = &pohmelfs_dir_inode_ops; - } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &pohmelfs_symlink_inode_operations; - inode->i_fop = &pohmelfs_file_ops; - } else { - inode->i_fop = &generic_ro_fops; - } -} - -static int pohmelfs_drop_inode(struct inode *inode) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - struct pohmelfs_inode *pi = POHMELFS_I(inode); - - spin_lock(&psb->ino_lock); - list_del_init(&pi->inode_entry); - spin_unlock(&psb->ino_lock); - - return generic_drop_inode(inode); -} - -static struct pohmelfs_inode *pohmelfs_get_inode_from_list(struct pohmelfs_sb *psb, - struct list_head *head, unsigned int *count) -{ - struct pohmelfs_inode *pi = NULL; - - spin_lock(&psb->ino_lock); - if (!list_empty(head)) { - pi = list_entry(head->next, struct pohmelfs_inode, - inode_entry); - list_del_init(&pi->inode_entry); - *count = pi->drop_count; - pi->drop_count = 0; - } - spin_unlock(&psb->ino_lock); - - return pi; -} - -static void pohmelfs_flush_transactions(struct pohmelfs_sb *psb) -{ - struct pohmelfs_config *c; - - mutex_lock(&psb->state_lock); - list_for_each_entry(c, &psb->state_list, config_entry) { - pohmelfs_state_flush_transactions(&c->state); - } - mutex_unlock(&psb->state_lock); -} - -/* - * ->put_super() callback. Invoked before superblock is destroyed, - * so it has to clean all private data. - */ -static void pohmelfs_put_super(struct super_block *sb) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(sb); - struct pohmelfs_inode *pi; - unsigned int count = 0; - unsigned int in_drop_list = 0; - struct inode *inode, *tmp; - - dprintk("%s.\n", __func__); - - /* - * Kill pending transactions, which could affect inodes in-flight. - */ - pohmelfs_flush_transactions(psb); - - while ((pi = pohmelfs_get_inode_from_list(psb, &psb->drop_list, &count))) { - inode = &pi->vfs_inode; - - dprintk("%s: ino: %llu, pi: %p, inode: %p, count: %u.\n", - __func__, pi->ino, pi, inode, count); - - if (atomic_read(&inode->i_count) != count) { - printk("%s: ino: %llu, pi: %p, inode: %p, count: %u, i_count: %d.\n", - __func__, pi->ino, pi, inode, count, - atomic_read(&inode->i_count)); - count = atomic_read(&inode->i_count); - in_drop_list++; - } - - while (count--) - iput(&pi->vfs_inode); - } - - list_for_each_entry_safe(inode, tmp, &sb->s_inodes, i_sb_list) { - pi = POHMELFS_I(inode); - - dprintk("%s: ino: %llu, pi: %p, inode: %p, i_count: %u.\n", - __func__, pi->ino, pi, inode, atomic_read(&inode->i_count)); - - /* - * These are special inodes, they were created during - * directory reading or lookup, and were not bound to dentry, - * so they live here with reference counter being 1 and prevent - * umount from succeed since it believes that they are busy. - */ - count = atomic_read(&inode->i_count); - if (count) { - list_del_init(&inode->i_sb_list); - while (count--) - iput(&pi->vfs_inode); - } - } - - psb->trans_scan_timeout = psb->drop_scan_timeout = 0; - cancel_delayed_work_sync(&psb->dwork); - cancel_delayed_work_sync(&psb->drop_dwork); - flush_scheduled_work(); - - dprintk("%s: stopped workqueues.\n", __func__); - - pohmelfs_crypto_exit(psb); - pohmelfs_state_exit(psb); - - bdi_destroy(&psb->bdi); - - kfree(psb); - sb->s_fs_info = NULL; -} - -static int pohmelfs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct pohmelfs_sb *psb = POHMELFS_SB(sb); - - /* - * There are no filesystem size limits yet. - */ - memset(buf, 0, sizeof(struct kstatfs)); - - buf->f_type = POHMELFS_MAGIC_NUM; /* 'POH.' */ - buf->f_bsize = sb->s_blocksize; - buf->f_files = psb->ino; - buf->f_namelen = 255; - buf->f_files = atomic_long_read(&psb->total_inodes); - buf->f_bfree = buf->f_bavail = psb->avail_size >> PAGE_SHIFT; - buf->f_blocks = psb->total_size >> PAGE_SHIFT; - - dprintk("%s: total: %llu, avail: %llu, inodes: %llu, bsize: %lu.\n", - __func__, psb->total_size, psb->avail_size, buf->f_files, sb->s_blocksize); - - return 0; -} - -static int pohmelfs_show_options(struct seq_file *seq, struct dentry *root) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(root->d_sb); - - seq_printf(seq, ",idx=%u", psb->idx); - seq_printf(seq, ",trans_scan_timeout=%u", jiffies_to_msecs(psb->trans_scan_timeout)); - seq_printf(seq, ",drop_scan_timeout=%u", jiffies_to_msecs(psb->drop_scan_timeout)); - seq_printf(seq, ",wait_on_page_timeout=%u", jiffies_to_msecs(psb->wait_on_page_timeout)); - seq_printf(seq, ",trans_retries=%u", psb->trans_retries); - seq_printf(seq, ",crypto_thread_num=%u", psb->crypto_thread_num); - seq_printf(seq, ",trans_max_pages=%u", psb->trans_max_pages); - seq_printf(seq, ",mcache_timeout=%u", jiffies_to_msecs(psb->mcache_timeout)); - if (psb->crypto_fail_unsupported) - seq_printf(seq, ",crypto_fail_unsupported"); - - return 0; -} - -enum { - pohmelfs_opt_idx, - pohmelfs_opt_crypto_thread_num, - pohmelfs_opt_trans_max_pages, - pohmelfs_opt_crypto_fail_unsupported, - - /* Remountable options */ - pohmelfs_opt_trans_scan_timeout, - pohmelfs_opt_drop_scan_timeout, - pohmelfs_opt_wait_on_page_timeout, - pohmelfs_opt_trans_retries, - pohmelfs_opt_mcache_timeout, -}; - -static struct match_token pohmelfs_tokens[] = { - {pohmelfs_opt_idx, "idx=%u"}, - {pohmelfs_opt_crypto_thread_num, "crypto_thread_num=%u"}, - {pohmelfs_opt_trans_max_pages, "trans_max_pages=%u"}, - {pohmelfs_opt_crypto_fail_unsupported, "crypto_fail_unsupported"}, - {pohmelfs_opt_trans_scan_timeout, "trans_scan_timeout=%u"}, - {pohmelfs_opt_drop_scan_timeout, "drop_scan_timeout=%u"}, - {pohmelfs_opt_wait_on_page_timeout, "wait_on_page_timeout=%u"}, - {pohmelfs_opt_trans_retries, "trans_retries=%u"}, - {pohmelfs_opt_mcache_timeout, "mcache_timeout=%u"}, -}; - -static int pohmelfs_parse_options(char *options, struct pohmelfs_sb *psb, int remount) -{ - char *p; - substring_t args[MAX_OPT_ARGS]; - int option, err; - - if (!options) - return 0; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, pohmelfs_tokens, args); - - err = match_int(&args[0], &option); - if (err) - return err; - - if (remount && token <= pohmelfs_opt_crypto_fail_unsupported) - continue; - - switch (token) { - case pohmelfs_opt_idx: - psb->idx = option; - break; - case pohmelfs_opt_trans_scan_timeout: - psb->trans_scan_timeout = msecs_to_jiffies(option); - break; - case pohmelfs_opt_drop_scan_timeout: - psb->drop_scan_timeout = msecs_to_jiffies(option); - break; - case pohmelfs_opt_wait_on_page_timeout: - psb->wait_on_page_timeout = msecs_to_jiffies(option); - break; - case pohmelfs_opt_mcache_timeout: - psb->mcache_timeout = msecs_to_jiffies(option); - break; - case pohmelfs_opt_trans_retries: - psb->trans_retries = option; - break; - case pohmelfs_opt_crypto_thread_num: - psb->crypto_thread_num = option; - break; - case pohmelfs_opt_trans_max_pages: - psb->trans_max_pages = option; - break; - case pohmelfs_opt_crypto_fail_unsupported: - psb->crypto_fail_unsupported = 1; - break; - default: - return -EINVAL; - } - } - - return 0; -} - -static int pohmelfs_remount(struct super_block *sb, int *flags, char *data) -{ - int err; - struct pohmelfs_sb *psb = POHMELFS_SB(sb); - unsigned long old_sb_flags = sb->s_flags; - - err = pohmelfs_parse_options(data, psb, 1); - if (err) - goto err_out_restore; - - if (!(*flags & MS_RDONLY)) - sb->s_flags &= ~MS_RDONLY; - return 0; - -err_out_restore: - sb->s_flags = old_sb_flags; - return err; -} - -static void pohmelfs_flush_inode(struct pohmelfs_inode *pi, unsigned int count) -{ - struct inode *inode = &pi->vfs_inode; - - dprintk("%s: %p: ino: %llu, owned: %d.\n", - __func__, inode, pi->ino, test_bit(NETFS_INODE_OWNED, &pi->state)); - - mutex_lock(&inode->i_mutex); - if (test_and_clear_bit(NETFS_INODE_OWNED, &pi->state)) { - filemap_fdatawrite(inode->i_mapping); - inode->i_sb->s_op->write_inode(inode, 0); - } - -#ifdef POHMELFS_TRUNCATE_ON_INODE_FLUSH - truncate_inode_pages(inode->i_mapping, 0); -#endif - - pohmelfs_data_unlock(pi, 0, ~0, POHMELFS_WRITE_LOCK); - mutex_unlock(&inode->i_mutex); -} - -static void pohmelfs_put_inode_count(struct pohmelfs_inode *pi, unsigned int count) -{ - dprintk("%s: ino: %llu, pi: %p, inode: %p, count: %u.\n", - __func__, pi->ino, pi, &pi->vfs_inode, count); - - if (test_and_clear_bit(NETFS_INODE_NEED_FLUSH, &pi->state)) - pohmelfs_flush_inode(pi, count); - - while (count--) - iput(&pi->vfs_inode); -} - -static void pohmelfs_drop_scan(struct work_struct *work) -{ - struct pohmelfs_sb *psb = - container_of(work, struct pohmelfs_sb, drop_dwork.work); - struct pohmelfs_inode *pi; - unsigned int count = 0; - - while ((pi = pohmelfs_get_inode_from_list(psb, &psb->drop_list, &count))) - pohmelfs_put_inode_count(pi, count); - - pohmelfs_check_states(psb); - - if (psb->drop_scan_timeout) - schedule_delayed_work(&psb->drop_dwork, psb->drop_scan_timeout); -} - -/* - * Run through all transactions starting from the oldest, - * drop transaction from current state and try to send it - * to all remote nodes, which are currently installed. - */ -static void pohmelfs_trans_scan_state(struct netfs_state *st) -{ - struct rb_node *rb_node; - struct netfs_trans_dst *dst; - struct pohmelfs_sb *psb = st->psb; - unsigned int timeout = psb->trans_scan_timeout; - struct netfs_trans *t; - int err; - - mutex_lock(&st->trans_lock); - for (rb_node = rb_first(&st->trans_root); rb_node; ) { - dst = rb_entry(rb_node, struct netfs_trans_dst, state_entry); - t = dst->trans; - - if (timeout && time_after(dst->send_time + timeout, jiffies) - && dst->retries == 0) - break; - - dprintk("%s: t: %p, gen: %u, st: %p, retries: %u, max: %u.\n", - __func__, t, t->gen, st, dst->retries, psb->trans_retries); - netfs_trans_get(t); - - rb_node = rb_next(rb_node); - - err = -ETIMEDOUT; - if (timeout && (++dst->retries < psb->trans_retries)) - err = netfs_trans_resend(t, psb); - - if (err || (t->flags & NETFS_TRANS_SINGLE_DST)) { - if (netfs_trans_remove_nolock(dst, st)) - netfs_trans_drop_dst_nostate(dst); - } - - t->result = err; - netfs_trans_put(t); - } - mutex_unlock(&st->trans_lock); -} - -/* - * Walk through all installed network states and resend all - * transactions, which are old enough. - */ -static void pohmelfs_trans_scan(struct work_struct *work) -{ - struct pohmelfs_sb *psb = - container_of(work, struct pohmelfs_sb, dwork.work); - struct netfs_state *st; - struct pohmelfs_config *c; - - mutex_lock(&psb->state_lock); - list_for_each_entry(c, &psb->state_list, config_entry) { - st = &c->state; - - pohmelfs_trans_scan_state(st); - } - mutex_unlock(&psb->state_lock); - - /* - * If no timeout specified then system is in the middle of umount process, - * so no need to reschedule scanning process again. - */ - if (psb->trans_scan_timeout) - schedule_delayed_work(&psb->dwork, psb->trans_scan_timeout); -} - -int pohmelfs_meta_command_data(struct pohmelfs_inode *pi, u64 id, unsigned int cmd_op, char *addon, - unsigned int flags, netfs_trans_complete_t complete, void *priv, u64 start) -{ - struct inode *inode = &pi->vfs_inode; - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - int err = 0, sz; - struct netfs_trans *t; - int path_len, addon_len = 0; - void *data; - struct netfs_inode_info *info; - struct netfs_cmd *cmd; - - dprintk("%s: ino: %llu, cmd: %u, addon: %p.\n", __func__, pi->ino, cmd_op, addon); - - path_len = pohmelfs_path_length(pi); - if (path_len < 0) { - err = path_len; - goto err_out_exit; - } - - if (addon) - addon_len = strlen(addon) + 1; /* 0-byte */ - sz = addon_len; - - if (cmd_op == NETFS_INODE_INFO) - sz += sizeof(struct netfs_inode_info); - - t = netfs_trans_alloc(psb, sz + path_len, flags, 0); - if (!t) { - err = -ENOMEM; - goto err_out_exit; - } - t->complete = complete; - t->private = priv; - - cmd = netfs_trans_current(t); - data = (void *)(cmd + 1); - - if (cmd_op == NETFS_INODE_INFO) { - info = (struct netfs_inode_info *)(cmd + 1); - data = (void *)(info + 1); - - /* - * We are under i_mutex, can read and change whatever we want... - */ - info->mode = inode->i_mode; - info->nlink = inode->i_nlink; - info->uid = inode->i_uid; - info->gid = inode->i_gid; - info->blocks = inode->i_blocks; - info->rdev = inode->i_rdev; - info->size = inode->i_size; - info->version = inode->i_version; - - netfs_convert_inode_info(info); - } - - path_len = pohmelfs_construct_path_string(pi, data, path_len); - if (path_len < 0) - goto err_out_free; - - dprintk("%s: path_len: %d.\n", __func__, path_len); - - if (addon) { - path_len--; /* Do not place null-byte before the addon */ - path_len += sprintf(data + path_len, "/%s", addon) + 1; /* 0 - byte */ - } - - sz += path_len; - - cmd->cmd = cmd_op; - cmd->ext = path_len; - cmd->size = sz; - cmd->id = id; - cmd->start = start; - - netfs_convert_cmd(cmd); - netfs_trans_update(cmd, t, sz); - - /* - * Note, that it is possible to leak error here: transaction callback will not - * be invoked for allocation path failure. - */ - return netfs_trans_finish(t, psb); - -err_out_free: - netfs_trans_free(t); -err_out_exit: - if (complete) - complete(NULL, 0, priv, err); - return err; -} - -int pohmelfs_meta_command(struct pohmelfs_inode *pi, unsigned int cmd_op, unsigned int flags, - netfs_trans_complete_t complete, void *priv, u64 start) -{ - return pohmelfs_meta_command_data(pi, pi->ino, cmd_op, NULL, flags, complete, priv, start); -} - -/* - * Send request and wait for POHMELFS root capabilities response, - * which will update server's informaion about size of the export, - * permissions, number of objects, available size and so on. - */ -static int pohmelfs_root_handshake(struct pohmelfs_sb *psb) -{ - struct netfs_trans *t; - struct netfs_cmd *cmd; - int err = -ENOMEM; - - t = netfs_trans_alloc(psb, 0, 0, 0); - if (!t) - goto err_out_exit; - - cmd = netfs_trans_current(t); - - cmd->cmd = NETFS_CAPABILITIES; - cmd->id = POHMELFS_ROOT_CAPABILITIES; - cmd->size = 0; - cmd->start = 0; - cmd->ext = 0; - cmd->csize = 0; - - netfs_convert_cmd(cmd); - netfs_trans_update(cmd, t, 0); - - err = netfs_trans_finish(t, psb); - if (err) - goto err_out_exit; - - psb->flags = ~0; - err = wait_event_interruptible_timeout(psb->wait, - (psb->flags != ~0), - psb->wait_on_page_timeout); - if (!err) - err = -ETIMEDOUT; - else if (err > 0) - err = -psb->flags; - - if (err) - goto err_out_exit; - - return 0; - -err_out_exit: - return err; -} - -static int pohmelfs_show_stats(struct seq_file *m, struct dentry *root) -{ - struct netfs_state *st; - struct pohmelfs_ctl *ctl; - struct pohmelfs_sb *psb = POHMELFS_SB(root->d_sb); - struct pohmelfs_config *c; - - mutex_lock(&psb->state_lock); - - seq_printf(m, "\nidx addr(:port) socket_type protocol active priority permissions\n"); - - list_for_each_entry(c, &psb->state_list, config_entry) { - st = &c->state; - ctl = &st->ctl; - - seq_printf(m, "%u ", ctl->idx); - if (ctl->addr.sa_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)&st->ctl.addr; - seq_printf(m, "%pI4:%u", &sin->sin_addr.s_addr, ntohs(sin->sin_port)); - } else if (ctl->addr.sa_family == AF_INET6) { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&st->ctl.addr; - seq_printf(m, "%pi6:%u", &sin->sin6_addr, ntohs(sin->sin6_port)); - } else { - unsigned int i; - for (i = 0; i < ctl->addrlen; ++i) - seq_printf(m, "%02x.", ctl->addr.addr[i]); - } - - seq_printf(m, " %u %u %d %u %x\n", - ctl->type, ctl->proto, - st->socket != NULL, - ctl->prio, ctl->perm); - } - mutex_unlock(&psb->state_lock); - - return 0; -} - -static const struct super_operations pohmelfs_sb_ops = { - .alloc_inode = pohmelfs_alloc_inode, - .destroy_inode = pohmelfs_destroy_inode, - .drop_inode = pohmelfs_drop_inode, - .write_inode = pohmelfs_write_inode, - .put_super = pohmelfs_put_super, - .remount_fs = pohmelfs_remount, - .statfs = pohmelfs_statfs, - .show_options = pohmelfs_show_options, - .show_stats = pohmelfs_show_stats, -}; - -/* - * Allocate private superblock and create root dir. - */ -static int pohmelfs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct pohmelfs_sb *psb; - int err = -ENOMEM; - struct inode *root; - struct pohmelfs_inode *npi; - struct qstr str; - - psb = kzalloc(sizeof(struct pohmelfs_sb), GFP_KERNEL); - if (!psb) - goto err_out_exit; - - err = bdi_init(&psb->bdi); - if (err) - goto err_out_free_sb; - - err = bdi_register(&psb->bdi, NULL, "pfs-%d", atomic_inc_return(&psb_bdi_num)); - if (err) { - bdi_destroy(&psb->bdi); - goto err_out_free_sb; - } - - sb->s_fs_info = psb; - sb->s_op = &pohmelfs_sb_ops; - sb->s_magic = POHMELFS_MAGIC_NUM; - sb->s_maxbytes = MAX_LFS_FILESIZE; - sb->s_blocksize = PAGE_SIZE; - sb->s_bdi = &psb->bdi; - - psb->sb = sb; - - psb->ino = 2; - psb->idx = 0; - psb->active_state = NULL; - psb->trans_retries = 5; - psb->trans_data_size = PAGE_SIZE; - psb->drop_scan_timeout = msecs_to_jiffies(1000); - psb->trans_scan_timeout = msecs_to_jiffies(5000); - psb->wait_on_page_timeout = msecs_to_jiffies(5000); - init_waitqueue_head(&psb->wait); - - spin_lock_init(&psb->ino_lock); - - INIT_LIST_HEAD(&psb->drop_list); - - mutex_init(&psb->mcache_lock); - psb->mcache_root = RB_ROOT; - psb->mcache_timeout = msecs_to_jiffies(5000); - atomic_long_set(&psb->mcache_gen, 0); - - psb->trans_max_pages = 100; - - psb->crypto_align_size = 16; - psb->crypto_attached_size = 0; - psb->hash_strlen = 0; - psb->cipher_strlen = 0; - psb->perform_crypto = 0; - psb->crypto_thread_num = 2; - psb->crypto_fail_unsupported = 0; - mutex_init(&psb->crypto_thread_lock); - INIT_LIST_HEAD(&psb->crypto_ready_list); - INIT_LIST_HEAD(&psb->crypto_active_list); - - atomic_set(&psb->trans_gen, 1); - atomic_long_set(&psb->total_inodes, 0); - - mutex_init(&psb->state_lock); - INIT_LIST_HEAD(&psb->state_list); - - err = pohmelfs_parse_options((char *) data, psb, 0); - if (err) - goto err_out_free_bdi; - - err = pohmelfs_copy_crypto(psb); - if (err) - goto err_out_free_bdi; - - err = pohmelfs_state_init(psb); - if (err) - goto err_out_free_strings; - - err = pohmelfs_crypto_init(psb); - if (err) - goto err_out_state_exit; - - err = pohmelfs_root_handshake(psb); - if (err) - goto err_out_crypto_exit; - - str.name = "/"; - str.hash = jhash("/", 1, 0); - str.len = 1; - - npi = pohmelfs_create_entry_local(psb, NULL, &str, 0, 0755|S_IFDIR); - if (IS_ERR(npi)) { - err = PTR_ERR(npi); - goto err_out_crypto_exit; - } - set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); - clear_bit(NETFS_INODE_OWNED, &npi->state); - - root = &npi->vfs_inode; - - sb->s_root = d_alloc_root(root); - if (!sb->s_root) - goto err_out_put_root; - - INIT_DELAYED_WORK(&psb->drop_dwork, pohmelfs_drop_scan); - schedule_delayed_work(&psb->drop_dwork, psb->drop_scan_timeout); - - INIT_DELAYED_WORK(&psb->dwork, pohmelfs_trans_scan); - schedule_delayed_work(&psb->dwork, psb->trans_scan_timeout); - - return 0; - -err_out_put_root: - iput(root); -err_out_crypto_exit: - pohmelfs_crypto_exit(psb); -err_out_state_exit: - pohmelfs_state_exit(psb); -err_out_free_strings: - kfree(psb->cipher_string); - kfree(psb->hash_string); -err_out_free_bdi: - bdi_destroy(&psb->bdi); -err_out_free_sb: - kfree(psb); -err_out_exit: - - dprintk("%s: err: %d.\n", __func__, err); - return err; -} - -/* - * Some VFS magic here... - */ -static struct dentry *pohmelfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) -{ - return mount_nodev(fs_type, flags, data, pohmelfs_fill_super); -} - -/* - * We need this to sync all inodes earlier, since when writeback - * is invoked from the umount/mntput path dcache is already shrunk, - * see generic_shutdown_super(), and no inodes can access the path. - */ -static void pohmelfs_kill_super(struct super_block *sb) -{ - sync_inodes_sb(sb); - kill_anon_super(sb); -} - -static struct file_system_type pohmel_fs_type = { - .owner = THIS_MODULE, - .name = "pohmel", - .mount = pohmelfs_mount, - .kill_sb = pohmelfs_kill_super, -}; - -/* - * Cache and module initializations and freeing routings. - */ -static void pohmelfs_init_once(void *data) -{ - struct pohmelfs_inode *pi = data; - - inode_init_once(&pi->vfs_inode); -} - -static int __init pohmelfs_init_inodecache(void) -{ - pohmelfs_inode_cache = kmem_cache_create("pohmelfs_inode_cache", - sizeof(struct pohmelfs_inode), - 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), - pohmelfs_init_once); - if (!pohmelfs_inode_cache) - return -ENOMEM; - - return 0; -} - -static void pohmelfs_destroy_inodecache(void) -{ - kmem_cache_destroy(pohmelfs_inode_cache); -} - -static int __init init_pohmel_fs(void) -{ - int err; - - err = pohmelfs_config_init(); - if (err) - goto err_out_exit; - - err = pohmelfs_init_inodecache(); - if (err) - goto err_out_config_exit; - - err = pohmelfs_mcache_init(); - if (err) - goto err_out_destroy; - - err = netfs_trans_init(); - if (err) - goto err_out_mcache_exit; - - err = register_filesystem(&pohmel_fs_type); - if (err) - goto err_out_trans; - - return 0; - -err_out_trans: - netfs_trans_exit(); -err_out_mcache_exit: - pohmelfs_mcache_exit(); -err_out_destroy: - pohmelfs_destroy_inodecache(); -err_out_config_exit: - pohmelfs_config_exit(); -err_out_exit: - return err; -} - -static void __exit exit_pohmel_fs(void) -{ - unregister_filesystem(&pohmel_fs_type); - pohmelfs_destroy_inodecache(); - pohmelfs_mcache_exit(); - pohmelfs_config_exit(); - netfs_trans_exit(); -} - -module_init(init_pohmel_fs); -module_exit(exit_pohmel_fs); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov "); -MODULE_DESCRIPTION("Pohmel filesystem"); diff --git a/drivers/staging/pohmelfs/lock.c b/drivers/staging/pohmelfs/lock.c deleted file mode 100644 index 6710114cd..0000000 --- a/drivers/staging/pohmelfs/lock.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include - -#include "netfs.h" - -static int pohmelfs_send_lock_trans(struct pohmelfs_inode *pi, - u64 id, u64 start, u32 size, int type) -{ - struct inode *inode = &pi->vfs_inode; - struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); - struct netfs_trans *t; - struct netfs_cmd *cmd; - int path_len, err; - void *data; - struct netfs_lock *l; - int isize = (type & POHMELFS_LOCK_GRAB) ? 0 : sizeof(struct netfs_inode_info); - - err = pohmelfs_path_length(pi); - if (err < 0) - goto err_out_exit; - - path_len = err; - - err = -ENOMEM; - t = netfs_trans_alloc(psb, path_len + sizeof(struct netfs_lock) + isize, - NETFS_TRANS_SINGLE_DST, 0); - if (!t) - goto err_out_exit; - - cmd = netfs_trans_current(t); - data = cmd + 1; - - err = pohmelfs_construct_path_string(pi, data, path_len); - if (err < 0) - goto err_out_free; - path_len = err; - - l = data + path_len; - - l->start = start; - l->size = size; - l->type = type; - l->ino = pi->ino; - - cmd->cmd = NETFS_LOCK; - cmd->start = 0; - cmd->id = id; - cmd->size = sizeof(struct netfs_lock) + path_len + isize; - cmd->ext = path_len; - cmd->csize = 0; - - netfs_convert_cmd(cmd); - netfs_convert_lock(l); - - if (isize) { - struct netfs_inode_info *info = (struct netfs_inode_info *)(l + 1); - - info->mode = inode->i_mode; - info->nlink = inode->i_nlink; - info->uid = inode->i_uid; - info->gid = inode->i_gid; - info->blocks = inode->i_blocks; - info->rdev = inode->i_rdev; - info->size = inode->i_size; - info->version = inode->i_version; - - netfs_convert_inode_info(info); - } - - netfs_trans_update(cmd, t, path_len + sizeof(struct netfs_lock) + isize); - - return netfs_trans_finish(t, psb); - -err_out_free: - netfs_trans_free(t); -err_out_exit: - printk("%s: err: %d.\n", __func__, err); - return err; -} - -int pohmelfs_data_lock(struct pohmelfs_inode *pi, u64 start, u32 size, int type) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); - struct pohmelfs_mcache *m; - int err = -ENOMEM; - struct iattr iattr; - struct inode *inode = &pi->vfs_inode; - - dprintk("%s: %p: ino: %llu, start: %llu, size: %u, " - "type: %d, locked as: %d, owned: %d.\n", - __func__, &pi->vfs_inode, pi->ino, - start, size, type, pi->lock_type, - !!test_bit(NETFS_INODE_OWNED, &pi->state)); - - if (!pohmelfs_need_lock(pi, type)) - return 0; - - m = pohmelfs_mcache_alloc(psb, start, size, NULL); - if (IS_ERR(m)) - return PTR_ERR(m); - - err = pohmelfs_send_lock_trans(pi, m->gen, start, size, - type | POHMELFS_LOCK_GRAB); - if (err) - goto err_out_put; - - err = wait_for_completion_timeout(&m->complete, psb->mcache_timeout); - if (err) - err = m->err; - else - err = -ETIMEDOUT; - - if (err) { - printk("%s: %p: ino: %llu, mgen: %llu, start: %llu, size: %u, err: %d.\n", - __func__, &pi->vfs_inode, pi->ino, m->gen, start, size, err); - } - - if (err && (err != -ENOENT)) - goto err_out_put; - - if (!err) { - netfs_convert_inode_info(&m->info); - - iattr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_SIZE | ATTR_ATIME; - iattr.ia_mode = m->info.mode; - iattr.ia_uid = m->info.uid; - iattr.ia_gid = m->info.gid; - iattr.ia_size = m->info.size; - iattr.ia_atime = CURRENT_TIME; - - dprintk("%s: %p: ino: %llu, mgen: %llu, start: %llu, isize: %llu -> %llu.\n", - __func__, &pi->vfs_inode, pi->ino, m->gen, start, inode->i_size, m->info.size); - - err = pohmelfs_setattr_raw(inode, &iattr); - if (!err) { - struct dentry *dentry = d_find_alias(inode); - if (dentry) { - fsnotify_change(dentry, iattr.ia_valid); - dput(dentry); - } - } - } - - pi->lock_type = type; - set_bit(NETFS_INODE_OWNED, &pi->state); - - pohmelfs_mcache_put(psb, m); - - return 0; - -err_out_put: - pohmelfs_mcache_put(psb, m); - return err; -} - -int pohmelfs_data_unlock(struct pohmelfs_inode *pi, u64 start, u32 size, int type) -{ - dprintk("%s: %p: ino: %llu, start: %llu, size: %u, type: %d.\n", - __func__, &pi->vfs_inode, pi->ino, start, size, type); - pi->lock_type = 0; - clear_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state); - clear_bit(NETFS_INODE_OWNED, &pi->state); - return pohmelfs_send_lock_trans(pi, pi->ino, start, size, type); -} diff --git a/drivers/staging/pohmelfs/mcache.c b/drivers/staging/pohmelfs/mcache.c deleted file mode 100644 index e22665c..0000000 --- a/drivers/staging/pohmelfs/mcache.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include - -#include "netfs.h" - -static struct kmem_cache *pohmelfs_mcache_cache; -static mempool_t *pohmelfs_mcache_pool; - -static inline int pohmelfs_mcache_cmp(u64 gen, u64 new) -{ - if (gen < new) - return 1; - if (gen > new) - return -1; - return 0; -} - -struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen) -{ - struct rb_root *root = &psb->mcache_root; - struct rb_node *n = root->rb_node; - struct pohmelfs_mcache *tmp, *ret = NULL; - int cmp; - - while (n) { - tmp = rb_entry(n, struct pohmelfs_mcache, mcache_entry); - - cmp = pohmelfs_mcache_cmp(tmp->gen, gen); - if (cmp < 0) - n = n->rb_left; - else if (cmp > 0) - n = n->rb_right; - else { - ret = tmp; - pohmelfs_mcache_get(ret); - break; - } - } - - return ret; -} - -static int pohmelfs_mcache_insert(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) -{ - struct rb_root *root = &psb->mcache_root; - struct rb_node **n = &root->rb_node, *parent = NULL; - struct pohmelfs_mcache *ret = NULL, *tmp; - int cmp; - - while (*n) { - parent = *n; - - tmp = rb_entry(parent, struct pohmelfs_mcache, mcache_entry); - - cmp = pohmelfs_mcache_cmp(tmp->gen, m->gen); - if (cmp < 0) - n = &parent->rb_left; - else if (cmp > 0) - n = &parent->rb_right; - else { - ret = tmp; - break; - } - } - - if (ret) - return -EEXIST; - - rb_link_node(&m->mcache_entry, parent, n); - rb_insert_color(&m->mcache_entry, root); - - return 0; -} - -static int pohmelfs_mcache_remove(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) -{ - if (m && m->mcache_entry.rb_parent_color) { - rb_erase(&m->mcache_entry, &psb->mcache_root); - m->mcache_entry.rb_parent_color = 0; - return 1; - } - return 0; -} - -void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) -{ - mutex_lock(&psb->mcache_lock); - pohmelfs_mcache_remove(psb, m); - mutex_unlock(&psb->mcache_lock); -} - -struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start, - unsigned int size, void *data) -{ - struct pohmelfs_mcache *m; - int err = -ENOMEM; - - m = mempool_alloc(pohmelfs_mcache_pool, GFP_KERNEL); - if (!m) - goto err_out_exit; - - init_completion(&m->complete); - m->err = 0; - atomic_set(&m->refcnt, 1); - m->data = data; - m->start = start; - m->size = size; - m->gen = atomic_long_inc_return(&psb->mcache_gen); - - mutex_lock(&psb->mcache_lock); - err = pohmelfs_mcache_insert(psb, m); - mutex_unlock(&psb->mcache_lock); - if (err) - goto err_out_free; - - return m; - -err_out_free: - mempool_free(m, pohmelfs_mcache_pool); -err_out_exit: - return ERR_PTR(err); -} - -void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m) -{ - pohmelfs_mcache_remove_locked(psb, m); - - mempool_free(m, pohmelfs_mcache_pool); -} - -int __init pohmelfs_mcache_init(void) -{ - pohmelfs_mcache_cache = kmem_cache_create("pohmelfs_mcache_cache", - sizeof(struct pohmelfs_mcache), - 0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), NULL); - if (!pohmelfs_mcache_cache) - goto err_out_exit; - - pohmelfs_mcache_pool = mempool_create_slab_pool(256, pohmelfs_mcache_cache); - if (!pohmelfs_mcache_pool) - goto err_out_free; - - return 0; - -err_out_free: - kmem_cache_destroy(pohmelfs_mcache_cache); -err_out_exit: - return -ENOMEM; -} - -void pohmelfs_mcache_exit(void) -{ - mempool_destroy(pohmelfs_mcache_pool); - kmem_cache_destroy(pohmelfs_mcache_cache); -} diff --git a/drivers/staging/pohmelfs/net.c b/drivers/staging/pohmelfs/net.c deleted file mode 100644 index b2e9186..0000000 --- a/drivers/staging/pohmelfs/net.c +++ /dev/null @@ -1,1209 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -/* - * Async machinery lives here. - * All commands being sent to server do _not_ require sync reply, - * instead, if it is really needed, like readdir or readpage, caller - * sleeps waiting for data, which will be placed into provided buffer - * and caller will be awakened. - * - * Every command response can come without some listener. For example - * readdir response will add new objects into cache without appropriate - * request from userspace. This is used in cache coherency. - * - * If object is not found for given data, it is discarded. - * - * All requests are received by dedicated kernel thread. - */ - -/* - * Basic network sending/receiving functions. - * Blocked mode is used. - */ -static int netfs_data_recv(struct netfs_state *st, void *buf, u64 size) -{ - struct msghdr msg; - struct kvec iov; - int err; - - BUG_ON(!size); - - iov.iov_base = buf; - iov.iov_len = size; - - msg.msg_iov = (struct iovec *)&iov; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = MSG_DONTWAIT; - - err = kernel_recvmsg(st->socket, &msg, &iov, 1, iov.iov_len, - msg.msg_flags); - if (err <= 0) { - printk("%s: failed to recv data: size: %llu, err: %d.\n", __func__, size, err); - if (err == 0) - err = -ECONNRESET; - } - - return err; -} - -static int pohmelfs_data_recv(struct netfs_state *st, void *data, unsigned int size) -{ - unsigned int revents = 0; - unsigned int err_mask = POLLERR | POLLHUP | POLLRDHUP; - unsigned int mask = err_mask | POLLIN; - int err = 0; - - while (size && !err) { - revents = netfs_state_poll(st); - - if (!(revents & mask)) { - DEFINE_WAIT(wait); - - for (;;) { - prepare_to_wait(&st->thread_wait, &wait, TASK_INTERRUPTIBLE); - if (kthread_should_stop()) - break; - - revents = netfs_state_poll(st); - - if (revents & mask) - break; - - if (signal_pending(current)) - break; - - schedule(); - continue; - } - finish_wait(&st->thread_wait, &wait); - } - - err = 0; - netfs_state_lock(st); - if (st->socket && (st->read_socket == st->socket) && (revents & POLLIN)) { - err = netfs_data_recv(st, data, size); - if (err > 0) { - data += err; - size -= err; - err = 0; - } else if (err == 0) - err = -ECONNRESET; - } - - if (revents & err_mask) { - printk("%s: revents: %x, socket: %p, size: %u, err: %d.\n", - __func__, revents, st->socket, size, err); - err = -ECONNRESET; - } - netfs_state_unlock(st); - - if (err < 0) { - if (netfs_state_trylock_send(st)) { - netfs_state_exit(st); - err = netfs_state_init(st); - if (!err) - err = -EAGAIN; - netfs_state_unlock_send(st); - } else { - st->need_reset = 1; - } - } - - if (kthread_should_stop()) - err = -ENODEV; - - if (err) - printk("%s: socket: %p, read_socket: %p, revents: %x, rev_error: %d, " - "should_stop: %d, size: %u, err: %d.\n", - __func__, st->socket, st->read_socket, - revents, revents & err_mask, kthread_should_stop(), size, err); - } - - return err; -} - -int pohmelfs_data_recv_and_check(struct netfs_state *st, void *data, unsigned int size) -{ - struct netfs_cmd *cmd = &st->cmd; - int err; - - err = pohmelfs_data_recv(st, data, size); - if (err) - return err; - - return pohmelfs_crypto_process_input_data(&st->eng, cmd->iv, data, NULL, size); -} - -/* - * Polling machinery. - */ - -struct netfs_poll_helper { - poll_table pt; - struct netfs_state *st; -}; - -static int netfs_queue_wake(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - struct netfs_state *st = container_of(wait, struct netfs_state, wait); - - wake_up(&st->thread_wait); - return 1; -} - -static void netfs_queue_func(struct file *file, wait_queue_head_t *whead, - poll_table *pt) -{ - struct netfs_state *st = container_of(pt, struct netfs_poll_helper, pt)->st; - - st->whead = whead; - init_waitqueue_func_entry(&st->wait, netfs_queue_wake); - add_wait_queue(whead, &st->wait); -} - -static void netfs_poll_exit(struct netfs_state *st) -{ - if (st->whead) { - remove_wait_queue(st->whead, &st->wait); - st->whead = NULL; - } -} - -static int netfs_poll_init(struct netfs_state *st) -{ - struct netfs_poll_helper ph; - - ph.st = st; - init_poll_funcptr(&ph.pt, &netfs_queue_func); - - st->socket->ops->poll(NULL, st->socket, &ph.pt); - return 0; -} - -/* - * Get response for readpage command. We search inode and page in its mapping - * and copy data into. If it was async request, then we queue page into shared - * data and wakeup listener, who will copy it to userspace. - * - * There is a work in progress of allowing to call copy_to_user() directly from - * async receiving kernel thread. - */ -static int pohmelfs_read_page_response(struct netfs_state *st) -{ - struct pohmelfs_sb *psb = st->psb; - struct netfs_cmd *cmd = &st->cmd; - struct inode *inode; - struct page *page; - int err = 0; - - if (cmd->size > PAGE_CACHE_SIZE) { - err = -EINVAL; - goto err_out_exit; - } - - inode = ilookup(st->psb->sb, cmd->id); - if (!inode) { - printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); - err = -ENOENT; - goto err_out_exit; - } - - page = find_get_page(inode->i_mapping, cmd->start >> PAGE_CACHE_SHIFT); - if (!page || !PageLocked(page)) { - printk("%s: failed to find/lock page: page: %p, id: %llu, start: %llu, index: %llu.\n", - __func__, page, cmd->id, cmd->start, cmd->start >> PAGE_CACHE_SHIFT); - - while (cmd->size) { - unsigned int sz = min(cmd->size, st->size); - - err = pohmelfs_data_recv(st, st->data, sz); - if (err) - break; - - cmd->size -= sz; - } - - err = -ENODEV; - if (page) - goto err_out_page_put; - goto err_out_put; - } - - if (cmd->size) { - void *addr; - - addr = kmap(page); - err = pohmelfs_data_recv(st, addr, cmd->size); - kunmap(page); - - if (err) - goto err_out_page_unlock; - } - - dprintk("%s: page: %p, start: %llu, size: %u, locked: %d.\n", - __func__, page, cmd->start, cmd->size, PageLocked(page)); - - SetPageChecked(page); - if ((psb->hash_string || psb->cipher_string) && psb->perform_crypto && cmd->size) { - err = pohmelfs_crypto_process_input_page(&st->eng, page, cmd->size, cmd->iv); - if (err < 0) - goto err_out_page_unlock; - } else { - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - } - - pohmelfs_put_inode(POHMELFS_I(inode)); - wake_up(&st->psb->wait); - - return 0; - -err_out_page_unlock: - SetPageError(page); - unlock_page(page); -err_out_page_put: - page_cache_release(page); -err_out_put: - pohmelfs_put_inode(POHMELFS_I(inode)); -err_out_exit: - wake_up(&st->psb->wait); - return err; -} - -static int pohmelfs_check_name(struct pohmelfs_inode *parent, struct qstr *str, - struct netfs_inode_info *info) -{ - struct inode *inode; - struct pohmelfs_name *n; - int err = 0; - u64 ino = 0; - - mutex_lock(&parent->offset_lock); - n = pohmelfs_search_hash(parent, str->hash); - if (n) - ino = n->ino; - mutex_unlock(&parent->offset_lock); - - if (!ino) - goto out; - - inode = ilookup(parent->vfs_inode.i_sb, ino); - if (!inode) - goto out; - - dprintk("%s: parent: %llu, inode: %llu.\n", __func__, parent->ino, ino); - - pohmelfs_fill_inode(inode, info); - pohmelfs_put_inode(POHMELFS_I(inode)); - err = -EEXIST; -out: - return err; -} - -/* - * Readdir response from server. If special field is set, we wakeup - * listener (readdir() call), which will copy data to userspace. - */ -static int pohmelfs_readdir_response(struct netfs_state *st) -{ - struct inode *inode; - struct netfs_cmd *cmd = &st->cmd; - struct netfs_inode_info *info; - struct pohmelfs_inode *parent = NULL, *npi; - int err = 0, last = cmd->ext; - struct qstr str; - - if (cmd->size > st->size) - return -EINVAL; - - inode = ilookup(st->psb->sb, cmd->id); - if (!inode) { - printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); - return -ENOENT; - } - parent = POHMELFS_I(inode); - - if (!cmd->size && cmd->start) { - err = -cmd->start; - goto out; - } - - if (cmd->size) { - char *name; - - err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); - if (err) - goto err_out_put; - - info = (struct netfs_inode_info *)(st->data); - - name = (char *)(info + 1); - str.len = cmd->size - sizeof(struct netfs_inode_info) - 1 - cmd->cpad; - name[str.len] = 0; - str.name = name; - str.hash = jhash(str.name, str.len, 0); - - netfs_convert_inode_info(info); - - if (parent) { - err = pohmelfs_check_name(parent, &str, info); - if (err) { - if (err == -EEXIST) - err = 0; - goto out; - } - } - - info->ino = cmd->start; - if (!info->ino) - info->ino = pohmelfs_new_ino(st->psb); - - dprintk("%s: parent: %llu, ino: %llu, name: '%s', hash: %x, len: %u, mode: %o.\n", - __func__, parent->ino, info->ino, str.name, str.hash, str.len, - info->mode); - - npi = pohmelfs_new_inode(st->psb, parent, &str, info, 0); - if (IS_ERR(npi)) { - err = PTR_ERR(npi); - - if (err != -EEXIST) - goto err_out_put; - } else { - struct dentry *dentry, *alias, *pd; - - set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); - clear_bit(NETFS_INODE_OWNED, &npi->state); - - pd = d_find_alias(&parent->vfs_inode); - if (pd) { - str.hash = full_name_hash(str.name, str.len); - dentry = d_alloc(pd, &str); - if (dentry) { - alias = d_materialise_unique(dentry, &npi->vfs_inode); - if (alias) - dput(alias); - } - - dput(dentry); - dput(pd); - } - } - } -out: - if (last) { - set_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state); - set_bit(NETFS_INODE_REMOTE_SYNCED, &parent->state); - wake_up(&st->psb->wait); - } - pohmelfs_put_inode(parent); - - return err; - -err_out_put: - clear_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state); - printk("%s: parent: %llu, ino: %llu, cmd_id: %llu.\n", __func__, parent->ino, cmd->start, cmd->id); - pohmelfs_put_inode(parent); - wake_up(&st->psb->wait); - return err; -} - -/* - * Lookup command response. - * It searches for inode to be looked at (if it exists) and substitutes - * its inode information (size, permission, mode and so on), if inode does - * not exist, new one will be created and inserted into caches. - */ -static int pohmelfs_lookup_response(struct netfs_state *st) -{ - struct inode *inode = NULL; - struct netfs_cmd *cmd = &st->cmd; - struct netfs_inode_info *info; - struct pohmelfs_inode *parent = NULL, *npi; - int err = -EINVAL; - char *name; - - inode = ilookup(st->psb->sb, cmd->id); - if (!inode) { - printk("%s: lookup response: id: %llu, start: %llu, size: %u.\n", - __func__, cmd->id, cmd->start, cmd->size); - err = -ENOENT; - goto err_out_exit; - } - parent = POHMELFS_I(inode); - - if (!cmd->size) { - err = -cmd->start; - goto err_out_put; - } - - if (cmd->size < sizeof(struct netfs_inode_info)) { - printk("%s: broken lookup response: id: %llu, start: %llu, size: %u.\n", - __func__, cmd->id, cmd->start, cmd->size); - err = -EINVAL; - goto err_out_put; - } - - err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); - if (err) - goto err_out_put; - - info = (struct netfs_inode_info *)(st->data); - name = (char *)(info + 1); - - netfs_convert_inode_info(info); - - info->ino = cmd->start; - if (!info->ino) - info->ino = pohmelfs_new_ino(st->psb); - - dprintk("%s: parent: %llu, ino: %llu, name: '%s', start: %llu.\n", - __func__, parent->ino, info->ino, name, cmd->start); - - if (cmd->start) - npi = pohmelfs_new_inode(st->psb, parent, NULL, info, 0); - else { - struct qstr str; - - str.name = name; - str.len = cmd->size - sizeof(struct netfs_inode_info) - 1 - cmd->cpad; - str.hash = jhash(name, str.len, 0); - - npi = pohmelfs_new_inode(st->psb, parent, &str, info, 0); - } - if (IS_ERR(npi)) { - err = PTR_ERR(npi); - - if (err != -EEXIST) - goto err_out_put; - } else { - set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); - clear_bit(NETFS_INODE_OWNED, &npi->state); - } - - clear_bit(NETFS_COMMAND_PENDING, &parent->state); - pohmelfs_put_inode(parent); - - wake_up(&st->psb->wait); - - return 0; - -err_out_put: - pohmelfs_put_inode(parent); -err_out_exit: - clear_bit(NETFS_COMMAND_PENDING, &parent->state); - wake_up(&st->psb->wait); - printk("%s: inode: %p, id: %llu, start: %llu, size: %u, err: %d.\n", - __func__, inode, cmd->id, cmd->start, cmd->size, err); - return err; -} - -/* - * Create response, just marks local inode as 'created', so that writeback - * for any of its children (or own) would not try to sync it again. - */ -static int pohmelfs_create_response(struct netfs_state *st) -{ - struct inode *inode; - struct netfs_cmd *cmd = &st->cmd; - struct pohmelfs_inode *pi; - - inode = ilookup(st->psb->sb, cmd->id); - if (!inode) { - printk("%s: failed to find inode: id: %llu, start: %llu.\n", - __func__, cmd->id, cmd->start); - goto err_out_exit; - } - - pi = POHMELFS_I(inode); - - /* - * To lock or not to lock? - * We actually do not care if it races... - */ - if (cmd->start) - make_bad_inode(inode); - set_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); - - pohmelfs_put_inode(pi); - - wake_up(&st->psb->wait); - return 0; - -err_out_exit: - wake_up(&st->psb->wait); - return -ENOENT; -} - -/* - * Object remove response. Just says that remove request has been received. - * Used in cache coherency protocol. - */ -static int pohmelfs_remove_response(struct netfs_state *st) -{ - struct netfs_cmd *cmd = &st->cmd; - int err; - - err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); - if (err) - return err; - - dprintk("%s: parent: %llu, path: '%s'.\n", __func__, cmd->id, (char *)st->data); - - return 0; -} - -/* - * Transaction reply processing. - * - * Find transaction based on its generation number, bump its reference counter, - * so that none could free it under us, drop from the trees and lists and - * drop reference counter. When it hits zero (when all destinations replied - * and all timeout handled by async scanning code), completion will be called - * and transaction will be freed. - */ -static int pohmelfs_transaction_response(struct netfs_state *st) -{ - struct netfs_trans_dst *dst; - struct netfs_trans *t = NULL; - struct netfs_cmd *cmd = &st->cmd; - short err = (signed)cmd->ext; - - mutex_lock(&st->trans_lock); - dst = netfs_trans_search(st, cmd->start); - if (dst) { - netfs_trans_remove_nolock(dst, st); - t = dst->trans; - } - mutex_unlock(&st->trans_lock); - - if (!t) { - printk("%s: failed to find transaction: start: %llu: id: %llu, size: %u, ext: %u.\n", - __func__, cmd->start, cmd->id, cmd->size, cmd->ext); - err = -EINVAL; - goto out; - } - - t->result = err; - netfs_trans_drop_dst_nostate(dst); - -out: - wake_up(&st->psb->wait); - return err; -} - -/* - * Inode metadata cache coherency message. - */ -static int pohmelfs_page_cache_response(struct netfs_state *st) -{ - struct netfs_cmd *cmd = &st->cmd; - struct inode *inode; - - dprintk("%s: st: %p, id: %llu, start: %llu, size: %u.\n", __func__, st, cmd->id, cmd->start, cmd->size); - - inode = ilookup(st->psb->sb, cmd->id); - if (!inode) { - printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); - return -ENOENT; - } - - set_bit(NETFS_INODE_NEED_FLUSH, &POHMELFS_I(inode)->state); - pohmelfs_put_inode(POHMELFS_I(inode)); - - return 0; -} - -/* - * Root capabilities response: export statistics - * like used and available size, number of files and dirs, - * permissions. - */ -static int pohmelfs_root_cap_response(struct netfs_state *st) -{ - struct netfs_cmd *cmd = &st->cmd; - struct netfs_root_capabilities *cap; - struct pohmelfs_sb *psb = st->psb; - - if (cmd->size != sizeof(struct netfs_root_capabilities)) { - psb->flags = EPROTO; - wake_up(&psb->wait); - return -EPROTO; - } - - cap = st->data; - - netfs_convert_root_capabilities(cap); - - if (psb->total_size < cap->used + cap->avail) - psb->total_size = cap->used + cap->avail; - if (cap->avail) - psb->avail_size = cap->avail; - psb->state_flags = cap->flags; - - if (psb->state_flags & POHMELFS_FLAGS_RO) { - psb->sb->s_flags |= MS_RDONLY; - printk(KERN_INFO "Mounting POHMELFS (%d) read-only.\n", psb->idx); - } - - if (psb->state_flags & POHMELFS_FLAGS_XATTR) - printk(KERN_INFO "Mounting POHMELFS (%d) " - "with extended attributes support.\n", psb->idx); - - if (atomic_long_read(&psb->total_inodes) <= 1) - atomic_long_set(&psb->total_inodes, cap->nr_files); - - dprintk("%s: total: %llu, avail: %llu, flags: %llx, inodes: %llu.\n", - __func__, psb->total_size, psb->avail_size, psb->state_flags, cap->nr_files); - - psb->flags = 0; - wake_up(&psb->wait); - return 0; -} - -/* - * Crypto capabilities of the server, where it says that - * it supports or does not requested hash/cipher algorithms. - */ -static int pohmelfs_crypto_cap_response(struct netfs_state *st) -{ - struct netfs_cmd *cmd = &st->cmd; - struct netfs_crypto_capabilities *cap; - struct pohmelfs_sb *psb = st->psb; - int err = 0; - - if (cmd->size != sizeof(struct netfs_crypto_capabilities)) { - psb->flags = EPROTO; - wake_up(&psb->wait); - return -EPROTO; - } - - cap = st->data; - - dprintk("%s: cipher '%s': %s, hash: '%s': %s.\n", - __func__, - psb->cipher_string, (cap->cipher_strlen) ? "SUPPORTED" : "NOT SUPPORTED", - psb->hash_string, (cap->hash_strlen) ? "SUPPORTED" : "NOT SUPPORTED"); - - if (!cap->hash_strlen) { - if (psb->hash_strlen && psb->crypto_fail_unsupported) - err = -ENOTSUPP; - psb->hash_strlen = 0; - kfree(psb->hash_string); - psb->hash_string = NULL; - } - - if (!cap->cipher_strlen) { - if (psb->cipher_strlen && psb->crypto_fail_unsupported) - err = -ENOTSUPP; - psb->cipher_strlen = 0; - kfree(psb->cipher_string); - psb->cipher_string = NULL; - } - - return err; -} - -/* - * Capabilities handshake response. - */ -static int pohmelfs_capabilities_response(struct netfs_state *st) -{ - struct netfs_cmd *cmd = &st->cmd; - int err = 0; - - err = pohmelfs_data_recv(st, st->data, cmd->size); - if (err) - return err; - - switch (cmd->id) { - case POHMELFS_CRYPTO_CAPABILITIES: - return pohmelfs_crypto_cap_response(st); - case POHMELFS_ROOT_CAPABILITIES: - return pohmelfs_root_cap_response(st); - default: - break; - } - return -EINVAL; -} - -/* - * Receiving extended attribute. - * Does not work properly if received size is more than requested one, - * it should not happen with current request/reply model though. - */ -static int pohmelfs_getxattr_response(struct netfs_state *st) -{ - struct pohmelfs_sb *psb = st->psb; - struct netfs_cmd *cmd = &st->cmd; - struct pohmelfs_mcache *m; - short error = (signed short)cmd->ext, err; - unsigned int sz, total_size; - - m = pohmelfs_mcache_search(psb, cmd->id); - - dprintk("%s: id: %llu, gen: %llu, err: %d.\n", - __func__, cmd->id, (m) ? m->gen : 0, error); - - if (!m) { - printk("%s: failed to find getxattr cache entry: id: %llu.\n", __func__, cmd->id); - return -ENOENT; - } - - if (cmd->size) { - sz = min_t(unsigned int, cmd->size, m->size); - err = pohmelfs_data_recv_and_check(st, m->data, sz); - if (err) { - error = err; - goto out; - } - - m->size = sz; - total_size = cmd->size - sz; - - while (total_size) { - sz = min(total_size, st->size); - - err = pohmelfs_data_recv_and_check(st, st->data, sz); - if (err) { - error = err; - break; - } - - total_size -= sz; - } - } - -out: - m->err = error; - complete(&m->complete); - pohmelfs_mcache_put(psb, m); - - return error; -} - -int pohmelfs_data_lock_response(struct netfs_state *st) -{ - struct pohmelfs_sb *psb = st->psb; - struct netfs_cmd *cmd = &st->cmd; - struct pohmelfs_mcache *m; - short err = (signed short)cmd->ext; - u64 id = cmd->id; - - m = pohmelfs_mcache_search(psb, id); - - dprintk("%s: id: %llu, gen: %llu, err: %d.\n", - __func__, cmd->id, (m) ? m->gen : 0, err); - - if (!m) { - pohmelfs_data_recv(st, st->data, cmd->size); - printk("%s: failed to find data lock response: id: %llu.\n", __func__, cmd->id); - return -ENOENT; - } - - if (cmd->size) - err = pohmelfs_data_recv_and_check(st, &m->info, cmd->size); - - m->err = err; - complete(&m->complete); - pohmelfs_mcache_put(psb, m); - - return err; -} - -static void __inline__ netfs_state_reset(struct netfs_state *st) -{ - netfs_state_lock_send(st); - netfs_state_exit(st); - netfs_state_init(st); - netfs_state_unlock_send(st); -} - -/* - * Main receiving function, called from dedicated kernel thread. - */ -static int pohmelfs_recv(void *data) -{ - int err = -EINTR; - struct netfs_state *st = data; - struct netfs_cmd *cmd = &st->cmd; - - while (!kthread_should_stop()) { - /* - * If socket will be reset after this statement, then - * pohmelfs_data_recv() will just fail and loop will - * start again, so it can be done without any locks. - * - * st->read_socket is needed to prevents state machine - * breaking between this data reading and subsequent one - * in protocol specific functions during connection reset. - * In case of reset we have to read next command and do - * not expect data for old command to magically appear in - * new connection. - */ - st->read_socket = st->socket; - err = pohmelfs_data_recv(st, cmd, sizeof(struct netfs_cmd)); - if (err) { - msleep(1000); - continue; - } - - netfs_convert_cmd(cmd); - - dprintk("%s: cmd: %u, id: %llu, start: %llu, size: %u, " - "ext: %u, csize: %u, cpad: %u.\n", - __func__, cmd->cmd, cmd->id, cmd->start, - cmd->size, cmd->ext, cmd->csize, cmd->cpad); - - if (cmd->csize) { - struct pohmelfs_crypto_engine *e = &st->eng; - - if (unlikely(cmd->csize > e->size/2)) { - netfs_state_reset(st); - continue; - } - - if (e->hash && unlikely(cmd->csize != st->psb->crypto_attached_size)) { - dprintk("%s: cmd: cmd: %u, id: %llu, start: %llu, size: %u, " - "csize: %u != digest size %u.\n", - __func__, cmd->cmd, cmd->id, cmd->start, cmd->size, - cmd->csize, st->psb->crypto_attached_size); - netfs_state_reset(st); - continue; - } - - err = pohmelfs_data_recv(st, e->data, cmd->csize); - if (err) { - netfs_state_reset(st); - continue; - } - -#ifdef CONFIG_POHMELFS_DEBUG - { - unsigned int i; - unsigned char *hash = e->data; - - dprintk("%s: received hash: ", __func__); - for (i = 0; i < cmd->csize; ++i) - printk("%02x ", hash[i]); - - printk("\n"); - } -#endif - cmd->size -= cmd->csize; - } - - /* - * This should catch protocol breakage and random garbage instead of commands. - */ - if (unlikely((cmd->size > st->size) && (cmd->cmd != NETFS_XATTR_GET))) { - netfs_state_reset(st); - continue; - } - - switch (cmd->cmd) { - case NETFS_READ_PAGE: - err = pohmelfs_read_page_response(st); - break; - case NETFS_READDIR: - err = pohmelfs_readdir_response(st); - break; - case NETFS_LOOKUP: - err = pohmelfs_lookup_response(st); - break; - case NETFS_CREATE: - err = pohmelfs_create_response(st); - break; - case NETFS_REMOVE: - err = pohmelfs_remove_response(st); - break; - case NETFS_TRANS: - err = pohmelfs_transaction_response(st); - break; - case NETFS_PAGE_CACHE: - err = pohmelfs_page_cache_response(st); - break; - case NETFS_CAPABILITIES: - err = pohmelfs_capabilities_response(st); - break; - case NETFS_LOCK: - err = pohmelfs_data_lock_response(st); - break; - case NETFS_XATTR_GET: - err = pohmelfs_getxattr_response(st); - break; - default: - printk("%s: wrong cmd: %u, id: %llu, start: %llu, size: %u, ext: %u.\n", - __func__, cmd->cmd, cmd->id, cmd->start, cmd->size, cmd->ext); - netfs_state_reset(st); - break; - } - } - - while (!kthread_should_stop()) - schedule_timeout_uninterruptible(msecs_to_jiffies(10)); - - return err; -} - -int netfs_state_init(struct netfs_state *st) -{ - int err; - struct pohmelfs_ctl *ctl = &st->ctl; - - err = sock_create(ctl->addr.sa_family, ctl->type, ctl->proto, &st->socket); - if (err) { - printk("%s: failed to create a socket: family: %d, type: %d, proto: %d, err: %d.\n", - __func__, ctl->addr.sa_family, ctl->type, ctl->proto, err); - goto err_out_exit; - } - - st->socket->sk->sk_allocation = GFP_NOIO; - st->socket->sk->sk_sndtimeo = st->socket->sk->sk_rcvtimeo = msecs_to_jiffies(60000); - - err = kernel_connect(st->socket, (struct sockaddr *)&ctl->addr, ctl->addrlen, 0); - if (err) { - printk("%s: failed to connect to server: idx: %u, err: %d.\n", - __func__, st->psb->idx, err); - goto err_out_release; - } - st->socket->sk->sk_sndtimeo = st->socket->sk->sk_rcvtimeo = msecs_to_jiffies(60000); - - err = netfs_poll_init(st); - if (err) - goto err_out_release; - - if (st->socket->ops->family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)&ctl->addr; - printk(KERN_INFO "%s: (re)connected to peer %pi4:%d.\n", __func__, - &sin->sin_addr.s_addr, ntohs(sin->sin_port)); - } else if (st->socket->ops->family == AF_INET6) { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&ctl->addr; - printk(KERN_INFO "%s: (re)connected to peer %pi6:%d", __func__, - &sin->sin6_addr, ntohs(sin->sin6_port)); - } - - return 0; - -err_out_release: - sock_release(st->socket); -err_out_exit: - st->socket = NULL; - return err; -} - -void netfs_state_exit(struct netfs_state *st) -{ - if (st->socket) { - netfs_poll_exit(st); - st->socket->ops->shutdown(st->socket, 2); - - if (st->socket->ops->family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)&st->ctl.addr; - printk(KERN_INFO "%s: disconnected from peer %pi4:%d.\n", __func__, - &sin->sin_addr.s_addr, ntohs(sin->sin_port)); - } else if (st->socket->ops->family == AF_INET6) { - struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&st->ctl.addr; - printk(KERN_INFO "%s: disconnected from peer %pi6:%d", __func__, - &sin->sin6_addr, ntohs(sin->sin6_port)); - } - - sock_release(st->socket); - st->socket = NULL; - st->read_socket = NULL; - st->need_reset = 0; - } -} - -int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf) -{ - struct netfs_state *st = &conf->state; - int err = -ENOMEM; - - mutex_init(&st->__state_lock); - mutex_init(&st->__state_send_lock); - init_waitqueue_head(&st->thread_wait); - - st->psb = psb; - st->trans_root = RB_ROOT; - mutex_init(&st->trans_lock); - - st->size = psb->trans_data_size; - st->data = kmalloc(st->size, GFP_KERNEL); - if (!st->data) - goto err_out_exit; - - if (psb->perform_crypto) { - err = pohmelfs_crypto_engine_init(&st->eng, psb); - if (err) - goto err_out_free_data; - } - - err = netfs_state_init(st); - if (err) - goto err_out_free_engine; - - st->thread = kthread_run(pohmelfs_recv, st, "pohmelfs/%u", psb->idx); - if (IS_ERR(st->thread)) { - err = PTR_ERR(st->thread); - goto err_out_netfs_exit; - } - - if (!psb->active_state) - psb->active_state = conf; - - dprintk("%s: conf: %p, st: %p, socket: %p.\n", - __func__, conf, st, st->socket); - return 0; - -err_out_netfs_exit: - netfs_state_exit(st); -err_out_free_engine: - pohmelfs_crypto_engine_exit(&st->eng); -err_out_free_data: - kfree(st->data); -err_out_exit: - return err; - -} - -void pohmelfs_state_flush_transactions(struct netfs_state *st) -{ - struct rb_node *rb_node; - struct netfs_trans_dst *dst; - - mutex_lock(&st->trans_lock); - for (rb_node = rb_first(&st->trans_root); rb_node; ) { - dst = rb_entry(rb_node, struct netfs_trans_dst, state_entry); - rb_node = rb_next(rb_node); - - dst->trans->result = -EINVAL; - netfs_trans_remove_nolock(dst, st); - netfs_trans_drop_dst_nostate(dst); - } - mutex_unlock(&st->trans_lock); -} - -static void pohmelfs_state_exit_one(struct pohmelfs_config *c) -{ - struct netfs_state *st = &c->state; - - dprintk("%s: exiting, st: %p.\n", __func__, st); - if (st->thread) { - kthread_stop(st->thread); - st->thread = NULL; - } - - netfs_state_lock_send(st); - netfs_state_exit(st); - netfs_state_unlock_send(st); - - pohmelfs_state_flush_transactions(st); - - pohmelfs_crypto_engine_exit(&st->eng); - kfree(st->data); - - kfree(c); -} - -/* - * Initialize network stack. It searches for given ID in global - * configuration table, this contains information of the remote server - * (address (any supported by socket interface) and port, protocol and so on). - */ -int pohmelfs_state_init(struct pohmelfs_sb *psb) -{ - int err = -ENOMEM; - - err = pohmelfs_copy_config(psb); - if (err) { - pohmelfs_state_exit(psb); - return err; - } - - return 0; -} - -void pohmelfs_state_exit(struct pohmelfs_sb *psb) -{ - struct pohmelfs_config *c, *tmp; - - list_for_each_entry_safe(c, tmp, &psb->state_list, config_entry) { - list_del(&c->config_entry); - pohmelfs_state_exit_one(c); - } -} - -void pohmelfs_switch_active(struct pohmelfs_sb *psb) -{ - struct pohmelfs_config *c = psb->active_state; - - if (!list_empty(&psb->state_list)) { - if (c->config_entry.next != &psb->state_list) { - psb->active_state = list_entry(c->config_entry.next, - struct pohmelfs_config, config_entry); - } else { - psb->active_state = list_entry(psb->state_list.next, - struct pohmelfs_config, config_entry); - } - - dprintk("%s: empty: %d, active %p -> %p.\n", - __func__, list_empty(&psb->state_list), c, - psb->active_state); - } else - psb->active_state = NULL; -} - -void pohmelfs_check_states(struct pohmelfs_sb *psb) -{ - struct pohmelfs_config *c, *tmp; - LIST_HEAD(delete_list); - - mutex_lock(&psb->state_lock); - list_for_each_entry_safe(c, tmp, &psb->state_list, config_entry) { - if (pohmelfs_config_check(c, psb->idx)) { - - if (psb->active_state == c) - pohmelfs_switch_active(psb); - list_move(&c->config_entry, &delete_list); - } - } - pohmelfs_copy_config(psb); - mutex_unlock(&psb->state_lock); - - list_for_each_entry_safe(c, tmp, &delete_list, config_entry) { - list_del(&c->config_entry); - pohmelfs_state_exit_one(c); - } -} diff --git a/drivers/staging/pohmelfs/netfs.h b/drivers/staging/pohmelfs/netfs.h deleted file mode 100644 index f26894f..0000000 --- a/drivers/staging/pohmelfs/netfs.h +++ /dev/null @@ -1,919 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __NETFS_H -#define __NETFS_H - -#include -#include -#include - -#define POHMELFS_CN_IDX 5 -#define POHMELFS_CN_VAL 0 - -#define POHMELFS_CTLINFO_ACK 1 -#define POHMELFS_NOINFO_ACK 2 - -#define POHMELFS_NULL_IDX 65535 - -/* - * Network command structure. - * Will be extended. - */ -struct netfs_cmd { - __u16 cmd; /* Command number */ - __u16 csize; /* Attached crypto information size */ - __u16 cpad; /* Attached padding size */ - __u16 ext; /* External flags */ - __u32 size; /* Size of the attached data */ - __u32 trans; /* Transaction id */ - __u64 id; /* Object ID to operate on. Used for feedback.*/ - __u64 start; /* Start of the object. */ - __u64 iv; /* IV sequence */ - __u8 data[0]; -}; - -static inline void netfs_convert_cmd(struct netfs_cmd *cmd) -{ - cmd->id = __be64_to_cpu(cmd->id); - cmd->start = __be64_to_cpu(cmd->start); - cmd->iv = __be64_to_cpu(cmd->iv); - cmd->cmd = __be16_to_cpu(cmd->cmd); - cmd->ext = __be16_to_cpu(cmd->ext); - cmd->csize = __be16_to_cpu(cmd->csize); - cmd->cpad = __be16_to_cpu(cmd->cpad); - cmd->size = __be32_to_cpu(cmd->size); -} - -#define NETFS_TRANS_SINGLE_DST (1<<0) - -enum { - NETFS_READDIR = 1, /* Read directory for given inode number */ - NETFS_READ_PAGE, /* Read data page from the server */ - NETFS_WRITE_PAGE, /* Write data page to the server */ - NETFS_CREATE, /* Create directory entry */ - NETFS_REMOVE, /* Remove directory entry */ - - NETFS_LOOKUP, /* Lookup single object */ - NETFS_LINK, /* Create a link */ - NETFS_TRANS, /* Transaction */ - NETFS_OPEN, /* Open intent */ - NETFS_INODE_INFO, /* Metadata cache coherency synchronization message */ - - NETFS_PAGE_CACHE, /* Page cache invalidation message */ - NETFS_READ_PAGES, /* Read multiple contiguous pages in one go */ - NETFS_RENAME, /* Rename object */ - NETFS_CAPABILITIES, /* Capabilities of the client, for example supported crypto */ - NETFS_LOCK, /* Distributed lock message */ - - NETFS_XATTR_SET, /* Set extended attribute */ - NETFS_XATTR_GET, /* Get extended attribute */ - NETFS_CMD_MAX -}; - -enum { - POHMELFS_FLAGS_ADD = 0, /* Network state control message for ADD */ - POHMELFS_FLAGS_DEL, /* Network state control message for DEL */ - POHMELFS_FLAGS_SHOW, /* Network state control message for SHOW */ - POHMELFS_FLAGS_CRYPTO, /* Crypto data control message */ - POHMELFS_FLAGS_MODIFY, /* Network state modification message */ - POHMELFS_FLAGS_DUMP, /* Network state control message for SHOW ALL */ - POHMELFS_FLAGS_FLUSH, /* Network state control message for FLUSH */ -}; - -/* - * Always wanted to copy it from socket headers into public one, - * since they are __KERNEL__ protected there. - */ -#define _K_SS_MAXSIZE 128 - -struct saddr { - unsigned short sa_family; - char addr[_K_SS_MAXSIZE]; -}; - -enum { - POHMELFS_CRYPTO_HASH = 0, - POHMELFS_CRYPTO_CIPHER, -}; - -struct pohmelfs_crypto { - unsigned int idx; /* Config index */ - unsigned short strlen; /* Size of the attached crypto string including 0-byte - * "cbc(aes)" for example */ - unsigned short type; /* HMAC, cipher, both */ - unsigned int keysize; /* Key size */ - unsigned char data[0]; /* Algorithm string, key and IV */ -}; - -#define POHMELFS_IO_PERM_READ (1<<0) -#define POHMELFS_IO_PERM_WRITE (1<<1) - -/* - * Configuration command used to create table of different remote servers. - */ -struct pohmelfs_ctl { - __u32 idx; /* Config index */ - __u32 type; /* Socket type */ - __u32 proto; /* Socket protocol */ - __u16 addrlen; /* Size of the address */ - __u16 perm; /* IO permission */ - __u16 prio; /* IO priority */ - struct saddr addr; /* Remote server address */ -}; - -/* - * Ack for userspace about requested command. - */ -struct pohmelfs_cn_ack { - struct cn_msg msg; - int error; - int msg_num; - int unused[3]; - struct pohmelfs_ctl ctl; -}; - -/* - * Inode info structure used to sync with server. - * Check what stat() returns. - */ -struct netfs_inode_info { - unsigned int mode; - unsigned int nlink; - unsigned int uid; - unsigned int gid; - unsigned int blocksize; - unsigned int padding; - __u64 ino; - __u64 blocks; - __u64 rdev; - __u64 size; - __u64 version; -}; - -static inline void netfs_convert_inode_info(struct netfs_inode_info *info) -{ - info->mode = __cpu_to_be32(info->mode); - info->nlink = __cpu_to_be32(info->nlink); - info->uid = __cpu_to_be32(info->uid); - info->gid = __cpu_to_be32(info->gid); - info->blocksize = __cpu_to_be32(info->blocksize); - info->blocks = __cpu_to_be64(info->blocks); - info->rdev = __cpu_to_be64(info->rdev); - info->size = __cpu_to_be64(info->size); - info->version = __cpu_to_be64(info->version); - info->ino = __cpu_to_be64(info->ino); -} - -/* - * Cache state machine. - */ -enum { - NETFS_COMMAND_PENDING = 0, /* Command is being executed */ - NETFS_INODE_REMOTE_SYNCED, /* Inode was synced to server */ - NETFS_INODE_REMOTE_DIR_SYNCED, /* Inode (directory) was synced from the server */ - NETFS_INODE_OWNED, /* Inode is owned by given host */ - NETFS_INODE_NEED_FLUSH, /* Inode has to be flushed to the server */ -}; - -/* - * POHMELFS capabilities: information about supported - * crypto operations (hash/cipher, modes, key sizes and so on), - * root information (used/available size, number of objects, permissions) - */ -enum pohmelfs_capabilities { - POHMELFS_CRYPTO_CAPABILITIES = 0, - POHMELFS_ROOT_CAPABILITIES, -}; - -/* Read-only mount */ -#define POHMELFS_FLAGS_RO (1<<0) -/* Extended attributes support on/off */ -#define POHMELFS_FLAGS_XATTR (1<<1) - -struct netfs_root_capabilities { - __u64 nr_files; - __u64 used, avail; - __u64 flags; -}; - -static inline void netfs_convert_root_capabilities(struct netfs_root_capabilities *cap) -{ - cap->nr_files = __cpu_to_be64(cap->nr_files); - cap->used = __cpu_to_be64(cap->used); - cap->avail = __cpu_to_be64(cap->avail); - cap->flags = __cpu_to_be64(cap->flags); -} - -struct netfs_crypto_capabilities { - unsigned short hash_strlen; /* Hash string length, like "hmac(sha1) including 0 byte "*/ - unsigned short cipher_strlen; /* Cipher string length with the same format */ - unsigned int cipher_keysize; /* Cipher key size */ -}; - -static inline void netfs_convert_crypto_capabilities(struct netfs_crypto_capabilities *cap) -{ - cap->hash_strlen = __cpu_to_be16(cap->hash_strlen); - cap->cipher_strlen = __cpu_to_be16(cap->cipher_strlen); - cap->cipher_keysize = __cpu_to_be32(cap->cipher_keysize); -} - -enum pohmelfs_lock_type { - POHMELFS_LOCK_GRAB = (1<<15), - - POHMELFS_READ_LOCK = 0, - POHMELFS_WRITE_LOCK, -}; - -struct netfs_lock { - __u64 start; - __u64 ino; - __u32 size; - __u32 type; -}; - -static inline void netfs_convert_lock(struct netfs_lock *lock) -{ - lock->start = __cpu_to_be64(lock->start); - lock->ino = __cpu_to_be64(lock->ino); - lock->size = __cpu_to_be32(lock->size); - lock->type = __cpu_to_be32(lock->type); -} - -#ifdef __KERNEL__ - -#include -#include -#include -#include -#include - -/* - * Private POHMELFS cache of objects in directory. - */ -struct pohmelfs_name { - struct rb_node hash_node; - - struct list_head sync_create_entry; - - u64 ino; - - u32 hash; - u32 mode; - u32 len; - - char *data; -}; - -/* - * POHMELFS inode. Main object. - */ -struct pohmelfs_inode { - struct list_head inode_entry; /* Entry in superblock list. - * Objects which are not bound to dentry require to be dropped - * in ->put_super() - */ - struct rb_root hash_root; /* The same, but indexed by name hash and len */ - struct mutex offset_lock; /* Protect both above trees */ - - struct list_head sync_create_list; /* List of created but not yet synced to the server children */ - - unsigned int drop_count; - - int lock_type; /* How this inode is locked: read or write */ - - int error; /* Transaction error for given inode */ - - long state; /* State machine above */ - - u64 ino; /* Inode number */ - u64 total_len; /* Total length of all children names, used to create offsets */ - - struct inode vfs_inode; -}; - -struct netfs_trans; -typedef int (*netfs_trans_complete_t)(struct page **pages, unsigned int page_num, - void *private, int err); - -struct netfs_state; -struct pohmelfs_sb; - -struct netfs_trans { - /* - * Transaction header and attached contiguous data live here. - */ - struct iovec iovec; - - /* - * Pages attached to transaction. - */ - struct page **pages; - - /* - * List and protecting lock for transaction destination - * network states. - */ - spinlock_t dst_lock; - struct list_head dst_list; - - /* - * Number of users for given transaction. - * For example each network state attached to transaction - * via dst_list increases it. - */ - atomic_t refcnt; - - /* - * Number of pages attached to given transaction. - * Some slots in above page array can be NULL, since - * for example page can be under writeback already, - * so we skip it in this transaction. - */ - unsigned int page_num; - - /* - * Transaction flags: single dst or broadcast and so on. - */ - unsigned int flags; - - /* - * Size of the data, which can be placed into - * iovec.iov_base area. - */ - unsigned int total_size; - - /* - * Number of pages to be sent to remote server. - * Usually equal to above page_num, but in case of partial - * writeback it can accumulate only pages already completed - * previous writeback. - */ - unsigned int attached_pages; - - /* - * Attached number of bytes in all above pages. - */ - unsigned int attached_size; - - /* - * Unique transacton generation number. - * Used as identity in the network state tree of transactions. - */ - unsigned int gen; - - /* - * Transaction completion status. - */ - int result; - - /* - * Superblock this transaction belongs to - */ - struct pohmelfs_sb *psb; - - /* - * Crypto engine, which processed this transaction. - * Can be not NULL only if crypto engine holds encrypted pages. - */ - struct pohmelfs_crypto_engine *eng; - - /* Private data */ - void *private; - - /* Completion callback, invoked just before transaction is destroyed */ - netfs_trans_complete_t complete; -}; - -static inline int netfs_trans_cur_len(struct netfs_trans *t) -{ - return (signed)(t->total_size - t->iovec.iov_len); -} - -static inline void *netfs_trans_current(struct netfs_trans *t) -{ - return t->iovec.iov_base + t->iovec.iov_len; -} - -struct netfs_trans *netfs_trans_alloc(struct pohmelfs_sb *psb, unsigned int size, - unsigned int flags, unsigned int nr); -void netfs_trans_free(struct netfs_trans *t); -int netfs_trans_finish(struct netfs_trans *t, struct pohmelfs_sb *psb); -int netfs_trans_finish_send(struct netfs_trans *t, struct pohmelfs_sb *psb); - -static inline void netfs_trans_reset(struct netfs_trans *t) -{ - t->complete = NULL; -} - -struct netfs_trans_dst { - struct list_head trans_entry; - struct rb_node state_entry; - - unsigned long send_time; - - /* - * Times this transaction was resent to its old or new, - * depending on flags, destinations. When it reaches maximum - * allowed number, specified in superblock->trans_retries, - * transaction will be freed with ETIMEDOUT error. - */ - unsigned int retries; - - struct netfs_trans *trans; - struct netfs_state *state; -}; - -struct netfs_trans_dst *netfs_trans_search(struct netfs_state *st, unsigned int gen); -void netfs_trans_drop_dst(struct netfs_trans_dst *dst); -void netfs_trans_drop_dst_nostate(struct netfs_trans_dst *dst); -void netfs_trans_drop_trans(struct netfs_trans *t, struct netfs_state *st); -void netfs_trans_drop_last(struct netfs_trans *t, struct netfs_state *st); -int netfs_trans_resend(struct netfs_trans *t, struct pohmelfs_sb *psb); -int netfs_trans_remove_nolock(struct netfs_trans_dst *dst, struct netfs_state *st); - -int netfs_trans_init(void); -void netfs_trans_exit(void); - -struct pohmelfs_crypto_engine { - u64 iv; /* Crypto IV for current operation */ - unsigned long timeout; /* Crypto waiting timeout */ - unsigned int size; /* Size of crypto scratchpad */ - void *data; /* Temporal crypto scratchpad */ - /* - * Crypto operations performed on objects. - */ - struct crypto_hash *hash; - struct crypto_ablkcipher *cipher; - - struct pohmelfs_crypto_thread *thread; /* Crypto thread which hosts this engine */ - - struct page **pages; - unsigned int page_num; -}; - -struct pohmelfs_crypto_thread { - struct list_head thread_entry; - - struct task_struct *thread; - struct pohmelfs_sb *psb; - - struct pohmelfs_crypto_engine eng; - - struct netfs_trans *trans; - - wait_queue_head_t wait; - int error; - - unsigned int size; - struct page *page; -}; - -void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th); - -/* - * Network state, attached to one server. - */ -struct netfs_state { - struct mutex __state_lock; /* Can not allow to use the same socket simultaneously */ - struct mutex __state_send_lock; - struct netfs_cmd cmd; /* Cached command */ - struct netfs_inode_info info; /* Cached inode info */ - - void *data; /* Cached some data */ - unsigned int size; /* Size of that data */ - - struct pohmelfs_sb *psb; /* Superblock */ - - struct task_struct *thread; /* Async receiving thread */ - - /* Waiting/polling machinery */ - wait_queue_t wait; - wait_queue_head_t *whead; - wait_queue_head_t thread_wait; - - struct mutex trans_lock; - struct rb_root trans_root; - - struct pohmelfs_ctl ctl; /* Remote peer */ - - struct socket *socket; /* Socket object */ - struct socket *read_socket; /* Cached pointer to socket object. - * Used to determine if between lock drops socket was changed. - * Never used to read data or any kind of access. - */ - /* - * Crypto engines to process incoming data. - */ - struct pohmelfs_crypto_engine eng; - - int need_reset; -}; - -int netfs_state_init(struct netfs_state *st); -void netfs_state_exit(struct netfs_state *st); - -static inline void netfs_state_lock_send(struct netfs_state *st) -{ - mutex_lock(&st->__state_send_lock); -} - -static inline int netfs_state_trylock_send(struct netfs_state *st) -{ - return mutex_trylock(&st->__state_send_lock); -} - -static inline void netfs_state_unlock_send(struct netfs_state *st) -{ - BUG_ON(!mutex_is_locked(&st->__state_send_lock)); - - mutex_unlock(&st->__state_send_lock); -} - -static inline void netfs_state_lock(struct netfs_state *st) -{ - mutex_lock(&st->__state_lock); -} - -static inline void netfs_state_unlock(struct netfs_state *st) -{ - BUG_ON(!mutex_is_locked(&st->__state_lock)); - - mutex_unlock(&st->__state_lock); -} - -static inline unsigned int netfs_state_poll(struct netfs_state *st) -{ - unsigned int revents = POLLHUP | POLLERR; - - netfs_state_lock(st); - if (st->socket) - revents = st->socket->ops->poll(NULL, st->socket, NULL); - netfs_state_unlock(st); - - return revents; -} - -struct pohmelfs_config; - -struct pohmelfs_sb { - struct rb_root mcache_root; - struct mutex mcache_lock; - atomic_long_t mcache_gen; - unsigned long mcache_timeout; - - unsigned int idx; - - unsigned int trans_retries; - - atomic_t trans_gen; - - unsigned int crypto_attached_size; - unsigned int crypto_align_size; - - unsigned int crypto_fail_unsupported; - - unsigned int crypto_thread_num; - struct list_head crypto_active_list, crypto_ready_list; - struct mutex crypto_thread_lock; - - unsigned int trans_max_pages; - unsigned long trans_data_size; - unsigned long trans_timeout; - - unsigned long drop_scan_timeout; - unsigned long trans_scan_timeout; - - unsigned long wait_on_page_timeout; - - struct list_head flush_list; - struct list_head drop_list; - spinlock_t ino_lock; - u64 ino; - - /* - * Remote nodes POHMELFS connected to. - */ - struct list_head state_list; - struct mutex state_lock; - - /* - * Currently active state to request data from. - */ - struct pohmelfs_config *active_state; - - - wait_queue_head_t wait; - - /* - * Timed checks: stale transactions, inodes to be freed and so on. - */ - struct delayed_work dwork; - struct delayed_work drop_dwork; - - struct super_block *sb; - - struct backing_dev_info bdi; - - /* - * Algorithm strings. - */ - char *hash_string; - char *cipher_string; - - u8 *hash_key; - u8 *cipher_key; - - /* - * Algorithm string lengths. - */ - unsigned int hash_strlen; - unsigned int cipher_strlen; - unsigned int hash_keysize; - unsigned int cipher_keysize; - - /* - * Controls whether to perfrom crypto processing or not. - */ - int perform_crypto; - - /* - * POHMELFS statistics. - */ - u64 total_size; - u64 avail_size; - atomic_long_t total_inodes; - - /* - * Xattr support, read-only and so on. - */ - u64 state_flags; - - /* - * Temporary storage to detect changes in the wait queue. - */ - long flags; -}; - -static inline void netfs_trans_update(struct netfs_cmd *cmd, - struct netfs_trans *t, unsigned int size) -{ - unsigned int sz = ALIGN(size, t->psb->crypto_align_size); - - t->iovec.iov_len += sizeof(struct netfs_cmd) + sz; - cmd->cpad = __cpu_to_be16(sz - size); -} - -static inline struct pohmelfs_sb *POHMELFS_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct pohmelfs_inode *POHMELFS_I(struct inode *inode) -{ - return container_of(inode, struct pohmelfs_inode, vfs_inode); -} - -static inline u64 pohmelfs_new_ino(struct pohmelfs_sb *psb) -{ - u64 ino; - - spin_lock(&psb->ino_lock); - ino = psb->ino++; - spin_unlock(&psb->ino_lock); - - return ino; -} - -static inline void pohmelfs_put_inode(struct pohmelfs_inode *pi) -{ - struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); - - spin_lock(&psb->ino_lock); - list_move_tail(&pi->inode_entry, &psb->drop_list); - pi->drop_count++; - spin_unlock(&psb->ino_lock); -} - -struct pohmelfs_config { - struct list_head config_entry; - - struct netfs_state state; -}; - -struct pohmelfs_config_group { - /* - * Entry in the global config group list. - */ - struct list_head group_entry; - - /* - * Index of the current group. - */ - unsigned int idx; - /* - * Number of config_list entries in this group entry. - */ - unsigned int num_entry; - /* - * Algorithm strings. - */ - char *hash_string; - char *cipher_string; - - /* - * Algorithm string lengths. - */ - unsigned int hash_strlen; - unsigned int cipher_strlen; - - /* - * Key and its size. - */ - unsigned int hash_keysize; - unsigned int cipher_keysize; - u8 *hash_key; - u8 *cipher_key; - - /* - * List of config entries (network state info) for given idx. - */ - struct list_head config_list; -}; - -int __init pohmelfs_config_init(void); -void pohmelfs_config_exit(void); -int pohmelfs_copy_config(struct pohmelfs_sb *psb); -int pohmelfs_copy_crypto(struct pohmelfs_sb *psb); -int pohmelfs_config_check(struct pohmelfs_config *config, int idx); -int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf); - -extern const struct file_operations pohmelfs_dir_fops; -extern const struct inode_operations pohmelfs_dir_inode_ops; - -int pohmelfs_state_init(struct pohmelfs_sb *psb); -void pohmelfs_state_exit(struct pohmelfs_sb *psb); -void pohmelfs_state_flush_transactions(struct netfs_state *st); - -void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info); - -void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *n); -void pohmelfs_free_names(struct pohmelfs_inode *parent); -struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash); - -void pohmelfs_inode_del_inode(struct pohmelfs_sb *psb, struct pohmelfs_inode *pi); - -struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb, - struct pohmelfs_inode *parent, struct qstr *str, u64 start, umode_t mode); - -int pohmelfs_write_create_inode(struct pohmelfs_inode *pi); - -int pohmelfs_write_inode_create(struct inode *inode, struct netfs_trans *trans); -int pohmelfs_remove_child(struct pohmelfs_inode *parent, struct pohmelfs_name *n); - -struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb, - struct pohmelfs_inode *parent, struct qstr *str, - struct netfs_inode_info *info, int link); - -int pohmelfs_setattr(struct dentry *dentry, struct iattr *attr); -int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr); - -int pohmelfs_meta_command(struct pohmelfs_inode *pi, unsigned int cmd_op, unsigned int flags, - netfs_trans_complete_t complete, void *priv, u64 start); -int pohmelfs_meta_command_data(struct pohmelfs_inode *pi, u64 id, unsigned int cmd_op, char *addon, - unsigned int flags, netfs_trans_complete_t complete, void *priv, u64 start); - -void pohmelfs_check_states(struct pohmelfs_sb *psb); -void pohmelfs_switch_active(struct pohmelfs_sb *psb); - -int pohmelfs_construct_path_string(struct pohmelfs_inode *pi, void *data, int len); -int pohmelfs_path_length(struct pohmelfs_inode *pi); - -struct pohmelfs_crypto_completion { - struct completion complete; - int error; -}; - -int pohmelfs_trans_crypt(struct netfs_trans *t, struct pohmelfs_sb *psb); -void pohmelfs_crypto_exit(struct pohmelfs_sb *psb); -int pohmelfs_crypto_init(struct pohmelfs_sb *psb); - -int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb); -void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e); - -int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 iv, - void *data, struct page *page, unsigned int size); -int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e, - struct page *page, unsigned int size, u64 iv); - -static inline u64 pohmelfs_gen_iv(struct netfs_trans *t) -{ - u64 iv = t->gen; - - iv <<= 32; - iv |= ((unsigned long)t) & 0xffffffff; - - return iv; -} - -int pohmelfs_data_lock(struct pohmelfs_inode *pi, u64 start, u32 size, int type); -int pohmelfs_data_unlock(struct pohmelfs_inode *pi, u64 start, u32 size, int type); -int pohmelfs_data_lock_response(struct netfs_state *st); - -static inline int pohmelfs_need_lock(struct pohmelfs_inode *pi, int type) -{ - if (test_bit(NETFS_INODE_OWNED, &pi->state)) { - if (type == pi->lock_type) - return 0; - if ((type == POHMELFS_READ_LOCK) && (pi->lock_type == POHMELFS_WRITE_LOCK)) - return 0; - } - - if (!test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) - return 0; - - return 1; -} - -int __init pohmelfs_mcache_init(void); -void pohmelfs_mcache_exit(void); - -/* #define CONFIG_POHMELFS_DEBUG */ - -#ifdef CONFIG_POHMELFS_DEBUG -#define dprintka(f, a...) printk(f, ##a) -#define dprintk(f, a...) printk("%d: " f, task_pid_vnr(current), ##a) -#else -#define dprintka(f, a...) do {} while (0) -#define dprintk(f, a...) do {} while (0) -#endif - -static inline void netfs_trans_get(struct netfs_trans *t) -{ - atomic_inc(&t->refcnt); -} - -static inline void netfs_trans_put(struct netfs_trans *t) -{ - if (atomic_dec_and_test(&t->refcnt)) { - dprintk("%s: t: %p, gen: %u, err: %d.\n", - __func__, t, t->gen, t->result); - if (t->complete) - t->complete(t->pages, t->page_num, - t->private, t->result); - netfs_trans_free(t); - } -} - -struct pohmelfs_mcache { - struct rb_node mcache_entry; - struct completion complete; - - atomic_t refcnt; - - u64 gen; - - void *data; - u64 start; - u32 size; - int err; - - struct netfs_inode_info info; -}; - -struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start, - unsigned int size, void *data); -void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m); -struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen); -void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m); - -static inline void pohmelfs_mcache_get(struct pohmelfs_mcache *m) -{ - atomic_inc(&m->refcnt); -} - -static inline void pohmelfs_mcache_put(struct pohmelfs_sb *psb, - struct pohmelfs_mcache *m) -{ - if (atomic_dec_and_test(&m->refcnt)) - pohmelfs_mcache_free(psb, m); -} - -/*#define POHMELFS_TRUNCATE_ON_INODE_FLUSH - */ - -#endif /* __KERNEL__*/ - -#endif /* __NETFS_H */ diff --git a/drivers/staging/pohmelfs/path_entry.c b/drivers/staging/pohmelfs/path_entry.c deleted file mode 100644 index 400a9fc..0000000 --- a/drivers/staging/pohmelfs/path_entry.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -#define UNHASHED_OBSCURE_STRING_SIZE sizeof(" (deleted)") - -/* - * Create path from root for given inode. - * Path is formed as set of stuctures, containing name of the object - * and its inode data (mode, permissions and so on). - */ -int pohmelfs_construct_path_string(struct pohmelfs_inode *pi, void *data, int len) -{ - struct path path; - struct dentry *d; - char *ptr; - int err = 0, strlen, reduce = 0; - - d = d_find_alias(&pi->vfs_inode); - if (!d) { - printk("%s: no alias, list_empty: %d.\n", __func__, list_empty(&pi->vfs_inode.i_dentry)); - return -ENOENT; - } - - spin_lock(¤t->fs->lock); - path.mnt = mntget(current->fs->root.mnt); - spin_unlock(¤t->fs->lock); - - path.dentry = d; - - if (!IS_ROOT(d) && d_unhashed(d)) - reduce = 1; - - ptr = d_path(&path, data, len); - if (IS_ERR(ptr)) { - err = PTR_ERR(ptr); - goto out; - } - - if (reduce && len >= UNHASHED_OBSCURE_STRING_SIZE) { - char *end = data + len - UNHASHED_OBSCURE_STRING_SIZE; - *end = '\0'; - } - - strlen = len - (ptr - (char *)data); - memmove(data, ptr, strlen); - ptr = data; - - err = strlen; - - dprintk("%s: dname: '%s', len: %u, maxlen: %u, name: '%s', strlen: %d.\n", - __func__, d->d_name.name, d->d_name.len, len, ptr, strlen); - -out: - dput(d); - mntput(path.mnt); - - return err; -} - -int pohmelfs_path_length(struct pohmelfs_inode *pi) -{ - struct dentry *d, *root, *first; - int len; - unsigned seq; - - first = d_find_alias(&pi->vfs_inode); - if (!first) { - dprintk("%s: ino: %llu, mode: %o.\n", __func__, pi->ino, pi->vfs_inode.i_mode); - return -ENOENT; - } - - spin_lock(¤t->fs->lock); - root = dget(current->fs->root.dentry); - spin_unlock(¤t->fs->lock); - -rename_retry: - len = 1; /* Root slash */ - d = first; - seq = read_seqbegin(&rename_lock); - rcu_read_lock(); - - if (!IS_ROOT(d) && d_unhashed(d)) - len += UNHASHED_OBSCURE_STRING_SIZE; /* Obscure " (deleted)" string */ - - while (d && d != root && !IS_ROOT(d)) { - len += d->d_name.len + 1; /* Plus slash */ - d = d->d_parent; - } - rcu_read_unlock(); - if (read_seqretry(&rename_lock, seq)) - goto rename_retry; - - dput(root); - dput(first); - - return len + 1; /* Including zero-byte */ -} diff --git a/drivers/staging/pohmelfs/trans.c b/drivers/staging/pohmelfs/trans.c deleted file mode 100644 index 06c1a74..0000000 --- a/drivers/staging/pohmelfs/trans.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - * 2007+ Copyright (c) Evgeniy Polyakov - * All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "netfs.h" - -static struct kmem_cache *netfs_trans_dst; -static mempool_t *netfs_trans_dst_pool; - -static void netfs_trans_init_static(struct netfs_trans *t, int num, int size) -{ - t->page_num = num; - t->total_size = size; - atomic_set(&t->refcnt, 1); - - spin_lock_init(&t->dst_lock); - INIT_LIST_HEAD(&t->dst_list); -} - -static int netfs_trans_send_pages(struct netfs_trans *t, struct netfs_state *st) -{ - int err = 0; - unsigned int i, attached_pages = t->attached_pages, ci; - struct msghdr msg; - struct page **pages = (t->eng) ? t->eng->pages : t->pages; - struct page *p; - unsigned int size; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = MSG_WAITALL | MSG_MORE; - - ci = 0; - for (i = 0; i < t->page_num; ++i) { - struct page *page = pages[ci]; - struct netfs_cmd cmd; - struct iovec io; - - p = t->pages[i]; - - if (!p) - continue; - - size = page_private(p); - - io.iov_base = &cmd; - io.iov_len = sizeof(struct netfs_cmd); - - cmd.cmd = NETFS_WRITE_PAGE; - cmd.ext = 0; - cmd.id = 0; - cmd.size = size; - cmd.start = p->index; - cmd.start <<= PAGE_CACHE_SHIFT; - cmd.csize = 0; - cmd.cpad = 0; - cmd.iv = pohmelfs_gen_iv(t); - - netfs_convert_cmd(&cmd); - - msg.msg_iov = &io; - msg.msg_iovlen = 1; - msg.msg_flags = MSG_WAITALL | MSG_MORE; - - err = kernel_sendmsg(st->socket, &msg, (struct kvec *)msg.msg_iov, 1, sizeof(struct netfs_cmd)); - if (err <= 0) { - printk("%s: %d/%d failed to send transaction header: t: %p, gen: %u, err: %d.\n", - __func__, i, t->page_num, t, t->gen, err); - if (err == 0) - err = -ECONNRESET; - goto err_out; - } - - msg.msg_flags = MSG_WAITALL | (attached_pages == 1 ? 0 : - MSG_MORE); - - err = kernel_sendpage(st->socket, page, 0, size, msg.msg_flags); - if (err <= 0) { - printk("%s: %d/%d failed to send transaction page: t: %p, gen: %u, size: %u, err: %d.\n", - __func__, i, t->page_num, t, t->gen, size, err); - if (err == 0) - err = -ECONNRESET; - goto err_out; - } - - dprintk("%s: %d/%d sent t: %p, gen: %u, page: %p/%p, size: %u.\n", - __func__, i, t->page_num, t, t->gen, page, p, size); - - err = 0; - attached_pages--; - if (!attached_pages) - break; - ci++; - - continue; - -err_out: - printk("%s: t: %p, gen: %u, err: %d.\n", __func__, t, t->gen, err); - netfs_state_exit(st); - break; - } - - return err; -} - -int netfs_trans_send(struct netfs_trans *t, struct netfs_state *st) -{ - int err; - struct msghdr msg; - - BUG_ON(!t->iovec.iov_len); - BUG_ON(t->iovec.iov_len > 1024*1024*1024); - - netfs_state_lock_send(st); - if (!st->socket) { - err = netfs_state_init(st); - if (err) - goto err_out_unlock_return; - } - - msg.msg_iov = &t->iovec; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = MSG_WAITALL; - - if (t->attached_pages) - msg.msg_flags |= MSG_MORE; - - err = kernel_sendmsg(st->socket, &msg, (struct kvec *)msg.msg_iov, 1, t->iovec.iov_len); - if (err <= 0) { - printk("%s: failed to send contig transaction: t: %p, gen: %u, size: %zu, err: %d.\n", - __func__, t, t->gen, t->iovec.iov_len, err); - if (err == 0) - err = -ECONNRESET; - goto err_out_unlock_return; - } - - dprintk("%s: sent %s transaction: t: %p, gen: %u, size: %zu, page_num: %u.\n", - __func__, (t->page_num) ? "partial" : "full", - t, t->gen, t->iovec.iov_len, t->page_num); - - err = 0; - if (t->attached_pages) - err = netfs_trans_send_pages(t, st); - -err_out_unlock_return: - - if (st->need_reset) - netfs_state_exit(st); - - netfs_state_unlock_send(st); - - dprintk("%s: t: %p, gen: %u, err: %d.\n", - __func__, t, t->gen, err); - - t->result = err; - return err; -} - -static inline int netfs_trans_cmp(unsigned int gen, unsigned int new) -{ - if (gen < new) - return 1; - if (gen > new) - return -1; - return 0; -} - -struct netfs_trans_dst *netfs_trans_search(struct netfs_state *st, unsigned int gen) -{ - struct rb_root *root = &st->trans_root; - struct rb_node *n = root->rb_node; - struct netfs_trans_dst *tmp, *ret = NULL; - struct netfs_trans *t; - int cmp; - - while (n) { - tmp = rb_entry(n, struct netfs_trans_dst, state_entry); - t = tmp->trans; - - cmp = netfs_trans_cmp(t->gen, gen); - if (cmp < 0) - n = n->rb_left; - else if (cmp > 0) - n = n->rb_right; - else { - ret = tmp; - break; - } - } - - return ret; -} - -static int netfs_trans_insert(struct netfs_trans_dst *ndst, struct netfs_state *st) -{ - struct rb_root *root = &st->trans_root; - struct rb_node **n = &root->rb_node, *parent = NULL; - struct netfs_trans_dst *ret = NULL, *tmp; - struct netfs_trans *t = NULL, *new = ndst->trans; - int cmp; - - while (*n) { - parent = *n; - - tmp = rb_entry(parent, struct netfs_trans_dst, state_entry); - t = tmp->trans; - - cmp = netfs_trans_cmp(t->gen, new->gen); - if (cmp < 0) - n = &parent->rb_left; - else if (cmp > 0) - n = &parent->rb_right; - else { - ret = tmp; - break; - } - } - - if (ret) { - printk("%s: exist: old: gen: %u, flags: %x, send_time: %lu, " - "new: gen: %u, flags: %x, send_time: %lu.\n", - __func__, t->gen, t->flags, ret->send_time, - new->gen, new->flags, ndst->send_time); - return -EEXIST; - } - - rb_link_node(&ndst->state_entry, parent, n); - rb_insert_color(&ndst->state_entry, root); - ndst->send_time = jiffies; - - return 0; -} - -int netfs_trans_remove_nolock(struct netfs_trans_dst *dst, struct netfs_state *st) -{ - if (dst && dst->state_entry.rb_parent_color) { - rb_erase(&dst->state_entry, &st->trans_root); - dst->state_entry.rb_parent_color = 0; - return 1; - } - return 0; -} - -static int netfs_trans_remove_state(struct netfs_trans_dst *dst) -{ - int ret; - struct netfs_state *st = dst->state; - - mutex_lock(&st->trans_lock); - ret = netfs_trans_remove_nolock(dst, st); - mutex_unlock(&st->trans_lock); - - return ret; -} - -/* - * Create new destination for given transaction associated with given network state. - * Transaction's reference counter is bumped and will be dropped when either - * reply is received or when async timeout detection task will fail resending - * and drop transaction. - */ -static int netfs_trans_push_dst(struct netfs_trans *t, struct netfs_state *st) -{ - struct netfs_trans_dst *dst; - int err; - - dst = mempool_alloc(netfs_trans_dst_pool, GFP_KERNEL); - if (!dst) - return -ENOMEM; - - dst->retries = 0; - dst->send_time = 0; - dst->state = st; - dst->trans = t; - netfs_trans_get(t); - - mutex_lock(&st->trans_lock); - err = netfs_trans_insert(dst, st); - mutex_unlock(&st->trans_lock); - - if (err) - goto err_out_free; - - spin_lock(&t->dst_lock); - list_add_tail(&dst->trans_entry, &t->dst_list); - spin_unlock(&t->dst_lock); - - return 0; - -err_out_free: - t->result = err; - netfs_trans_put(t); - mempool_free(dst, netfs_trans_dst_pool); - return err; -} - -static void netfs_trans_free_dst(struct netfs_trans_dst *dst) -{ - netfs_trans_put(dst->trans); - mempool_free(dst, netfs_trans_dst_pool); -} - -static void netfs_trans_remove_dst(struct netfs_trans_dst *dst) -{ - if (netfs_trans_remove_state(dst)) - netfs_trans_free_dst(dst); -} - -/* - * Drop destination transaction entry when we know it. - */ -void netfs_trans_drop_dst(struct netfs_trans_dst *dst) -{ - struct netfs_trans *t = dst->trans; - - spin_lock(&t->dst_lock); - list_del_init(&dst->trans_entry); - spin_unlock(&t->dst_lock); - - netfs_trans_remove_dst(dst); -} - -/* - * Drop destination transaction entry when we know it and when we - * already removed dst from state tree. - */ -void netfs_trans_drop_dst_nostate(struct netfs_trans_dst *dst) -{ - struct netfs_trans *t = dst->trans; - - spin_lock(&t->dst_lock); - list_del_init(&dst->trans_entry); - spin_unlock(&t->dst_lock); - - netfs_trans_free_dst(dst); -} - -/* - * This drops destination transaction entry from appropriate network state - * tree and drops related reference counter. It is possible that transaction - * will be freed here if its reference counter hits zero. - * Destination transaction entry will be freed. - */ -void netfs_trans_drop_trans(struct netfs_trans *t, struct netfs_state *st) -{ - struct netfs_trans_dst *dst, *tmp, *ret = NULL; - - spin_lock(&t->dst_lock); - list_for_each_entry_safe(dst, tmp, &t->dst_list, trans_entry) { - if (dst->state == st) { - ret = dst; - list_del(&dst->trans_entry); - break; - } - } - spin_unlock(&t->dst_lock); - - if (ret) - netfs_trans_remove_dst(ret); -} - -/* - * This drops destination transaction entry from appropriate network state - * tree and drops related reference counter. It is possible that transaction - * will be freed here if its reference counter hits zero. - * Destination transaction entry will be freed. - */ -void netfs_trans_drop_last(struct netfs_trans *t, struct netfs_state *st) -{ - struct netfs_trans_dst *dst, *tmp, *ret; - - spin_lock(&t->dst_lock); - ret = list_entry(t->dst_list.prev, struct netfs_trans_dst, trans_entry); - if (ret->state != st) { - ret = NULL; - list_for_each_entry_safe(dst, tmp, &t->dst_list, trans_entry) { - if (dst->state == st) { - ret = dst; - list_del_init(&dst->trans_entry); - break; - } - } - } else { - list_del(&ret->trans_entry); - } - spin_unlock(&t->dst_lock); - - if (ret) - netfs_trans_remove_dst(ret); -} - -static int netfs_trans_push(struct netfs_trans *t, struct netfs_state *st) -{ - int err; - - err = netfs_trans_push_dst(t, st); - if (err) - return err; - - err = netfs_trans_send(t, st); - if (err) - goto err_out_free; - - if (t->flags & NETFS_TRANS_SINGLE_DST) - pohmelfs_switch_active(st->psb); - - return 0; - -err_out_free: - t->result = err; - netfs_trans_drop_last(t, st); - - return err; -} - -int netfs_trans_finish_send(struct netfs_trans *t, struct pohmelfs_sb *psb) -{ - struct pohmelfs_config *c; - int err = -ENODEV; - struct netfs_state *st; -#if 0 - dprintk("%s: t: %p, gen: %u, size: %u, page_num: %u, active: %p.\n", - __func__, t, t->gen, t->iovec.iov_len, t->page_num, psb->active_state); -#endif - mutex_lock(&psb->state_lock); - list_for_each_entry(c, &psb->state_list, config_entry) { - st = &c->state; - - if (t->flags & NETFS_TRANS_SINGLE_DST) { - if (!(st->ctl.perm & POHMELFS_IO_PERM_READ)) - continue; - } else { - if (!(st->ctl.perm & POHMELFS_IO_PERM_WRITE)) - continue; - } - - if (psb->active_state && (psb->active_state->state.ctl.prio >= st->ctl.prio) && - (t->flags & NETFS_TRANS_SINGLE_DST)) - st = &psb->active_state->state; - - err = netfs_trans_push(t, st); - if (!err && (t->flags & NETFS_TRANS_SINGLE_DST)) - break; - } - - mutex_unlock(&psb->state_lock); -#if 0 - dprintk("%s: fully sent t: %p, gen: %u, size: %u, page_num: %u, err: %d.\n", - __func__, t, t->gen, t->iovec.iov_len, t->page_num, err); -#endif - if (err) - t->result = err; - return err; -} - -int netfs_trans_finish(struct netfs_trans *t, struct pohmelfs_sb *psb) -{ - int err; - struct netfs_cmd *cmd = t->iovec.iov_base; - - t->gen = atomic_inc_return(&psb->trans_gen); - - cmd->size = t->iovec.iov_len - sizeof(struct netfs_cmd) + - t->attached_size + t->attached_pages * sizeof(struct netfs_cmd); - cmd->cmd = NETFS_TRANS; - cmd->start = t->gen; - cmd->id = 0; - - if (psb->perform_crypto) { - cmd->ext = psb->crypto_attached_size; - cmd->csize = psb->crypto_attached_size; - } - - dprintk("%s: t: %u, size: %u, iov_len: %zu, attached_size: %u, attached_pages: %u.\n", - __func__, t->gen, cmd->size, t->iovec.iov_len, t->attached_size, t->attached_pages); - err = pohmelfs_trans_crypt(t, psb); - if (err) { - t->result = err; - netfs_convert_cmd(cmd); - dprintk("%s: trans: %llu, crypto_attached_size: %u, attached_size: %u, attached_pages: %d, trans_size: %u, err: %d.\n", - __func__, cmd->start, psb->crypto_attached_size, t->attached_size, t->attached_pages, cmd->size, err); - } - netfs_trans_put(t); - return err; -} - -/* - * Resend transaction to remote server(s). - * If new servers were added into superblock, we can try to send data - * to them too. - * - * It is called under superblock's state_lock, so we can safely - * dereference psb->state_list. Also, transaction's reference counter is - * bumped, so it can not go away under us, thus we can safely access all - * its members. State is locked. - * - * This function returns 0 if transaction was successfully sent to at - * least one destination target. - */ -int netfs_trans_resend(struct netfs_trans *t, struct pohmelfs_sb *psb) -{ - struct netfs_trans_dst *dst; - struct netfs_state *st; - struct pohmelfs_config *c; - int err, exist, error = -ENODEV; - - list_for_each_entry(c, &psb->state_list, config_entry) { - st = &c->state; - - exist = 0; - spin_lock(&t->dst_lock); - list_for_each_entry(dst, &t->dst_list, trans_entry) { - if (st == dst->state) { - exist = 1; - break; - } - } - spin_unlock(&t->dst_lock); - - if (exist) { - if (!(t->flags & NETFS_TRANS_SINGLE_DST) || - (c->config_entry.next == &psb->state_list)) { - dprintk("%s: resending st: %p, t: %p, gen: %u.\n", - __func__, st, t, t->gen); - err = netfs_trans_send(t, st); - if (!err) - error = 0; - } - continue; - } - - dprintk("%s: pushing/resending st: %p, t: %p, gen: %u.\n", - __func__, st, t, t->gen); - err = netfs_trans_push(t, st); - if (err) - continue; - error = 0; - if (t->flags & NETFS_TRANS_SINGLE_DST) - break; - } - - t->result = error; - return error; -} - -void *netfs_trans_add(struct netfs_trans *t, unsigned int size) -{ - struct iovec *io = &t->iovec; - void *ptr; - - if (size > t->total_size) { - ptr = ERR_PTR(-EINVAL); - goto out; - } - - if (io->iov_len + size > t->total_size) { - dprintk("%s: too big size t: %p, gen: %u, iov_len: %zu, size: %u, total: %u.\n", - __func__, t, t->gen, io->iov_len, size, t->total_size); - ptr = ERR_PTR(-E2BIG); - goto out; - } - - ptr = io->iov_base + io->iov_len; - io->iov_len += size; - -out: - dprintk("%s: t: %p, gen: %u, size: %u, total: %zu.\n", - __func__, t, t->gen, size, io->iov_len); - return ptr; -} - -void netfs_trans_free(struct netfs_trans *t) -{ - if (t->eng) - pohmelfs_crypto_thread_make_ready(t->eng->thread); - kfree(t); -} - -struct netfs_trans *netfs_trans_alloc(struct pohmelfs_sb *psb, unsigned int size, - unsigned int flags, unsigned int nr) -{ - struct netfs_trans *t; - unsigned int num, cont, pad, size_no_trans; - unsigned int crypto_added = 0; - struct netfs_cmd *cmd; - - if (psb->perform_crypto) - crypto_added = psb->crypto_attached_size; - - /* - * |sizeof(struct netfs_trans)| - * |sizeof(struct netfs_cmd)| - transaction header - * |size| - buffer with requested size - * |padding| - crypto padding, zero bytes - * |nr * sizeof(struct page *)| - array of page pointers - * - * Overall size should be less than PAGE_SIZE for guaranteed allocation. - */ - - cont = size; - size = ALIGN(size, psb->crypto_align_size); - pad = size - cont; - - size_no_trans = size + sizeof(struct netfs_cmd) * 2 + crypto_added; - - cont = sizeof(struct netfs_trans) + size_no_trans; - - num = (PAGE_SIZE - cont)/sizeof(struct page *); - - if (nr > num) - nr = num; - - t = kzalloc(cont + nr*sizeof(struct page *), GFP_NOIO); - if (!t) - goto err_out_exit; - - t->iovec.iov_base = (void *)(t + 1); - t->pages = (struct page **)(t->iovec.iov_base + size_no_trans); - - /* - * Reserving space for transaction header. - */ - t->iovec.iov_len = sizeof(struct netfs_cmd) + crypto_added; - - netfs_trans_init_static(t, nr, size_no_trans); - - t->flags = flags; - t->psb = psb; - - cmd = (struct netfs_cmd *)t->iovec.iov_base; - - cmd->size = size; - cmd->cpad = pad; - cmd->csize = crypto_added; - - dprintk("%s: t: %p, gen: %u, size: %u, padding: %u, align_size: %u, flags: %x, " - "page_num: %u, base: %p, pages: %p.\n", - __func__, t, t->gen, size, pad, psb->crypto_align_size, flags, nr, - t->iovec.iov_base, t->pages); - - return t; - -err_out_exit: - return NULL; -} - -int netfs_trans_init(void) -{ - int err = -ENOMEM; - - netfs_trans_dst = kmem_cache_create("netfs_trans_dst", sizeof(struct netfs_trans_dst), - 0, 0, NULL); - if (!netfs_trans_dst) - goto err_out_exit; - - netfs_trans_dst_pool = mempool_create_slab_pool(256, netfs_trans_dst); - if (!netfs_trans_dst_pool) - goto err_out_free; - - return 0; - -err_out_free: - kmem_cache_destroy(netfs_trans_dst); -err_out_exit: - return err; -} - -void netfs_trans_exit(void) -{ - mempool_destroy(netfs_trans_dst_pool); - kmem_cache_destroy(netfs_trans_dst); -} -- cgit v0.10.2 From 1ecd3c7ea76488c63b4b0a2561fd7eaf96cc8028 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Wed, 8 Feb 2012 17:13:37 -0800 Subject: nilfs2: avoid overflowing segment numbers in nilfs_ioctl_clean_segments() nsegs is read from userspace. Limit its value and avoid overflowing nsegs * sizeof(__u64) in the subsequent call to memdup_user(). This patch complements 481fe17e973fb9 ("nilfs2: potential integer overflow in nilfs_ioctl_clean_segments()"). Signed-off-by: Xi Wang Cc: Haogang Chen Acked-by: Ryusuke Konishi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 8866496..2a70fce 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -603,6 +603,8 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, nsegs = argv[4].v_nmembs; if (argv[4].v_size != argsz[4]) goto out; + if (nsegs > UINT_MAX / sizeof(__u64)) + goto out; /* * argv[4] points to segment numbers this ioctl cleans. We -- cgit v0.10.2 From dc9086004b3d5db75997a645b3fe08d9138b7ad0 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 8 Feb 2012 17:13:38 -0800 Subject: mm: compaction: check for overlapping nodes during isolation for migration When isolating pages for migration, migration starts at the start of a zone while the free scanner starts at the end of the zone. Migration avoids entering a new zone by never going beyond the free scanned. Unfortunately, in very rare cases nodes can overlap. When this happens, migration isolates pages without the LRU lock held, corrupting lists which will trigger errors in reclaim or during page free such as in the following oops BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [] free_pcppages_bulk+0xcc/0x450 PGD 1dda554067 PUD 1e1cb58067 PMD 0 Oops: 0000 [#1] SMP CPU 37 Pid: 17088, comm: memcg_process_s Tainted: G X RIP: free_pcppages_bulk+0xcc/0x450 Process memcg_process_s (pid: 17088, threadinfo ffff881c2926e000, task ffff881c2926c0c0) Call Trace: free_hot_cold_page+0x17e/0x1f0 __pagevec_free+0x90/0xb0 release_pages+0x22a/0x260 pagevec_lru_move_fn+0xf3/0x110 putback_lru_page+0x66/0xe0 unmap_and_move+0x156/0x180 migrate_pages+0x9e/0x1b0 compact_zone+0x1f3/0x2f0 compact_zone_order+0xa2/0xe0 try_to_compact_pages+0xdf/0x110 __alloc_pages_direct_compact+0xee/0x1c0 __alloc_pages_slowpath+0x370/0x830 __alloc_pages_nodemask+0x1b1/0x1c0 alloc_pages_vma+0x9b/0x160 do_huge_pmd_anonymous_page+0x160/0x270 do_page_fault+0x207/0x4c0 page_fault+0x25/0x30 The "X" in the taint flag means that external modules were loaded but but is unrelated to the bug triggering. The real problem was because the PFN layout looks like this Zone PFN ranges: DMA 0x00000010 -> 0x00001000 DMA32 0x00001000 -> 0x00100000 Normal 0x00100000 -> 0x01e80000 Movable zone start PFN for each node early_node_map[14] active PFN ranges 0: 0x00000010 -> 0x0000009b 0: 0x00000100 -> 0x0007a1ec 0: 0x0007a354 -> 0x0007a379 0: 0x0007f7ff -> 0x0007f800 0: 0x00100000 -> 0x00680000 1: 0x00680000 -> 0x00e80000 0: 0x00e80000 -> 0x01080000 1: 0x01080000 -> 0x01280000 0: 0x01280000 -> 0x01480000 1: 0x01480000 -> 0x01680000 0: 0x01680000 -> 0x01880000 1: 0x01880000 -> 0x01a80000 0: 0x01a80000 -> 0x01c80000 1: 0x01c80000 -> 0x01e80000 The fix is straight-forward. isolate_migratepages() has to make a similar check to isolate_freepage to ensure that it never isolates pages from a zone it does not hold the LRU lock for. This was discovered in a 3.0-based kernel but it affects 3.1.x, 3.2.x and current mainline. Signed-off-by: Mel Gorman Acked-by: Michal Nazarewicz Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/compaction.c b/mm/compaction.c index bd939a5..d9ebebe 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -330,8 +330,17 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, continue; nr_scanned++; - /* Get the page and skip if free */ + /* + * Get the page and ensure the page is within the same zone. + * See the comment in isolate_freepages about overlapping + * nodes. It is deliberate that the new zone lock is not taken + * as memory compaction should not move pages between nodes. + */ page = pfn_to_page(low_pfn); + if (page_zone(page) != zone) + continue; + + /* Skip if free */ if (PageBuddy(page)) continue; -- cgit v0.10.2 From ec44fd429879f7d4f28021f1a7f9fb0b5f831aab Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 8 Feb 2012 17:13:39 -0800 Subject: drivers/leds/leds-lm3530.c: fix setting pltfm->als_vmax In current code, pltfm->als_vmin is set to LM3530_ALS_WINDOW_mV and pltfm->als_vmax is 0. This does not make sense. I think what we want here is setting pltfm->als_vmax to LM3530_ALS_WINDOW_mV. Both als_vmin and als_vmax local variables will be set to pltfm->als_vmin and pltfm->als_vmax by a few lines latter. Thus also remove a redundant assignment for als_vmin and als_vmax in this patch. Signed-off-by: Axel Lin Cc: Shreshtha Kumar Sahu Acked-by: Milo(Woogyom) Kim Tested-by: Milo(Woogyom) Kim Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 45e6878..e59c166 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -164,8 +164,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) if (drvdata->mode == LM3530_BL_MODE_ALS) { if (pltfm->als_vmax == 0) { - pltfm->als_vmin = als_vmin = 0; - pltfm->als_vmin = als_vmax = LM3530_ALS_WINDOW_mV; + pltfm->als_vmin = 0; + pltfm->als_vmax = LM3530_ALS_WINDOW_mV; } als_vmin = pltfm->als_vmin; -- cgit v0.10.2 From b9980cdcf2524c5fe15d8cbae9c97b3ed6385563 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 8 Feb 2012 17:13:40 -0800 Subject: mm: fix UP THP spin_is_locked BUGs Fix CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_SMP=n CONFIG_DEBUG_VM=y CONFIG_DEBUG_SPINLOCK=n kernel: spin_is_locked() is then always false, and so triggers some BUGs in Transparent HugePage codepaths. asm-generic/bug.h mentions this problem, and provides a WARN_ON_SMP(x); but being too lazy to add VM_BUG_ON_SMP, BUG_ON_SMP, WARN_ON_SMP_ONCE, VM_WARN_ON_SMP_ONCE, just test NR_CPUS != 1 in the existing VM_BUG_ONs. Signed-off-by: Hugh Dickins Cc: Andrea Arcangeli Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b3ffc21..91d3efb 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2083,7 +2083,7 @@ static void collect_mm_slot(struct mm_slot *mm_slot) { struct mm_struct *mm = mm_slot->mm; - VM_BUG_ON(!spin_is_locked(&khugepaged_mm_lock)); + VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&khugepaged_mm_lock)); if (khugepaged_test_exit(mm)) { /* free mm_slot */ @@ -2113,7 +2113,7 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, int progress = 0; VM_BUG_ON(!pages); - VM_BUG_ON(!spin_is_locked(&khugepaged_mm_lock)); + VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&khugepaged_mm_lock)); if (khugepaged_scan.mm_slot) mm_slot = khugepaged_scan.mm_slot; diff --git a/mm/swap.c b/mm/swap.c index b0f529b..fff1ff7 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -659,7 +659,7 @@ void lru_add_page_tail(struct zone* zone, VM_BUG_ON(!PageHead(page)); VM_BUG_ON(PageCompound(page_tail)); VM_BUG_ON(PageLRU(page_tail)); - VM_BUG_ON(!spin_is_locked(&zone->lru_lock)); + VM_BUG_ON(NR_CPUS != 1 && !spin_is_locked(&zone->lru_lock)); SetPageLRU(page_tail); -- cgit v0.10.2 From 025e4ab3db07fcbf62c01e4f30d1012234beb980 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 8 Feb 2012 17:13:41 -0800 Subject: pcmcia: fix socket refcount decrementing on each resume This fixes a memory-corrupting bug: not only does it cause the warning, but as a result of dropping the refcount to zero, it causes the pcmcia_socket0 device structure to be freed while it still has references, causing slab caches corruption. A fatal oops quickly follows this warning - often even just a 'dmesg' following the warning causes the kernel to oops. While testing suspend/resume on an ARM device with PCMCIA support, and a CF card inserted, I found that after five suspend and resumes, the kernel would complain, and shortly die after with slab corruption. WARNING: at include/linux/kref.h:41 kobject_get+0x28/0x50() As the message doesn't give a clue about which kobject, and the built-in debugging in drivers/base/power/main.c happens too late, this was added right before each get_device(): printk("%s: %p [%s] %u\n", __func__, dev, kobject_name(&dev->kobj), atomic_read(&dev->kobj.kref.refcount)); and on the 3rd s2ram cycle, the following behaviour observed: On the 3rd suspend/resume cycle: dpm_prepare: c1a0d998 [pcmcia_socket0] 3 dpm_suspend: c1a0d998 [pcmcia_socket0] 3 dpm_suspend_noirq: c1a0d998 [pcmcia_socket0] 3 dpm_resume_noirq: c1a0d998 [pcmcia_socket0] 3 dpm_resume: c1a0d998 [pcmcia_socket0] 3 dpm_complete: c1a0d998 [pcmcia_socket0] 2 4th: dpm_prepare: c1a0d998 [pcmcia_socket0] 2 dpm_suspend: c1a0d998 [pcmcia_socket0] 2 dpm_suspend_noirq: c1a0d998 [pcmcia_socket0] 2 dpm_resume_noirq: c1a0d998 [pcmcia_socket0] 2 dpm_resume: c1a0d998 [pcmcia_socket0] 2 dpm_complete: c1a0d998 [pcmcia_socket0] 1 5th: dpm_prepare: c1a0d998 [pcmcia_socket0] 1 dpm_suspend: c1a0d998 [pcmcia_socket0] 1 dpm_suspend_noirq: c1a0d998 [pcmcia_socket0] 1 dpm_resume_noirq: c1a0d998 [pcmcia_socket0] 1 dpm_resume: c1a0d998 [pcmcia_socket0] 1 dpm_complete: c1a0d998 [pcmcia_socket0] 0 ------------[ cut here ]------------ WARNING: at include/linux/kref.h:41 kobject_get+0x28/0x50() Modules linked in: ucb1x00_core Backtrace: [] (dump_backtrace+0x0/0x110) from [] (dump_stack+0x18/0x1c) [] (dump_stack+0x0/0x1c) from [] (warn_slowpath_common+0x50/0x68) [] (warn_slowpath_common+0x0/0x68) from [] (warn_slowpath_null+0x24/0x28) [] (warn_slowpath_null+0x0/0x28) from [] (kobject_get+0x28/0x50) [] (kobject_get+0x0/0x50) from [] (get_device+0x1c/0x24) [] (dpm_complete+0x0/0x1a0) from [] (dpm_resume_end+0x1c/0x20) ... Looking at commit 7b24e7988263 ("pcmcia: split up central event handler"), the following change was made to cs.c: return 0; } #endif - - send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); + if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) + skt->callback->early_resume(skt); return 0; } And the corresponding change in ds.c is from: -static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) -{ - struct pcmcia_socket *s = pcmcia_get_socket(skt); ... - switch (event) { ... - case CS_EVENT_PM_RESUME: - if (verify_cis_cache(skt) != 0) { - dev_dbg(&skt->dev, "cis mismatch - different card\n"); - /* first, remove the card */ - ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); - mutex_lock(&s->ops_mutex); - destroy_cis_cache(skt); - kfree(skt->fake_cis); - skt->fake_cis = NULL; - s->functions = 0; - mutex_unlock(&s->ops_mutex); - /* now, add the new card */ - ds_event(skt, CS_EVENT_CARD_INSERTION, - CS_EVENT_PRI_LOW); - } - break; ... - } - pcmcia_put_socket(s); - return 0; -} /* ds_event */ to: +static int pcmcia_bus_early_resume(struct pcmcia_socket *skt) +{ + if (!verify_cis_cache(skt)) { + pcmcia_put_socket(skt); + return 0; + } + dev_dbg(&skt->dev, "cis mismatch - different card\n"); + /* first, remove the card */ + pcmcia_bus_remove(skt); + mutex_lock(&skt->ops_mutex); + destroy_cis_cache(skt); + kfree(skt->fake_cis); + skt->fake_cis = NULL; + skt->functions = 0; + mutex_unlock(&skt->ops_mutex); + /* now, add the new card */ + pcmcia_bus_add(skt); + return 0; +} As can be seen, the original function called pcmcia_get_socket() and pcmcia_put_socket() around the guts, whereas the replacement code calls pcmcia_put_socket() only in one path. This creates an imbalance in the refcounting. Testing with pcmcia_put_socket() put removed shows that the bug is gone: dpm_suspend: c1a10998 [pcmcia_socket0] 5 dpm_suspend_noirq: c1a10998 [pcmcia_socket0] 5 dpm_resume_noirq: c1a10998 [pcmcia_socket0] 5 dpm_resume: c1a10998 [pcmcia_socket0] 5 dpm_complete: c1a10998 [pcmcia_socket0] 5 Tested-by: Russell King Signed-off-by: Russell King Cc: Dominik Brodowski Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 749c2a1..1932029 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1269,10 +1269,8 @@ static int pcmcia_bus_add(struct pcmcia_socket *skt) static int pcmcia_bus_early_resume(struct pcmcia_socket *skt) { - if (!verify_cis_cache(skt)) { - pcmcia_put_socket(skt); + if (!verify_cis_cache(skt)) return 0; - } dev_dbg(&skt->dev, "cis mismatch - different card\n"); -- cgit v0.10.2 From d65b4e98d7ea3038b767b70fe8be959b2913f16d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 8 Feb 2012 19:21:53 -0800 Subject: Linux 3.3-rc3 diff --git a/Makefile b/Makefile index e3b23e8..7c44b67 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc3 NAME = Saber-toothed Squirrel # *DOCUMENTATION* -- cgit v0.10.2 From eb564e1dbc3685c08a52a0eea3821815fac50511 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Feb 2012 01:46:42 -0800 Subject: hwmon: (w83627ehf) Remove duplicate code Commit ec3e5a16446fef1891611fe3bdfa5954d1ddf5e4 slipped in some duplicate code. Remove it. Signed-off-by: Guenter Roeck Acked-by: Jean Delvare diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 4d383e7..5276d19 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -2354,11 +2354,6 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) for (i = 0; i < data->pwm_num; i++) data->pwm_enable_orig[i] = data->pwm_enable[i]; - /* Read pwm data to save original values */ - w83627ehf_update_pwm_common(dev, data); - for (i = 0; i < data->pwm_num; i++) - data->pwm_enable_orig[i] = data->pwm_enable[i]; - /* Register sysfs hooks */ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) { err = device_create_file(dev, &sda_sf3_arrays[i].dev_attr); -- cgit v0.10.2 From 09e87e5c4f9af656af2a8a3afc03487c5d9287c3 Mon Sep 17 00:00:00 2001 From: Nikolaus Schulz Date: Wed, 8 Feb 2012 18:56:08 +0100 Subject: hwmon: (f75375s) Fix automatic pwm mode setting for F75373 & F75375 In order to enable temperature mode aka automatic mode for the F75373 and F75375 chips, the two FANx_MODE bits in the fan configuration register need be set to 01, not 10. Signed-off-by: Nikolaus Schulz Cc: stable@kernel.org # 2.6.32+ Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index eedf574..c81ad45 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -369,7 +369,7 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) fanmode |= (3 << FAN_CTRL_MODE(nr)); break; case 2: /* AUTOMATIC*/ - fanmode |= (2 << FAN_CTRL_MODE(nr)); + fanmode |= (1 << FAN_CTRL_MODE(nr)); break; case 3: /* fan speed */ break; -- cgit v0.10.2 From a367a1e08b21e995c7112ff32b5efc97d104d405 Mon Sep 17 00:00:00 2001 From: Nikolaus Schulz Date: Wed, 8 Feb 2012 18:56:09 +0100 Subject: hwmon: (f75375s) Fix reading of wrong register when initializing the F75387 Unlike the other chips supported by this driver, the F75387 stores the pwm_mode in register F75375_REG_FAN_TIMER, not F75375_REG_CONFIG1. Signed-off-by: Nikolaus Schulz Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index c81ad45..b3eef44 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -723,7 +723,7 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data, if (data->kind == f75387) { bool manu, duty; - if (!(conf & (1 << F75387_FAN_CTRL_LINEAR(nr)))) + if (!(mode & (1 << F75387_FAN_CTRL_LINEAR(nr)))) data->pwm_mode[nr] = 1; manu = ((mode >> F75387_FAN_MANU_MODE(nr)) & 1); -- cgit v0.10.2 From eb2f255b2d360df3f500042a2258dcf2fcbe89a2 Mon Sep 17 00:00:00 2001 From: Nikolaus Schulz Date: Wed, 8 Feb 2012 18:56:10 +0100 Subject: hwmon: (f75375s) Fix bit shifting in f75375_write16 In order to extract the high byte of the 16-bit word, shift the word to the right, not to the left. Signed-off-by: Nikolaus Schulz Cc: stable@kernel.org # 2.6.32+ Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index b3eef44..0aabaaf 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -172,7 +172,7 @@ static inline void f75375_write8(struct i2c_client *client, u8 reg, static inline void f75375_write16(struct i2c_client *client, u8 reg, u16 value) { - int err = i2c_smbus_write_byte_data(client, reg, (value << 8)); + int err = i2c_smbus_write_byte_data(client, reg, (value >> 8)); if (err) return; i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF)); -- cgit v0.10.2 From a1e0c3cf7fb07227fe1f26161d969101dba78287 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Feb 2012 09:32:19 +0100 Subject: ALSA: hda - Fix mute-LED VREF value for new HP laptops The new HP laptops turns off the mute LED with VREF50 or VREF80, but not in HIZ unlike the previous models. Since VREF50 (also 80) works with the previous models, let's use VREF50 for all. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 948f0be..6345df1 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5078,9 +5078,9 @@ static int stac92xx_update_led_status(struct hda_codec *codec) spec->gpio_dir, spec->gpio_data); } else { notmtd_lvl = spec->gpio_led_polarity ? - AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD; + AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; muted_lvl = spec->gpio_led_polarity ? - AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ; + AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_50; spec->vref_led = muted ? muted_lvl : notmtd_lvl; stac_vrefout_set(codec, spec->vref_mute_led_nid, spec->vref_led); -- cgit v0.10.2 From b868179c47e9e8eadcd04c1f3105998e528988a3 Mon Sep 17 00:00:00 2001 From: Dean Nelson Date: Thu, 19 Jan 2012 17:47:24 +0000 Subject: e1000: add dropped DMA receive enable back in for WoL Commit d5bc77a223b0e9b9dfb002048d2b34a79e7d0b48 broke Wake-on-LAN by inadvertently dropping the enabling of DMA receives. Restore the enabling of DMA receives for WoL. This is applicable to 3.1+ stable trees. CC: stable@vger.stable.org Reported-by: Tobias Klausmann Signed-off-by: Dean Nelson Tested-by: Tobias Klausmann Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 669ca38..d94d64b 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -4740,12 +4740,14 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) e1000_setup_rctl(adapter); e1000_set_rx_mode(netdev); + rctl = er32(RCTL); + /* turn on all-multi mode if wake on multicast is enabled */ - if (wufc & E1000_WUFC_MC) { - rctl = er32(RCTL); + if (wufc & E1000_WUFC_MC) rctl |= E1000_RCTL_MPE; - ew32(RCTL, rctl); - } + + /* enable receives in the hardware */ + ew32(RCTL, rctl | E1000_RCTL_EN); if (hw->mac_type >= e1000_82540) { ctrl = er32(CTRL); -- cgit v0.10.2 From 0629292117572a60465f38cdedde2f8164c3df0b Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Thu, 2 Feb 2012 23:51:43 +0000 Subject: igb: fix vf lookup Recent addition of code to find already allocated VFs failed to take account that systems with 2 or more multi-port SR-IOV capable controllers might have already enabled VFs. Make sure that the VFs the function is finding are actually subordinate to the particular instance of the adapter that is looking for them and not subordinate to some device that has previously enabled SR-IOV. This is applicable to 3.2+ kernels. CC: stable@vger.kernel.org Reported-by: David Ahern Signed-off-by: Greg Rose Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e91d73c..94be6c3 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -5012,7 +5012,8 @@ static int igb_find_enabled_vfs(struct igb_adapter *adapter) vf_devfn = pdev->devfn + 0x80; pvfdev = pci_get_device(hw->vendor_id, device_id, NULL); while (pvfdev) { - if (pvfdev->devfn == vf_devfn) + if (pvfdev->devfn == vf_devfn && + (pvfdev->bus->number >= pdev->bus->number)) vfs_found++; vf_devfn += vf_stride; pvfdev = pci_get_device(hw->vendor_id, -- cgit v0.10.2 From a4b08329c74985e5cc3a44b6d2b2c59444ed8079 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Fri, 3 Feb 2012 00:54:13 +0000 Subject: ixgbe: fix vf lookup Recent addition of code to find already allocated VFs failed to take account that systems with 2 or more multi-port SR-IOV capable controllers might have already enabled VFs. Make sure that the VFs the function is finding are actually subordinate to the particular instance of the adapter that is looking for them and not subordinate to some device that has previously enabled SR-IOV. This bug exists in 3.2 stable as well as 3.3 release candidates. CC: stable@vger.kernel.org Reported-by: David Ahern Signed-off-by: Greg Rose Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 8d8cdbc..b01ecb4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -67,7 +67,8 @@ static int ixgbe_find_enabled_vfs(struct ixgbe_adapter *adapter) vf_devfn = pdev->devfn + 0x80; pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, device_id, NULL); while (pvfdev) { - if (pvfdev->devfn == vf_devfn) + if (pvfdev->devfn == vf_devfn && + (pvfdev->bus->number >= pdev->bus->number)) vfs_found++; vf_devfn += 2; pvfdev = pci_get_device(IXGBE_INTEL_VENDOR_ID, -- cgit v0.10.2 From 4cd6923d3481773f3fbcae5ca940c8823aa33475 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Wed, 25 Jan 2012 07:59:37 +0000 Subject: ixgbe: Fix case of Tx Hang in PF with 32 VFs A check for the number of VFs allocated should have used a greater than equal operator instead of just greater than. This caused allocation of exactly 32 VFs to not enable the PF transmit and receive enables. Signed-off-by: Greg Rose Tested-by: Robert E Garrett Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 92192c6..819b5c0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2830,7 +2830,7 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl | vt_reg_bits); vf_shift = adapter->num_vfs % 32; - reg_offset = (adapter->num_vfs > 32) ? 1 : 0; + reg_offset = (adapter->num_vfs >= 32) ? 1 : 0; /* Enable only the PF's pool for Tx/Rx */ IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset), (1 << vf_shift)); -- cgit v0.10.2 From 642c680e9314fc102bc1f096f48ae3974931ef42 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 10 Nov 2011 09:09:17 +0000 Subject: ixgbe: Fix broken dependency on MAX_SKB_FRAGS being related to page size This patch fixes an issue in which RSC will generate corrupted frames when PAGE_SIZE is larger than 8K. Specifically it looks like that in 2.6.39 a change was made so that GRO would always have at least 16 frags available for coalescing, but the ixgbe RSC logic was not updated. As such the RSC feature would generate a frame larger than 64K and then overflow the value in the IP length field. To correct that I am now basing things on the PAGE_SIZE. Signed-off-by: Alexander Duyck Tested-by: Stephen Ko Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 819b5c0..0688704 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2633,22 +2633,22 @@ static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, /* * we must limit the number of descriptors so that the * total size of max desc * buf_len is not greater - * than 65535 + * than 65536 */ if (ring_is_ps_enabled(ring)) { -#if (MAX_SKB_FRAGS > 16) +#if (PAGE_SIZE < 8192) rscctrl |= IXGBE_RSCCTL_MAXDESC_16; -#elif (MAX_SKB_FRAGS > 8) +#elif (PAGE_SIZE < 16384) rscctrl |= IXGBE_RSCCTL_MAXDESC_8; -#elif (MAX_SKB_FRAGS > 4) +#elif (PAGE_SIZE < 32768) rscctrl |= IXGBE_RSCCTL_MAXDESC_4; #else rscctrl |= IXGBE_RSCCTL_MAXDESC_1; #endif } else { - if (rx_buf_len < IXGBE_RXBUFFER_4K) + if (rx_buf_len <= IXGBE_RXBUFFER_4K) rscctrl |= IXGBE_RSCCTL_MAXDESC_16; - else if (rx_buf_len < IXGBE_RXBUFFER_8K) + else if (rx_buf_len <= IXGBE_RXBUFFER_8K) rscctrl |= IXGBE_RSCCTL_MAXDESC_8; else rscctrl |= IXGBE_RSCCTL_MAXDESC_4; -- cgit v0.10.2 From 9d837ea2b72321ffbdaf83c980923d23ae898baa Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Sat, 7 Jan 2012 08:39:50 +0000 Subject: ixgbe: do not update real num queues when netdev is going away If the netdev is already in NETREG_UNREGISTERING/_UNREGISTERED state, do not update the real num tx queues. netdev_queue_update_kobjects() is already called via remove_queue_kobjects() at NETREG_UNREGISTERING time. So, when upper layer driver, e.g., FCoE protocol stack is monitoring the netdev event of NETDEV_UNREGISTER and calls back to LLD ndo_fcoe_disable() to remove extra queues allocated for FCoE, the associated txq sysfs kobjects are already removed, and trying to update the real num queues would cause something like below: ... PID: 25138 TASK: ffff88021e64c440 CPU: 3 COMMAND: "kworker/3:3" #0 [ffff88021f007760] machine_kexec at ffffffff810226d9 #1 [ffff88021f0077d0] crash_kexec at ffffffff81089d2d #2 [ffff88021f0078a0] oops_end at ffffffff813bca78 #3 [ffff88021f0078d0] no_context at ffffffff81029e72 #4 [ffff88021f007920] __bad_area_nosemaphore at ffffffff8102a155 #5 [ffff88021f0079f0] bad_area_nosemaphore at ffffffff8102a23e #6 [ffff88021f007a00] do_page_fault at ffffffff813bf32e #7 [ffff88021f007b10] page_fault at ffffffff813bc045 [exception RIP: sysfs_find_dirent+17] RIP: ffffffff81178611 RSP: ffff88021f007bc0 RFLAGS: 00010246 RAX: ffff88021e64c440 RBX: ffffffff8156cc63 RCX: 0000000000000004 RDX: ffffffff8156cc63 RSI: 0000000000000000 RDI: 0000000000000000 RBP: ffff88021f007be0 R8: 0000000000000004 R9: 0000000000000008 R10: ffffffff816fed00 R11: 0000000000000004 R12: 0000000000000000 R13: ffffffff8156cc63 R14: 0000000000000000 R15: ffff8802222a0000 ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 #8 [ffff88021f007be8] sysfs_get_dirent at ffffffff81178c07 #9 [ffff88021f007c18] sysfs_remove_group at ffffffff8117ac27 #10 [ffff88021f007c48] netdev_queue_update_kobjects at ffffffff813178f9 #11 [ffff88021f007c88] netif_set_real_num_tx_queues at ffffffff81303e38 #12 [ffff88021f007cc8] ixgbe_set_num_queues at ffffffffa0249763 [ixgbe] #13 [ffff88021f007cf8] ixgbe_init_interrupt_scheme at ffffffffa024ea89 [ixgbe] #14 [ffff88021f007d48] ixgbe_fcoe_disable at ffffffffa0267113 [ixgbe] #15 [ffff88021f007d68] vlan_dev_fcoe_disable at ffffffffa014fef5 [8021q] #16 [ffff88021f007d78] fcoe_interface_cleanup at ffffffffa02b7dfd [fcoe] #17 [ffff88021f007df8] fcoe_destroy_work at ffffffffa02b7f08 [fcoe] #18 [ffff88021f007e18] process_one_work at ffffffff8105d7ca #19 [ffff88021f007e68] worker_thread at ffffffff81060513 #20 [ffff88021f007ee8] kthread at ffffffff810648b6 #21 [ffff88021f007f48] kernel_thread_helper at ffffffff813c40f4 Signed-off-by: Yi Zou Tested-by: Ross Brattain Tested-by: Stephen Ko Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0688704..3dc6cef 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -4330,6 +4330,10 @@ static int ixgbe_set_num_queues(struct ixgbe_adapter *adapter) adapter->num_tx_queues = 1; done: + if ((adapter->netdev->reg_state == NETREG_UNREGISTERED) || + (adapter->netdev->reg_state == NETREG_UNREGISTERING)) + return 0; + /* Notify the stack of the (possibly) reduced queue counts. */ netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); return netif_set_real_num_rx_queues(adapter->netdev, -- cgit v0.10.2 From 5facb8e0c4dc1c8c47b71fd7f376defe16185733 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sat, 28 Jan 2012 01:22:35 +0000 Subject: ixgbe: dcb: up2tc mapping lost on disable/enable CEE DCB state Users expect the up2tc mapping to be maintained across a DCB enable/disable/enable transition. And since we maintain all the other DCB attributes we should do this for up2tc mappings as well just to be consistent. Also without this we break user space applications that expect this to occur that previously worked. Signed-off-by: John Fastabend Tested-by: Stephen Ko Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index 005e5f2..79a92fe 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -112,6 +112,8 @@ static u8 ixgbe_dcbnl_get_state(struct net_device *netdev) static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) { u8 err = 0; + u8 prio_tc[MAX_USER_PRIORITY] = {0}; + int i; struct ixgbe_adapter *adapter = netdev_priv(netdev); /* Fail command if not in CEE mode */ @@ -122,10 +124,15 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state) if (!!state != !(adapter->flags & IXGBE_FLAG_DCB_ENABLED)) return err; - if (state > 0) + if (state > 0) { err = ixgbe_setup_tc(netdev, adapter->dcb_cfg.num_tcs.pg_tcs); - else + ixgbe_dcb_unpack_map(&adapter->dcb_cfg, DCB_TX_CONFIG, prio_tc); + } else { err = ixgbe_setup_tc(netdev, 0); + } + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) + netdev_set_prio_tc_map(netdev, i, prio_tc[i]); return err; } -- cgit v0.10.2 From 9cc00b51a3cbb3b933065a55eaa9bfc306411b7a Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sat, 28 Jan 2012 03:32:17 +0000 Subject: ixgbe: ethtool: stats user buffer overrun If the number of tx/rx queues changes the ethtool ioctl ETHTOOL_GSTATS may overrun the userspace buffer. This occurs because the general practice in user space to query stats is to issue a ETHTOOL_GSSET cmd to learn the buffer size needed, allocate the buffer, then call ETHTOOL_GSTIRNGS and ETHTOOL_GSTATS. If the number of real_num_queues is changed or flow control attributes are changed after ETHTOOL_GSSET but before the ETHTOOL_GSTRINGS/ETHTOOL_GSTATS a user space buffer overrun occurs. To fix the overrun always return the max buffer size needed from get_sset_count() then return all strings and stats from get_strings()/get_ethtool_stats(). This _will_ change the output from the ioctl() call which could break applications and script parsing in theory. I believe these changes should not break existing tools because the only changes will be more {tx|rx}_queues and the {tx|rx}_pb_* stats will always be returned. Existing scripts already need to handle changing number of queues because this occurs today depending on system and current features. The {tx|rx}_pb_* stats are at the end of the output and should be handled by scripts today regardless. Finally get_ethtool_stats and get_strings are free-form outputs tools parsing these outputs should be defensive anyways. In the end these updates are better then having a tool segfault because of a buffer overrun. Signed-off-by: John Fastabend Signed-off-by: Jeff Kirsher diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 1f31a04..a629754 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -120,19 +120,23 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = { #endif /* IXGBE_FCOE */ }; -#define IXGBE_QUEUE_STATS_LEN \ - ((((struct ixgbe_adapter *)netdev_priv(netdev))->num_tx_queues + \ - ((struct ixgbe_adapter *)netdev_priv(netdev))->num_rx_queues) * \ +/* ixgbe allocates num_tx_queues and num_rx_queues symmetrically so + * we set the num_rx_queues to evaluate to num_tx_queues. This is + * used because we do not have a good way to get the max number of + * rx queues with CONFIG_RPS disabled. + */ +#define IXGBE_NUM_RX_QUEUES netdev->num_tx_queues + +#define IXGBE_QUEUE_STATS_LEN ( \ + (netdev->num_tx_queues + IXGBE_NUM_RX_QUEUES) * \ (sizeof(struct ixgbe_queue_stats) / sizeof(u64))) #define IXGBE_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbe_gstrings_stats) #define IXGBE_PB_STATS_LEN ( \ - (((struct ixgbe_adapter *)netdev_priv(netdev))->flags & \ - IXGBE_FLAG_DCB_ENABLED) ? \ - (sizeof(((struct ixgbe_adapter *)0)->stats.pxonrxc) + \ - sizeof(((struct ixgbe_adapter *)0)->stats.pxontxc) + \ - sizeof(((struct ixgbe_adapter *)0)->stats.pxoffrxc) + \ - sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \ - / sizeof(u64) : 0) + (sizeof(((struct ixgbe_adapter *)0)->stats.pxonrxc) + \ + sizeof(((struct ixgbe_adapter *)0)->stats.pxontxc) + \ + sizeof(((struct ixgbe_adapter *)0)->stats.pxoffrxc) + \ + sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \ + / sizeof(u64)) #define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + \ IXGBE_PB_STATS_LEN + \ IXGBE_QUEUE_STATS_LEN) @@ -1078,8 +1082,15 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = (ixgbe_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } - for (j = 0; j < adapter->num_tx_queues; j++) { + for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { ring = adapter->tx_ring[j]; + if (!ring) { + data[i] = 0; + data[i+1] = 0; + i += 2; + continue; + } + do { start = u64_stats_fetch_begin_bh(&ring->syncp); data[i] = ring->stats.packets; @@ -1087,8 +1098,15 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; } - for (j = 0; j < adapter->num_rx_queues; j++) { + for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { ring = adapter->rx_ring[j]; + if (!ring) { + data[i] = 0; + data[i+1] = 0; + i += 2; + continue; + } + do { start = u64_stats_fetch_begin_bh(&ring->syncp); data[i] = ring->stats.packets; @@ -1096,22 +1114,20 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; } - if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { - for (j = 0; j < MAX_TX_PACKET_BUFFERS; j++) { - data[i++] = adapter->stats.pxontxc[j]; - data[i++] = adapter->stats.pxofftxc[j]; - } - for (j = 0; j < MAX_RX_PACKET_BUFFERS; j++) { - data[i++] = adapter->stats.pxonrxc[j]; - data[i++] = adapter->stats.pxoffrxc[j]; - } + + for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) { + data[i++] = adapter->stats.pxontxc[j]; + data[i++] = adapter->stats.pxofftxc[j]; + } + for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) { + data[i++] = adapter->stats.pxonrxc[j]; + data[i++] = adapter->stats.pxoffrxc[j]; } } static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { - struct ixgbe_adapter *adapter = netdev_priv(netdev); char *p = (char *)data; int i; @@ -1126,31 +1142,29 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - for (i = 0; i < adapter->num_tx_queues; i++) { + for (i = 0; i < netdev->num_tx_queues; i++) { sprintf(p, "tx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; } - for (i = 0; i < adapter->num_rx_queues; i++) { + for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) { sprintf(p, "rx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "rx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; } - if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { - for (i = 0; i < MAX_TX_PACKET_BUFFERS; i++) { - sprintf(p, "tx_pb_%u_pxon", i); - p += ETH_GSTRING_LEN; - sprintf(p, "tx_pb_%u_pxoff", i); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < MAX_RX_PACKET_BUFFERS; i++) { - sprintf(p, "rx_pb_%u_pxon", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rx_pb_%u_pxoff", i); - p += ETH_GSTRING_LEN; - } + for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { + sprintf(p, "tx_pb_%u_pxon", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_pb_%u_pxoff", i); + p += ETH_GSTRING_LEN; + } + for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { + sprintf(p, "rx_pb_%u_pxon", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_pb_%u_pxoff", i); + p += ETH_GSTRING_LEN; } /* BUG_ON(p - data != IXGBE_STATS_LEN * ETH_GSTRING_LEN); */ break; -- cgit v0.10.2 From 35bded8f91e887bf2d692071bb720b03c6ccd3fe Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 8 Feb 2012 11:42:39 +0900 Subject: ARM: EXYNOS: Bring exynos4-dt up to date This commit brings exynos4-dt in line with recent changes to mach-exynos tree, specifically: - Fixes build break related to replacing plat/exynos4.h with common.h in commit cc511b8d84d8 ("ARM: 7257/1: EXYNOS: introduce arch/arm/mach-exynos/common.[ch]") - Converts machine to use CONFIG_MULTI_IRQ_HANDLER as done for other machines in commit 4e44d2cb95bd ("ARM: exynos4: convert to CONFIG_MULTI_IRQ_HANDLER") - Adds restart specifier as done for other machines in commit 9eb4859564d6 ("ARM: 7262/1: restart: EXYNOS: use new restart hook") Signed-off-by: Karol Lewandowski Signed-off-by: Kyungmin Park Cc: Thomas Abraham Signed-off-by: Kukjin Kim diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c index 85fa027..e6b02fd 100644 --- a/arch/arm/mach-exynos/mach-exynos4-dt.c +++ b/arch/arm/mach-exynos/mach-exynos4-dt.c @@ -15,11 +15,13 @@ #include #include +#include #include #include #include -#include + +#include "common.h" /* * The following lookup table is used to override device names when devices @@ -60,7 +62,7 @@ static const struct of_dev_auxdata exynos4210_auxdata_lookup[] __initconst = { static void __init exynos4210_dt_map_io(void) { - s5p_init_io(NULL, 0, S5P_VA_CHIPID); + exynos_init_io(NULL, 0); s3c24xx_init_clocks(24000000); } @@ -79,7 +81,9 @@ DT_MACHINE_START(EXYNOS4210_DT, "Samsung Exynos4 (Flattened Device Tree)") /* Maintainer: Thomas Abraham */ .init_irq = exynos4_init_irq, .map_io = exynos4210_dt_map_io, + .handle_irq = gic_handle_irq, .init_machine = exynos4210_dt_machine_init, .timer = &exynos4_timer, .dt_compat = exynos4210_dt_compat, + .restart = exynos4_restart, MACHINE_END -- cgit v0.10.2 From da911782be4c82dc1222aa0cb5bef28605d1e117 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 8 Feb 2012 11:42:43 +0900 Subject: ARM: EXYNOS: Add cpu-offset property in gic device tree node Commit db0d4db22a78 ('ARM: gic: allow GIC to support non-banked setups) requires a cpu-offset property to be specified for non-banked gic controllers, which is the case for Exynos4. Reported-and-Tested-by: Karol Lewandowski Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 63d7578..a1dd2ee 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -29,6 +29,7 @@ compatible = "arm,cortex-a9-gic"; #interrupt-cells = <3>; interrupt-controller; + cpu-offset = <0x8000>; reg = <0x10490000 0x1000>, <0x10480000 0x100>; }; -- cgit v0.10.2 From 1987877d869027ab63dc9df515e11f19279a8091 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 8 Feb 2012 16:57:29 -0800 Subject: ASoC: fsi: fixup fsi_pointer() calculation method current fsi_pointer() calculation was not correct for FSI driver. This patch fix it up. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index db6c89a..ea4a82d0 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1152,12 +1152,8 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream)); - int samples_pos = io->buff_sample_pos - 1; - if (samples_pos < 0) - samples_pos = 0; - - return fsi_sample2frame(fsi, samples_pos); + return fsi_sample2frame(fsi, io->buff_sample_pos); } static struct snd_pcm_ops fsi_pcm_ops = { -- cgit v0.10.2 From b8b9987ffdc2ab9c5e2c1edad556b23ccb38249b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Feb 2012 02:46:38 +0100 Subject: ARM: 7320/1: Fix proc_info table alignment With an admittedly exotic choice of configuration options (CC_OPTIMIZE_FOR_SIZE, THUMB2, some other size-minimizing ones) and compiler, the proc_info table can end up being misaligned, and the kernel being unbootable (Error: unrecognized/unsupported processor variant). Forcing the alignement to 4 bytes in the linker script fixes the issue. Signed-off-by: Marc Zyngier Signed-off-by: Russell King diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 1e19691..43a31fb 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -10,6 +10,7 @@ #include #define PROC_INFO \ + . = ALIGN(4); \ VMLINUX_SYMBOL(__proc_info_begin) = .; \ *(.proc.info.init) \ VMLINUX_SYMBOL(__proc_info_end) = .; -- cgit v0.10.2 From b46c0f74657d1fe1c1b0c1452631cc38a9e6987f Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 7 Feb 2012 19:42:07 +0100 Subject: ARM: 7321/1: cache-v7: Disable preemption when reading CCSIDR armv7's flush_cache_all() flushes caches via set/way. To determine the cache attributes (line size, number of sets, etc.) the assembly first writes the CSSELR register to select a cache level and then reads the CCSIDR register. The CSSELR register is banked per-cpu and is used to determine which cache level CCSIDR reads. If the task is migrated between when the CSSELR is written and the CCSIDR is read the CCSIDR value may be for an unexpected cache level (for example L1 instead of L2) and incorrect cache flushing could occur. Disable interrupts across the write and read so that the correct cache attributes are read and used for the cache flushing routine. We disable interrupts instead of disabling preemption because the critical section is only 3 instructions and we want to call v7_dcache_flush_all from __v7_setup which doesn't have a full kernel stack with a struct thread_info. This fixes a problem we see in scm_call() when flush_cache_all() is called from preemptible context and sometimes the L2 cache is not properly flushed out. Signed-off-by: Stephen Boyd Acked-by: Catalin Marinas Reviewed-by: Nicolas Pitre Cc: stable@vger.kernel.org Signed-off-by: Russell King diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 07c4bc8..7a24d39 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -54,9 +54,15 @@ loop1: and r1, r1, #7 @ mask of the bits for current cache only cmp r1, #2 @ see what cache we have at this level blt skip @ skip if no cache, or just i-cache +#ifdef CONFIG_PREEMPT + save_and_disable_irqs r9 @ make cssr&csidr read atomic +#endif mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr isb @ isb to sych the new cssr&csidr mrc p15, 1, r1, c0, c0, 0 @ read the new csidr +#ifdef CONFIG_PREEMPT + restore_irqs_notrace r9 +#endif and r2, r1, #7 @ extract the length of the cache lines add r2, r2, #4 @ add 4 (line length offset) ldr r4, =0x3ff -- cgit v0.10.2 From bdf800c4fceb6d8dbe65471d214eb44a61f5bfc9 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 7 Feb 2012 19:42:33 +0100 Subject: ARM: 7322/1: Print BUG instead of undefined instruction on BUG_ON() The ARM kernel uses undefined instructions to implement BUG/BUG_ON(). This leads to problems where people don't read one line above the Oops message and see the "kernel BUG at ..." message and so they wrongly assume the kernel has hit an undefined instruction. Instead of printing: Internal error: Oops - undefined instruction: 0 [#1] PREEMPT SMP print Internal error: Oops - BUG: 0 [#1] PREEMPT SMP This should prevent people from thinking the BUG_ON was an undefined instruction when it was actually intentional. Signed-off-by: Stephen Boyd Acked-by: Simon Glass Tested-by: Simon Glass Signed-off-by: Russell King diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 99a5727..f84dfe6 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -266,6 +266,7 @@ void die(const char *str, struct pt_regs *regs, int err) { struct thread_info *thread = current_thread_info(); int ret; + enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE; oops_enter(); @@ -273,7 +274,9 @@ void die(const char *str, struct pt_regs *regs, int err) console_verbose(); bust_spinlocks(1); if (!user_mode(regs)) - report_bug(regs->ARM_pc, regs); + bug_type = report_bug(regs->ARM_pc, regs); + if (bug_type != BUG_TRAP_TYPE_NONE) + str = "Oops - BUG"; ret = __die(str, err, thread, regs); if (regs && kexec_should_crash(thread->task)) -- cgit v0.10.2 From d980e0f8d858c6963d676013e976ff00ab7acb2b Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 09:42:11 +0000 Subject: ARM: omap: fix oops in arch/arm/mach-omap2/vp.c when pmic is not found When the PMIC is not found, voltdm->pmic will be NULL. vp.c's initialization function tries to dereferences this, which causes an oops: Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT Modules linked in: CPU: 0 Not tainted (3.3.0-rc2+ #204) PC is at omap_vp_init+0x5c/0x15c LR is at omap_vp_init+0x58/0x15c pc : [] lr : [] psr: 60000013 sp : c181ff30 ip : c181ff68 fp : c181ff64 r10: c0407808 r9 : c040786c r8 : c0407814 r7 : c0026868 r6 : c00264fc r5 : c040ad6c r4 : 00000000 r3 : 00000040 r2 : 000032c8 r1 : 0000fa00 r0 : 000032c8 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 80004019 DAC: 00000015 Process swapper (pid: 1, stack limit = 0xc181e2e8) Stack: (0xc181ff30 to 0xc1820000) ff20: c0381d00 c02e9c6d c0383582 c040786c ff40: c040ad6c c00264fc c0026868 c0407814 00000000 c03d9de4 c181ff8c c181ff68 ff60: c03db448 c03db830 c02e982c c03fdfb8 c03fe004 c0039988 00000013 00000000 ff80: c181ff9c c181ff90 c03d9df8 c03db390 c181ffdc c181ffa0 c0008798 c03d9df0 ffa0: c181ffc4 c181ffb0 c0055a44 c0187050 c0039988 c03fdfb8 c03fe004 c0039988 ffc0: 00000013 00000000 00000000 00000000 c181fff4 c181ffe0 c03d1284 c0008708 ffe0: 00000000 c03d1208 00000000 c181fff8 c0039988 c03d1214 1077ce40 01f7ee08 Backtrace: [] (omap_vp_init+0x0/0x15c) from [] (omap_voltage_late_init+0xc4/0xfc) [] (omap_voltage_late_init+0x0/0xfc) from [] (omap2_common_pm_late_init+0x14/0x54) r8:00000000 r7:00000013 r6:c0039988 r5:c03fe004 r4:c03fdfb8 [] (omap2_common_pm_late_init+0x0/0x54) from [] (do_one_initcall+0x9c/0x164) [] (do_one_initcall+0x0/0x164) from [] (kernel_init+0x7c/0x120) [] (kernel_init+0x0/0x120) from [] (do_exit+0x0/0x2cc) r5:c03d1208 r4:00000000 Code: e5ca300b e5900034 ebf69027 e5994024 (e5941000) ---[ end trace aed617dddaf32c3d ]--- Kernel panic - not syncing: Attempted to kill init! Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/vp.c b/arch/arm/mach-omap2/vp.c index 807391d..0df8882 100644 --- a/arch/arm/mach-omap2/vp.c +++ b/arch/arm/mach-omap2/vp.c @@ -41,6 +41,11 @@ void __init omap_vp_init(struct voltagedomain *voltdm) u32 val, sys_clk_rate, timeout, waittime; u32 vddmin, vddmax, vstepmin, vstepmax; + if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { + pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); + return; + } + if (!voltdm->read || !voltdm->write) { pr_err("%s: No read/write API for accessing vdd_%s regs\n", __func__, voltdm->name); -- cgit v0.10.2 From 40410715715178ec196314dd0c19150c06901f80 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 09:44:55 +0000 Subject: ARM: omap: fix oops in drivers/video/omap2/dss/dpi.c When a PMIC is not found, this driver is unable to obtain its 'vdds_dsi_reg' regulator. Even through its initialization function fails, other code still calls its enable function, which fails to check whether it has this regulator before asking for it to be enabled. This fixes the oops, however a better fix would be to sort out the upper layers to prevent them calling into a module which failed to initialize. Unable to handle kernel NULL pointer dereference at virtual address 00000038 pgd = c0004000 [00000038] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT Modules linked in: CPU: 0 Not tainted (3.3.0-rc2+ #228) PC is at regulator_enable+0x10/0x70 LR is at omapdss_dpi_display_enable+0x54/0x15c pc : [] lr : [] psr: 60000013 sp : c181fd90 ip : c181fdb0 fp : c181fdac r10: c042eff0 r9 : 00000060 r8 : c044a164 r7 : c042c0e4 r6 : c042bd60 r5 : 00000000 r4 : c042bd60 r3 : c084de48 r2 : c181e000 r1 : c042bd60 r0 : 00000000 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 80004019 DAC: 00000015 Process swapper (pid: 1, stack limit = 0xc181e2e8) Stack: (0xc181fd90 to 0xc1820000) fd80: c001754c c042bd60 00000000 c042bd60 fda0: c181fdcc c181fdb0 c01af994 c01b9a04 c0016104 c042bd60 c042bd60 c044a338 fdc0: c181fdec c181fdd0 c01b5ed0 c01af94c c042bd60 c042bd60 c1aa8000 c1aa8a0c fde0: c181fe04 c181fdf0 c01b5f54 c01b5ea8 c02fc18c c042bd60 c181fe3c c181fe08 fe00: c01b2a18 c01b5f48 c01aed14 c02fc160 c01df8ec 00000002 c042bd60 00000003 fe20: c042bd60 c1aa8000 c1aa8a0c c042eff8 c181fe84 c181fe40 c01b3874 c01b29fc fe40: c042eff8 00000000 c042f000 c0449db8 c044ed78 00000000 c181fe74 c042eff8 fe60: c042eff8 c0449db8 c0449db8 c044ed78 00000000 00000000 c181fe94 c181fe88 fe80: c01e452c c01b35e8 c181feb4 c181fe98 c01e2fdc c01e4518 c042eff8 c0449db8 fea0: c0449db8 c181fef0 c181fecc c181feb8 c01e3104 c01e2f48 c042eff8 c042f02c fec0: c181feec c181fed0 c01e3190 c01e30c0 c01e311c 00000000 c01e311c c0449db8 fee0: c181ff14 c181fef0 c01e1998 c01e3128 c18330a8 c1892290 c04165e8 c0449db8 ff00: c0449db8 c1ab60c0 c181ff24 c181ff18 c01e2e28 c01e194c c181ff54 c181ff28 ff20: c01e2218 c01e2e14 c039afed c181ff38 c04165e8 c041660c c0449db8 00000013 ff40: 00000000 c03ffdb8 c181ff7c c181ff58 c01e384c c01e217c c181ff7c c04165e8 ff60: c041660c c003a37c 00000013 00000000 c181ff8c c181ff80 c01e488c c01e3790 ff80: c181ff9c c181ff90 c03ffdcc c01e484c c181ffdc c181ffa0 c0008798 c03ffdc4 ffa0: c181ffc4 c181ffb0 c0056440 c0187810 c003a37c c04165e8 c041660c c003a37c ffc0: 00000013 00000000 00000000 00000000 c181fff4 c181ffe0 c03ea284 c0008708 ffe0: 00000000 c03ea208 00000000 c181fff8 c003a37c c03ea214 1073cec0 01f7ee08 Backtrace: [] (regulator_enable+0x0/0x70) from [] (omapdss_dpi_display_enable+0x54/0x15c) r6:c042bd60 r5:00000000 r4:c042bd60 [] (omapdss_dpi_display_enable+0x0/0x15c) from [] (generic_dpi_panel_power_on+0x34/0x78) r6:c044a338 r5:c042bd60 r4:c042bd60 [] (generic_dpi_panel_power_on+0x0/0x78) from [] (generic_dpi_panel_enable+0x18/0x28) r7:c1aa8a0c r6:c1aa8000 r5:c042bd60 r4:c042bd60 [] (generic_dpi_panel_enable+0x0/0x28) from [] (omapfb_init_display+0x28/0x150) r4:c042bd60 [] (omapfb_init_display+0x0/0x150) from [] (omapfb_probe+0x298/0x318) r8:c042eff8 r7:c1aa8a0c r6:c1aa8000 r5:c042bd60 r4:00000003 [] (omapfb_probe+0x0/0x318) from [] (platform_drv_probe+0x20/0x24) [] (platform_drv_probe+0x0/0x24) from [] (really_probe+0xa0/0x178) [] (really_probe+0x0/0x178) from [] (driver_probe_device+0x50/0x68) r7:c181fef0 r6:c0449db8 r5:c0449db8 r4:c042eff8 [] (driver_probe_device+0x0/0x68) from [] (__driver_attach+0x74/0x98) r5:c042f02c r4:c042eff8 [] (__driver_attach+0x0/0x98) from [] (bus_for_each_dev+0x58/0x98) r6:c0449db8 r5:c01e311c r4:00000000 [] (bus_for_each_dev+0x0/0x98) from [] (driver_attach+0x20/0x28) r7:c1ab60c0 r6:c0449db8 r5:c0449db8 r4:c04165e8 [] (driver_attach+0x0/0x28) from [] (bus_add_driver+0xa8/0x22c) [] (bus_add_driver+0x0/0x22c) from [] (driver_register+0xc8/0x154) [] (driver_register+0x0/0x154) from [] (platform_driver_register+0x4c/0x60) r8:00000000 r7:00000013 r6:c003a37c r5:c041660c r4:c04165e8 [] (platform_driver_register+0x0/0x60) from [] (omapfb_init+0x14/0x34) [] (omapfb_init+0x0/0x34) from [] (do_one_initcall+0x9c/0x164) [] (do_one_initcall+0x0/0x164) from [] (kernel_init+0x7c/0x120) [] (kernel_init+0x0/0x120) from [] (do_exit+0x0/0x2d8) r5:c03ea208 r4:00000000 Code: e1a0c00d e92dd870 e24cb004 e24dd004 (e5906038) ---[ end trace 9e2474c2e193b223 ]--- Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 395d658..faaf305 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -180,6 +180,11 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) { int r; + if (cpu_is_omap34xx() && !dpi.vdds_dsi_reg) { + DSSERR("no VDSS_DSI regulator\n"); + return -ENODEV; + } + if (dssdev->manager == NULL) { DSSERR("failed to enable display: no manager\n"); return -ENODEV; -- cgit v0.10.2 From 6252547b8a7acced581b649af4ebf6d65f63a34b Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 09:47:21 +0000 Subject: ARM: omap: fix broken twl-core dependencies and ifdefs In commit aeb5032b3f, a dependency on IRQ_DOMAIN was added, which causes regressions on previously working setups: a previously working non-DT kernel configuration now loses its PMIC support. The lack of PMIC support in turn causes the loss of other functionality the kernel had. This dependency was added because the driver now registers its interrupts with the IRQ domain code, presumably to prevent a build error. The result is that OMAP3 oopses in the vp.c code (fixed by a previous commit) due to the lack of PMIC support. However, even with IRQ_DOMAIN enabled, the driver oopses: Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 5 [#1] SMP Modules linked in: CPU: 1 Not tainted (3.3.0-rc2+ #271) PC is at irq_domain_add+0x1c/0x134 LR is at twl_probe+0xd0/0x370 pc : [] lr : [] psr: 00000113 sp : df843c48 ip : df843c68 fp : df843c64 r10: c02b93e4 r9 : 00000000 r8 : c029b9dc r7 : df9d8a00 r6 : c03bef90 r5 : 00000000 r4 : c03f5240 r3 : 00000000 r2 : c03f5240 r1 : 00000015 r0 : c03f5240 Flags: nzcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 8000404a DAC: 00000015 Process swapper/0 (pid: 1, stack limit = 0xdf8422f0) Stack: (0xdf843c48 to 0xdf844000) 3c40: 00000014 00000170 00000014 c03bef90 df843c9c df843c68 3c60: c029baac c007bac0 00000000 df9d8a20 00000001 c03cd238 c02b93e4 df9d8a20 3c80: df9d8a04 df9d8a00 c029b9dc df8cae08 df843cc4 df843ca0 c01eee70 c029b9e8 ... Backtrace: [] (irq_domain_add+0x0/0x134) from [] (twl_probe+0xd0/0x370) r6:c03bef90 r5:00000014 r4:00000170 [] (twl_probe+0x0/0x370) from [] (i2c_device_probe+0xb0/0xe4) [] (i2c_device_probe+0x0/0xe4) from [] (really_probe+0xa0/0x178) r8:df8f0070 r7:c03cd238 r6:df9d8a20 r5:df9d8a20 r4:df9d8a20 [] (really_probe+0x0/0x178) from [] (driver_probe_device+0x50/0x68) r7:df843d18 r6:df9d8a20 r5:c03cd238 r4:df9d8a20 [] (driver_probe_device+0x0/0x68) from [] (__device_attach+0x44/0x48) r5:df9d8a20 r4:c03cd238 [] (__device_attach+0x0/0x48) from [] (bus_for_each_drv+0x58/0x98) r5:c01d2104 r4:00000000 [] (bus_for_each_drv+0x0/0x98) from [] (device_attach+0x80/0xac) r7:df9d8a28 r6:df9d8a54 r5:c03cd978 r4:df9d8a20 [] (device_attach+0x0/0xac) from [] (bus_probe_device+0x34/0xa4) r6:df9d8a20 r5:c03cd978 r4:df9d8a20 [] (bus_probe_device+0x0/0xa4) from [] (device_add+0x2a0/0x420) r6:00000000 r5:df9d8a20 r4:df9d8a20 [] (device_add+0x0/0x420) from [] (device_register+0x20/0x24) r8:df9d8a00 r7:df9d8a04 r6:df8f0048 r5:df9d8a00 r4:df9d8a20 [] (device_register+0x0/0x24) from [] (i2c_new_device+0x118/0x180) r4:df9d8a20 [] (i2c_new_device+0x0/0x180) from [] (i2c_register_adapter+0x140/0x204) r8:c03cd970 r7:00000000 r6:df8f0070 r5:df8a6300 r4:df8f0048 [] (i2c_register_adapter+0x0/0x204) from [] (i2c_add_numbered_adapter+0xb4/0xcc) r8:df8a4c54 r7:df8cae00 r6:df843e2c r5:df8f0048 r4:00000000 [] (i2c_add_numbered_adapter+0x0/0xcc) from [] (omap_i2c_probe+0x2f8/0x3b4) r6:00000000 r5:df8f0000 r4:df8f0070 [] (omap_i2c_probe+0x0/0x3b4) from [] (platform_drv_probe+0x20/0x24) [] (platform_drv_probe+0x0/0x24) from [] (really_probe+0xa0/0x178) [] (really_probe+0x0/0x178) from [] (driver_probe_device+0x50/0x68) r7:df843ef0 r6:c03cdb2c r5:c03cdb2c r4:df8cae08 [] (driver_probe_device+0x0/0x68) from [] (__driver_attach+0x6c/0x90) r5:df8cae3c r4:df8cae08 [] (__driver_attach+0x0/0x90) from [] (bus_for_each_dev+0x58/0x98) r6:c03cdb2c r5:c01d2074 r4:00000000 [] (bus_for_each_dev+0x0/0x98) from [] (driver_attach+0x20/0x28) r7:df880b80 r6:c03cdb2c r5:c03cdb2c r4:c0394f28 [] (driver_attach+0x0/0x28) from [] (bus_add_driver+0xb4/0x230) [] (bus_add_driver+0x0/0x230) from [] (driver_register+0xc8/0x154) [] (driver_register+0x0/0x154) from [] (platform_driver_register+0x4c/0x60) r8:00000000 r7:00000013 r6:c00384c8 r5:c0395180 r4:c0394f28 [] (platform_driver_register+0x0/0x60) from [] (omap_i2c_init_driver+0x14/0x1c) [] (omap_i2c_init_driver+0x0/0x1c) from [] (do_one_initcall+0x9c/0x164) [] (do_one_initcall+0x0/0x164) from [] (kernel_init+0x90/0x138) [] (kernel_init+0x0/0x138) from [] (do_exit+0x0/0x2ec) r5:c036c264 r4:00000000 <0>Code: e24dd004 e5903014 e1a04000 e5905010 (e5933000) <4>---[ end trace 1b75b31a2719ed1c ]--- This happens because we try to register an IRQ domain with a NULL ops structure, and the first thing irq_domain_add() does is try to dereference this ops structure. So, fix the problem by getting rid of the incorrect OF_IRQ ifdef and wrapping the IRQ domain bits of the driver with an IRQ_DOMAIN ifdef instead. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cd13e9f..f147395 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -200,7 +200,7 @@ config MENELAUS config TWL4030_CORE bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" - depends on I2C=y && GENERIC_HARDIRQS && IRQ_DOMAIN + depends on I2C=y && GENERIC_HARDIRQS help Say yes here if you have TWL4030 / TWL6030 family chip on your board. This core driver provides register access and IRQ handling diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index e04e04d..8ce3959 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -263,7 +263,9 @@ struct twl_client { static struct twl_client twl_modules[TWL_NUM_SLAVES]; +#ifdef CONFIG_IRQ_DOMAIN static struct irq_domain domain; +#endif /* mapping the module id to slave id and base address */ struct twl_mapping { @@ -1226,13 +1228,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) pdata->irq_base = status; pdata->irq_end = pdata->irq_base + nr_irqs; +#ifdef CONFIG_IRQ_DOMAIN domain.irq_base = pdata->irq_base; domain.nr_irq = nr_irqs; -#ifdef CONFIG_OF_IRQ domain.of_node = of_node_get(node); domain.ops = &irq_domain_simple_ops; -#endif irq_domain_add(&domain); +#endif if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "can't talk I2C?\n"); -- cgit v0.10.2 From 690d137f448d4c4da9001871e6569d5636f116c7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 9 Feb 2012 15:48:20 +0000 Subject: Reduce the number of expensive division instructions done by _parse_integer() _parse_integer() does one or two division instructions (which are slow) per digit parsed to perform the overflow check. Furthermore, these are particularly expensive examples of division instruction as the number of clock cycles required to complete them may go up with the position of the most significant set bit in the dividend: if (*res > div_u64(ULLONG_MAX - val, base)) which is as maximal as possible. Worse, on 32-bit arches, more than one of these division instructions may be required per digit. So, assuming we don't support a base of more than 16, skip the check if the top nibble of the result is not set at this point. Signed-off-by: David Howells [ Changed it to not dereference the pointer all the time - even if the compiler can and does optimize it away, the code just looks cleaner. And edited the top nybble test slightly to make the code generated on x86-64 better in the loop - test against a hoisted constant instead of shifting and testing the result ] Signed-off-by: Linus Torvalds diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 7a94c8f..b1dd3e7 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -44,12 +44,13 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) * * Don't you dare use this function. */ -unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res) +unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) { + unsigned long long res; unsigned int rv; int overflow; - *res = 0; + res = 0; rv = 0; overflow = 0; while (*s) { @@ -64,12 +65,19 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long if (val >= base) break; - if (*res > div_u64(ULLONG_MAX - val, base)) - overflow = 1; - *res = *res * base + val; + /* + * Check for overflow only if we are within range of + * it in the max base we support (16) + */ + if (unlikely(res & (~0ull << 60))) { + if (res > div_u64(ULLONG_MAX - val, base)) + overflow = 1; + } + res = res * base + val; rv++; s++; } + *p = res; if (overflow) rv |= KSTRTOX_OVERFLOW; return rv; -- cgit v0.10.2 From 0ba5f66836c9253c67c469fb4016f94ea30ff2ce Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 25 Jan 2012 19:50:36 -0700 Subject: tty: serial: OMAP: use a 1-byte RX FIFO threshold in PIO mode In the (default) PIO mode, use a one-byte RX FIFO threshold. The OMAP UART IP blocks do not appear to be capable of waking the system under an RX timeout condition. Since the previous RX FIFO threshold was 16 bytes, this meant that omap-serial.c did not become aware of any received data until all those bytes arrived or until another UART interrupt occurred. This made the serial console and presumably other serial applications (GPS, serial Bluetooth) unusable or extremely slow. A 1-byte RX FIFO threshold also allows the MPU to enter a low-power consumption state while waiting for the FIFO to fill. This can be verified using the serial console by comparing the behavior when "0123456789abcde" is pasted in from another window, with the behavior when "0123456789abcdef" is pasted in. Since the former string is less than sixteen bytes long, the string is not echoed for some time, while the latter string is echoed immediately. DMA operation is unaffected by this patch. Thanks to Russell King - ARM Linux for some additional information on the standard behavior of the RX timeout event, which was used to improve this commit description. Signed-off-by: Paul Walmsley Cc: Tomi Valkeinen Cc: Govindraj Raja Cc: Alan Cox Cc: Russell King Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 1c24269..a3f5ea4 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -46,6 +46,13 @@ #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ +/* SCR register bitmasks */ +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) + +/* FCR register bitmasks */ +#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 +#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) + static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ @@ -811,14 +818,21 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->mcr = serial_in(up, UART_MCR); serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); /* FIFO ENABLE, DMA MODE */ - serial_out(up, UART_FCR, up->fcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; if (up->use_dma) { serial_out(up, UART_TI752_TLR, 0); - up->scr |= (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8); + up->scr |= UART_FCR_TRIGGER_4; + } else { + /* Set receive FIFO threshold to 1 byte */ + up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; + up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT); } + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_OMAP_SCR, up->scr); serial_out(up, UART_EFR, up->efr); -- cgit v0.10.2 From be4b0281956c5cae4f63f31f11d07625a6988766 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 25 Jan 2012 19:50:52 -0700 Subject: tty: serial: OMAP: block idle while the UART is transferring data in PIO mode Prevent OMAP UARTs from going idle while they are still transferring data in PIO mode. This works around an oversight in the OMAP UART hardware present in OMAP34xx and earlier: an idle UART won't send a wakeup when the TX FIFO threshold is reached. This causes long delays during data transmission when the MPU powerdomain enters a low-power mode. The MPU interrupt controller is not able to respond to interrupts when it's in a low-power state, so the TX buffer is not refilled until another wakeup event occurs. This fix changes the erratum i291 DMA idle workaround. Rather than toggling between force-idle and no-idle, it will toggle between smart-idle and no-idle. The important part of the workaround is the no-idle part, so this shouldn't result in any change in behavior. This fix should work on all OMAP UARTs. Future patches intended for the 3.4 merge window will make this workaround conditional on a "feature" flag, and will use the OMAP36xx+ TX event wakeup support. Thanks to Kevin Hilman for mentioning the erratum i291 workaround, which led to the development of this approach. Signed-off-by: Paul Walmsley Cc: Alan Cox Cc: Tomi Valkeinen Acked-by: Govindraj.R Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 247d894..f590afc 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -107,18 +107,18 @@ static void omap_uart_set_noidle(struct platform_device *pdev) omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO); } -static void omap_uart_set_forceidle(struct platform_device *pdev) +static void omap_uart_set_smartidle(struct platform_device *pdev) { struct omap_device *od = to_omap_device(pdev); - omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_FORCE); + omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART); } #else static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable) {} static void omap_uart_set_noidle(struct platform_device *pdev) {} -static void omap_uart_set_forceidle(struct platform_device *pdev) {} +static void omap_uart_set_smartidle(struct platform_device *pdev) {} #endif /* CONFIG_PM */ #ifdef CONFIG_OMAP_MUX @@ -349,7 +349,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata, omap_up.uartclk = OMAP24XX_BASE_BAUD * 16; omap_up.flags = UPF_BOOT_AUTOCONF; omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count; - omap_up.set_forceidle = omap_uart_set_forceidle; + omap_up.set_forceidle = omap_uart_set_smartidle; omap_up.set_noidle = omap_uart_set_noidle; omap_up.enable_wakeup = omap_uart_enable_wakeup; omap_up.dma_rx_buf_size = info->dma_rx_buf_size; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index a3f5ea4..18d1324 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -136,6 +136,7 @@ static void serial_omap_enable_ms(struct uart_port *port) static void serial_omap_stop_tx(struct uart_port *port) { struct uart_omap_port *up = (struct uart_omap_port *)port; + struct omap_uart_port_info *pdata = up->pdev->dev.platform_data; if (up->use_dma && up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { @@ -158,6 +159,9 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } + if (!up->use_dma && pdata->set_forceidle) + pdata->set_forceidle(up->pdev); + pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); } @@ -286,6 +290,7 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) { struct uart_omap_port *up = (struct uart_omap_port *)port; + struct omap_uart_port_info *pdata = up->pdev->dev.platform_data; struct circ_buf *xmit; unsigned int start; int ret = 0; @@ -293,6 +298,8 @@ static void serial_omap_start_tx(struct uart_port *port) if (!up->use_dma) { pm_runtime_get_sync(&up->pdev->dev); serial_omap_enable_ier_thri(up); + if (pdata->set_noidle) + pdata->set_noidle(up->pdev); pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); return; -- cgit v0.10.2 From 197234520bd83aba2199add57a9d2678ee7f9781 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 25 Jan 2012 19:50:56 -0700 Subject: tty: serial: omap-serial: wakeup latency constraint is in microseconds, not milliseconds The receive FIFO wakeup latency estimate in the omap-serial driver is three orders of magnitude too small. This effectively prevents the MPU from going to a low-power state when CONFIG_CPU_IDLE=y. This is a major power management regression and masks some other FIFO-related bugs in the driver. Fix by correcting the most egregious problem in the RX wakeup latency estimate. There are several other flaws in the estimator; these will be fixed by a separate patch series intended for 3.4. The difference in low-power states with this patch can be observed via debugfs in pm_debug/count. This estimate does not have any effect when CONFIG_CPU_IDLE=n. Signed-off-by: Paul Walmsley Cc: Tomi Valkeinen Cc: Alan Cox Acked-by: Govindraj.R Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 18d1324..f809041 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -740,8 +740,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, quot = serial_omap_get_divisor(port, baud); /* calculate wakeup latency constraint */ - up->calc_latency = (1000000 * up->port.fifosize) / - (1000 * baud / 8); + up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8); up->latency = up->calc_latency; schedule_work(&up->qos_work); -- cgit v0.10.2 From 16bda13d90c8d5da243e2cfa1677e62ecce26860 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 6 Feb 2012 15:14:37 -0500 Subject: net: Make qdisc_skb_cb upper size bound explicit. Just like skb->cb[], so that qdisc_skb_cb can be encapsulated inside of other data structures. This is intended to be used by IPoIB so that it can remember addressing information stored at hard_header_ops->create() time that it can fetch when the packet gets to the transmit routine. Signed-off-by: David S. Miller diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f6bb08b..55ce96b 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -220,9 +220,16 @@ struct tcf_proto { struct qdisc_skb_cb { unsigned int pkt_len; - long data[]; + unsigned char data[24]; }; +static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) +{ + struct qdisc_skb_cb *qcb; + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(unsigned int) + sz); + BUILD_BUG_ON(sizeof(qcb->data) < sz); +} + static inline int qdisc_qlen(const struct Qdisc *q) { return q->q.qlen; diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index e465064..7e267d7 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -148,8 +148,7 @@ struct choke_skb_cb { static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct choke_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb)); return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 2776012..e83d61c 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -130,8 +130,7 @@ struct netem_skb_cb { static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 96e42ca..d7eea99 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -94,8 +94,7 @@ struct sfb_skb_cb { static inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct sfb_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct sfb_skb_cb)); return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 67494ae..60d4718 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -166,9 +166,8 @@ struct sfq_skb_cb { static inline struct sfq_skb_cb *sfq_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct sfq_skb_cb)); - return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data; + qdisc_cb_private_validate(skb, sizeof(struct sfq_skb_cb)); + return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data; } static unsigned int sfq_hash(const struct sfq_sched_data *q, -- cgit v0.10.2 From a1c1baf00e5c08dda9c0ee25658bf0ce301102ae Mon Sep 17 00:00:00 2001 From: Nikolaus Schulz Date: Wed, 8 Feb 2012 18:56:11 +0100 Subject: hwmon: (f75375s) Let f75375_update_device treat pwmX as a measured value Treat pwmX as a measured value, not as a (mostly static) limit value, so that it is updated more frequently from the device register. Signed-off-by: Nikolaus Schulz Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 0aabaaf..f609b57 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -200,9 +200,6 @@ static struct f75375_data *f75375_update_device(struct device *dev) f75375_read16(client, F75375_REG_FAN_MIN(nr)); data->fan_target[nr] = f75375_read16(client, F75375_REG_FAN_EXP(nr)); - data->pwm[nr] = f75375_read8(client, - F75375_REG_FAN_PWM_DUTY(nr)); - } for (nr = 0; nr < 4; nr++) { data->in_max[nr] = @@ -218,6 +215,8 @@ static struct f75375_data *f75375_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { for (nr = 0; nr < 2; nr++) { + data->pwm[nr] = f75375_read8(client, + F75375_REG_FAN_PWM_DUTY(nr)); /* assign MSB, therefore shift it by 8 bits */ data->temp11[nr] = f75375_read8(client, F75375_REG_TEMP(nr)) << 8; -- cgit v0.10.2 From b57e6b560fc2a2742910ac5ca0eb2c46e45aeac2 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 9 Feb 2012 19:59:43 +0530 Subject: mac80211: Fix a rwlock bad magic bug read_lock(&tpt_trig->trig.leddev_list_lock) is accessed via the path ieee80211_open (->) ieee80211_do_open (->) ieee80211_mod_tpt_led_trig (->) ieee80211_start_tpt_led_trig (->) tpt_trig_timer before initializing it. the intilization of this read/write lock happens via the path ieee80211_led_init (->) led_trigger_register, but we are doing 'ieee80211_led_init' after 'ieeee80211_if_add' where we register netdev_ops. so we access leddev_list_lock before initializing it and causes the following bug in chrome laptops with AR928X cards with the following script while true do sudo modprobe -v ath9k sleep 3 sudo modprobe -r ath9k sleep 3 done BUG: rwlock bad magic on CPU#1, wpa_supplicant/358, f5b9eccc Pid: 358, comm: wpa_supplicant Not tainted 3.0.13 #1 Call Trace: [<8137b9df>] rwlock_bug+0x3d/0x47 [<81179830>] do_raw_read_lock+0x19/0x29 [<8137f063>] _raw_read_lock+0xd/0xf [] tpt_trig_timer+0xc3/0x145 [mac80211] [] ieee80211_mod_tpt_led_trig+0x152/0x174 [mac80211] [] ieee80211_do_open+0x11e/0x42e [mac80211] [] ? ieee80211_check_concurrent_iface+0x26/0x13c [mac80211] [] ieee80211_open+0x48/0x4c [mac80211] [<812dbed8>] __dev_open+0x82/0xab [<812dc0c9>] __dev_change_flags+0x9c/0x113 [<812dc1ae>] dev_change_flags+0x18/0x44 [<8132144f>] devinet_ioctl+0x243/0x51a [<81321ba9>] inet_ioctl+0x93/0xac [<812cc951>] sock_ioctl+0x1c6/0x1ea [<812cc78b>] ? might_fault+0x20/0x20 [<810b1ebb>] do_vfs_ioctl+0x46e/0x4a2 [<810a6ebb>] ? fget_light+0x2f/0x70 [<812ce549>] ? sys_recvmsg+0x3e/0x48 [<810b1f35>] sys_ioctl+0x46/0x69 [<8137fa77>] sysenter_do_call+0x12/0x2 Cc: Cc: Gary Morain Cc: Paul Stewart Cc: Abhijit Pradhan Cc: Vasanthakumar Thiagarajan Cc: Rajkumar Manoharan Acked-by: Johannes Berg Tested-by: Mohammed Shafi Shajakhan Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0a0d94a..b142bd4 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -910,6 +910,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", result); + ieee80211_led_init(local); + rtnl_lock(); result = ieee80211_init_rate_ctrl_alg(local, @@ -931,8 +933,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) rtnl_unlock(); - ieee80211_led_init(local); - local->network_latency_notifier.notifier_call = ieee80211_max_network_latency; result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY, -- cgit v0.10.2 From c27111e5b8d6b1e9296f7075cdc35872f672b524 Mon Sep 17 00:00:00 2001 From: Simon Graham Date: Thu, 9 Feb 2012 09:55:13 -0500 Subject: rtlwifi: Modify rtl_pci_init to return 0 on success Fixes problem where caller would think routine succeeded when it failed leading to divide by zero panic. Signed-off-by: Simon Graham Acked-by: Larry Finger Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 39e0907..9245d88 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1501,7 +1501,7 @@ static int rtl_pci_init(struct ieee80211_hw *hw, struct pci_dev *pdev) return err; } - return 1; + return 0; } static int rtl_pci_start(struct ieee80211_hw *hw) @@ -1870,7 +1870,7 @@ int __devinit rtl_pci_probe(struct pci_dev *pdev, } /* Init PCI sw */ - err = !rtl_pci_init(hw, pdev); + err = rtl_pci_init(hw, pdev); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("Failed to init PCI.\n")); -- cgit v0.10.2 From 5a46e0f95664bb65e3256f3c53b14b8a627c656b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 9 Feb 2012 00:46:47 +0000 Subject: isdn: type bug in isdn_net_header() We use len to store the return value from eth_header(). eth_header() can return -ETH_HLEN (-14). We want to pass this back instead of truncating it to 65522 and returning that. Signed-off-by: Dan Carpenter Acked-by: Neil Horman Signed-off-by: David S. Miller diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 2339d73..802ab87 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1901,7 +1901,7 @@ static int isdn_net_header(struct sk_buff *skb, struct net_device *dev, { isdn_net_local *lp = netdev_priv(dev); unsigned char *p; - ushort len = 0; + int len = 0; switch (lp->p_encap) { case ISDN_NET_ENCAP_ETHER: -- cgit v0.10.2 From 027a3b617c5490b5287d98921675abcbebeb32df Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 9 Feb 2012 00:49:34 +0000 Subject: bna: fix error handling of bnad_get_flash_partition_by_offset() The current error handling doesn't work because we flash_part is a u32 so the checks for negative error codes don't work. I considered making things signed but I don't know the hardware enough to say if that's a problem. Really, we don't use the error codes so just returning zero for all problems is fine. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 9b44ec8..803ea32 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -946,7 +946,7 @@ bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, flash_attr = kzalloc(sizeof(struct bfa_flash_attr), GFP_KERNEL); if (!flash_attr) - return -ENOMEM; + return 0; fcomp.bnad = bnad; fcomp.comp_status = 0; @@ -958,7 +958,7 @@ bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, if (ret != BFA_STATUS_OK) { spin_unlock_irqrestore(&bnad->bna_lock, flags); kfree(flash_attr); - goto out_err; + return 0; } spin_unlock_irqrestore(&bnad->bna_lock, flags); wait_for_completion(&fcomp.comp); @@ -978,8 +978,6 @@ bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset, } kfree(flash_attr); return flash_part; -out_err: - return -EINVAL; } static int @@ -1006,7 +1004,7 @@ bnad_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, /* Query the flash partition based on the offset */ flash_part = bnad_get_flash_partition_by_offset(bnad, eeprom->offset, &base_offset); - if (flash_part <= 0) + if (flash_part == 0) return -EFAULT; fcomp.bnad = bnad; @@ -1048,7 +1046,7 @@ bnad_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom, /* Query the flash partition based on the offset */ flash_part = bnad_get_flash_partition_by_offset(bnad, eeprom->offset, &base_offset); - if (flash_part <= 0) + if (flash_part == 0) return -EFAULT; fcomp.bnad = bnad; -- cgit v0.10.2 From b9f9a03150969e4bd9967c20bce67c4de769058f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 9 Feb 2012 15:31:36 -0500 Subject: NFSv4: Ensure we throw out bad delegation stateids on NFS4ERR_BAD_STATEID To ensure that we don't just reuse the bad delegation when we attempt to recover the nfs4_state that received the bad stateid error. Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index a53f33b..4539203 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1132,6 +1132,8 @@ void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4 { struct nfs_client *clp = server->nfs_client; + if (test_and_clear_bit(NFS_DELEGATED_STATE, &state->flags)) + nfs_async_inode_return_delegation(state->inode, &state->stateid); nfs4_state_mark_reclaim_nograce(clp, state); nfs4_schedule_state_manager(clp); } -- cgit v0.10.2 From 532691635475487b8c1d6fc618bd123084904897 Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 9 Feb 2012 16:43:01 -0500 Subject: regulator: Fix mc13xxx regulator modular build (again) Since mc13xxx-regulator-core.c and the actual drivers can get built into seperate modules, you have to export the DT support symbols "mc13xxx_get_num_regulators_dt" and "mc13xxx_parse_regulators_dt" otherwise the allmodconfig build fails on sparc64. [Updated the subject; the same thing was previously reported and fixed in -next but for some reason nobody noticed for some considerable time after the issue was introduced -- broonie] Signed-off-by: David S. Miller Signed-off-by: Mark Brown diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 80ecafe..62dcd0a 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -254,6 +254,7 @@ int __devinit mc13xxx_get_num_regulators_dt(struct platform_device *pdev) return num; } +EXPORT_SYMBOL_GPL(mc13xxx_get_num_regulators_dt); struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt( struct platform_device *pdev, struct mc13xxx_regulator *regulators, @@ -291,6 +292,7 @@ struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt( return data; } +EXPORT_SYMBOL_GPL(mc13xxx_parse_regulators_dt); #endif MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From b06540371063f0f07aafc1d1ac5e974da85c973c Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 8 Feb 2012 15:52:07 +0100 Subject: ARM: orion: Fix Orion5x GPIO regression from MPP cleanup Patchset "ARM: orion: Refactor the MPP code common in the orion platform" broke at least Orion5x based platforms. These platforms have pins configured as GPIO when the selector is not 0x0. However the common code assumes the selector is always 0x0 for a GPIO lines. It then ignores the GPIO bits in the MPP definitions, resulting in that Orion5x machines cannot correctly configure there GPIO lines. The Fix removes the assumption that the selector is always 0x0. In order that none GPIO configurations are correctly blocked, Kirkwood and mv78xx0 MPP definitions are corrected to only set the GPIO bits for GPIO configurations. This third version, which does not contain any whitespace changes, and is rebased on v3.3-rc2. Signed-off-by: Andrew Lunn Acked-by: Nicolas Pitre Signed-off-by: Olof Johansson diff --git a/arch/arm/mach-kirkwood/mpp.h b/arch/arm/mach-kirkwood/mpp.h index e8fda45..d5a0d1d 100644 --- a/arch/arm/mach-kirkwood/mpp.h +++ b/arch/arm/mach-kirkwood/mpp.h @@ -31,314 +31,314 @@ #define MPP_F6282_MASK MPP( 0, 0x0, 0, 0, 0, 0, 0, 0, 1 ) #define MPP0_GPIO MPP( 0, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP0_NF_IO2 MPP( 0, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP0_SPI_SCn MPP( 0, 0x2, 0, 1, 1, 1, 1, 1, 1 ) +#define MPP0_NF_IO2 MPP( 0, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP0_SPI_SCn MPP( 0, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP1_GPO MPP( 1, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP1_NF_IO3 MPP( 1, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP1_SPI_MOSI MPP( 1, 0x2, 0, 1, 1, 1, 1, 1, 1 ) +#define MPP1_NF_IO3 MPP( 1, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP1_SPI_MOSI MPP( 1, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP2_GPO MPP( 2, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP2_NF_IO4 MPP( 2, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP2_SPI_SCK MPP( 2, 0x2, 0, 1, 1, 1, 1, 1, 1 ) +#define MPP2_NF_IO4 MPP( 2, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP2_SPI_SCK MPP( 2, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP3_GPO MPP( 3, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP3_NF_IO5 MPP( 3, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP3_SPI_MISO MPP( 3, 0x2, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP3_NF_IO5 MPP( 3, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP3_SPI_MISO MPP( 3, 0x2, 0, 0, 1, 1, 1, 1, 1 ) #define MPP4_GPIO MPP( 4, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP4_NF_IO6 MPP( 4, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP4_UART0_RXD MPP( 4, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP4_SATA1_ACTn MPP( 4, 0x5, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP4_NF_IO6 MPP( 4, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP4_UART0_RXD MPP( 4, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP4_SATA1_ACTn MPP( 4, 0x5, 0, 0, 0, 0, 1, 1, 1 ) #define MPP4_LCD_VGA_HSYNC MPP( 4, 0xb, 0, 0, 0, 0, 0, 0, 1 ) -#define MPP4_PTP_CLK MPP( 4, 0xd, 1, 0, 1, 1, 1, 1, 0 ) +#define MPP4_PTP_CLK MPP( 4, 0xd, 0, 0, 1, 1, 1, 1, 0 ) #define MPP5_GPO MPP( 5, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP5_NF_IO7 MPP( 5, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP5_UART0_TXD MPP( 5, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP5_PTP_TRIG_GEN MPP( 5, 0x4, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP5_SATA0_ACTn MPP( 5, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP5_NF_IO7 MPP( 5, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP5_UART0_TXD MPP( 5, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP5_PTP_TRIG_GEN MPP( 5, 0x4, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP5_SATA0_ACTn MPP( 5, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP5_LCD_VGA_VSYNC MPP( 5, 0xb, 0, 0, 0, 0, 0, 0, 1 ) -#define MPP6_SYSRST_OUTn MPP( 6, 0x1, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP6_SPI_MOSI MPP( 6, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP6_PTP_TRIG_GEN MPP( 6, 0x3, 0, 1, 1, 1, 1, 1, 0 ) +#define MPP6_SYSRST_OUTn MPP( 6, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP6_SPI_MOSI MPP( 6, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP6_PTP_TRIG_GEN MPP( 6, 0x3, 0, 0, 1, 1, 1, 1, 0 ) #define MPP7_GPO MPP( 7, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP7_PEX_RST_OUTn MPP( 7, 0x1, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP7_SPI_SCn MPP( 7, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP7_PTP_TRIG_GEN MPP( 7, 0x3, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP7_LCD_PWM MPP( 7, 0xb, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP7_PEX_RST_OUTn MPP( 7, 0x1, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP7_SPI_SCn MPP( 7, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP7_PTP_TRIG_GEN MPP( 7, 0x3, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP7_LCD_PWM MPP( 7, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP8_GPIO MPP( 8, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP8_TW0_SDA MPP( 8, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP8_UART0_RTS MPP( 8, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP8_UART1_RTS MPP( 8, 0x3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP8_MII0_RXERR MPP( 8, 0x4, 1, 0, 0, 1, 1, 1, 1 ) -#define MPP8_SATA1_PRESENTn MPP( 8, 0x5, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP8_PTP_CLK MPP( 8, 0xc, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP8_MII0_COL MPP( 8, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP8_TW0_SDA MPP( 8, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP8_UART0_RTS MPP( 8, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP8_UART1_RTS MPP( 8, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP8_MII0_RXERR MPP( 8, 0x4, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP8_SATA1_PRESENTn MPP( 8, 0x5, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP8_PTP_CLK MPP( 8, 0xc, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP8_MII0_COL MPP( 8, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP9_GPIO MPP( 9, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP9_TW0_SCK MPP( 9, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP9_UART0_CTS MPP( 9, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP9_UART1_CTS MPP( 9, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP9_SATA0_PRESENTn MPP( 9, 0x5, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP9_PTP_EVENT_REQ MPP( 9, 0xc, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP9_MII0_CRS MPP( 9, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP9_TW0_SCK MPP( 9, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP9_UART0_CTS MPP( 9, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP9_UART1_CTS MPP( 9, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP9_SATA0_PRESENTn MPP( 9, 0x5, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP9_PTP_EVENT_REQ MPP( 9, 0xc, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP9_MII0_CRS MPP( 9, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP10_GPO MPP( 10, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP10_SPI_SCK MPP( 10, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP10_UART0_TXD MPP( 10, 0X3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP10_SATA1_ACTn MPP( 10, 0x5, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP10_PTP_TRIG_GEN MPP( 10, 0xc, 0, 1, 1, 1, 1, 1, 0 ) +#define MPP10_SPI_SCK MPP( 10, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP10_UART0_TXD MPP( 10, 0X3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP10_SATA1_ACTn MPP( 10, 0x5, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP10_PTP_TRIG_GEN MPP( 10, 0xc, 0, 0, 1, 1, 1, 1, 0 ) #define MPP11_GPIO MPP( 11, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP11_SPI_MISO MPP( 11, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP11_UART0_RXD MPP( 11, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP11_PTP_EVENT_REQ MPP( 11, 0x4, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP11_PTP_TRIG_GEN MPP( 11, 0xc, 0, 1, 1, 1, 1, 1, 0 ) -#define MPP11_PTP_CLK MPP( 11, 0xd, 1, 0, 1, 1, 1, 1, 0 ) -#define MPP11_SATA0_ACTn MPP( 11, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP11_SPI_MISO MPP( 11, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP11_UART0_RXD MPP( 11, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP11_PTP_EVENT_REQ MPP( 11, 0x4, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP11_PTP_TRIG_GEN MPP( 11, 0xc, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP11_PTP_CLK MPP( 11, 0xd, 0, 0, 1, 1, 1, 1, 0 ) +#define MPP11_SATA0_ACTn MPP( 11, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP12_GPO MPP( 12, 0x0, 0, 1, 1, 1, 1, 1, 1 ) #define MPP12_GPIO MPP( 12, 0x0, 1, 1, 0, 0, 0, 1, 0 ) -#define MPP12_SD_CLK MPP( 12, 0x1, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP12_AU_SPDIF0 MPP( 12, 0xa, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP12_SPI_MOSI MPP( 12, 0xb, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP12_TW1_SDA MPP( 12, 0xd, 1, 0, 0, 0, 0, 0, 1 ) +#define MPP12_SD_CLK MPP( 12, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP12_AU_SPDIF0 MPP( 12, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP12_SPI_MOSI MPP( 12, 0xb, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP12_TW1_SDA MPP( 12, 0xd, 0, 0, 0, 0, 0, 0, 1 ) #define MPP13_GPIO MPP( 13, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP13_SD_CMD MPP( 13, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP13_UART1_TXD MPP( 13, 0x3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP13_AU_SPDIFRMCLK MPP( 13, 0xa, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP13_LCDPWM MPP( 13, 0xb, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP13_SD_CMD MPP( 13, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP13_UART1_TXD MPP( 13, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP13_AU_SPDIFRMCLK MPP( 13, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP13_LCDPWM MPP( 13, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP14_GPIO MPP( 14, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP14_SD_D0 MPP( 14, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP14_UART1_RXD MPP( 14, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP14_SATA1_PRESENTn MPP( 14, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP14_AU_SPDIFI MPP( 14, 0xa, 1, 0, 0, 0, 0, 0, 1 ) -#define MPP14_AU_I2SDI MPP( 14, 0xb, 1, 0, 0, 0, 0, 0, 1 ) -#define MPP14_MII0_COL MPP( 14, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP14_SD_D0 MPP( 14, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP14_UART1_RXD MPP( 14, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP14_SATA1_PRESENTn MPP( 14, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP14_AU_SPDIFI MPP( 14, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP14_AU_I2SDI MPP( 14, 0xb, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP14_MII0_COL MPP( 14, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP15_GPIO MPP( 15, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP15_SD_D1 MPP( 15, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP15_UART0_RTS MPP( 15, 0x2, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP15_UART1_TXD MPP( 15, 0x3, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP15_SATA0_ACTn MPP( 15, 0x4, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP15_SPI_CSn MPP( 15, 0xb, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP15_SD_D1 MPP( 15, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP15_UART0_RTS MPP( 15, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP15_UART1_TXD MPP( 15, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP15_SATA0_ACTn MPP( 15, 0x4, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP15_SPI_CSn MPP( 15, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP16_GPIO MPP( 16, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP16_SD_D2 MPP( 16, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP16_UART0_CTS MPP( 16, 0x2, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP16_UART1_RXD MPP( 16, 0x3, 1, 0, 1, 1, 1, 1, 1 ) -#define MPP16_SATA1_ACTn MPP( 16, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP16_LCD_EXT_REF_CLK MPP( 16, 0xb, 1, 0, 0, 0, 0, 0, 1 ) -#define MPP16_MII0_CRS MPP( 16, 0xd, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP16_SD_D2 MPP( 16, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP16_UART0_CTS MPP( 16, 0x2, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP16_UART1_RXD MPP( 16, 0x3, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP16_SATA1_ACTn MPP( 16, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP16_LCD_EXT_REF_CLK MPP( 16, 0xb, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP16_MII0_CRS MPP( 16, 0xd, 0, 0, 1, 1, 1, 1, 1 ) #define MPP17_GPIO MPP( 17, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP17_SD_D3 MPP( 17, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP17_SATA0_PRESENTn MPP( 17, 0x4, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP17_SATA1_ACTn MPP( 17, 0xa, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP17_TW1_SCK MPP( 17, 0xd, 1, 1, 0, 0, 0, 0, 1 ) +#define MPP17_SD_D3 MPP( 17, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP17_SATA0_PRESENTn MPP( 17, 0x4, 0, 0, 0, 1, 1, 1, 1 ) +#define MPP17_SATA1_ACTn MPP( 17, 0xa, 0, 0, 0, 0, 0, 0, 1 ) +#define MPP17_TW1_SCK MPP( 17, 0xd, 0, 0, 0, 0, 0, 0, 1 ) #define MPP18_GPO MPP( 18, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP18_NF_IO0 MPP( 18, 0x1, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP18_PEX0_CLKREQ MPP( 18, 0x2, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP18_NF_IO0 MPP( 18, 0x1, 0, 0, 1, 1, 1, 1, 1 ) +#define MPP18_PEX0_CLKREQ MPP( 18, 0x2, 0, 0, 0, 0, 0, 0, 1 ) #define MPP19_GPO MPP( 19, 0x0, 0, 1, 1, 1, 1, 1, 1 ) -#define MPP19_NF_IO1 MPP( 19, 0x1, 1, 1, 1, 1, 1, 1, 1 ) +#define MPP19_NF_IO1 MPP( 19, 0x1, 0, 0, 1, 1, 1, 1, 1 ) #define MPP20_GPIO MPP( 20, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP20_TSMP0 MPP( 20, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP20_TDM_CH0_TX_QL MPP( 20, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP20_TSMP0 MPP( 20, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP20_TDM_CH0_TX_QL MPP( 20, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP20_GE1_TXD0 MPP( 20, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP20_AU_SPDIFI MPP( 20, 0x4, 1, 0, 0, 0, 1, 1, 1 ) -#define MPP20_SATA1_ACTn MPP( 20, 0x5, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP20_AU_SPDIFI MPP( 20, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP20_SATA1_ACTn MPP( 20, 0x5, 0, 0, 0, 0, 1, 1, 1 ) #define MPP20_LCD_D0 MPP( 20, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP21_GPIO MPP( 21, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP21_TSMP1 MPP( 21, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP21_TDM_CH0_RX_QL MPP( 21, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP21_TSMP1 MPP( 21, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP21_TDM_CH0_RX_QL MPP( 21, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP21_GE1_TXD1 MPP( 21, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP21_AU_SPDIFO MPP( 21, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP21_SATA0_ACTn MPP( 21, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP21_AU_SPDIFO MPP( 21, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP21_SATA0_ACTn MPP( 21, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP21_LCD_D1 MPP( 21, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP22_GPIO MPP( 22, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP22_TSMP2 MPP( 22, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP22_TDM_CH2_TX_QL MPP( 22, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP22_TSMP2 MPP( 22, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP22_TDM_CH2_TX_QL MPP( 22, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP22_GE1_TXD2 MPP( 22, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP22_AU_SPDIFRMKCLK MPP( 22, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP22_SATA1_PRESENTn MPP( 22, 0x5, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP22_AU_SPDIFRMKCLK MPP( 22, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP22_SATA1_PRESENTn MPP( 22, 0x5, 0, 0, 0, 0, 1, 1, 1 ) #define MPP22_LCD_D2 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP23_GPIO MPP( 23, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP23_TSMP3 MPP( 23, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP23_TDM_CH2_RX_QL MPP( 23, 0x2, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP23_TSMP3 MPP( 23, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP23_TDM_CH2_RX_QL MPP( 23, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP23_GE1_TXD3 MPP( 23, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP23_AU_I2SBCLK MPP( 23, 0x4, 0, 1, 0, 0, 1, 1, 1 ) -#define MPP23_SATA0_PRESENTn MPP( 23, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP23_AU_I2SBCLK MPP( 23, 0x4, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP23_SATA0_PRESENTn MPP( 23, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP23_LCD_D3 MPP( 23, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP24_GPIO MPP( 24, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP24_TSMP4 MPP( 24, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP24_TDM_SPI_CS0 MPP( 24, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP24_TSMP4 MPP( 24, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP24_TDM_SPI_CS0 MPP( 24, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP24_GE1_RXD0 MPP( 24, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP24_AU_I2SDO MPP( 24, 0x4, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP24_AU_I2SDO MPP( 24, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP24_LCD_D4 MPP( 24, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP25_GPIO MPP( 25, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP25_TSMP5 MPP( 25, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP25_TDM_SPI_SCK MPP( 25, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP25_TSMP5 MPP( 25, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP25_TDM_SPI_SCK MPP( 25, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP25_GE1_RXD1 MPP( 25, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP25_AU_I2SLRCLK MPP( 25, 0x4, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP25_AU_I2SLRCLK MPP( 25, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP25_LCD_D5 MPP( 25, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP26_GPIO MPP( 26, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP26_TSMP6 MPP( 26, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP26_TDM_SPI_MISO MPP( 26, 0x2, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP26_TSMP6 MPP( 26, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP26_TDM_SPI_MISO MPP( 26, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP26_GE1_RXD2 MPP( 26, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP26_AU_I2SMCLK MPP( 26, 0x4, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP26_AU_I2SMCLK MPP( 26, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP26_LCD_D6 MPP( 26, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP27_GPIO MPP( 27, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP27_TSMP7 MPP( 27, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP27_TDM_SPI_MOSI MPP( 27, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP27_TSMP7 MPP( 27, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP27_TDM_SPI_MOSI MPP( 27, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP27_GE1_RXD3 MPP( 27, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP27_AU_I2SDI MPP( 27, 0x4, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP27_AU_I2SDI MPP( 27, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP27_LCD_D7 MPP( 27, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP28_GPIO MPP( 28, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP28_TSMP8 MPP( 28, 0x1, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP28_TSMP8 MPP( 28, 0x1, 0, 0, 0, 0, 1, 1, 1 ) #define MPP28_TDM_CODEC_INTn MPP( 28, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP28_GE1_COL MPP( 28, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP28_AU_EXTCLK MPP( 28, 0x4, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP28_AU_EXTCLK MPP( 28, 0x4, 0, 0, 0, 0, 1, 1, 1 ) #define MPP28_LCD_D8 MPP( 28, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP29_GPIO MPP( 29, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP29_TSMP9 MPP( 29, 0x1, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP29_TSMP9 MPP( 29, 0x1, 0, 0, 0, 0, 1, 1, 1 ) #define MPP29_TDM_CODEC_RSTn MPP( 29, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP29_GE1_TCLK MPP( 29, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP29_LCD_D9 MPP( 29, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP30_GPIO MPP( 30, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP30_TSMP10 MPP( 30, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP30_TDM_PCLK MPP( 30, 0x2, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP30_TSMP10 MPP( 30, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP30_TDM_PCLK MPP( 30, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP30_GE1_RXCTL MPP( 30, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP30_LCD_D10 MPP( 30, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP31_GPIO MPP( 31, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP31_TSMP11 MPP( 31, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP31_TDM_FS MPP( 31, 0x2, 1, 1, 0, 0, 1, 1, 1 ) +#define MPP31_TSMP11 MPP( 31, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP31_TDM_FS MPP( 31, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP31_GE1_RXCLK MPP( 31, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP31_LCD_D11 MPP( 31, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP32_GPIO MPP( 32, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP32_TSMP12 MPP( 32, 0x1, 1, 1, 0, 0, 1, 1, 1 ) -#define MPP32_TDM_DRX MPP( 32, 0x2, 1, 0, 0, 0, 1, 1, 1 ) +#define MPP32_TSMP12 MPP( 32, 0x1, 0, 0, 0, 0, 1, 1, 1 ) +#define MPP32_TDM_DRX MPP( 32, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP32_GE1_TCLKOUT MPP( 32, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP32_LCD_D12 MPP( 32, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP33_GPO MPP( 33, 0x0, 0, 1, 0, 1, 1, 1, 1 ) -#define MPP33_TDM_DTX MPP( 33, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP33_TDM_DTX MPP( 33, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP33_GE1_TXCTL MPP( 33, 0x3, 0, 0, 0, 1, 1, 1, 1 ) #define MPP33_LCD_D13 MPP( 33, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP34_GPIO MPP( 34, 0x0, 1, 1, 0, 1, 1, 1, 1 ) -#define MPP34_TDM_SPI_CS1 MPP( 34, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP34_TDM_SPI_CS1 MPP( 34, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP34_GE1_TXEN MPP( 34, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP34_SATA1_ACTn MPP( 34, 0x5, 0, 1, 0, 0, 0, 1, 1 ) +#define MPP34_SATA1_ACTn MPP( 34, 0x5, 0, 0, 0, 0, 0, 1, 1 ) #define MPP34_LCD_D14 MPP( 34, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP35_GPIO MPP( 35, 0x0, 1, 1, 1, 1, 1, 1, 1 ) -#define MPP35_TDM_CH0_TX_QL MPP( 35, 0x2, 0, 1, 0, 0, 1, 1, 1 ) +#define MPP35_TDM_CH0_TX_QL MPP( 35, 0x2, 0, 0, 0, 0, 1, 1, 1 ) #define MPP35_GE1_RXERR MPP( 35, 0x3, 0, 0, 0, 1, 1, 1, 1 ) -#define MPP35_SATA0_ACTn MPP( 35, 0x5, 0, 1, 0, 1, 1, 1, 1 ) +#define MPP35_SATA0_ACTn MPP( 35, 0x5, 0, 0, 0, 1, 1, 1, 1 ) #define MPP35_LCD_D15 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) -#define MPP35_MII0_RXERR MPP( 35, 0xc, 1, 0, 1, 1, 1, 1, 1 ) +#define MPP35_MII0_RXERR MPP( 35, 0xc, 0, 0, 1, 1, 1, 1, 1 ) #define MPP36_GPIO MPP( 36, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP36_TSMP0 MPP( 36, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP36_TDM_SPI_CS1 MPP( 36, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP36_AU_SPDIFI MPP( 36, 0x4, 1, 0, 1, 0, 0, 1, 1 ) -#define MPP36_TW1_SDA MPP( 36, 0xb, 1, 1, 0, 0, 0, 0, 1 ) +#define MPP36_TSMP0 MPP( 36, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP36_TDM_SPI_CS1 MPP( 36, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP36_AU_SPDIFI MPP( 36, 0x4, 0, 0, 1, 0, 0, 1, 1 ) +#define MPP36_TW1_SDA MPP( 36, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP37_GPIO MPP( 37, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP37_TSMP1 MPP( 37, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP37_TDM_CH2_TX_QL MPP( 37, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP37_AU_SPDIFO MPP( 37, 0x4, 0, 1, 1, 0, 0, 1, 1 ) -#define MPP37_TW1_SCK MPP( 37, 0xb, 1, 1, 0, 0, 0, 0, 1 ) +#define MPP37_TSMP1 MPP( 37, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP37_TDM_CH2_TX_QL MPP( 37, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP37_AU_SPDIFO MPP( 37, 0x4, 0, 0, 1, 0, 0, 1, 1 ) +#define MPP37_TW1_SCK MPP( 37, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP38_GPIO MPP( 38, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP38_TSMP2 MPP( 38, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP38_TDM_CH2_RX_QL MPP( 38, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP38_AU_SPDIFRMLCLK MPP( 38, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP38_TSMP2 MPP( 38, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP38_TDM_CH2_RX_QL MPP( 38, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP38_AU_SPDIFRMLCLK MPP( 38, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP38_LCD_D18 MPP( 38, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP39_GPIO MPP( 39, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP39_TSMP3 MPP( 39, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP39_TDM_SPI_CS0 MPP( 39, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP39_AU_I2SBCLK MPP( 39, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP39_TSMP3 MPP( 39, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP39_TDM_SPI_CS0 MPP( 39, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP39_AU_I2SBCLK MPP( 39, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP39_LCD_D19 MPP( 39, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP40_GPIO MPP( 40, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP40_TSMP4 MPP( 40, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP40_TDM_SPI_SCK MPP( 40, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP40_AU_I2SDO MPP( 40, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP40_TSMP4 MPP( 40, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP40_TDM_SPI_SCK MPP( 40, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP40_AU_I2SDO MPP( 40, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP40_LCD_D20 MPP( 40, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP41_GPIO MPP( 41, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP41_TSMP5 MPP( 41, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP41_TDM_SPI_MISO MPP( 41, 0x2, 1, 0, 0, 0, 0, 1, 1 ) -#define MPP41_AU_I2SLRCLK MPP( 41, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP41_TSMP5 MPP( 41, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP41_TDM_SPI_MISO MPP( 41, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP41_AU_I2SLRCLK MPP( 41, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP41_LCD_D21 MPP( 41, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP42_GPIO MPP( 42, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP42_TSMP6 MPP( 42, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP42_TDM_SPI_MOSI MPP( 42, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP42_AU_I2SMCLK MPP( 42, 0x4, 0, 1, 1, 0, 0, 1, 1 ) +#define MPP42_TSMP6 MPP( 42, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP42_TDM_SPI_MOSI MPP( 42, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP42_AU_I2SMCLK MPP( 42, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP42_LCD_D22 MPP( 42, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP43_GPIO MPP( 43, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP43_TSMP7 MPP( 43, 0x1, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP43_TSMP7 MPP( 43, 0x1, 0, 0, 0, 0, 0, 1, 1 ) #define MPP43_TDM_CODEC_INTn MPP( 43, 0x2, 0, 0, 0, 0, 0, 1, 1 ) -#define MPP43_AU_I2SDI MPP( 43, 0x4, 1, 0, 1, 0, 0, 1, 1 ) +#define MPP43_AU_I2SDI MPP( 43, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP43_LCD_D23 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP44_GPIO MPP( 44, 0x0, 1, 1, 1, 0, 0, 1, 1 ) -#define MPP44_TSMP8 MPP( 44, 0x1, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP44_TSMP8 MPP( 44, 0x1, 0, 0, 0, 0, 0, 1, 1 ) #define MPP44_TDM_CODEC_RSTn MPP( 44, 0x2, 0, 0, 0, 0, 0, 1, 1 ) -#define MPP44_AU_EXTCLK MPP( 44, 0x4, 1, 0, 1, 0, 0, 1, 1 ) +#define MPP44_AU_EXTCLK MPP( 44, 0x4, 0, 0, 1, 0, 0, 1, 1 ) #define MPP44_LCD_CLK MPP( 44, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP45_GPIO MPP( 45, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP45_TSMP9 MPP( 45, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP45_TDM_PCLK MPP( 45, 0x2, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP45_TSMP9 MPP( 45, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP45_TDM_PCLK MPP( 45, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP245_LCD_E MPP( 45, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP46_GPIO MPP( 46, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP46_TSMP10 MPP( 46, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP46_TDM_FS MPP( 46, 0x2, 1, 1, 0, 0, 0, 1, 1 ) +#define MPP46_TSMP10 MPP( 46, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP46_TDM_FS MPP( 46, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP46_LCD_HSYNC MPP( 46, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP47_GPIO MPP( 47, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP47_TSMP11 MPP( 47, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP47_TDM_DRX MPP( 47, 0x2, 1, 0, 0, 0, 0, 1, 1 ) +#define MPP47_TSMP11 MPP( 47, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP47_TDM_DRX MPP( 47, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP47_LCD_VSYNC MPP( 47, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP48_GPIO MPP( 48, 0x0, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP48_TSMP12 MPP( 48, 0x1, 1, 1, 0, 0, 0, 1, 1 ) -#define MPP48_TDM_DTX MPP( 48, 0x2, 0, 1, 0, 0, 0, 1, 1 ) +#define MPP48_TSMP12 MPP( 48, 0x1, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP48_TDM_DTX MPP( 48, 0x2, 0, 0, 0, 0, 0, 1, 1 ) #define MPP48_LCD_D16 MPP( 22, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP49_GPIO MPP( 49, 0x0, 1, 1, 0, 0, 0, 1, 0 ) #define MPP49_GPO MPP( 49, 0x0, 0, 1, 0, 0, 0, 0, 1 ) -#define MPP49_TSMP9 MPP( 49, 0x1, 1, 1, 0, 0, 0, 1, 0 ) -#define MPP49_TDM_CH0_RX_QL MPP( 49, 0x2, 0, 1, 0, 0, 0, 1, 1 ) -#define MPP49_PTP_CLK MPP( 49, 0x5, 1, 0, 0, 0, 0, 1, 0 ) -#define MPP49_PEX0_CLKREQ MPP( 49, 0xa, 0, 1, 0, 0, 0, 0, 1 ) +#define MPP49_TSMP9 MPP( 49, 0x1, 0, 0, 0, 0, 0, 1, 0 ) +#define MPP49_TDM_CH0_RX_QL MPP( 49, 0x2, 0, 0, 0, 0, 0, 1, 1 ) +#define MPP49_PTP_CLK MPP( 49, 0x5, 0, 0, 0, 0, 0, 1, 0 ) +#define MPP49_PEX0_CLKREQ MPP( 49, 0xa, 0, 0, 0, 0, 0, 0, 1 ) #define MPP49_LCD_D17 MPP( 49, 0xb, 0, 0, 0, 0, 0, 0, 1 ) #define MPP_MAX 49 diff --git a/arch/arm/mach-mv78xx0/mpp.h b/arch/arm/mach-mv78xx0/mpp.h index b61b509..3752302 100644 --- a/arch/arm/mach-mv78xx0/mpp.h +++ b/arch/arm/mach-mv78xx0/mpp.h @@ -24,296 +24,296 @@ #define MPP_78100_A0_MASK MPP(0, 0x0, 0, 0, 1) #define MPP0_GPIO MPP(0, 0x0, 1, 1, 1) -#define MPP0_GE0_COL MPP(0, 0x1, 1, 0, 1) -#define MPP0_GE1_TXCLK MPP(0, 0x2, 0, 1, 1) +#define MPP0_GE0_COL MPP(0, 0x1, 0, 0, 1) +#define MPP0_GE1_TXCLK MPP(0, 0x2, 0, 0, 1) #define MPP0_UNUSED MPP(0, 0x3, 0, 0, 1) #define MPP1_GPIO MPP(1, 0x0, 1, 1, 1) -#define MPP1_GE0_RXERR MPP(1, 0x1, 1, 0, 1) -#define MPP1_GE1_TXCTL MPP(1, 0x2, 0, 1, 1) +#define MPP1_GE0_RXERR MPP(1, 0x1, 0, 0, 1) +#define MPP1_GE1_TXCTL MPP(1, 0x2, 0, 0, 1) #define MPP1_UNUSED MPP(1, 0x3, 0, 0, 1) #define MPP2_GPIO MPP(2, 0x0, 1, 1, 1) -#define MPP2_GE0_CRS MPP(2, 0x1, 1, 0, 1) -#define MPP2_GE1_RXCTL MPP(2, 0x2, 1, 0, 1) +#define MPP2_GE0_CRS MPP(2, 0x1, 0, 0, 1) +#define MPP2_GE1_RXCTL MPP(2, 0x2, 0, 0, 1) #define MPP2_UNUSED MPP(2, 0x3, 0, 0, 1) #define MPP3_GPIO MPP(3, 0x0, 1, 1, 1) -#define MPP3_GE0_TXERR MPP(3, 0x1, 0, 1, 1) -#define MPP3_GE1_RXCLK MPP(3, 0x2, 1, 0, 1) +#define MPP3_GE0_TXERR MPP(3, 0x1, 0, 0, 1) +#define MPP3_GE1_RXCLK MPP(3, 0x2, 0, 0, 1) #define MPP3_UNUSED MPP(3, 0x3, 0, 0, 1) #define MPP4_GPIO MPP(4, 0x0, 1, 1, 1) -#define MPP4_GE0_TXD4 MPP(4, 0x1, 0, 1, 1) -#define MPP4_GE1_TXD0 MPP(4, 0x2, 0, 1, 1) +#define MPP4_GE0_TXD4 MPP(4, 0x1, 0, 0, 1) +#define MPP4_GE1_TXD0 MPP(4, 0x2, 0, 0, 1) #define MPP4_UNUSED MPP(4, 0x3, 0, 0, 1) #define MPP5_GPIO MPP(5, 0x0, 1, 1, 1) -#define MPP5_GE0_TXD5 MPP(5, 0x1, 0, 1, 1) -#define MPP5_GE1_TXD1 MPP(5, 0x2, 0, 1, 1) +#define MPP5_GE0_TXD5 MPP(5, 0x1, 0, 0, 1) +#define MPP5_GE1_TXD1 MPP(5, 0x2, 0, 0, 1) #define MPP5_UNUSED MPP(5, 0x3, 0, 0, 1) #define MPP6_GPIO MPP(6, 0x0, 1, 1, 1) -#define MPP6_GE0_TXD6 MPP(6, 0x1, 0, 1, 1) -#define MPP6_GE1_TXD2 MPP(6, 0x2, 0, 1, 1) +#define MPP6_GE0_TXD6 MPP(6, 0x1, 0, 0, 1) +#define MPP6_GE1_TXD2 MPP(6, 0x2, 0, 0, 1) #define MPP6_UNUSED MPP(6, 0x3, 0, 0, 1) #define MPP7_GPIO MPP(7, 0x0, 1, 1, 1) -#define MPP7_GE0_TXD7 MPP(7, 0x1, 0, 1, 1) -#define MPP7_GE1_TXD3 MPP(7, 0x2, 0, 1, 1) +#define MPP7_GE0_TXD7 MPP(7, 0x1, 0, 0, 1) +#define MPP7_GE1_TXD3 MPP(7, 0x2, 0, 0, 1) #define MPP7_UNUSED MPP(7, 0x3, 0, 0, 1) #define MPP8_GPIO MPP(8, 0x0, 1, 1, 1) -#define MPP8_GE0_RXD4 MPP(8, 0x1, 1, 0, 1) -#define MPP8_GE1_RXD0 MPP(8, 0x2, 1, 0, 1) +#define MPP8_GE0_RXD4 MPP(8, 0x1, 0, 0, 1) +#define MPP8_GE1_RXD0 MPP(8, 0x2, 0, 0, 1) #define MPP8_UNUSED MPP(8, 0x3, 0, 0, 1) #define MPP9_GPIO MPP(9, 0x0, 1, 1, 1) -#define MPP9_GE0_RXD5 MPP(9, 0x1, 1, 0, 1) -#define MPP9_GE1_RXD1 MPP(9, 0x2, 1, 0, 1) +#define MPP9_GE0_RXD5 MPP(9, 0x1, 0, 0, 1) +#define MPP9_GE1_RXD1 MPP(9, 0x2, 0, 0, 1) #define MPP9_UNUSED MPP(9, 0x3, 0, 0, 1) #define MPP10_GPIO MPP(10, 0x0, 1, 1, 1) -#define MPP10_GE0_RXD6 MPP(10, 0x1, 1, 0, 1) -#define MPP10_GE1_RXD2 MPP(10, 0x2, 1, 0, 1) +#define MPP10_GE0_RXD6 MPP(10, 0x1, 0, 0, 1) +#define MPP10_GE1_RXD2 MPP(10, 0x2, 0, 0, 1) #define MPP10_UNUSED MPP(10, 0x3, 0, 0, 1) #define MPP11_GPIO MPP(11, 0x0, 1, 1, 1) -#define MPP11_GE0_RXD7 MPP(11, 0x1, 1, 0, 1) -#define MPP11_GE1_RXD3 MPP(11, 0x2, 1, 0, 1) +#define MPP11_GE0_RXD7 MPP(11, 0x1, 0, 0, 1) +#define MPP11_GE1_RXD3 MPP(11, 0x2, 0, 0, 1) #define MPP11_UNUSED MPP(11, 0x3, 0, 0, 1) #define MPP12_GPIO MPP(12, 0x0, 1, 1, 1) -#define MPP12_M_BB MPP(12, 0x3, 1, 0, 1) -#define MPP12_UA0_CTSn MPP(12, 0x4, 1, 0, 1) -#define MPP12_NAND_FLASH_REn0 MPP(12, 0x5, 0, 1, 1) -#define MPP12_TDM0_SCSn MPP(12, 0X6, 0, 1, 1) +#define MPP12_M_BB MPP(12, 0x3, 0, 0, 1) +#define MPP12_UA0_CTSn MPP(12, 0x4, 0, 0, 1) +#define MPP12_NAND_FLASH_REn0 MPP(12, 0x5, 0, 0, 1) +#define MPP12_TDM0_SCSn MPP(12, 0X6, 0, 0, 1) #define MPP12_UNUSED MPP(12, 0x1, 0, 0, 1) #define MPP13_GPIO MPP(13, 0x0, 1, 1, 1) -#define MPP13_SYSRST_OUTn MPP(13, 0x3, 0, 1, 1) -#define MPP13_UA0_RTSn MPP(13, 0x4, 0, 1, 1) -#define MPP13_NAN_FLASH_WEn0 MPP(13, 0x5, 0, 1, 1) -#define MPP13_TDM_SCLK MPP(13, 0x6, 0, 1, 1) +#define MPP13_SYSRST_OUTn MPP(13, 0x3, 0, 0, 1) +#define MPP13_UA0_RTSn MPP(13, 0x4, 0, 0, 1) +#define MPP13_NAN_FLASH_WEn0 MPP(13, 0x5, 0, 0, 1) +#define MPP13_TDM_SCLK MPP(13, 0x6, 0, 0, 1) #define MPP13_UNUSED MPP(13, 0x1, 0, 0, 1) #define MPP14_GPIO MPP(14, 0x0, 1, 1, 1) -#define MPP14_SATA1_ACTn MPP(14, 0x3, 0, 1, 1) -#define MPP14_UA1_CTSn MPP(14, 0x4, 1, 0, 1) -#define MPP14_NAND_FLASH_REn1 MPP(14, 0x5, 0, 1, 1) -#define MPP14_TDM_SMOSI MPP(14, 0x6, 0, 1, 1) +#define MPP14_SATA1_ACTn MPP(14, 0x3, 0, 0, 1) +#define MPP14_UA1_CTSn MPP(14, 0x4, 0, 0, 1) +#define MPP14_NAND_FLASH_REn1 MPP(14, 0x5, 0, 0, 1) +#define MPP14_TDM_SMOSI MPP(14, 0x6, 0, 0, 1) #define MPP14_UNUSED MPP(14, 0x1, 0, 0, 1) #define MPP15_GPIO MPP(15, 0x0, 1, 1, 1) -#define MPP15_SATA0_ACTn MPP(15, 0x3, 0, 1, 1) -#define MPP15_UA1_RTSn MPP(15, 0x4, 0, 1, 1) -#define MPP15_NAND_FLASH_WEn1 MPP(15, 0x5, 0, 1, 1) -#define MPP15_TDM_SMISO MPP(15, 0x6, 1, 0, 1) +#define MPP15_SATA0_ACTn MPP(15, 0x3, 0, 0, 1) +#define MPP15_UA1_RTSn MPP(15, 0x4, 0, 0, 1) +#define MPP15_NAND_FLASH_WEn1 MPP(15, 0x5, 0, 0, 1) +#define MPP15_TDM_SMISO MPP(15, 0x6, 0, 0, 1) #define MPP15_UNUSED MPP(15, 0x1, 0, 0, 1) #define MPP16_GPIO MPP(16, 0x0, 1, 1, 1) -#define MPP16_SATA1_PRESENTn MPP(16, 0x3, 0, 1, 1) -#define MPP16_UA2_TXD MPP(16, 0x4, 0, 1, 1) -#define MPP16_NAND_FLASH_REn3 MPP(16, 0x5, 0, 1, 1) -#define MPP16_TDM_INTn MPP(16, 0x6, 1, 0, 1) +#define MPP16_SATA1_PRESENTn MPP(16, 0x3, 0, 0, 1) +#define MPP16_UA2_TXD MPP(16, 0x4, 0, 0, 1) +#define MPP16_NAND_FLASH_REn3 MPP(16, 0x5, 0, 0, 1) +#define MPP16_TDM_INTn MPP(16, 0x6, 0, 0, 1) #define MPP16_UNUSED MPP(16, 0x1, 0, 0, 1) #define MPP17_GPIO MPP(17, 0x0, 1, 1, 1) -#define MPP17_SATA0_PRESENTn MPP(17, 0x3, 0, 1, 1) -#define MPP17_UA2_RXD MPP(17, 0x4, 1, 0, 1) -#define MPP17_NAND_FLASH_WEn3 MPP(17, 0x5, 0, 1, 1) -#define MPP17_TDM_RSTn MPP(17, 0x6, 0, 1, 1) +#define MPP17_SATA0_PRESENTn MPP(17, 0x3, 0, 0, 1) +#define MPP17_UA2_RXD MPP(17, 0x4, 0, 0, 1) +#define MPP17_NAND_FLASH_WEn3 MPP(17, 0x5, 0, 0, 1) +#define MPP17_TDM_RSTn MPP(17, 0x6, 0, 0, 1) #define MPP17_UNUSED MPP(17, 0x1, 0, 0, 1) #define MPP18_GPIO MPP(18, 0x0, 1, 1, 1) -#define MPP18_UA0_CTSn MPP(18, 0x4, 1, 0, 1) -#define MPP18_BOOT_FLASH_REn MPP(18, 0x5, 0, 1, 1) +#define MPP18_UA0_CTSn MPP(18, 0x4, 0, 0, 1) +#define MPP18_BOOT_FLASH_REn MPP(18, 0x5, 0, 0, 1) #define MPP18_UNUSED MPP(18, 0x1, 0, 0, 1) #define MPP19_GPIO MPP(19, 0x0, 1, 1, 1) -#define MPP19_UA0_CTSn MPP(19, 0x4, 0, 1, 1) -#define MPP19_BOOT_FLASH_WEn MPP(19, 0x5, 0, 1, 1) +#define MPP19_UA0_CTSn MPP(19, 0x4, 0, 0, 1) +#define MPP19_BOOT_FLASH_WEn MPP(19, 0x5, 0, 0, 1) #define MPP19_UNUSED MPP(19, 0x1, 0, 0, 1) #define MPP20_GPIO MPP(20, 0x0, 1, 1, 1) -#define MPP20_UA1_CTSs MPP(20, 0x4, 1, 0, 1) -#define MPP20_TDM_PCLK MPP(20, 0x6, 1, 1, 0) +#define MPP20_UA1_CTSs MPP(20, 0x4, 0, 0, 1) +#define MPP20_TDM_PCLK MPP(20, 0x6, 0, 0, 0) #define MPP20_UNUSED MPP(20, 0x1, 0, 0, 1) #define MPP21_GPIO MPP(21, 0x0, 1, 1, 1) -#define MPP21_UA1_CTSs MPP(21, 0x4, 0, 1, 1) -#define MPP21_TDM_FSYNC MPP(21, 0x6, 1, 1, 0) +#define MPP21_UA1_CTSs MPP(21, 0x4, 0, 0, 1) +#define MPP21_TDM_FSYNC MPP(21, 0x6, 0, 0, 0) #define MPP21_UNUSED MPP(21, 0x1, 0, 0, 1) #define MPP22_GPIO MPP(22, 0x0, 1, 1, 1) -#define MPP22_UA3_TDX MPP(22, 0x4, 0, 1, 1) -#define MPP22_NAND_FLASH_REn2 MPP(22, 0x5, 0, 1, 1) -#define MPP22_TDM_DRX MPP(22, 0x6, 1, 0, 1) +#define MPP22_UA3_TDX MPP(22, 0x4, 0, 0, 1) +#define MPP22_NAND_FLASH_REn2 MPP(22, 0x5, 0, 0, 1) +#define MPP22_TDM_DRX MPP(22, 0x6, 0, 0, 1) #define MPP22_UNUSED MPP(22, 0x1, 0, 0, 1) #define MPP23_GPIO MPP(23, 0x0, 1, 1, 1) -#define MPP23_UA3_RDX MPP(23, 0x4, 1, 0, 1) -#define MPP23_NAND_FLASH_WEn2 MPP(23, 0x5, 0, 1, 1) -#define MPP23_TDM_DTX MPP(23, 0x6, 0, 1, 1) +#define MPP23_UA3_RDX MPP(23, 0x4, 0, 0, 1) +#define MPP23_NAND_FLASH_WEn2 MPP(23, 0x5, 0, 0, 1) +#define MPP23_TDM_DTX MPP(23, 0x6, 0, 0, 1) #define MPP23_UNUSED MPP(23, 0x1, 0, 0, 1) #define MPP24_GPIO MPP(24, 0x0, 1, 1, 1) -#define MPP24_UA2_TXD MPP(24, 0x4, 0, 1, 1) -#define MPP24_TDM_INTn MPP(24, 0x6, 1, 0, 1) +#define MPP24_UA2_TXD MPP(24, 0x4, 0, 0, 1) +#define MPP24_TDM_INTn MPP(24, 0x6, 0, 0, 1) #define MPP24_UNUSED MPP(24, 0x1, 0, 0, 1) #define MPP25_GPIO MPP(25, 0x0, 1, 1, 1) -#define MPP25_UA2_RXD MPP(25, 0x4, 1, 0, 1) -#define MPP25_TDM_RSTn MPP(25, 0x6, 0, 1, 1) +#define MPP25_UA2_RXD MPP(25, 0x4, 0, 0, 1) +#define MPP25_TDM_RSTn MPP(25, 0x6, 0, 0, 1) #define MPP25_UNUSED MPP(25, 0x1, 0, 0, 1) #define MPP26_GPIO MPP(26, 0x0, 1, 1, 1) -#define MPP26_UA2_CTSn MPP(26, 0x4, 1, 0, 1) -#define MPP26_TDM_PCLK MPP(26, 0x6, 1, 1, 1) +#define MPP26_UA2_CTSn MPP(26, 0x4, 0, 0, 1) +#define MPP26_TDM_PCLK MPP(26, 0x6, 0, 0, 1) #define MPP26_UNUSED MPP(26, 0x1, 0, 0, 1) #define MPP27_GPIO MPP(27, 0x0, 1, 1, 1) -#define MPP27_UA2_RTSn MPP(27, 0x4, 0, 1, 1) -#define MPP27_TDM_FSYNC MPP(27, 0x6, 1, 1, 1) +#define MPP27_UA2_RTSn MPP(27, 0x4, 0, 0, 1) +#define MPP27_TDM_FSYNC MPP(27, 0x6, 0, 0, 1) #define MPP27_UNUSED MPP(27, 0x1, 0, 0, 1) #define MPP28_GPIO MPP(28, 0x0, 1, 1, 1) -#define MPP28_UA3_TXD MPP(28, 0x4, 0, 1, 1) -#define MPP28_TDM_DRX MPP(28, 0x6, 1, 0, 1) +#define MPP28_UA3_TXD MPP(28, 0x4, 0, 0, 1) +#define MPP28_TDM_DRX MPP(28, 0x6, 0, 0, 1) #define MPP28_UNUSED MPP(28, 0x1, 0, 0, 1) #define MPP29_GPIO MPP(29, 0x0, 1, 1, 1) -#define MPP29_UA3_RXD MPP(29, 0x4, 1, 0, 1) -#define MPP29_SYSRST_OUTn MPP(29, 0x5, 0, 1, 1) -#define MPP29_TDM_DTX MPP(29, 0x6, 0, 1, 1) +#define MPP29_UA3_RXD MPP(29, 0x4, 0, 0, 1) +#define MPP29_SYSRST_OUTn MPP(29, 0x5, 0, 0, 1) +#define MPP29_TDM_DTX MPP(29, 0x6, 0, 0, 1) #define MPP29_UNUSED MPP(29, 0x1, 0, 0, 1) #define MPP30_GPIO MPP(30, 0x0, 1, 1, 1) -#define MPP30_UA3_CTSn MPP(30, 0x4, 1, 0, 1) +#define MPP30_UA3_CTSn MPP(30, 0x4, 0, 0, 1) #define MPP30_UNUSED MPP(30, 0x1, 0, 0, 1) #define MPP31_GPIO MPP(31, 0x0, 1, 1, 1) -#define MPP31_UA3_RTSn MPP(31, 0x4, 0, 1, 1) -#define MPP31_TDM1_SCSn MPP(31, 0x6, 0, 1, 1) +#define MPP31_UA3_RTSn MPP(31, 0x4, 0, 0, 1) +#define MPP31_TDM1_SCSn MPP(31, 0x6, 0, 0, 1) #define MPP31_UNUSED MPP(31, 0x1, 0, 0, 1) #define MPP32_GPIO MPP(32, 0x1, 1, 1, 1) -#define MPP32_UA3_TDX MPP(32, 0x4, 0, 1, 1) -#define MPP32_SYSRST_OUTn MPP(32, 0x5, 0, 1, 1) -#define MPP32_TDM0_RXQ MPP(32, 0x6, 0, 1, 1) +#define MPP32_UA3_TDX MPP(32, 0x4, 0, 0, 1) +#define MPP32_SYSRST_OUTn MPP(32, 0x5, 0, 0, 1) +#define MPP32_TDM0_RXQ MPP(32, 0x6, 0, 0, 1) #define MPP32_UNUSED MPP(32, 0x3, 0, 0, 1) #define MPP33_GPIO MPP(33, 0x1, 1, 1, 1) -#define MPP33_UA3_RDX MPP(33, 0x4, 1, 0, 1) -#define MPP33_TDM0_TXQ MPP(33, 0x6, 0, 1, 1) +#define MPP33_UA3_RDX MPP(33, 0x4, 0, 0, 1) +#define MPP33_TDM0_TXQ MPP(33, 0x6, 0, 0, 1) #define MPP33_UNUSED MPP(33, 0x3, 0, 0, 1) #define MPP34_GPIO MPP(34, 0x1, 1, 1, 1) -#define MPP34_UA2_TDX MPP(34, 0x4, 0, 1, 1) -#define MPP34_TDM1_RXQ MPP(34, 0x6, 0, 1, 1) +#define MPP34_UA2_TDX MPP(34, 0x4, 0, 0, 1) +#define MPP34_TDM1_RXQ MPP(34, 0x6, 0, 0, 1) #define MPP34_UNUSED MPP(34, 0x3, 0, 0, 1) #define MPP35_GPIO MPP(35, 0x1, 1, 1, 1) -#define MPP35_UA2_RDX MPP(35, 0x4, 1, 0, 1) -#define MPP35_TDM1_TXQ MPP(35, 0x6, 0, 1, 1) +#define MPP35_UA2_RDX MPP(35, 0x4, 0, 0, 1) +#define MPP35_TDM1_TXQ MPP(35, 0x6, 0, 0, 1) #define MPP35_UNUSED MPP(35, 0x3, 0, 0, 1) #define MPP36_GPIO MPP(36, 0x1, 1, 1, 1) -#define MPP36_UA0_CTSn MPP(36, 0x2, 1, 0, 1) -#define MPP36_UA2_TDX MPP(36, 0x4, 0, 1, 1) -#define MPP36_TDM0_SCSn MPP(36, 0x6, 0, 1, 1) +#define MPP36_UA0_CTSn MPP(36, 0x2, 0, 0, 1) +#define MPP36_UA2_TDX MPP(36, 0x4, 0, 0, 1) +#define MPP36_TDM0_SCSn MPP(36, 0x6, 0, 0, 1) #define MPP36_UNUSED MPP(36, 0x3, 0, 0, 1) #define MPP37_GPIO MPP(37, 0x1, 1, 1, 1) -#define MPP37_UA0_RTSn MPP(37, 0x2, 0, 1, 1) -#define MPP37_UA2_RXD MPP(37, 0x4, 1, 0, 1) -#define MPP37_SYSRST_OUTn MPP(37, 0x5, 0, 1, 1) -#define MPP37_TDM_SCLK MPP(37, 0x6, 0, 1, 1) +#define MPP37_UA0_RTSn MPP(37, 0x2, 0, 0, 1) +#define MPP37_UA2_RXD MPP(37, 0x4, 0, 0, 1) +#define MPP37_SYSRST_OUTn MPP(37, 0x5, 0, 0, 1) +#define MPP37_TDM_SCLK MPP(37, 0x6, 0, 0, 1) #define MPP37_UNUSED MPP(37, 0x3, 0, 0, 1) #define MPP38_GPIO MPP(38, 0x1, 1, 1, 1) -#define MPP38_UA1_CTSn MPP(38, 0x2, 1, 0, 1) -#define MPP38_UA3_TXD MPP(38, 0x4, 0, 1, 1) -#define MPP38_SYSRST_OUTn MPP(38, 0x5, 0, 1, 1) -#define MPP38_TDM_SMOSI MPP(38, 0x6, 0, 1, 1) +#define MPP38_UA1_CTSn MPP(38, 0x2, 0, 0, 1) +#define MPP38_UA3_TXD MPP(38, 0x4, 0, 0, 1) +#define MPP38_SYSRST_OUTn MPP(38, 0x5, 0, 0, 1) +#define MPP38_TDM_SMOSI MPP(38, 0x6, 0, 0, 1) #define MPP38_UNUSED MPP(38, 0x3, 0, 0, 1) #define MPP39_GPIO MPP(39, 0x1, 1, 1, 1) -#define MPP39_UA1_RTSn MPP(39, 0x2, 0, 1, 1) -#define MPP39_UA3_RXD MPP(39, 0x4, 1, 0, 1) -#define MPP39_SYSRST_OUTn MPP(39, 0x5, 0, 1, 1) -#define MPP39_TDM_SMISO MPP(39, 0x6, 1, 0, 1) +#define MPP39_UA1_RTSn MPP(39, 0x2, 0, 0, 1) +#define MPP39_UA3_RXD MPP(39, 0x4, 0, 0, 1) +#define MPP39_SYSRST_OUTn MPP(39, 0x5, 0, 0, 1) +#define MPP39_TDM_SMISO MPP(39, 0x6, 0, 0, 1) #define MPP39_UNUSED MPP(39, 0x3, 0, 0, 1) #define MPP40_GPIO MPP(40, 0x1, 1, 1, 1) -#define MPP40_TDM_INTn MPP(40, 0x6, 1, 0, 1) +#define MPP40_TDM_INTn MPP(40, 0x6, 0, 0, 1) #define MPP40_UNUSED MPP(40, 0x0, 0, 0, 1) #define MPP41_GPIO MPP(41, 0x1, 1, 1, 1) -#define MPP41_TDM_RSTn MPP(41, 0x6, 0, 1, 1) +#define MPP41_TDM_RSTn MPP(41, 0x6, 0, 0, 1) #define MPP41_UNUSED MPP(41, 0x0, 0, 0, 1) #define MPP42_GPIO MPP(42, 0x1, 1, 1, 1) -#define MPP42_TDM_PCLK MPP(42, 0x6, 1, 1, 1) +#define MPP42_TDM_PCLK MPP(42, 0x6, 0, 0, 1) #define MPP42_UNUSED MPP(42, 0x0, 0, 0, 1) #define MPP43_GPIO MPP(43, 0x1, 1, 1, 1) -#define MPP43_TDM_FSYNC MPP(43, 0x6, 1, 1, 1) +#define MPP43_TDM_FSYNC MPP(43, 0x6, 0, 0, 1) #define MPP43_UNUSED MPP(43, 0x0, 0, 0, 1) #define MPP44_GPIO MPP(44, 0x1, 1, 1, 1) -#define MPP44_TDM_DRX MPP(44, 0x6, 1, 0, 1) +#define MPP44_TDM_DRX MPP(44, 0x6, 0, 0, 1) #define MPP44_UNUSED MPP(44, 0x0, 0, 0, 1) #define MPP45_GPIO MPP(45, 0x1, 1, 1, 1) -#define MPP45_SATA0_ACTn MPP(45, 0x3, 0, 1, 1) -#define MPP45_TDM_DRX MPP(45, 0x6, 0, 1, 1) +#define MPP45_SATA0_ACTn MPP(45, 0x3, 0, 0, 1) +#define MPP45_TDM_DRX MPP(45, 0x6, 0, 0, 1) #define MPP45_UNUSED MPP(45, 0x0, 0, 0, 1) #define MPP46_GPIO MPP(46, 0x1, 1, 1, 1) -#define MPP46_TDM_SCSn MPP(46, 0x6, 0, 1, 1) +#define MPP46_TDM_SCSn MPP(46, 0x6, 0, 0, 1) #define MPP46_UNUSED MPP(46, 0x0, 0, 0, 1) @@ -323,14 +323,14 @@ #define MPP48_GPIO MPP(48, 0x1, 1, 1, 1) -#define MPP48_SATA1_ACTn MPP(48, 0x3, 0, 1, 1) +#define MPP48_SATA1_ACTn MPP(48, 0x3, 0, 0, 1) #define MPP48_UNUSED MPP(48, 0x2, 0, 0, 1) #define MPP49_GPIO MPP(49, 0x1, 1, 1, 1) -#define MPP49_SATA0_ACTn MPP(49, 0x3, 0, 1, 1) -#define MPP49_M_BB MPP(49, 0x4, 1, 0, 1) +#define MPP49_SATA0_ACTn MPP(49, 0x3, 0, 0, 1) +#define MPP49_M_BB MPP(49, 0x4, 0, 0, 1) #define MPP49_UNUSED MPP(49, 0x2, 0, 0, 1) diff --git a/arch/arm/plat-orion/mpp.c b/arch/arm/plat-orion/mpp.c index 9155343..3b1e17b 100644 --- a/arch/arm/plat-orion/mpp.c +++ b/arch/arm/plat-orion/mpp.c @@ -64,8 +64,7 @@ void __init orion_mpp_conf(unsigned int *mpp_list, unsigned int variant_mask, gpio_mode |= GPIO_INPUT_OK; if (*mpp_list & MPP_OUTPUT_MASK) gpio_mode |= GPIO_OUTPUT_OK; - if (sel != 0) - gpio_mode = 0; + orion_gpio_set_valid(num, gpio_mode); } -- cgit v0.10.2 From 72053353583230952c4b187e110e9da00dfc3afb Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 8 Feb 2012 15:52:47 +0100 Subject: ARM: orion: Fix USB phy for orion5x. The patch "ARM: orion: Consolidate USB platform setup code.", commit 4fcd3f374a928081d391cd9a570afe3b2c692fdc broke USB on TS-7800 and other orion5x boards, because the wrong type of PHY was being passed to the EHCI driver in the platform data. Orion5x needs EHCI_PHY_ORION and all the others want EHCI_PHY_NA. Allow the mach- code to tell the generic plat-orion code which USB PHY enum to place into the platform data. Version 2: Rebase to v3.3-rc2. Reported-by: Ambroz Bizjak Signed-off-by: Andrew Lunn Tested-by: Ambroz Bizjak Acked-by: Nicolas Pitre Signed-off-by: Olof Johansson diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index dd1429ae..bda7aca 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "common.h" @@ -71,7 +72,7 @@ void __init dove_map_io(void) ****************************************************************************/ void __init dove_ehci0_init(void) { - orion_ehci_init(DOVE_USB0_PHYS_BASE, IRQ_DOVE_USB0); + orion_ehci_init(DOVE_USB0_PHYS_BASE, IRQ_DOVE_USB0, EHCI_PHY_NA); } /***************************************************************************** diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index cc15426..77d4852 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +74,7 @@ unsigned int kirkwood_clk_ctrl = CGC_DUNIT | CGC_RESERVED; void __init kirkwood_ehci_init(void) { kirkwood_clk_ctrl |= CGC_USB0; - orion_ehci_init(USB_PHYS_BASE, IRQ_KIRKWOOD_USB); + orion_ehci_init(USB_PHYS_BASE, IRQ_KIRKWOOD_USB, EHCI_PHY_NA); } diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c index 0cdd410..a5dcf766 100644 --- a/arch/arm/mach-mv78xx0/common.c +++ b/arch/arm/mach-mv78xx0/common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -169,7 +170,7 @@ void __init mv78xx0_map_io(void) ****************************************************************************/ void __init mv78xx0_ehci0_init(void) { - orion_ehci_init(USB0_PHYS_BASE, IRQ_MV78XX0_USB_0); + orion_ehci_init(USB0_PHYS_BASE, IRQ_MV78XX0_USB_0, EHCI_PHY_NA); } diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 0e28bae2..5dad38e 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +73,8 @@ void __init orion5x_map_io(void) ****************************************************************************/ void __init orion5x_ehci0_init(void) { - orion_ehci_init(ORION5X_USB0_PHYS_BASE, IRQ_ORION5X_USB0_CTRL); + orion_ehci_init(ORION5X_USB0_PHYS_BASE, IRQ_ORION5X_USB0_CTRL, + EHCI_PHY_ORION); } diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index e5a2fde..089899a 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -789,10 +789,7 @@ void __init orion_xor1_init(unsigned long mapbase_low, /***************************************************************************** * EHCI ****************************************************************************/ -static struct orion_ehci_data orion_ehci_data = { - .phy_version = EHCI_PHY_NA, -}; - +static struct orion_ehci_data orion_ehci_data; static u64 ehci_dmamask = DMA_BIT_MASK(32); @@ -812,8 +809,10 @@ static struct platform_device orion_ehci = { }; void __init orion_ehci_init(unsigned long mapbase, - unsigned long irq) + unsigned long irq, + enum orion_ehci_phy_ver phy_version) { + orion_ehci_data.phy_version = phy_version; fill_resources(&orion_ehci, orion_ehci_resources, mapbase, SZ_4K - 1, irq); diff --git a/arch/arm/plat-orion/include/plat/common.h b/arch/arm/plat-orion/include/plat/common.h index 0fe08d7..a7fa005 100644 --- a/arch/arm/plat-orion/include/plat/common.h +++ b/arch/arm/plat-orion/include/plat/common.h @@ -89,7 +89,8 @@ void __init orion_xor1_init(unsigned long mapbase_low, unsigned long irq_1); void __init orion_ehci_init(unsigned long mapbase, - unsigned long irq); + unsigned long irq, + enum orion_ehci_phy_ver phy_version); void __init orion_ehci_1_init(unsigned long mapbase, unsigned long irq); -- cgit v0.10.2 From 1e056dddabc1b7a909d1f992fefb1d5d5bc8ff0d Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Thu, 9 Feb 2012 18:24:03 -0700 Subject: ARM: OMAP2xxx: PM: fix OMAP2xxx-specific UART idle bug in v3.3 On OMAP2420-based systems, the PM code ignores the state of the UART functional clocks when determining what idle state to enter. This breaks the serial port now that the UART driver's clock behavior can be controlled via the PM autosuspend timeout. To fix, remove the special-case idle handling for the UARTs in the OMAP2420/2430 PM idle code added by commit 4af4016c53f52b26461b8030211f8427a58fa5ed ("OMAP3: PM: UART: disable clocks when idle and off-mode support"). Tested on Nokia N800. This patch is a collaboration between Tony Lindgren and Paul Walmsley . Signed-off-by: Paul Walmsley Acked-by: Kevin Hilman Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c index b8822f8..23de98d 100644 --- a/arch/arm/mach-omap2/pm24xx.c +++ b/arch/arm/mach-omap2/pm24xx.c @@ -82,13 +82,7 @@ static int omap2_fclks_active(void) f1 = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); f2 = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); - /* Ignore UART clocks. These are handled by UART core (serial.c) */ - f1 &= ~(OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_UART2_MASK); - f2 &= ~OMAP24XX_EN_UART3_MASK; - - if (f1 | f2) - return 1; - return 0; + return (f1 | f2) ? 1 : 0; } static void omap2_enter_full_retention(void) -- cgit v0.10.2 From f6302f1bcd75a042df69866d98b8d775a668f8f1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 10 Feb 2012 09:03:58 +0100 Subject: relay: prevent integer overflow in relay_open() "subbuf_size" and "n_subbufs" come from the user and they need to be capped to prevent an integer overflow. Signed-off-by: Dan Carpenter Cc: stable@kernel.org Signed-off-by: Jens Axboe diff --git a/kernel/relay.c b/kernel/relay.c index 4335e1d..ab56a17 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -164,10 +164,14 @@ depopulate: */ static struct rchan_buf *relay_create_buf(struct rchan *chan) { - struct rchan_buf *buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); - if (!buf) + struct rchan_buf *buf; + + if (chan->n_subbufs > UINT_MAX / sizeof(size_t *)) return NULL; + buf = kzalloc(sizeof(struct rchan_buf), GFP_KERNEL); + if (!buf) + return NULL; buf->padding = kmalloc(chan->n_subbufs * sizeof(size_t *), GFP_KERNEL); if (!buf->padding) goto free_buf; @@ -574,6 +578,8 @@ struct rchan *relay_open(const char *base_filename, if (!(subbuf_size && n_subbufs)) return NULL; + if (subbuf_size > UINT_MAX / n_subbufs) + return NULL; chan = kzalloc(sizeof(struct rchan), GFP_KERNEL); if (!chan) -- cgit v0.10.2 From 9fc886a1880766fb91cb27c2e96bd814cef640d6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 7 Feb 2012 11:12:55 +0800 Subject: regulator: Fix getting voltage in max8649_enable_time() Current code takes wrong parameter while calling max8649_list_voltage. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index b06a239..d0e1180 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -150,7 +150,7 @@ static int max8649_enable_time(struct regulator_dev *rdev) if (ret != 0) return ret; val &= MAX8649_VOL_MASK; - voltage = max8649_list_voltage(rdev, (unsigned char)ret); /* uV */ + voltage = max8649_list_voltage(rdev, (unsigned char)val); /* uV */ /* get rate */ ret = regmap_read(info->regmap, MAX8649_RAMP, &val); -- cgit v0.10.2 From 04da0c8196ac0b12fb6b84f4b7a51ad2fa56d869 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 1 Feb 2012 13:57:20 +0000 Subject: xfs: use a normal shrinker for the dquot freelist Stop reusing dquots from the freelist when allocating new ones directly, and implement a shrinker that actually follows the specifications for the interface. The shrinker implementation is still highly suboptimal at this point, but we can gradually work on it. This also fixes an bug in the previous lock ordering, where we would take the hash and dqlist locks inside of the freelist lock against the normal lock ordering. This is only solvable by introducing the dispose list, and thus not when using direct reclaim of unused dquots for new allocations. As a side-effect the quota upper bound and used to free ratio values in /proc/fs/xfs/xqm are set to 0 as these values don't make any sense in the new world order. Signed-off-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h index 292eff1..ab7c53f 100644 --- a/fs/xfs/kmem.h +++ b/fs/xfs/kmem.h @@ -110,10 +110,4 @@ kmem_zone_destroy(kmem_zone_t *zone) extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast); extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast); -static inline int -kmem_shake_allow(gfp_t gfp_mask) -{ - return ((gfp_mask & __GFP_WAIT) && (gfp_mask & __GFP_FS)); -} - #endif /* __XFS_SUPPORT_KMEM_H__ */ diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index b4ff40b..cbcb7be 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -63,82 +63,6 @@ int xfs_dqerror_mod = 33; static struct lock_class_key xfs_dquot_other_class; /* - * Allocate and initialize a dquot. We don't always allocate fresh memory; - * we try to reclaim a free dquot if the number of incore dquots are above - * a threshold. - * The only field inside the core that gets initialized at this point - * is the d_id field. The idea is to fill in the entire q_core - * when we read in the on disk dquot. - */ -STATIC xfs_dquot_t * -xfs_qm_dqinit( - xfs_mount_t *mp, - xfs_dqid_t id, - uint type) -{ - xfs_dquot_t *dqp; - boolean_t brandnewdquot; - - brandnewdquot = xfs_qm_dqalloc_incore(&dqp); - dqp->dq_flags = type; - dqp->q_core.d_id = cpu_to_be32(id); - dqp->q_mount = mp; - - /* - * No need to re-initialize these if this is a reclaimed dquot. - */ - if (brandnewdquot) { - INIT_LIST_HEAD(&dqp->q_freelist); - mutex_init(&dqp->q_qlock); - init_waitqueue_head(&dqp->q_pinwait); - - /* - * Because we want to use a counting completion, complete - * the flush completion once to allow a single access to - * the flush completion without blocking. - */ - init_completion(&dqp->q_flush); - complete(&dqp->q_flush); - - trace_xfs_dqinit(dqp); - } else { - /* - * Only the q_core portion was zeroed in dqreclaim_one(). - * So, we need to reset others. - */ - dqp->q_nrefs = 0; - dqp->q_blkno = 0; - INIT_LIST_HEAD(&dqp->q_mplist); - INIT_LIST_HEAD(&dqp->q_hashlist); - dqp->q_bufoffset = 0; - dqp->q_fileoffset = 0; - dqp->q_transp = NULL; - dqp->q_gdquot = NULL; - dqp->q_res_bcount = 0; - dqp->q_res_icount = 0; - dqp->q_res_rtbcount = 0; - atomic_set(&dqp->q_pincount, 0); - dqp->q_hash = NULL; - ASSERT(list_empty(&dqp->q_freelist)); - - trace_xfs_dqreuse(dqp); - } - - /* - * In either case we need to make sure group quotas have a different - * lock class than user quotas, to make sure lockdep knows we can - * locks of one of each at the same time. - */ - if (!(type & XFS_DQ_USER)) - lockdep_set_class(&dqp->q_qlock, &xfs_dquot_other_class); - - /* - * log item gets initialized later - */ - return (dqp); -} - -/* * This is called to free all the memory associated with a dquot */ void @@ -567,7 +491,32 @@ xfs_qm_dqread( int error; int cancelflags = 0; - dqp = xfs_qm_dqinit(mp, id, type); + + dqp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); + + dqp->dq_flags = type; + dqp->q_core.d_id = cpu_to_be32(id); + dqp->q_mount = mp; + INIT_LIST_HEAD(&dqp->q_freelist); + mutex_init(&dqp->q_qlock); + init_waitqueue_head(&dqp->q_pinwait); + + /* + * Because we want to use a counting completion, complete + * the flush completion once to allow a single access to + * the flush completion without blocking. + */ + init_completion(&dqp->q_flush); + complete(&dqp->q_flush); + + /* + * Make sure group quotas have a different lock class than user + * quotas. + */ + if (!(type & XFS_DQ_USER)) + lockdep_set_class(&dqp->q_qlock, &xfs_dquot_other_class); + + atomic_inc(&xfs_Gqm->qm_totaldquots); trace_xfs_dqread(dqp); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 671f37e..c436def 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -50,7 +50,6 @@ */ struct mutex xfs_Gqm_lock; struct xfs_qm *xfs_Gqm; -uint ndquot; kmem_zone_t *qm_dqzone; kmem_zone_t *qm_dqtrxzone; @@ -93,7 +92,6 @@ xfs_Gqm_init(void) goto out_free_udqhash; hsize /= sizeof(xfs_dqhash_t); - ndquot = hsize << 8; xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP); xqm->qm_dqhashmask = hsize - 1; @@ -137,7 +135,6 @@ xfs_Gqm_init(void) xqm->qm_dqtrxzone = qm_dqtrxzone; atomic_set(&xqm->qm_totaldquots, 0); - xqm->qm_dqfree_ratio = XFS_QM_DQFREE_RATIO; xqm->qm_nrefs = 0; return xqm; @@ -1600,216 +1597,150 @@ xfs_qm_init_quotainos( return 0; } +STATIC void +xfs_qm_dqfree_one( + struct xfs_dquot *dqp) +{ + struct xfs_mount *mp = dqp->q_mount; + struct xfs_quotainfo *qi = mp->m_quotainfo; + mutex_lock(&dqp->q_hash->qh_lock); + list_del_init(&dqp->q_hashlist); + dqp->q_hash->qh_version++; + mutex_unlock(&dqp->q_hash->qh_lock); -/* - * Pop the least recently used dquot off the freelist and recycle it. - */ -STATIC struct xfs_dquot * -xfs_qm_dqreclaim_one(void) + mutex_lock(&qi->qi_dqlist_lock); + list_del_init(&dqp->q_mplist); + qi->qi_dquots--; + qi->qi_dqreclaims++; + mutex_unlock(&qi->qi_dqlist_lock); + + xfs_qm_dqdestroy(dqp); +} + +STATIC void +xfs_qm_dqreclaim_one( + struct xfs_dquot *dqp, + struct list_head *dispose_list) { - struct xfs_dquot *dqp; - int restarts = 0; + struct xfs_mount *mp = dqp->q_mount; + int error; - mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); -restart: - list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { - struct xfs_mount *mp = dqp->q_mount; + if (!xfs_dqlock_nowait(dqp)) + goto out_busy; - if (!xfs_dqlock_nowait(dqp)) - continue; + /* + * This dquot has acquired a reference in the meantime remove it from + * the freelist and try again. + */ + if (dqp->q_nrefs) { + xfs_dqunlock(dqp); - /* - * This dquot has already been grabbed by dqlookup. - * Remove it from the freelist and try again. - */ - if (dqp->q_nrefs) { - trace_xfs_dqreclaim_want(dqp); - XQM_STATS_INC(xqmstats.xs_qm_dqwants); - - list_del_init(&dqp->q_freelist); - xfs_Gqm->qm_dqfrlist_cnt--; - restarts++; - goto dqunlock; - } + trace_xfs_dqreclaim_want(dqp); + XQM_STATS_INC(xqmstats.xs_qm_dqwants); - ASSERT(dqp->q_hash); - ASSERT(!list_empty(&dqp->q_mplist)); + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; + return; + } - /* - * Try to grab the flush lock. If this dquot is in the process - * of getting flushed to disk, we don't want to reclaim it. - */ - if (!xfs_dqflock_nowait(dqp)) - goto dqunlock; + ASSERT(dqp->q_hash); + ASSERT(!list_empty(&dqp->q_mplist)); - /* - * We have the flush lock so we know that this is not in the - * process of being flushed. So, if this is dirty, flush it - * DELWRI so that we don't get a freelist infested with - * dirty dquots. - */ - if (XFS_DQ_IS_DIRTY(dqp)) { - int error; + /* + * Try to grab the flush lock. If this dquot is in the process of + * getting flushed to disk, we don't want to reclaim it. + */ + if (!xfs_dqflock_nowait(dqp)) + goto out_busy; - trace_xfs_dqreclaim_dirty(dqp); + /* + * We have the flush lock so we know that this is not in the + * process of being flushed. So, if this is dirty, flush it + * DELWRI so that we don't get a freelist infested with + * dirty dquots. + */ + if (XFS_DQ_IS_DIRTY(dqp)) { + trace_xfs_dqreclaim_dirty(dqp); - /* - * We flush it delayed write, so don't bother - * releasing the freelist lock. - */ - error = xfs_qm_dqflush(dqp, SYNC_TRYLOCK); - if (error) { - xfs_warn(mp, "%s: dquot %p flush failed", - __func__, dqp); - } - goto dqunlock; + /* + * We flush it delayed write, so don't bother releasing the + * freelist lock. + */ + error = xfs_qm_dqflush(dqp, 0); + if (error) { + xfs_warn(mp, "%s: dquot %p flush failed", + __func__, dqp); } - xfs_dqfunlock(dqp); /* - * Prevent lookup now that we are going to reclaim the dquot. - * Once XFS_DQ_FREEING is set lookup won't touch the dquot, - * thus we can drop the lock now. + * Give the dquot another try on the freelist, as the + * flushing will take some time. */ - dqp->dq_flags |= XFS_DQ_FREEING; - xfs_dqunlock(dqp); - - mutex_lock(&dqp->q_hash->qh_lock); - list_del_init(&dqp->q_hashlist); - dqp->q_hash->qh_version++; - mutex_unlock(&dqp->q_hash->qh_lock); - - mutex_lock(&mp->m_quotainfo->qi_dqlist_lock); - list_del_init(&dqp->q_mplist); - mp->m_quotainfo->qi_dquots--; - mp->m_quotainfo->qi_dqreclaims++; - mutex_unlock(&mp->m_quotainfo->qi_dqlist_lock); + goto out_busy; + } + xfs_dqfunlock(dqp); - ASSERT(dqp->q_nrefs == 0); - list_del_init(&dqp->q_freelist); - xfs_Gqm->qm_dqfrlist_cnt--; + /* + * Prevent lookups now that we are past the point of no return. + */ + dqp->dq_flags |= XFS_DQ_FREEING; + xfs_dqunlock(dqp); - mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); - return dqp; -dqunlock: - xfs_dqunlock(dqp); - if (restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) - break; - goto restart; - } + ASSERT(dqp->q_nrefs == 0); + list_move_tail(&dqp->q_freelist, dispose_list); + xfs_Gqm->qm_dqfrlist_cnt--; - mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); - return NULL; -} + trace_xfs_dqreclaim_done(dqp); + XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); + return; -/* - * Traverse the freelist of dquots and attempt to reclaim a maximum of - * 'howmany' dquots. This operation races with dqlookup(), and attempts to - * favor the lookup function ... - */ -STATIC int -xfs_qm_shake_freelist( - int howmany) -{ - int nreclaimed = 0; - xfs_dquot_t *dqp; +out_busy: + xfs_dqunlock(dqp); - if (howmany <= 0) - return 0; + /* + * Move the dquot to the tail of the list so that we don't spin on it. + */ + list_move_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist); - while (nreclaimed < howmany) { - dqp = xfs_qm_dqreclaim_one(); - if (!dqp) - return nreclaimed; - xfs_qm_dqdestroy(dqp); - nreclaimed++; - } - return nreclaimed; + trace_xfs_dqreclaim_busy(dqp); + XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); } -/* - * The kmem_shake interface is invoked when memory is running low. - */ -/* ARGSUSED */ STATIC int xfs_qm_shake( - struct shrinker *shrink, - struct shrink_control *sc) + struct shrinker *shrink, + struct shrink_control *sc) { - int ndqused, nfree, n; - gfp_t gfp_mask = sc->gfp_mask; - - if (!kmem_shake_allow(gfp_mask)) - return 0; - if (!xfs_Gqm) - return 0; - - nfree = xfs_Gqm->qm_dqfrlist_cnt; /* free dquots */ - /* incore dquots in all f/s's */ - ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree; - - ASSERT(ndqused >= 0); + int nr_to_scan = sc->nr_to_scan; + LIST_HEAD (dispose_list); + struct xfs_dquot *dqp; - if (nfree <= ndqused && nfree < ndquot) + if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT)) return 0; + if (!nr_to_scan) + goto out; - ndqused *= xfs_Gqm->qm_dqfree_ratio; /* target # of free dquots */ - n = nfree - ndqused - ndquot; /* # over target */ - - return xfs_qm_shake_freelist(MAX(nfree, n)); -} - - -/*------------------------------------------------------------------*/ - -/* - * Return a new incore dquot. Depending on the number of - * dquots in the system, we either allocate a new one on the kernel heap, - * or reclaim a free one. - * Return value is B_TRUE if we allocated a new dquot, B_FALSE if we managed - * to reclaim an existing one from the freelist. - */ -boolean_t -xfs_qm_dqalloc_incore( - xfs_dquot_t **O_dqpp) -{ - xfs_dquot_t *dqp; - - /* - * Check against high water mark to see if we want to pop - * a nincompoop dquot off the freelist. - */ - if (atomic_read(&xfs_Gqm->qm_totaldquots) >= ndquot) { - /* - * Try to recycle a dquot from the freelist. - */ - if ((dqp = xfs_qm_dqreclaim_one())) { - XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); - /* - * Just zero the core here. The rest will get - * reinitialized by caller. XXX we shouldn't even - * do this zero ... - */ - memset(&dqp->q_core, 0, sizeof(dqp->q_core)); - *O_dqpp = dqp; - return B_FALSE; - } - XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); + mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); + while (!list_empty(&xfs_Gqm->qm_dqfrlist)) { + if (nr_to_scan-- <= 0) + break; + dqp = list_first_entry(&xfs_Gqm->qm_dqfrlist, struct xfs_dquot, + q_freelist); + xfs_qm_dqreclaim_one(dqp, &dispose_list); } + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); - /* - * Allocate a brand new dquot on the kernel heap and return it - * to the caller to initialize. - */ - ASSERT(xfs_Gqm->qm_dqzone != NULL); - *O_dqpp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); - atomic_inc(&xfs_Gqm->qm_totaldquots); - - return B_TRUE; + while (!list_empty(&dispose_list)) { + dqp = list_first_entry(&dispose_list, struct xfs_dquot, + q_freelist); + list_del_init(&dqp->q_freelist); + xfs_qm_dqfree_one(dqp); + } +out: + return (xfs_Gqm->qm_dqfrlist_cnt / 100) * sysctl_vfs_cache_pressure; } - /* * Start a transaction and write the incore superblock changes to * disk. flags parameter indicates which fields have changed. diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 9b4f3ad..9a9b997 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -26,24 +26,12 @@ struct xfs_qm; struct xfs_inode; -extern uint ndquot; extern struct mutex xfs_Gqm_lock; extern struct xfs_qm *xfs_Gqm; extern kmem_zone_t *qm_dqzone; extern kmem_zone_t *qm_dqtrxzone; /* - * Ditto, for xfs_qm_dqreclaim_one. - */ -#define XFS_QM_RECLAIM_MAX_RESTARTS 4 - -/* - * Ideal ratio of free to in use dquots. Quota manager makes an attempt - * to keep this balance. - */ -#define XFS_QM_DQFREE_RATIO 2 - -/* * Dquot hashtable constants/threshold values. */ #define XFS_QM_HASHSIZE_LOW (PAGE_SIZE / sizeof(xfs_dqhash_t)) @@ -74,7 +62,6 @@ typedef struct xfs_qm { int qm_dqfrlist_cnt; atomic_t qm_totaldquots; /* total incore dquots */ uint qm_nrefs; /* file systems with quota on */ - int qm_dqfree_ratio;/* ratio of free to inuse dquots */ kmem_zone_t *qm_dqzone; /* dquot mem-alloc zone */ kmem_zone_t *qm_dqtrxzone; /* t_dqinfo of transactions */ } xfs_qm_t; @@ -143,7 +130,6 @@ extern int xfs_qm_quotacheck(xfs_mount_t *); extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t); /* dquot stuff */ -extern boolean_t xfs_qm_dqalloc_incore(xfs_dquot_t **); extern int xfs_qm_dqpurge_all(xfs_mount_t *, uint); extern void xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint); diff --git a/fs/xfs/xfs_qm_stats.c b/fs/xfs/xfs_qm_stats.c index 8671a0b..5729ba5 100644 --- a/fs/xfs/xfs_qm_stats.c +++ b/fs/xfs/xfs_qm_stats.c @@ -42,9 +42,9 @@ static int xqm_proc_show(struct seq_file *m, void *v) { /* maximum; incore; ratio free to inuse; freelist */ seq_printf(m, "%d\t%d\t%d\t%u\n", - ndquot, + 0, xfs_Gqm? atomic_read(&xfs_Gqm->qm_totaldquots) : 0, - xfs_Gqm? xfs_Gqm->qm_dqfree_ratio : 0, + 0, xfs_Gqm? xfs_Gqm->qm_dqfrlist_cnt : 0); return 0; } diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 6b6df58..bb134a8 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -733,11 +733,10 @@ DEFINE_EVENT(xfs_dquot_class, name, \ DEFINE_DQUOT_EVENT(xfs_dqadjust); DEFINE_DQUOT_EVENT(xfs_dqreclaim_want); DEFINE_DQUOT_EVENT(xfs_dqreclaim_dirty); -DEFINE_DQUOT_EVENT(xfs_dqreclaim_unlink); +DEFINE_DQUOT_EVENT(xfs_dqreclaim_busy); +DEFINE_DQUOT_EVENT(xfs_dqreclaim_done); DEFINE_DQUOT_EVENT(xfs_dqattach_found); DEFINE_DQUOT_EVENT(xfs_dqattach_get); -DEFINE_DQUOT_EVENT(xfs_dqinit); -DEFINE_DQUOT_EVENT(xfs_dqreuse); DEFINE_DQUOT_EVENT(xfs_dqalloc); DEFINE_DQUOT_EVENT(xfs_dqtobp_read); DEFINE_DQUOT_EVENT(xfs_dqread); -- cgit v0.10.2 From 71f6bd4a23130cd2f4b036010c5790b1295290b9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 30 Jan 2012 12:25:24 +0100 Subject: PCI: workaround hard-wired bus number V2 Fixes PCI device detection on IBM xSeries IBM 3850 M2 / x3950 M2 when using ACPI resources (_CRS). This is default, a manual workaround (without this patch) would be pci=nocrs boot param. V2: Add dev_warn if the workaround is hit. This should reveal how common such setups are (via google) and point to possible problems if things are still not working as expected. -> Suggested by Jan Beulich. Cc: stable@vger.kernel.org Tested-by: garyhade@us.ibm.com Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7cc9e2f..71eac9c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -651,6 +651,11 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, dev_dbg(&dev->dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n", secondary, subordinate, pass); + if (!primary && (primary != bus->number) && secondary && subordinate) { + dev_warn(&dev->dev, "Primary bus is hard wired to 0\n"); + primary = bus->number; + } + /* Check if setup is sensible at all */ if (!pass && (primary != bus->number || secondary <= bus->number)) { -- cgit v0.10.2 From 8161fe91d8da34c1a231b6e2d4454b4b3179a5d4 Mon Sep 17 00:00:00 2001 From: Vaidyanathan Srinivasan Date: Thu, 2 Feb 2012 23:11:20 +0530 Subject: PCI: set pci sriov page size before reading SRIOV BAR For an SRIOV device, PCI_SRIOV_SYS_PGSIZE should be set before the PCI_SRIOV_BAR are queried. The sys pagesize defaults to 4k, so this change is required on powerpc box with 64k base page size. This is a regression caused due to moving SRIOV init to sriov_enable(). | commit afd24ece5c76af87f6fc477f2747b83a764f161c | Author: Ram Pai | PCI: delay configuration of SRIOV capability | The SRIOV capability, namely page size and total_vfs of a device are | configured during enumeration phase of the device. This can potentially | interfere with the PCI operations of the platform, if the IOV capability | of the device is not enabled. Signed-off-by: Vaidyanathan Srinivasan Acked-by: Ram Pai Signed-off-by: Jesse Barnes diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 0321fa3..0dab5ec 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -347,8 +347,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) return rc; } - pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); - iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; pci_cfg_access_lock(dev); pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); @@ -466,6 +464,7 @@ found: return -EIO; pgsz &= ~(pgsz - 1); + pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); nres = 0; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { -- cgit v0.10.2 From a87dfe14a78501c931a4d5481efff6a809aa907d Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 10 Feb 2012 05:43:36 +0000 Subject: netprio_cgroup: fix an off-by-one bug # mount -t cgroup xxx /mnt # mkdir /mnt/tmp # cat /mnt/tmp/net_prio.ifpriomap lo 0 eth0 0 virbr0 0 # echo 'lo 999' > /mnt/tmp/net_prio.ifpriomap # cat /mnt/tmp/net_prio.ifpriomap lo 999 eth0 0 virbr0 4101267344 We got weired output, because we exceeded the boundary of the array. We may even crash the kernel.. Origionally-authored-by: Li Zefan Signed-off-by: Li Zefan Signed-off-by: Neil Horman CC: "David S. Miller" Signed-off-by: David S. Miller diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 9ae183a..72c6387 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -108,7 +108,7 @@ static void extend_netdev_table(struct net_device *dev, u32 new_len) static void update_netdev_tables(void) { struct net_device *dev; - u32 max_len = atomic_read(&max_prioidx); + u32 max_len = atomic_read(&max_prioidx) + 1; struct netprio_map *map; rtnl_lock(); -- cgit v0.10.2 From f5c38208d32412d72b97a4f0d44af0eb39feb20b Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 10 Feb 2012 05:43:37 +0000 Subject: netprio_cgroup: don't allocate prio table when a device is registered So we delay the allocation till the priority is set through cgroup, and this makes skb_update_priority() faster when it's not set. This also eliminates an off-by-one bug similar with the one fixed in the previous patch. Origionally-authored-by: Li Zefan Signed-off-by: Li Zefan Signed-off-by: Neil Horman CC: "David S. Miller" Signed-off-by: David S. Miller diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 72c6387..4dacc44 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -271,7 +271,6 @@ static int netprio_device_event(struct notifier_block *unused, { struct net_device *dev = ptr; struct netprio_map *old; - u32 max_len = atomic_read(&max_prioidx); /* * Note this is called with rtnl_lock held so we have update side @@ -279,11 +278,6 @@ static int netprio_device_event(struct notifier_block *unused, */ switch (event) { - - case NETDEV_REGISTER: - if (max_len) - extend_netdev_table(dev, max_len); - break; case NETDEV_UNREGISTER: old = rtnl_dereference(dev->priomap); RCU_INIT_POINTER(dev->priomap, NULL); -- cgit v0.10.2 From 2b73bc65e2771372c818db7955709c8caedbf8b9 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 10 Feb 2012 05:43:38 +0000 Subject: netprio_cgroup: fix wrong memory access when NETPRIO_CGROUP=m When the netprio_cgroup module is not loaded, net_prio_subsys_id is -1, and so sock_update_prioidx() accesses cgroup_subsys array with negative index subsys[-1]. Make the code resembles cls_cgroup code, which is bug free. Origionally-authored-by: Li Zefan Signed-off-by: Li Zefan Signed-off-by: Neil Horman CC: "David S. Miller" Signed-off-by: David S. Miller diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h index 7b2d431..d58fdec 100644 --- a/include/net/netprio_cgroup.h +++ b/include/net/netprio_cgroup.h @@ -37,19 +37,51 @@ extern int net_prio_subsys_id; extern void sock_update_netprioidx(struct sock *sk); -static inline struct cgroup_netprio_state - *task_netprio_state(struct task_struct *p) +#if IS_BUILTIN(CONFIG_NETPRIO_CGROUP) + +static inline u32 task_netprioidx(struct task_struct *p) { -#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) - return container_of(task_subsys_state(p, net_prio_subsys_id), - struct cgroup_netprio_state, css); -#else - return NULL; -#endif + struct cgroup_netprio_state *state; + u32 idx; + + rcu_read_lock(); + state = container_of(task_subsys_state(p, net_prio_subsys_id), + struct cgroup_netprio_state, css); + idx = state->prioidx; + rcu_read_unlock(); + return idx; +} + +#elif IS_MODULE(CONFIG_NETPRIO_CGROUP) + +static inline u32 task_netprioidx(struct task_struct *p) +{ + struct cgroup_netprio_state *state; + int subsys_id; + u32 idx = 0; + + rcu_read_lock(); + subsys_id = rcu_dereference_index_check(net_prio_subsys_id, + rcu_read_lock_held()); + if (subsys_id >= 0) { + state = container_of(task_subsys_state(p, subsys_id), + struct cgroup_netprio_state, css); + idx = state->prioidx; + } + rcu_read_unlock(); + return idx; } #else +static inline u32 task_netprioidx(struct task_struct *p) +{ + return 0; +} + +#endif /* CONFIG_NETPRIO_CGROUP */ + +#else #define sock_update_netprioidx(sk) #endif diff --git a/net/core/sock.c b/net/core/sock.c index 3e81fd2..02f8dfe 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1171,13 +1171,10 @@ EXPORT_SYMBOL(sock_update_classid); void sock_update_netprioidx(struct sock *sk) { - struct cgroup_netprio_state *state; if (in_interrupt()) return; - rcu_read_lock(); - state = task_netprio_state(current); - sk->sk_cgrp_prioidx = state ? state->prioidx : 0; - rcu_read_unlock(); + + sk->sk_cgrp_prioidx = task_netprioidx(current); } EXPORT_SYMBOL_GPL(sock_update_netprioidx); #endif -- cgit v0.10.2 From 5dc7883f2a7c25f8df40d7479687153558cd531b Mon Sep 17 00:00:00 2001 From: Li Wei Date: Thu, 9 Feb 2012 21:15:25 +0000 Subject: ipv4: Fix wrong order of ip_rt_get_source() and update iph->daddr. This patch fix a bug which introduced by commit ac8a4810 (ipv4: Save nexthop address of LSRR/SSRR option to IPCB.).In that patch, we saved the nexthop of SRR in ip_option->nexthop and update iph->daddr until we get to ip_forward_options(), but we need to update it before ip_rt_get_source(), otherwise we may get a wrong src. Signed-off-by: Li Wei Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 1e60f76..42dd1a9 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -573,8 +573,8 @@ void ip_forward_options(struct sk_buff *skb) } if (srrptr + 3 <= srrspace) { opt->is_changed = 1; - ip_rt_get_source(&optptr[srrptr-1], skb, rt); ip_hdr(skb)->daddr = opt->nexthop; + ip_rt_get_source(&optptr[srrptr-1], skb, rt); optptr[2] = srrptr+4; } else if (net_ratelimit()) printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); -- cgit v0.10.2 From 70620c46ac2b45c24b0f22002fdf5ddd1f7daf81 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 10 Feb 2012 04:07:11 +0000 Subject: net: Don't proxy arp respond if iif == rt->dst.dev if private VLAN is disabled Commit 653241 (net: RFC3069, private VLAN proxy arp support) changed the behavior of arp proxy to send arp replies back out on the interface the request came in even if the private VLAN feature is disabled. Previously we checked rt->dst.dev != skb->dev for in scenarios, when proxy arp is enabled on for the netdevice and also when individual proxy neighbour entries have been added. This patch adds the check back for the pneigh_lookup() scenario. Signed-off-by: Thomas Graf Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 59402be..63e4989 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -863,7 +863,8 @@ static int arp_process(struct sk_buff *skb) if (addr_type == RTN_UNICAST && (arp_fwd_proxy(in_dev, dev, rt) || arp_fwd_pvlan(in_dev, dev, rt, sip, tip) || - pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) { + (rt->dst.dev != dev && + pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); if (n) neigh_release(n); -- cgit v0.10.2 From 3682a3946d2b0bad621db871e3bead83e523a238 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 4 Feb 2012 22:55:00 -0800 Subject: PCI: Fix pci cardbus removal During test busn_res allocation with cardbus, found pci card removal is not working anymore, and it turns out it is broken by: |commit 79cc9601c3e42b4f0650fe7e69132ebce7ab48f9 |Date: Tue Nov 22 21:06:53 2011 -0800 | | PCI: Only call pci_stop_bus_device() one time for child devices at remove The above changed the behavior of pci_remove_behind_bridge that yenta_cardbus depended on. So restore the old behavoir of pci_remove_behind_bridge (which requires stopping and removing of all devices) by: 1. rename pci_remove_behind_bridge to __pci_remove_behind_bridge, and let __pci_remove_bus_device() call it instead. 2. add pci_stop_behind_bridge that will stop devices behind a bridge 3. add back pci_remove_behind_bridge that will stop and remove devices under bridge. -v2: update commit description a little bit. Tested-by: Dominik Brodowski Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 6def362..ef8b18c 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -77,6 +77,7 @@ void pci_remove_bus(struct pci_bus *pci_bus) } EXPORT_SYMBOL(pci_remove_bus); +static void __pci_remove_behind_bridge(struct pci_dev *dev); /** * pci_remove_bus_device - remove a PCI device and any children * @dev: the device to remove @@ -94,7 +95,7 @@ static void __pci_remove_bus_device(struct pci_dev *dev) if (dev->subordinate) { struct pci_bus *b = dev->subordinate; - pci_remove_behind_bridge(dev); + __pci_remove_behind_bridge(dev); pci_remove_bus(b); dev->subordinate = NULL; } @@ -107,6 +108,24 @@ void pci_remove_bus_device(struct pci_dev *dev) __pci_remove_bus_device(dev); } +static void __pci_remove_behind_bridge(struct pci_dev *dev) +{ + struct list_head *l, *n; + + if (dev->subordinate) + list_for_each_safe(l, n, &dev->subordinate->devices) + __pci_remove_bus_device(pci_dev_b(l)); +} + +static void pci_stop_behind_bridge(struct pci_dev *dev) +{ + struct list_head *l, *n; + + if (dev->subordinate) + list_for_each_safe(l, n, &dev->subordinate->devices) + pci_stop_bus_device(pci_dev_b(l)); +} + /** * pci_remove_behind_bridge - remove all devices behind a PCI bridge * @dev: PCI bridge device @@ -117,11 +136,8 @@ void pci_remove_bus_device(struct pci_dev *dev) */ void pci_remove_behind_bridge(struct pci_dev *dev) { - struct list_head *l, *n; - - if (dev->subordinate) - list_for_each_safe(l, n, &dev->subordinate->devices) - __pci_remove_bus_device(pci_dev_b(l)); + pci_stop_behind_bridge(dev); + __pci_remove_behind_bridge(dev); } static void pci_stop_bus_devices(struct pci_bus *bus) -- cgit v0.10.2 From eae66b50c760233fad526edf4a0d327be17a055d Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 8 Feb 2012 12:53:49 -0800 Subject: drm/i915: gen7: implement rczunit workaround This is yet another workaround related to clock gating which we need on Ivy Bridge. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41353 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44610 Tested-by: Eugeni Dodonov Signed-off-by: Eugeni Dodonov Signed-off-by: Kenneth Graunke Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index c3afb78..80fd6b5 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3618,6 +3618,7 @@ #define GT_FIFO_NUM_RESERVED_ENTRIES 20 #define GEN6_UCGCTL2 0x9404 +# define GEN6_RCZUNIT_CLOCK_GATE_DISABLE (1 << 13) # define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE (1 << 12) # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b3b51c4..643c525 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8461,6 +8461,11 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(WM2_LP_ILK, 0); I915_WRITE(WM1_LP_ILK, 0); + /* According to the spec, bit 13 (RCZUNIT) must be set on IVB. + * This implements the WaDisableRCZUnitClockGating workaround. + */ + I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE); + I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE); I915_WRITE(IVB_CHICKEN3, -- cgit v0.10.2 From e4e0c058a19c41150d12ad2d3023b3cf09c5de67 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 8 Feb 2012 12:53:50 -0800 Subject: drm/i915: gen7: Implement an L3 caching workaround. This adds two cache-related workarounds for Ivy Bridge which can lead to 3D ring hangs and corruptions. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41353 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44610 Tested-by: Eugeni Dodonov Signed-off-by: Eugeni Dodonov Signed-off-by: Kenneth Graunke Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 80fd6b5..ca4737e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3028,6 +3028,13 @@ #define DISP_TILE_SURFACE_SWIZZLING (1<<13) #define DISP_FBC_WM_DIS (1<<15) +/* GEN7 chicken */ +#define GEN7_L3CNTLREG1 0xB01C +#define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C4FFF8C + +#define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030 +#define GEN7_WA_L3_CHICKEN_MODE 0x20000000 + /* PCH */ /* south display engine interrupt */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 643c525..928840a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8472,6 +8472,12 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); + /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + I915_WRITE(GEN7_L3CNTLREG1, + GEN7_WA_FOR_GEN7_L3_CONTROL); + I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, + GEN7_WA_L3_CHICKEN_MODE); + for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | -- cgit v0.10.2 From db099c8f963fe656108e0a068274c5580a17f69b Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 8 Feb 2012 12:53:51 -0800 Subject: drm/i915: gen7: work around a system hang on IVB This adds the workaround for WaCatErrorRejectionIssue which could result in a system hang. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41353 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44610 Tested-by: Eugeni Dodonov Reviewed-by: Kenneth Graunke Signed-off-by: Eugeni Dodonov Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ca4737e..4e1ee90 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3035,6 +3035,10 @@ #define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030 #define GEN7_WA_L3_CHICKEN_MODE 0x20000000 +/* WaCatErrorRejectionIssue */ +#define GEN7_SQ_CHICKEN_MBCUNIT_CONFIG 0x9030 +#define GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB (1<<11) + /* PCH */ /* south display engine interrupt */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 928840a..a527615 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8478,6 +8478,11 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); + /* This is required by WaCatErrorRejectionIssue */ + I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, + I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | + GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); + for_each_pipe(pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | -- cgit v0.10.2 From d71de14ddf423ccc9a2e3f7e37553c99ead20d7c Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Wed, 8 Feb 2012 12:53:52 -0800 Subject: drm/i915: gen7: Disable the RHWO optimization as it can cause GPU hangs. The BSpec Workarounds page states that bits 10 and 26 must be set to avoid 3D ring hangs. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41353 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44610 Tested-by: Eugeni Dodonov Signed-off-by: Kenneth Graunke Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4e1ee90..03c53fc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3029,6 +3029,9 @@ #define DISP_FBC_WM_DIS (1<<15) /* GEN7 chicken */ +#define GEN7_COMMON_SLICE_CHICKEN1 0x7010 +# define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26)) + #define GEN7_L3CNTLREG1 0xB01C #define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C4FFF8C diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a527615..d9b042b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8472,6 +8472,10 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); + /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, + GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); + /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ I915_WRITE(GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL); -- cgit v0.10.2 From d9f5343e35d9138432657202afa8e3ddb2ade360 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jan 2012 16:28:54 -0800 Subject: USB: Remove duplicate USB 3.0 hub feature #defines. Somehow we ended up with duplicate hub feature #defines in ch11.h. Tatyana Brokhman first created the USB 3.0 hub feature macros in 2.6.38 with commit 0eadcc09203349b11ca477ec367079b23d32ab91 "usb: USB3.0 ch11 definitions". In 2.6.39, I modified a patch from John Youn that added similar macros in a different place in the same file, and committed dbe79bbe9dcb22cb3651c46f18943477141ca452 "USB 3.0 Hub Changes". Some of the #defines used different names for the same values. Others used exactly the same names with the same values, like these gems: #define USB_PORT_FEAT_BH_PORT_RESET 28 ... #define USB_PORT_FEAT_BH_PORT_RESET 28 According to my very geeky husband (who looked it up in the C99 spec), it is allowed to have object-like macros with duplicate names as long as the replacement list is exactly the same. However, he recalled that some compilers will give warnings when they find duplicate macros. It's probably best to remove the duplicates in the stable tree, so that the code compiles for everyone. The macros are now fixed to move the feature requests that are specific to USB 3.0 hubs into a new section (out of the USB 2.0 hub feature section), and use the most common macro name. This patch should be backported to 2.6.39. Signed-off-by: Sarah Sharp Cc: Tatyana Brokhman Cc: John Youn Cc: Jamey Sharp Cc: stable@vger.kernel.org diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 31fdb4c..0b83acd 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -61,12 +61,6 @@ #define USB_PORT_FEAT_TEST 21 #define USB_PORT_FEAT_INDICATOR 22 #define USB_PORT_FEAT_C_PORT_L1 23 -#define USB_PORT_FEAT_C_PORT_LINK_STATE 25 -#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26 -#define USB_PORT_FEAT_PORT_REMOTE_WAKE_MASK 27 -#define USB_PORT_FEAT_BH_PORT_RESET 28 -#define USB_PORT_FEAT_C_BH_PORT_RESET 29 -#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30 /* * Port feature selectors added by USB 3.0 spec. @@ -75,8 +69,8 @@ #define USB_PORT_FEAT_LINK_STATE 5 #define USB_PORT_FEAT_U1_TIMEOUT 23 #define USB_PORT_FEAT_U2_TIMEOUT 24 -#define USB_PORT_FEAT_C_LINK_STATE 25 -#define USB_PORT_FEAT_C_CONFIG_ERR 26 +#define USB_PORT_FEAT_C_PORT_LINK_STATE 25 +#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26 #define USB_PORT_FEAT_REMOTE_WAKE_MASK 27 #define USB_PORT_FEAT_BH_PORT_RESET 28 #define USB_PORT_FEAT_C_BH_PORT_RESET 29 -- cgit v0.10.2 From 3278a55a1aebe2bbd47fbb5196209e5326a88b56 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 9 Feb 2012 14:43:44 -0800 Subject: xhci: Fix oops caused by more USB2 ports than USB3 ports. The code to set the device removable bits in the USB 2.0 roothub descriptor was accidentally looking at the USB 3.0 port registers instead of the USB 2.0 registers. This can cause an oops if there are more USB 2.0 registers than USB 3.0 registers. This should be backported to kernels as old as 2.6.39, that contain the commit 4bbb0ace9a3de8392527e3c87926309d541d3b00 "xhci: Return a USB 3.0 hub descriptor for USB3 roothub." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 35e257f..557b6f3 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -93,7 +93,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb3_ports[i]); + portsc = xhci_readl(xhci, xhci->usb2_ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ -- cgit v0.10.2 From d8c66c5d59247e25a69428aced0b79d33b9c66d6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 11 Feb 2012 12:37:25 +0100 Subject: block: fix lockdep warning on io_context release put_io_context() 11a3122f6c "block: strip out locking optimization in put_io_context()" removed ioc_lock depth lockdep annoation along with locking optimization; however, while recursing from put_io_context() is no longer possible, ioc_release_fn() may still end up putting the last reference of another ioc through elevator, which wlil grab ioc->lock triggering spurious (as the ioc is always different one) A-A deadlock warning. As this can only happen one time from ioc_release_fn(), using non-zero subclass from ioc_release_fn() is enough. Use subclass 1. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 9884fd7..8b782a6 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -80,8 +80,15 @@ static void ioc_release_fn(struct work_struct *work) struct io_context *ioc = container_of(work, struct io_context, release_work); struct request_queue *last_q = NULL; + unsigned long flags; - spin_lock_irq(&ioc->lock); + /* + * Exiting icq may call into put_io_context() through elevator + * which will trigger lockdep warning. The ioc's are guaranteed to + * be different, use a different locking subclass here. Use + * irqsave variant as there's no spin_lock_irq_nested(). + */ + spin_lock_irqsave_nested(&ioc->lock, flags, 1); while (!hlist_empty(&ioc->icq_list)) { struct io_cq *icq = hlist_entry(ioc->icq_list.first, @@ -103,15 +110,15 @@ static void ioc_release_fn(struct work_struct *work) */ if (last_q) { spin_unlock(last_q->queue_lock); - spin_unlock_irq(&ioc->lock); + spin_unlock_irqrestore(&ioc->lock, flags); blk_put_queue(last_q); } else { - spin_unlock_irq(&ioc->lock); + spin_unlock_irqrestore(&ioc->lock, flags); } last_q = this_q; - spin_lock_irq(this_q->queue_lock); - spin_lock(&ioc->lock); + spin_lock_irqsave(this_q->queue_lock, flags); + spin_lock_nested(&ioc->lock, 1); continue; } ioc_exit_icq(icq); @@ -119,10 +126,10 @@ static void ioc_release_fn(struct work_struct *work) if (last_q) { spin_unlock(last_q->queue_lock); - spin_unlock_irq(&ioc->lock); + spin_unlock_irqrestore(&ioc->lock, flags); blk_put_queue(last_q); } else { - spin_unlock_irq(&ioc->lock); + spin_unlock_irqrestore(&ioc->lock, flags); } kmem_cache_free(iocontext_cachep, ioc); -- cgit v0.10.2 From ba8c4dc99808b1de809a0eda71a32f26efdafd92 Mon Sep 17 00:00:00 2001 From: Roy Zang Date: Fri, 13 Jan 2012 15:02:01 +0800 Subject: mmc: esdhc: fix errors when booting kernel on Freescale eSDHC version 2.3 When eSDHC module is enabled on P5020/P3041/P2041/P1010 with eSDHC version 2.3, there is following errors: mmc0: Timeout waiting for hardware interrupt. mmc0: error -110 whilst initialising SD card mmc0: Unexpected interrupt 0x02000000. mmc0: Timeout waiting for hardware interrupt. mmc0: error -110 whilst initialising SD card mmc0: Unexpected interrupt 0x02000000. It is because eSDHC controller has different bit setting for PROCTL register at 0x28 comparing SD specification. This patch sets DMAS bits correctly for byte operation and does not change the default value of other field of PROCTL register. For other FSL chips, such as MPC8536/P2020, PROCTL[DMAS] bits are reserved and even if they are set to wrong bits, it will not take effective. Signed-off-by: Roy Zang Acked-by: Anton Vorontsov Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index ff4adc0..5d876ff 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -38,6 +38,23 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) int base = reg & ~0x3; int shift = (reg & 0x3) * 8; u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; + + /* + * "DMA select" locates at offset 0x28 in SD specification, but on + * P5020 or P3041, it locates at 0x29. + */ + if (reg == SDHCI_HOST_CONTROL) { + u32 dma_bits; + + dma_bits = in_be32(host->ioaddr + reg); + /* DMA select is 22,23 bits in Protocol Control Register */ + dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; + + /* fixup the result */ + ret &= ~SDHCI_CTRL_DMA_MASK; + ret |= dma_bits; + } + return ret; } @@ -56,6 +73,21 @@ static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) { + /* + * "DMA select" location is offset 0x28 in SD specification, but on + * P5020 or P3041, it's located at 0x29. + */ + if (reg == SDHCI_HOST_CONTROL) { + u32 dma_bits; + + /* DMA select is 22,23 bits in Protocol Control Register */ + dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; + clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, + dma_bits); + val &= ~SDHCI_CTRL_DMA_MASK; + val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; + } + /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ if (reg == SDHCI_HOST_CONTROL) val &= ~ESDHC_HOST_CONTROL_RES; -- cgit v0.10.2 From cc9a672ee522d4805495b98680f4a3db5d0a0af9 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Sun, 12 Feb 2012 18:37:09 +0000 Subject: tcp: allow tcp_sacktag_one() to tag ranges not aligned with skbs This commit allows callers of tcp_sacktag_one() to pass in sequence ranges that do not align with skb boundaries, as tcp_shifted_skb() needs to do in an upcoming fix in this patch series. In fact, now tcp_sacktag_one() does not need to depend on an input skb at all, which makes its semantics and dependencies more clear. Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 976034f..4e8a81f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1307,25 +1307,26 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, return in_sack; } -static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk, - struct tcp_sacktag_state *state, +/* Mark the given newly-SACKed range as such, adjusting counters and hints. */ +static u8 tcp_sacktag_one(struct sock *sk, + struct tcp_sacktag_state *state, u8 sacked, + u32 start_seq, u32 end_seq, int dup_sack, int pcount) { struct tcp_sock *tp = tcp_sk(sk); - u8 sacked = TCP_SKB_CB(skb)->sacked; int fack_count = state->fack_count; /* Account D-SACK for retransmitted packet. */ if (dup_sack && (sacked & TCPCB_RETRANS)) { if (tp->undo_marker && tp->undo_retrans && - after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker)) + after(end_seq, tp->undo_marker)) tp->undo_retrans--; if (sacked & TCPCB_SACKED_ACKED) state->reord = min(fack_count, state->reord); } /* Nothing to do; acked frame is about to be dropped (was ACKed). */ - if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) + if (!after(end_seq, tp->snd_una)) return sacked; if (!(sacked & TCPCB_SACKED_ACKED)) { @@ -1344,13 +1345,13 @@ static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk, /* New sack for not retransmitted frame, * which was in hole. It is reordering. */ - if (before(TCP_SKB_CB(skb)->seq, + if (before(start_seq, tcp_highest_sack_seq(tp))) state->reord = min(fack_count, state->reord); /* SACK enhanced F-RTO (RFC4138; Appendix B) */ - if (!after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark)) + if (!after(end_seq, tp->frto_highmark)) state->flag |= FLAG_ONLY_ORIG_SACKED; } @@ -1368,8 +1369,7 @@ static u8 tcp_sacktag_one(const struct sk_buff *skb, struct sock *sk, /* Lost marker hint past SACKed? Tweak RFC3517 cnt */ if (!tcp_is_fack(tp) && (tp->lost_skb_hint != NULL) && - before(TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(tp->lost_skb_hint)->seq)) + before(start_seq, TCP_SKB_CB(tp->lost_skb_hint)->seq)) tp->lost_cnt_hint += pcount; if (fack_count > tp->fackets_out) @@ -1425,7 +1425,11 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, } /* We discard results */ - tcp_sacktag_one(skb, sk, state, dup_sack, pcount); + tcp_sacktag_one(sk, state, + TCP_SKB_CB(skb)->sacked, + TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(skb)->end_seq, + dup_sack, pcount); /* Difference in this won't matter, both ACKed by the same cumul. ACK */ TCP_SKB_CB(prev)->sacked |= (TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS); @@ -1664,10 +1668,14 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, break; if (in_sack) { - TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(skb, sk, - state, - dup_sack, - tcp_skb_pcount(skb)); + TCP_SKB_CB(skb)->sacked = + tcp_sacktag_one(sk, + state, + TCP_SKB_CB(skb)->sacked, + TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(skb)->end_seq, + dup_sack, + tcp_skb_pcount(skb)); if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) -- cgit v0.10.2 From daef52bab1fd26e24e8e9578f8fb33ba1d0cb412 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Sun, 12 Feb 2012 18:37:10 +0000 Subject: tcp: fix range tcp_shifted_skb() passes to tcp_sacktag_one() Fix the newly-SACKed range to be the range of newly-shifted bytes. Previously - since 832d11c5cd076abc0aa1eaf7be96c81d1a59ce41 - tcp_shifted_skb() incorrectly called tcp_sacktag_one() with the start and end sequence numbers of the skb it passes in set to the range just beyond the range that is newly-SACKed. This commit also removes a special-case adjustment to lost_cnt_hint in tcp_shifted_skb() since the pre-existing adjustment of lost_cnt_hint in tcp_sacktag_one() now properly handles this things now that the correct start sequence number is passed in. Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4e8a81f..8116d06 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1388,6 +1388,9 @@ static u8 tcp_sacktag_one(struct sock *sk, return sacked; } +/* Shift newly-SACKed bytes from this skb to the immediately previous + * already-SACKed sk_buff. Mark the newly-SACKed bytes as such. + */ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, struct tcp_sacktag_state *state, unsigned int pcount, int shifted, int mss, @@ -1395,12 +1398,11 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *prev = tcp_write_queue_prev(sk, skb); + u32 start_seq = TCP_SKB_CB(skb)->seq; /* start of newly-SACKed */ + u32 end_seq = start_seq + shifted; /* end of newly-SACKed */ BUG_ON(!pcount); - if (skb == tp->lost_skb_hint) - tp->lost_cnt_hint += pcount; - TCP_SKB_CB(prev)->end_seq += shifted; TCP_SKB_CB(skb)->seq += shifted; @@ -1424,12 +1426,11 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, skb_shinfo(skb)->gso_type = 0; } - /* We discard results */ - tcp_sacktag_one(sk, state, - TCP_SKB_CB(skb)->sacked, - TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(skb)->end_seq, - dup_sack, pcount); + /* Adjust counters and hints for the newly sacked sequence range but + * discard the return value since prev is already marked. + */ + tcp_sacktag_one(sk, state, TCP_SKB_CB(skb)->sacked, + start_seq, end_seq, dup_sack, pcount); /* Difference in this won't matter, both ACKed by the same cumul. ACK */ TCP_SKB_CB(prev)->sacked |= (TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS); -- cgit v0.10.2 From e9c6c5dfd1dba03802b98aea518c08ab48cbbcc4 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 8 Feb 2012 09:53:44 -0700 Subject: ep93xx: fix build of vision_ep93xx.c Fix build breakage due to the following commits: Commit bd5f12a24766c1f299def0a78b008d4746f528f2 ARM: 7042/3: mach-ep93xx: break out GPIO driver specifics Commit 257af9f9725aa8a863b306659208a031135d59e7 ARM: 7041/1: gpio-ep93xx: hookup the to_irq callback in the driver The vision_ep9307 machine uses the ep93xx build-in gpios and needs to include to pickup the defines. The gpio_to_irq() call is now a callback to the gpio-ep93xx.c driver and cannot be used as a constant initializer for the .irq member of struct i2c_board_info. Signed-off-by: Hartley Sweeten Acked-by: Ryan Mallon Acked-by: Mika Westerberg Signed-off-by: Arnd Bergmann diff --git a/arch/arm/mach-ep93xx/vision_ep9307.c b/arch/arm/mach-ep93xx/vision_ep9307.c index 03dd401..d5fb44f 100644 --- a/arch/arm/mach-ep93xx/vision_ep9307.c +++ b/arch/arm/mach-ep93xx/vision_ep9307.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -153,7 +154,6 @@ static struct i2c_board_info vision_i2c_info[] __initdata = { }, { I2C_BOARD_INFO("pca9539", 0x74), .platform_data = &pca953x_74_gpio_data, - .irq = gpio_to_irq(EP93XX_GPIO_LINE_F(7)), }, { I2C_BOARD_INFO("pca9539", 0x75), .platform_data = &pca953x_75_gpio_data, @@ -348,6 +348,8 @@ static void __init vision_init_machine(void) "pca9539:74")) pr_warn("cannot request interrupt gpio for pca9539:74\n"); + vision_i2c_info[1].irq = gpio_to_irq(EP93XX_GPIO_LINE_F(7)); + ep93xx_register_i2c(&vision_i2c_gpio_data, vision_i2c_info, ARRAY_SIZE(vision_i2c_info)); ep93xx_register_spi(&vision_spi_master, vision_spi_board_info, -- cgit v0.10.2 From e6fa35aa9c4e4a32e616d307986283c4070cff78 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 09:58:57 +0000 Subject: ARM: omap: fix prm44xx.c OMAP44XX_IRQ_PRCM build error When CONFIG_OF is disabled, the compile fails with: arch/arm/mach-omap2/prm44xx.c:41: error: 'OMAP44XX_IRQ_PRCM' undeclared here (not in a function) Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index 33dd655..a1d6154 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c @@ -19,6 +19,7 @@ #include "common.h" #include +#include #include #include "vp.h" -- cgit v0.10.2 From 2d5b4790b1e7cffb8987e535d4969d10b62f3163 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:13:02 +0000 Subject: ARM: omap: fix vc.c PMIC error message While testing on my OMAP3430 platform, this error message was emitted: omap_vc_init_channel: PMIC info requried to configure vc forvdd_core not populated.Hence cannot initialize vc Trying to find this message was difficult because it was wrapped across several lines. It also mis-spells "required", doesn't read very well, and has spaces lacking. Let's replace it with a more concise: omap_vc_init_channel: No PMIC info for vdd_core While we're here, fix a simple spelling error in a comment. Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 031d116..a7da3da 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -247,7 +247,7 @@ static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) * omap_vc_i2c_init - initialize I2C interface to PMIC * @voltdm: voltage domain containing VC data * - * Use PMIC supplied seetings for I2C high-speed mode and + * Use PMIC supplied settings for I2C high-speed mode and * master code (if set) and program the VC I2C configuration * register. * @@ -292,9 +292,7 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) u32 val; if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { - pr_err("%s: PMIC info requried to configure vc for" - "vdd_%s not populated.Hence cannot initialize vc\n", - __func__, voltdm->name); + pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); return; } -- cgit v0.10.2 From 0bf68f53f164e169c2bc77f707338fc595b6ccfc Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:23:43 +0000 Subject: ARM: omap: fix uninformative vc/i2c configuration error message On my OMAP4 platform, I'm getting this error message repeated several times at boot: omap_vc_i2c_init: I2C config for all channels must match. omap_vc_i2c_init: I2C config for all channels must match. This doesn't help identify what the problem is. Fix this message to be more informative: omap_vc_i2c_init: I2C config for vdd_iva does not match other channels (0). omap_vc_i2c_init: I2C config for vdd_mpu does not match other channels (0). This allows us to identify which voltage domains have a problem, and what the I2C configuration state (a boolean, i2c_high_speed) setting being used actually is. From this we find that omap4_core_pmic has i2c_high_speed false, but omap4_iva_pmic and omap4_mpu_pmic both have it set true. Acked-by: Tony Lindgren Acked-by: Kevin Hilman Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index a7da3da..175b7d8 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -265,8 +265,8 @@ static void __init omap_vc_i2c_init(struct voltagedomain *voltdm) if (initialized) { if (voltdm->pmic->i2c_high_speed != i2c_high_speed) - pr_warn("%s: I2C config for all channels must match.", - __func__); + pr_warn("%s: I2C config for vdd_%s does not match other channels (%u).", + __func__, voltdm->name, i2c_high_speed); return; } -- cgit v0.10.2 From a0bb10e86642064f6cec9e23f523127e5cfbe609 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:00:34 +0000 Subject: ARM: omap: fix section mismatch errors in TWL PMIC driver WARNING: drivers/mfd/built-in.o(.devinit.text+0x258): Section mismatch in reference from the function twl_probe() to the function .init.text:twl4030_power_init() The function __devinit twl_probe() references a function __init twl4030_power_init(). If twl4030_power_init is only used by twl_probe then annotate twl4030_power_init with a matching annotation. twl4030_power_init() references other __init marked functions, so these too must become __devinit. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index d905f51..79ca33d 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -124,7 +124,7 @@ static u8 res_config_addrs[] = { [RES_MAIN_REF] = 0x94, }; -static int __init twl4030_write_script_byte(u8 address, u8 byte) +static int __devinit twl4030_write_script_byte(u8 address, u8 byte) { int err; @@ -138,7 +138,7 @@ out: return err; } -static int __init twl4030_write_script_ins(u8 address, u16 pmb_message, +static int __devinit twl4030_write_script_ins(u8 address, u16 pmb_message, u8 delay, u8 next) { int err; @@ -158,7 +158,7 @@ out: return err; } -static int __init twl4030_write_script(u8 address, struct twl4030_ins *script, +static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script, int len) { int err; @@ -183,7 +183,7 @@ static int __init twl4030_write_script(u8 address, struct twl4030_ins *script, return err; } -static int __init twl4030_config_wakeup3_sequence(u8 address) +static int __devinit twl4030_config_wakeup3_sequence(u8 address) { int err; u8 data; @@ -208,7 +208,7 @@ out: return err; } -static int __init twl4030_config_wakeup12_sequence(u8 address) +static int __devinit twl4030_config_wakeup12_sequence(u8 address) { int err = 0; u8 data; @@ -262,7 +262,7 @@ out: return err; } -static int __init twl4030_config_sleep_sequence(u8 address) +static int __devinit twl4030_config_sleep_sequence(u8 address) { int err; @@ -276,7 +276,7 @@ static int __init twl4030_config_sleep_sequence(u8 address) return err; } -static int __init twl4030_config_warmreset_sequence(u8 address) +static int __devinit twl4030_config_warmreset_sequence(u8 address) { int err; u8 rd_data; @@ -324,7 +324,7 @@ out: return err; } -static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig) +static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfig) { int rconfig_addr; int err; @@ -416,7 +416,7 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig) return 0; } -static int __init load_twl4030_script(struct twl4030_script *tscript, +static int __devinit load_twl4030_script(struct twl4030_script *tscript, u8 address) { int err; @@ -527,7 +527,7 @@ void twl4030_power_off(void) pr_err("TWL4030 Unable to power off\n"); } -void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts) +void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts) { int err = 0; int i; -- cgit v0.10.2 From 27d8d3bf06c574b8bc88d1cf50ed3e3b2c40935b Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:18:34 +0000 Subject: ARM: omap: fix section mismatch warning in mux.c WARNING: arch/arm/mach-omap2/built-in.o(.text+0x15a4): Section mismatch in reference from the function omap_mux_init_signals() to the function .init.text:omap_mux_init_signal() The function omap_mux_init_signals() references the function __init omap_mux_init_signal(). This is often because omap_mux_init_signals lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index e1cc75d..f26b2fa 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -1094,8 +1094,8 @@ static void omap_mux_init_package(struct omap_mux *superset, omap_mux_package_init_balls(package_balls, superset); } -static void omap_mux_init_signals(struct omap_mux_partition *partition, - struct omap_board_mux *board_mux) +static void __init omap_mux_init_signals(struct omap_mux_partition *partition, + struct omap_board_mux *board_mux) { omap_mux_set_cmdline_signals(); omap_mux_write_array(partition, board_mux); @@ -1109,8 +1109,8 @@ static void omap_mux_init_package(struct omap_mux *superset, { } -static void omap_mux_init_signals(struct omap_mux_partition *partition, - struct omap_board_mux *board_mux) +static void __init omap_mux_init_signals(struct omap_mux_partition *partition, + struct omap_board_mux *board_mux) { } -- cgit v0.10.2 From d5de63f5f84d7def5e25a90e44234c58003876c1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 11:07:24 +0000 Subject: ARM: omap: preemptively fix section mismatch in omap4_sdp4430_wifi_mux_init() Found by review. omap4_sdp4430_wifi_mux_init() is called by an __init marked function, and only calls omap_mux_init_gpio() and omap_mux_init_signal() which are both also an __init marked functions. The only reason this doesn't issue a warning is because the compiler inlines omap4_sdp4430_wifi_mux_init() into omap4_sdp4430_wifi_init(). So, lets add the __init annotation to ensure this remains safe should the compiler choose not to inline. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 21fc876..f2682e3 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -851,7 +851,7 @@ static struct omap_board_mux board_mux[] __initdata = { #define board_mux NULL #endif -static void omap4_sdp4430_wifi_mux_init(void) +static void __init omap4_sdp4430_wifi_mux_init(void) { omap_mux_init_gpio(GPIO_WIFI_IRQ, OMAP_PIN_INPUT | OMAP_PIN_OFF_WAKEUPENABLE); -- cgit v0.10.2 From 45176f4cf7aa84da97c2c1e66569cb2e44cb97ce Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:34:01 +0000 Subject: ARM: omap: fix section mismatch warning for omap_secondary_startup() WARNING: vmlinux.o(.text+0x1c664): Section mismatch in reference from the function omap_secondary_startup() to the function .cpuinit.text:secondary_startup() The function omap_secondary_startup() references the function __cpuinit secondary_startup(). This is often because omap_secondary_startup lacks a __cpuinit annotation or the annotation of secondary_startup is wrong. Unfortunately, fixing this causes a new warning which is harder to solve: WARNING: arch/arm/mach-omap2/built-in.o(.text+0x5328): Section mismatch in reference from the function omap4_hotplug_cpu() to the function .cpuinit.text:omap_secondary_startup() The function omap4_hotplug_cpu() references the function __cpuinit omap_secondary_startup(). This is often because omap4_hotplug_cpu lacks a __cpuinit annotation or the annotation of omap_secondary_startup is wrong. because omap4_hotplug_cpu() is used by power management code as well, which may not end up using omap_secondary_startup(). Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S index b13ef7e..503ac77 100644 --- a/arch/arm/mach-omap2/omap-headsmp.S +++ b/arch/arm/mach-omap2/omap-headsmp.S @@ -18,6 +18,7 @@ #include #include + __CPUINIT /* * OMAP4 specific entry point for secondary CPU to jump from ROM * code. This routine also provides a holding flag into which -- cgit v0.10.2 From e3958fe05d78643ec6c0e651747b59361553a840 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:41:34 +0000 Subject: ARM: omap: fix section mismatch error for omap_4430sdp_display_init() WARNING: arch/arm/mach-omap2/built-in.o(.text+0xb798): Section mismatch in reference from the function omap_4430sdp_display_init() to the function .init.text:omap_display_init() The function omap_4430sdp_display_init() references the function __init omap_display_init(). This is often because omap_4430sdp_display_init lacks a __init annotation or the annotation of omap_display_init is wrong. Fix this by adding __init to omap_4430sdp_display_init(). Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index f2682e3..50d2412 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -814,7 +814,7 @@ static struct omap_dss_board_info sdp4430_dss_data = { .default_device = &sdp4430_lcd_device, }; -static void omap_4430sdp_display_init(void) +static void __init omap_4430sdp_display_init(void) { int r; -- cgit v0.10.2 From a98f77bb0a86914a39e3d0d001716965add5063e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:45:50 +0000 Subject: ARM: omap: fix section mismatch warning for sdp3430_twl_gpio_setup() WARNING: arch/arm/mach-omap2/built-in.o(.text+0xd0f0): Section mismatch in reference from the function sdp3430_twl_gpio_setup() to the function .init.text:omap2_hsmmc_init() The function sdp3430_twl_gpio_setup() references the function __init omap2_hsmmc_init(). This is often because sdp3430_twl_gpio_setup lacks a __init annotation or the annotation of omap2_hsmmc_init is wrong. sdp3430_twl_gpio_setup() is called via platform data from the gpio-twl4030 module, which can be inserted and removed at runtime. This makes sdp3430_twl_gpio_setup() callable at runtime, and prevents it being marked with an __init annotation. As it calls omap2_hsmmc_init() unconditionally, the only resolution to this warning is to remove the __init markings from omap2_hsmmc_init() and its called functions. This addresses the functions in hsmmc.c. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index ad0adb5..b40c288 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -293,8 +293,8 @@ static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller, } } -static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, - struct omap_mmc_platform_data *mmc) +static int omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, + struct omap_mmc_platform_data *mmc) { char *hc_name; @@ -430,7 +430,7 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, #define MAX_OMAP_MMC_HWMOD_NAME_LEN 16 -void __init omap_init_hsmmc(struct omap2_hsmmc_info *hsmmcinfo, int ctrl_nr) +void omap_init_hsmmc(struct omap2_hsmmc_info *hsmmcinfo, int ctrl_nr) { struct omap_hwmod *oh; struct platform_device *pdev; @@ -487,7 +487,7 @@ done: kfree(mmc_data); } -void __init omap2_hsmmc_init(struct omap2_hsmmc_info *controllers) +void omap2_hsmmc_init(struct omap2_hsmmc_info *controllers) { u32 reg; -- cgit v0.10.2 From 8930b4e3c31cf67140e6281879b28feac8381e29 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:51:39 +0000 Subject: ARM: omap: fix section mismatch warnings in mux.c caused by hsmmc.c The previous commit causes new section mismatch warnings: WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb30): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_gpio() The function omap_init_hsmmc() references the function __init omap_mux_init_gpio(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_gpio is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb4c): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_gpio() The function omap_init_hsmmc() references the function __init omap_mux_init_gpio(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_gpio is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb60): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb6c): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb78): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb90): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdb9c): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdba8): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdbc0): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdbcc): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdbd8): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdbf8): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc04): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc10): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc28): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc34): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc40): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc58): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc64): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc70): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. WARNING: arch/arm/mach-omap2/built-in.o(.text+0xdc7c): Section mismatch in reference from the function omap_init_hsmmc() to the function .init.text:omap_mux_init_signal() The function omap_init_hsmmc() references the function __init omap_mux_init_signal(). This is often because omap_init_hsmmc lacks a __init annotation or the annotation of omap_mux_init_signal is wrong. Again, as for omap2_hsmmc_init(), these functions are callable at runtime via the gpio-twl4030.c driver, and so these can't be marked __init. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index f26b2fa..fb8bc9f 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -100,8 +100,8 @@ void omap_mux_write_array(struct omap_mux_partition *partition, static char *omap_mux_options; -static int __init _omap_mux_init_gpio(struct omap_mux_partition *partition, - int gpio, int val) +static int _omap_mux_init_gpio(struct omap_mux_partition *partition, + int gpio, int val) { struct omap_mux_entry *e; struct omap_mux *gpio_mux = NULL; @@ -145,7 +145,7 @@ static int __init _omap_mux_init_gpio(struct omap_mux_partition *partition, return 0; } -int __init omap_mux_init_gpio(int gpio, int val) +int omap_mux_init_gpio(int gpio, int val) { struct omap_mux_partition *partition; int ret; @@ -159,9 +159,9 @@ int __init omap_mux_init_gpio(int gpio, int val) return -ENODEV; } -static int __init _omap_mux_get_by_name(struct omap_mux_partition *partition, - const char *muxname, - struct omap_mux **found_mux) +static int _omap_mux_get_by_name(struct omap_mux_partition *partition, + const char *muxname, + struct omap_mux **found_mux) { struct omap_mux *mux = NULL; struct omap_mux_entry *e; @@ -240,7 +240,7 @@ omap_mux_get_by_name(const char *muxname, return -ENODEV; } -int __init omap_mux_init_signal(const char *muxname, int val) +int omap_mux_init_signal(const char *muxname, int val) { struct omap_mux_partition *partition = NULL; struct omap_mux *mux = NULL; -- cgit v0.10.2 From 4f8a428dac431e7bd09673b404769d87df948eef Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 10:59:37 +0000 Subject: ARM: omap: fix wrapped error messages in omap_hwmod.c While trying to debug my OMAP platforms, they emitted this message: omap_hwmod: %s: enabled state can only be entered from initialized, idle, or disabled state The following backtrace said it was from a function called '_enable', which didn't provide much clue. Grepping didn't find it either. The message is wrapped, so unwrap the message so grep can find it. Do the same for three other messages in this file. Acked-by: Paul Walmsley Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 5192cab..eba6cd3 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -1517,8 +1517,8 @@ static int _enable(struct omap_hwmod *oh) if (oh->_state != _HWMOD_STATE_INITIALIZED && oh->_state != _HWMOD_STATE_IDLE && oh->_state != _HWMOD_STATE_DISABLED) { - WARN(1, "omap_hwmod: %s: enabled state can only be entered " - "from initialized, idle, or disabled state\n", oh->name); + WARN(1, "omap_hwmod: %s: enabled state can only be entered from initialized, idle, or disabled state\n", + oh->name); return -EINVAL; } @@ -1600,8 +1600,8 @@ static int _idle(struct omap_hwmod *oh) pr_debug("omap_hwmod: %s: idling\n", oh->name); if (oh->_state != _HWMOD_STATE_ENABLED) { - WARN(1, "omap_hwmod: %s: idle state can only be entered from " - "enabled state\n", oh->name); + WARN(1, "omap_hwmod: %s: idle state can only be entered from enabled state\n", + oh->name); return -EINVAL; } @@ -1682,8 +1682,8 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->_state != _HWMOD_STATE_IDLE && oh->_state != _HWMOD_STATE_ENABLED) { - WARN(1, "omap_hwmod: %s: disabled state can only be entered " - "from idle, or enabled state\n", oh->name); + WARN(1, "omap_hwmod: %s: disabled state can only be entered from idle, or enabled state\n", + oh->name); return -EINVAL; } @@ -2240,8 +2240,8 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh) BUG_ON(!oh); if (!oh->class->sysc || !oh->class->sysc->sysc_flags) { - WARN(1, "omap_device: %s: OCP barrier impossible due to " - "device configuration\n", oh->name); + WARN(1, "omap_device: %s: OCP barrier impossible due to device configuration\n", + oh->name); return; } -- cgit v0.10.2 From 70d669de7356f6476db454dd8d053cd9c674a0d5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 7 Feb 2012 11:03:23 +0000 Subject: ARM: omap: resolve nebulous 'Error setting wl12xx data' It's useful to print the error code when a called function fails so a diagnosis of why it failed is possible. In this case, it fails because we try to register some data for the wl12xx driver, but as the driver is not configured, a stub function is used which simply returns -ENOSYS. Let's do the simple thing for -rc and print the error code. Also, the return code from platform_register_device() at each of these sites was not being checked. Add some checking, and again print the error code. This should be fixed properly for the next merge window so we don't issue error messages merely because a driver is not configured. Acked-by: Tony Lindgren Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 50d2412..4e90715 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -878,12 +878,17 @@ static struct wl12xx_platform_data omap4_sdp4430_wlan_data __initdata = { .board_tcxo_clock = WL12XX_TCXOCLOCK_26, }; -static void omap4_sdp4430_wifi_init(void) +static void __init omap4_sdp4430_wifi_init(void) { + int ret; + omap4_sdp4430_wifi_mux_init(); - if (wl12xx_set_platform_data(&omap4_sdp4430_wlan_data)) - pr_err("Error setting wl12xx data\n"); - platform_device_register(&omap_vwlan_device); + ret = wl12xx_set_platform_data(&omap4_sdp4430_wlan_data); + if (ret) + pr_err("Error setting wl12xx data: %d\n", ret); + ret = platform_device_register(&omap_vwlan_device); + if (ret) + pr_err("Error registering wl12xx device: %d\n", ret); } static void __init omap_4430sdp_init(void) diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 003fe34..c775bea 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -617,6 +617,21 @@ static struct gpio omap3_evm_ehci_gpios[] __initdata = { { OMAP3_EVM_EHCI_SELECT, GPIOF_OUT_INIT_LOW, "select EHCI port" }, }; +static void __init omap3_evm_wl12xx_init(void) +{ +#ifdef CONFIG_WL12XX_PLATFORM_DATA + int ret; + + /* WL12xx WLAN Init */ + ret = wl12xx_set_platform_data(&omap3evm_wlan_data); + if (ret) + pr_err("error setting wl12xx data: %d\n", ret); + ret = platform_device_register(&omap3evm_wlan_regulator); + if (ret) + pr_err("error registering wl12xx device: %d\n", ret); +#endif +} + static void __init omap3_evm_init(void) { omap3_evm_get_revision(); @@ -665,13 +680,7 @@ static void __init omap3_evm_init(void) omap_ads7846_init(1, OMAP3_EVM_TS_GPIO, 310, NULL); omap3evm_init_smsc911x(); omap3_evm_display_init(); - -#ifdef CONFIG_WL12XX_PLATFORM_DATA - /* WL12xx WLAN Init */ - if (wl12xx_set_platform_data(&omap3evm_wlan_data)) - pr_err("error setting wl12xx data\n"); - platform_device_register(&omap3evm_wlan_regulator); -#endif + omap3_evm_wl12xx_init(); } MACHINE_START(OMAP3EVM, "OMAP3 EVM") diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index b7779c20..28fc271 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -488,13 +488,15 @@ void omap4_panda_display_init(void) static void __init omap4_panda_init(void) { int package = OMAP_PACKAGE_CBS; + int ret; if (omap_rev() == OMAP4430_REV_ES1_0) package = OMAP_PACKAGE_CBL; omap4_mux_init(board_mux, NULL, package); - if (wl12xx_set_platform_data(&omap_panda_wlan_data)) - pr_err("error setting wl12xx data\n"); + ret = wl12xx_set_platform_data(&omap_panda_wlan_data); + if (ret) + pr_err("error setting wl12xx data: %d\n", ret); omap4_panda_i2c_init(); platform_add_devices(panda_devices, ARRAY_SIZE(panda_devices)); diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index 8d7ce11..c126461 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -296,8 +296,10 @@ static void enable_board_wakeup_source(void) void __init zoom_peripherals_init(void) { - if (wl12xx_set_platform_data(&omap_zoom_wlan_data)) - pr_err("error setting wl12xx data\n"); + int ret = wl12xx_set_platform_data(&omap_zoom_wlan_data); + + if (ret) + pr_err("error setting wl12xx data: %d\n", ret); omap_i2c_init(); platform_device_register(&omap_vwlan_device); -- cgit v0.10.2 From fc1156c0b0f7ad45ec03d919866349eeca2bf18c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Feb 2012 15:04:06 +0100 Subject: ALSA: hda - Fix initialization of secondary capture source on VT1705 VT1705 codec has two ADCs where the secondary ADC has no MUX but only a fixed connection to the mic pin. This confused the driver and it tries always overriding the input-source selection by assumption of the existing MUX for the secondary ADC, resulted in resetting the input-source at each time PM (including power-saving) occurs. The fix is simply to check the existence of MUX for secondary ADCs in the initialization code. Tested-by: Anisse Astier Cc: [v3.1+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 284e311..dff9a00 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -666,6 +666,9 @@ static void via_auto_init_analog_input(struct hda_codec *codec) /* init input-src */ for (i = 0; i < spec->num_adc_nids; i++) { int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; + /* secondary ADCs must have the unique MUX */ + if (i > 0 && !spec->mux_nids[i]) + break; if (spec->mux_nids[adc_idx]) { int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, -- cgit v0.10.2 From 02a237b24d57e2e2d5402c92549e9e792aa24359 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 13 Feb 2012 15:25:07 +0100 Subject: ALSA: hda - Fix silent speaker output on Acer Aspire 6935 Since 3.2 kernel, the driver starts trying to assign the multi-io DACs before the speaker, thus it assigns DAC2/3 for multi-io and DAC4 for the speaker for a standard laptop setup like a HP, a speaker, a mic-in and a line-in. However, on Acer Aspire 6935, it seems that the speaker pin 0x14 must be connected with either DAC1 or 2; otherwise it results in silence by some reason, although the codec itself allows the routing to DAC3/4. As a workaround, the connection list of each pin is reduced to be mapped to either only DAC1/2 or DAC3/4, so that the compatible assignment as in kernel 3.1 is achieved. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42740 Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1d07e8f..c4bde71 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4201,8 +4201,26 @@ enum { PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, PINFIX_ASUS_W90V, + ALC889_FIXUP_DAC_ROUTE, }; +/* Fix the connection of some pins for ALC889: + * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't + * work correctly (bko#42740) + */ +static void alc889_fixup_dac_route(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + if (action == ALC_FIXUP_ACT_PRE_PROBE) { + hda_nid_t conn1[2] = { 0x0c, 0x0d }; + hda_nid_t conn2[2] = { 0x0e, 0x0f }; + snd_hda_override_conn_list(codec, 0x14, 2, conn1); + snd_hda_override_conn_list(codec, 0x15, 2, conn1); + snd_hda_override_conn_list(codec, 0x18, 2, conn2); + snd_hda_override_conn_list(codec, 0x1a, 2, conn2); + } +} + static const struct alc_fixup alc882_fixups[] = { [PINFIX_ABIT_AW9D_MAX] = { .type = ALC_FIXUP_PINS, @@ -4239,10 +4257,15 @@ static const struct alc_fixup alc882_fixups[] = { { } } }, + [ALC889_FIXUP_DAC_ROUTE] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc889_fixup_dac_route, + }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), + SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", PINFIX_ASUS_W90V), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), -- cgit v0.10.2 From e8c9dc93e27d891636defbc269f182a83e6abba8 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 27 Jan 2012 11:14:44 +0100 Subject: ARM: at91: USB AT91 gadget registration for module Registration of at91_udc as a module will enable SoC related code. Fix following an idea from Karel Znamenacek. Signed-off-by: Nicolas Ferre Acked-by: Karel Znamenacek Acked-by: Jean-Christophe PLAGNIOL-VILLARD Cc: stable diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c index 18bacec..97676bd 100644 --- a/arch/arm/mach-at91/at91rm9200_devices.c +++ b/arch/arm/mach-at91/at91rm9200_devices.c @@ -83,7 +83,7 @@ void __init at91_add_device_usbh(struct at91_usbh_data *data) {} * USB Device (Gadget) * -------------------------------------------------------------------- */ -#ifdef CONFIG_USB_AT91 +#if defined(CONFIG_USB_AT91) || defined(CONFIG_USB_AT91_MODULE) static struct at91_udc_data udc_data; static struct resource udc_resources[] = { diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 642ccb6..f1a1bb45 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -84,7 +84,7 @@ void __init at91_add_device_usbh(struct at91_usbh_data *data) {} * USB Device (Gadget) * -------------------------------------------------------------------- */ -#ifdef CONFIG_USB_AT91 +#if defined(CONFIG_USB_AT91) || defined(CONFIG_USB_AT91_MODULE) static struct at91_udc_data udc_data; static struct resource udc_resources[] = { diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index fc59cbd..1e28bed 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -87,7 +87,7 @@ void __init at91_add_device_usbh(struct at91_usbh_data *data) {} * USB Device (Gadget) * -------------------------------------------------------------------- */ -#ifdef CONFIG_USB_AT91 +#if defined(CONFIG_USB_AT91) || defined(CONFIG_USB_AT91_MODULE) static struct at91_udc_data udc_data; static struct resource udc_resources[] = { diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index 7b46b27..618013d 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -92,7 +92,7 @@ void __init at91_add_device_usbh(struct at91_usbh_data *data) {} * USB Device (Gadget) * -------------------------------------------------------------------- */ -#ifdef CONFIG_USB_AT91 +#if defined(CONFIG_USB_AT91) || defined(CONFIG_USB_AT91_MODULE) static struct at91_udc_data udc_data; static struct resource udc_resources[] = { -- cgit v0.10.2 From 2dcc90e6ac06d78433aec682961d9b587a07d4b5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 18 Sep 2011 10:17:54 +0800 Subject: ARM: at91:rtc/rtc-at91sam9: ioremap register bank Instead of computing virtual address with AT91_VA_BASE_SYS, use the appropriate ioremap() call on the driver "memory" resource. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index a3ad957..ee3c122 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -307,8 +307,12 @@ static int __init at91_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, rtc); - rtc->rtt = (void __force __iomem *) (AT91_VA_BASE_SYS - AT91_BASE_SYS); - rtc->rtt += r->start; + rtc->rtt = ioremap(r->start, resource_size(r)); + if (!rtc->rtt) { + dev_err(&pdev->dev, "failed to map registers, aborting.\n"); + ret = -ENOMEM; + goto fail; + } mr = rtt_readl(rtc, MR); @@ -326,7 +330,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) &at91_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtcdev)) { ret = PTR_ERR(rtc->rtcdev); - goto fail; + goto fail_register; } /* register irq handler after we know what name we'll use */ @@ -351,6 +355,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev) return 0; +fail_register: + iounmap(rtc->rtt); fail: platform_set_drvdata(pdev, NULL); kfree(rtc); @@ -371,6 +377,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) rtc_device_unregister(rtc->rtcdev); + iounmap(rtc->rtt); platform_set_drvdata(pdev, NULL); kfree(rtc); return 0; -- cgit v0.10.2 From 59594e13e4d83239332746395fe03ba49b8efa12 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 27 Nov 2011 19:29:57 +0800 Subject: ARM: at91: add accessor to manage SMC SMC, Static Memory Controller will need more accessors to fine configure its parameters. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/include/mach/at91sam9_smc.h b/arch/arm/mach-at91/include/mach/at91sam9_smc.h index eb18a70..175e1fd 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9_smc.h +++ b/arch/arm/mach-at91/include/mach/at91sam9_smc.h @@ -18,6 +18,35 @@ #include +#ifndef __ASSEMBLY__ +struct sam9_smc_config { + /* Setup register */ + u8 ncs_read_setup; + u8 nrd_setup; + u8 ncs_write_setup; + u8 nwe_setup; + + /* Pulse register */ + u8 ncs_read_pulse; + u8 nrd_pulse; + u8 ncs_write_pulse; + u8 nwe_pulse; + + /* Cycle register */ + u16 read_cycle; + u16 write_cycle; + + /* Mode register */ + u32 mode; + u8 tdf_cycles:4; +}; + +extern void sam9_smc_configure(int id, int cs, struct sam9_smc_config *config); +extern void sam9_smc_read(int id, int cs, struct sam9_smc_config *config); +extern void sam9_smc_read_mode(int id, int cs, struct sam9_smc_config *config); +extern void sam9_smc_write_mode(int id, int cs, struct sam9_smc_config *config); +#endif + #define AT91_SMC_SETUP 0x00 /* Setup Register for CS n */ #define AT91_SMC_NWESETUP (0x3f << 0) /* NWE Setup Length */ #define AT91_SMC_NWESETUP_(x) ((x) << 0) diff --git a/arch/arm/mach-at91/sam9_smc.c b/arch/arm/mach-at91/sam9_smc.c index 8294783..99a0a1d 100644 --- a/arch/arm/mach-at91/sam9_smc.c +++ b/arch/arm/mach-at91/sam9_smc.c @@ -2,6 +2,7 @@ * linux/arch/arm/mach-at91/sam9_smc.c * * Copyright (C) 2008 Andrew Victor + * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,7 +23,22 @@ static void __iomem *smc_base_addr[2]; -static void __init sam9_smc_cs_configure(void __iomem *base, struct sam9_smc_config* config) +static void sam9_smc_cs_write_mode(void __iomem *base, + struct sam9_smc_config *config) +{ + __raw_writel(config->mode + | AT91_SMC_TDF_(config->tdf_cycles), + base + AT91_SMC_MODE); +} + +void sam9_smc_write_mode(int id, int cs, + struct sam9_smc_config *config) +{ + sam9_smc_cs_write_mode(AT91_SMC_CS(id, cs), config); +} + +static void sam9_smc_cs_configure(void __iomem *base, + struct sam9_smc_config *config) { /* Setup register */ @@ -45,16 +61,66 @@ static void __init sam9_smc_cs_configure(void __iomem *base, struct sam9_smc_con base + AT91_SMC_CYCLE); /* Mode register */ - __raw_writel(config->mode - | AT91_SMC_TDF_(config->tdf_cycles), - base + AT91_SMC_MODE); + sam9_smc_cs_write_mode(base, config); } -void __init sam9_smc_configure(int id, int cs, struct sam9_smc_config* config) +void sam9_smc_configure(int id, int cs, + struct sam9_smc_config *config) { sam9_smc_cs_configure(AT91_SMC_CS(id, cs), config); } +static void sam9_smc_cs_read_mode(void __iomem *base, + struct sam9_smc_config *config) +{ + u32 val = __raw_readl(base + AT91_SMC_MODE); + + config->mode = (val & ~AT91_SMC_NWECYCLE); + config->tdf_cycles = (val & AT91_SMC_NWECYCLE) >> 16 ; +} + +void sam9_smc_read_mode(int id, int cs, + struct sam9_smc_config *config) +{ + sam9_smc_cs_read_mode(AT91_SMC_CS(id, cs), config); +} + +static void sam9_smc_cs_read(void __iomem *base, + struct sam9_smc_config *config) +{ + u32 val; + + /* Setup register */ + val = __raw_readl(base + AT91_SMC_SETUP); + + config->nwe_setup = val & AT91_SMC_NWESETUP; + config->ncs_write_setup = (val & AT91_SMC_NCS_WRSETUP) >> 8; + config->nrd_setup = (val & AT91_SMC_NRDSETUP) >> 16; + config->ncs_read_setup = (val & AT91_SMC_NCS_RDSETUP) >> 24; + + /* Pulse register */ + val = __raw_readl(base + AT91_SMC_PULSE); + + config->nwe_setup = val & AT91_SMC_NWEPULSE; + config->ncs_write_pulse = (val & AT91_SMC_NCS_WRPULSE) >> 8; + config->nrd_pulse = (val & AT91_SMC_NRDPULSE) >> 16; + config->ncs_read_pulse = (val & AT91_SMC_NCS_RDPULSE) >> 24; + + /* Cycle register */ + val = __raw_readl(base + AT91_SMC_CYCLE); + + config->write_cycle = val & AT91_SMC_NWECYCLE; + config->read_cycle = (val & AT91_SMC_NRDCYCLE) >> 16; + + /* Mode register */ + sam9_smc_cs_read_mode(base, config); +} + +void sam9_smc_read(int id, int cs, struct sam9_smc_config *config) +{ + sam9_smc_cs_read(AT91_SMC_CS(id, cs), config); +} + void __init at91sam9_ioremap_smc(int id, u32 addr) { if (id > 1) { diff --git a/arch/arm/mach-at91/sam9_smc.h b/arch/arm/mach-at91/sam9_smc.h index 039c5ce..3e52dcd4 100644 --- a/arch/arm/mach-at91/sam9_smc.h +++ b/arch/arm/mach-at91/sam9_smc.h @@ -8,27 +8,4 @@ * published by the Free Software Foundation. */ -struct sam9_smc_config { - /* Setup register */ - u8 ncs_read_setup; - u8 nrd_setup; - u8 ncs_write_setup; - u8 nwe_setup; - - /* Pulse register */ - u8 ncs_read_pulse; - u8 nrd_pulse; - u8 ncs_write_pulse; - u8 nwe_pulse; - - /* Cycle register */ - u16 read_cycle; - u16 write_cycle; - - /* Mode register */ - u32 mode; - u8 tdf_cycles:4; -}; - -extern void __init sam9_smc_configure(int id, int cs, struct sam9_smc_config* config); extern void __init at91sam9_ioremap_smc(int id, u32 addr); -- cgit v0.10.2 From 88dcde98ad113411f45ea7e8261839f7a1ba3c33 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 27 Nov 2011 19:30:29 +0800 Subject: pata/at91: use newly introduced SMC accessors pata_at91 driver is broken since faee0cc: "make smc register base soc independent" Fix it with newly introduced SMC accessors. The overall action of removal of at91_sys_read/write will allow to use the pata_at91 on a single zImage kernel. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Cc: linux-ide@vger.kernel.org diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index a7d91a7..53d3770 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -207,11 +207,11 @@ static void set_smc_timing(struct device *dev, struct ata_device *adev, { int ret = 0; int use_iordy; + struct sam9_smc_config smc; unsigned int t6z; /* data tristate time in ns */ unsigned int cycle; /* SMC Cycle width in MCK ticks */ unsigned int setup; /* SMC Setup width in MCK ticks */ unsigned int pulse; /* CFIOR and CFIOW pulse width in MCK ticks */ - unsigned int cs_setup = 0;/* CS4 or CS5 setup width in MCK ticks */ unsigned int cs_pulse; /* CS4 or CS5 pulse width in MCK ticks*/ unsigned int tdf_cycles; /* SMC TDF MCK ticks */ unsigned long mck_hz; /* MCK frequency in Hz */ @@ -244,26 +244,20 @@ static void set_smc_timing(struct device *dev, struct ata_device *adev, } dev_dbg(dev, "Use IORDY=%u, TDF Cycles=%u\n", use_iordy, tdf_cycles); - info->mode |= AT91_SMC_TDF_(tdf_cycles); - - /* write SMC Setup Register */ - at91_sys_write(AT91_SMC_SETUP(info->cs), - AT91_SMC_NWESETUP_(setup) | - AT91_SMC_NRDSETUP_(setup) | - AT91_SMC_NCS_WRSETUP_(cs_setup) | - AT91_SMC_NCS_RDSETUP_(cs_setup)); - /* write SMC Pulse Register */ - at91_sys_write(AT91_SMC_PULSE(info->cs), - AT91_SMC_NWEPULSE_(pulse) | - AT91_SMC_NRDPULSE_(pulse) | - AT91_SMC_NCS_WRPULSE_(cs_pulse) | - AT91_SMC_NCS_RDPULSE_(cs_pulse)); - /* write SMC Cycle Register */ - at91_sys_write(AT91_SMC_CYCLE(info->cs), - AT91_SMC_NWECYCLE_(cycle) | - AT91_SMC_NRDCYCLE_(cycle)); - /* write SMC Mode Register*/ - at91_sys_write(AT91_SMC_MODE(info->cs), info->mode); + + /* SMC Setup Register */ + smc.nwe_setup = smc.nrd_setup = setup; + smc.ncs_write_setup = smc.ncs_read_setup = 0; + /* SMC Pulse Register */ + smc.nwe_pulse = smc.nrd_pulse = pulse; + smc.ncs_write_pulse = smc.ncs_read_pulse = cs_pulse; + /* SMC Cycle Register */ + smc.write_cycle = smc.read_cycle = cycle; + /* SMC Mode Register*/ + smc.tdf_cycles = tdf_cycles; + smc.mode = info->mode; + + sam9_smc_configure(0, info->cs, &smc); } static void pata_at91_set_piomode(struct ata_port *ap, struct ata_device *adev) @@ -288,20 +282,20 @@ static unsigned int pata_at91_data_xfer_noirq(struct ata_device *dev, struct at91_ide_info *info = dev->link->ap->host->private_data; unsigned int consumed; unsigned long flags; - unsigned int mode; + struct sam9_smc_config smc; local_irq_save(flags); - mode = at91_sys_read(AT91_SMC_MODE(info->cs)); + sam9_smc_read_mode(0, info->cs, &smc); /* set 16bit mode before writing data */ - at91_sys_write(AT91_SMC_MODE(info->cs), - (mode & ~AT91_SMC_DBW) | AT91_SMC_DBW_16); + smc.mode = (smc.mode & ~AT91_SMC_DBW) | AT91_SMC_DBW_16; + sam9_smc_write_mode(0, info->cs, &smc); consumed = ata_sff_data_xfer(dev, buf, buflen, rw); /* restore 8bit mode after data is written */ - at91_sys_write(AT91_SMC_MODE(info->cs), - (mode & ~AT91_SMC_DBW) | AT91_SMC_DBW_8); + smc.mode = (smc.mode & ~AT91_SMC_DBW) | AT91_SMC_DBW_8; + sam9_smc_write_mode(0, info->cs, &smc); local_irq_restore(flags); return consumed; -- cgit v0.10.2 From cf844751fb25e095d8fa30332cb173a73e5a736c Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 15 Dec 2011 21:24:03 +0800 Subject: ARM: at91: drop ide driver in favor of the pata one Driver at91_ide is broken and should not be fixed: remove it. Modification of device files that where making use of it. The PATA driver (pata_at91) is able to replace at91_ide. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index f1a1bb45..5a24f0b 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1215,8 +1215,7 @@ void __init at91_add_device_serial(void) {} * CF/IDE * -------------------------------------------------------------------- */ -#if defined(CONFIG_BLK_DEV_IDE_AT91) || defined(CONFIG_BLK_DEV_IDE_AT91_MODULE) || \ - defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE) || \ +#if defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE) || \ defined(CONFIG_AT91_CF) || defined(CONFIG_AT91_CF_MODULE) static struct at91_cf_data cf0_data; @@ -1313,10 +1312,8 @@ void __init at91_add_device_cf(struct at91_cf_data *data) if (data->flags & AT91_CF_TRUE_IDE) #if defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE) pdev->name = "pata_at91"; -#elif defined(CONFIG_BLK_DEV_IDE_AT91) || defined(CONFIG_BLK_DEV_IDE_AT91_MODULE) - pdev->name = "at91_ide"; #else -#warning "board requires AT91_CF_TRUE_IDE: enable either at91_ide or pata_at91" +#warning "board requires AT91_CF_TRUE_IDE: enable pata_at91" #endif else pdev->name = "at91_cf"; diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index 618013d..366a776 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -355,8 +355,8 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {} * Compact Flash (PCMCIA or IDE) * -------------------------------------------------------------------- */ -#if defined(CONFIG_AT91_CF) || defined(CONFIG_AT91_CF_MODULE) || \ - defined(CONFIG_BLK_DEV_IDE_AT91) || defined(CONFIG_BLK_DEV_IDE_AT91_MODULE) +#if defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE) || \ + defined(CONFIG_AT91_CF) || defined(CONFIG_AT91_CF_MODULE) static struct at91_cf_data cf0_data; @@ -450,7 +450,7 @@ void __init at91_add_device_cf(struct at91_cf_data *data) at91_set_A_periph(AT91_PIN_PD9, 0); /* CFCE2 */ at91_set_A_periph(AT91_PIN_PD14, 0); /* CFNRW */ - pdev->name = (data->flags & AT91_CF_TRUE_IDE) ? "at91_ide" : "at91_cf"; + pdev->name = (data->flags & AT91_CF_TRUE_IDE) ? "pata_at91" : "at91_cf"; platform_device_register(pdev); } #else diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 7f879b2..af8d016 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -116,4 +116,3 @@ obj-$(CONFIG_BLK_DEV_IDE_AU1XXX) += au1xxx-ide.o obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o -obj-$(CONFIG_BLK_DEV_IDE_AT91) += at91_ide.o diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c deleted file mode 100644 index 41d4155..0000000 --- a/drivers/ide/at91_ide.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * IDE host driver for AT91 (SAM9, CAP9, AT572D940HF) Static Memory Controller - * with Compact Flash True IDE logic - * - * Copyright (c) 2008, 2009 Kelvatek Ltd. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define DRV_NAME "at91_ide" - -#define perr(fmt, args...) pr_err(DRV_NAME ": " fmt, ##args) -#define pdbg(fmt, args...) pr_debug("%s " fmt, __func__, ##args) - -/* - * Access to IDE device is possible through EBI Static Memory Controller - * with Compact Flash logic. For details see EBI and SMC datasheet sections - * of any microcontroller from AT91SAM9 family. - * - * Within SMC chip select address space, lines A[23:21] distinguish Compact - * Flash modes (I/O, common memory, attribute memory, True IDE). IDE modes are: - * 0x00c0000 - True IDE - * 0x00e0000 - Alternate True IDE (Alt Status Register) - * - * On True IDE mode Task File and Data Register are mapped at the same address. - * To distinguish access between these two different bus data width is used: - * 8Bit for Task File, 16Bit for Data I/O. - * - * After initialization we do 8/16 bit flipping (changes in SMC MODE register) - * only inside IDE callback routines which are serialized by IDE layer, - * so no additional locking needed. - */ - -#define TASK_FILE 0x00c00000 -#define ALT_MODE 0x00e00000 -#define REGS_SIZE 8 - -#define enter_16bit(cs, mode) do { \ - mode = at91_sys_read(AT91_SMC_MODE(cs)); \ - at91_sys_write(AT91_SMC_MODE(cs), mode | AT91_SMC_DBW_16); \ -} while (0) - -#define leave_16bit(cs, mode) at91_sys_write(AT91_SMC_MODE(cs), mode); - -static void set_smc_timings(const u8 chipselect, const u16 cycle, - const u16 setup, const u16 pulse, - const u16 data_float, int use_iordy) -{ - unsigned long mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | - AT91_SMC_BAT_SELECT; - - /* disable or enable waiting for IORDY signal */ - if (use_iordy) - mode |= AT91_SMC_EXNWMODE_READY; - - /* add data float cycles if needed */ - if (data_float) - mode |= AT91_SMC_TDF_(data_float); - - at91_sys_write(AT91_SMC_MODE(chipselect), mode); - - /* setup timings in SMC */ - at91_sys_write(AT91_SMC_SETUP(chipselect), AT91_SMC_NWESETUP_(setup) | - AT91_SMC_NCS_WRSETUP_(0) | - AT91_SMC_NRDSETUP_(setup) | - AT91_SMC_NCS_RDSETUP_(0)); - at91_sys_write(AT91_SMC_PULSE(chipselect), AT91_SMC_NWEPULSE_(pulse) | - AT91_SMC_NCS_WRPULSE_(cycle) | - AT91_SMC_NRDPULSE_(pulse) | - AT91_SMC_NCS_RDPULSE_(cycle)); - at91_sys_write(AT91_SMC_CYCLE(chipselect), AT91_SMC_NWECYCLE_(cycle) | - AT91_SMC_NRDCYCLE_(cycle)); -} - -static unsigned int calc_mck_cycles(unsigned int ns, unsigned int mck_hz) -{ - u64 tmp = ns; - - tmp *= mck_hz; - tmp += 1000*1000*1000 - 1; /* round up */ - do_div(tmp, 1000*1000*1000); - return (unsigned int) tmp; -} - -static void apply_timings(const u8 chipselect, const u8 pio, - const struct ide_timing *timing, int use_iordy) -{ - unsigned int t0, t1, t2, t6z; - unsigned int cycle, setup, pulse, data_float; - unsigned int mck_hz; - struct clk *mck; - - /* see table 22 of Compact Flash standard 4.1 for the meaning, - * we do not stretch active (t2) time, so setup (t1) + hold time (th) - * assure at least minimal recovery (t2i) time */ - t0 = timing->cyc8b; - t1 = timing->setup; - t2 = timing->act8b; - t6z = (pio < 5) ? 30 : 20; - - pdbg("t0=%u t1=%u t2=%u t6z=%u\n", t0, t1, t2, t6z); - - mck = clk_get(NULL, "mck"); - BUG_ON(IS_ERR(mck)); - mck_hz = clk_get_rate(mck); - pdbg("mck_hz=%u\n", mck_hz); - - cycle = calc_mck_cycles(t0, mck_hz); - setup = calc_mck_cycles(t1, mck_hz); - pulse = calc_mck_cycles(t2, mck_hz); - data_float = calc_mck_cycles(t6z, mck_hz); - - pdbg("cycle=%u setup=%u pulse=%u data_float=%u\n", - cycle, setup, pulse, data_float); - - set_smc_timings(chipselect, cycle, setup, pulse, data_float, use_iordy); -} - -static void at91_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, - void *buf, unsigned int len) -{ - ide_hwif_t *hwif = drive->hwif; - struct ide_io_ports *io_ports = &hwif->io_ports; - u8 chipselect = hwif->select_data; - unsigned long mode; - - pdbg("cs %u buf %p len %d\n", chipselect, buf, len); - - len++; - - enter_16bit(chipselect, mode); - readsw((void __iomem *)io_ports->data_addr, buf, len / 2); - leave_16bit(chipselect, mode); -} - -static void at91_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, - void *buf, unsigned int len) -{ - ide_hwif_t *hwif = drive->hwif; - struct ide_io_ports *io_ports = &hwif->io_ports; - u8 chipselect = hwif->select_data; - unsigned long mode; - - pdbg("cs %u buf %p len %d\n", chipselect, buf, len); - - enter_16bit(chipselect, mode); - writesw((void __iomem *)io_ports->data_addr, buf, len / 2); - leave_16bit(chipselect, mode); -} - -static void at91_ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) -{ - struct ide_timing *timing; - u8 chipselect = hwif->select_data; - int use_iordy = 0; - const u8 pio = drive->pio_mode - XFER_PIO_0; - - pdbg("chipselect %u pio %u\n", chipselect, pio); - - timing = ide_timing_find_mode(XFER_PIO_0 + pio); - BUG_ON(!timing); - - if (ide_pio_need_iordy(drive, pio)) - use_iordy = 1; - - apply_timings(chipselect, pio, timing, use_iordy); -} - -static const struct ide_tp_ops at91_ide_tp_ops = { - .exec_command = ide_exec_command, - .read_status = ide_read_status, - .read_altstatus = ide_read_altstatus, - .write_devctl = ide_write_devctl, - - .dev_select = ide_dev_select, - .tf_load = ide_tf_load, - .tf_read = ide_tf_read, - - .input_data = at91_ide_input_data, - .output_data = at91_ide_output_data, -}; - -static const struct ide_port_ops at91_ide_port_ops = { - .set_pio_mode = at91_ide_set_pio_mode, -}; - -static const struct ide_port_info at91_ide_port_info __initdata = { - .port_ops = &at91_ide_port_ops, - .tp_ops = &at91_ide_tp_ops, - .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA | IDE_HFLAG_SINGLE | - IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_UNMASK_IRQS, - .pio_mask = ATA_PIO6, - .chipset = ide_generic, -}; - -/* - * If interrupt is delivered through GPIO, IRQ are triggered on falling - * and rising edge of signal. Whereas IDE device request interrupt on high - * level (rising edge in our case). This mean we have fake interrupts, so - * we need to check interrupt pin and exit instantly from ISR when line - * is on low level. - */ - -irqreturn_t at91_irq_handler(int irq, void *dev_id) -{ - int ntries = 8; - int pin_val1, pin_val2; - - /* additional deglitch, line can be noisy in badly designed PCB */ - do { - pin_val1 = at91_get_gpio_value(irq); - pin_val2 = at91_get_gpio_value(irq); - } while (pin_val1 != pin_val2 && --ntries > 0); - - if (pin_val1 == 0 || ntries <= 0) - return IRQ_HANDLED; - - return ide_intr(irq, dev_id); -} - -static int __init at91_ide_probe(struct platform_device *pdev) -{ - int ret; - struct ide_hw hw, *hws[] = { &hw }; - struct ide_host *host; - struct resource *res; - unsigned long tf_base = 0, ctl_base = 0; - struct at91_cf_data *board = pdev->dev.platform_data; - - if (!board) - return -ENODEV; - - if (board->det_pin && at91_get_gpio_value(board->det_pin) != 0) { - perr("no device detected\n"); - return -ENODEV; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - perr("can't get memory resource\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(&pdev->dev, res->start + TASK_FILE, - REGS_SIZE, "ide") || - !devm_request_mem_region(&pdev->dev, res->start + ALT_MODE, - REGS_SIZE, "alt")) { - perr("memory resources in use\n"); - return -EBUSY; - } - - pdbg("chipselect %u irq %u res %08lx\n", board->chipselect, - board->irq_pin, (unsigned long) res->start); - - tf_base = (unsigned long) devm_ioremap(&pdev->dev, res->start + TASK_FILE, - REGS_SIZE); - ctl_base = (unsigned long) devm_ioremap(&pdev->dev, res->start + ALT_MODE, - REGS_SIZE); - if (!tf_base || !ctl_base) { - perr("can't map memory regions\n"); - return -EBUSY; - } - - memset(&hw, 0, sizeof(hw)); - - if (board->flags & AT91_IDE_SWAP_A0_A2) { - /* workaround for stupid hardware bug */ - hw.io_ports.data_addr = tf_base + 0; - hw.io_ports.error_addr = tf_base + 4; - hw.io_ports.nsect_addr = tf_base + 2; - hw.io_ports.lbal_addr = tf_base + 6; - hw.io_ports.lbam_addr = tf_base + 1; - hw.io_ports.lbah_addr = tf_base + 5; - hw.io_ports.device_addr = tf_base + 3; - hw.io_ports.command_addr = tf_base + 7; - hw.io_ports.ctl_addr = ctl_base + 3; - } else - ide_std_init_ports(&hw, tf_base, ctl_base + 6); - - hw.irq = board->irq_pin; - hw.dev = &pdev->dev; - - host = ide_host_alloc(&at91_ide_port_info, hws, 1); - if (!host) { - perr("failed to allocate ide host\n"); - return -ENOMEM; - } - - /* setup Static Memory Controller - PIO 0 as default */ - apply_timings(board->chipselect, 0, ide_timing_find_mode(XFER_PIO_0), 0); - - /* with GPIO interrupt we have to do quirks in handler */ - if (gpio_is_valid(board->irq_pin)) - host->irq_handler = at91_irq_handler; - - host->ports[0]->select_data = board->chipselect; - - ret = ide_host_register(host, &at91_ide_port_info, hws); - if (ret) { - perr("failed to register ide host\n"); - goto err_free_host; - } - platform_set_drvdata(pdev, host); - return 0; - -err_free_host: - ide_host_free(host); - return ret; -} - -static int __exit at91_ide_remove(struct platform_device *pdev) -{ - struct ide_host *host = platform_get_drvdata(pdev); - - ide_host_remove(host); - return 0; -} - -static struct platform_driver at91_ide_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .remove = __exit_p(at91_ide_remove), -}; - -static int __init at91_ide_init(void) -{ - return platform_driver_probe(&at91_ide_driver, at91_ide_probe); -} - -static void __exit at91_ide_exit(void) -{ - platform_driver_unregister(&at91_ide_driver); -} - -module_init(at91_ide_init); -module_exit(at91_ide_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Stanislaw Gruszka "); - -- cgit v0.10.2 From 758ff235b3f50f0cb391ba3c5e10793305c42f45 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 12 Feb 2012 15:14:39 +0000 Subject: mlx4: Fix kcalloc parameters swapped The first parameter should be "number of elements" and the second parameter should be "element size". Signed-off-by: Axel Lin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 55d7bd4..8fa41f3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -815,8 +815,9 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) int err; int i; - priv->eq_table.uar_map = kcalloc(sizeof *priv->eq_table.uar_map, - mlx4_num_eq_uar(dev), GFP_KERNEL); + priv->eq_table.uar_map = kcalloc(mlx4_num_eq_uar(dev), + sizeof *priv->eq_table.uar_map, + GFP_KERNEL); if (!priv->eq_table.uar_map) { err = -ENOMEM; goto err_out_free; -- cgit v0.10.2 From a19c5d68410a90c19521d966c88939700c4a1b3d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 13 Feb 2012 01:23:20 +0000 Subject: cpmac: fix PHY name to match MDIO bus name Commit d1733f07: cpmac: use an unique MDIO bus name changed the MDIO bus name from "1" to "cpmac-1", this breaks the PHY connection logic because the PHY name still uses the old bus names "0" and "1", fix that to always use the mdio bus id instead. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 4d9a28f..cbc8df7 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -1122,7 +1122,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (external_switch || dumb_switch) { - strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); /* fixed phys bus */ + strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); /* fixed phys bus */ phy_id = pdev->id; } else { for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { @@ -1138,7 +1138,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) if (phy_id == PHY_MAX_ADDR) { dev_err(&pdev->dev, "no PHY present, falling back " "to switch on MDIO bus 0\n"); - strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); /* fixed phys bus */ + strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); /* fixed phys bus */ phy_id = pdev->id; } -- cgit v0.10.2 From c56e9e2ae766b687ed66c3c9c3bb6b2ee15405b2 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 13 Feb 2012 01:23:21 +0000 Subject: bcm63xx-enet: fix PHY name to match MDIO bus name Commit 3e617506: bcm63xx_enet: use an unique MDIO bus name introduced a regression in the PHY connection logic, since the PHY name was formatted to expect the bus name to be "0" or "1", whereas it is now "bcm63xx-enet-0" or "bcm63xx-enet-1". Reported-by: Joel EJC Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 986019b..c7ca7ec 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -797,7 +797,7 @@ static int bcm_enet_open(struct net_device *dev) if (priv->has_phy) { /* connect to PHY */ snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, - priv->mac_id ? "1" : "0", priv->phy_id); + priv->mii_bus->id, priv->phy_id); phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, 0, PHY_INTERFACE_MODE_MII); -- cgit v0.10.2 From ea51ade9398f3c94f63a25f512445ee7cbbbf284 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 13 Feb 2012 01:23:22 +0000 Subject: fec: fix PHY name to match fixed MDIO bus name Commit "391420f7: fec: use an unique MDIO bus name" first modified the MDIO bus name to include the platform name, then in commit "a7ed07d5: net: fec: correct phy_name buffer length when init phy_name" the PHY name formatting was fixed in the case the PHY matches a PHY driver. The FEC driver however, also handles the case where we want to attach to the fixed MDIO bus name, which was previously named "0", and now "fixed-0". Change the PHY formatting logic to account for that. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 1c7aad8..e92ef1b 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -986,7 +986,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) printk(KERN_INFO "%s: no PHY, assuming direct connection to switch\n", ndev->name); - strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); + strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); phy_id = 0; } -- cgit v0.10.2 From b0c06e12b6f1dad480c459ddc5412a0fd8582201 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 13 Feb 2012 01:23:23 +0000 Subject: octeon: fix PHY name to match MDIO bus name Commit "d6c25be: mdio-octeon: use an unique MDIO bus name" changed the octeon MDIO bus name from "0" to "mdio-octeon-0", change the PHY formatting logic to account for that name change, so that PHY connection on this bus succeeds. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index 212f43b..cd827ff 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -670,7 +670,7 @@ static void octeon_mgmt_adjust_link(struct net_device *netdev) static int octeon_mgmt_init_phy(struct net_device *netdev) { struct octeon_mgmt *p = netdev_priv(netdev); - char phy_id[20]; + char phy_id[MII_BUS_ID_SIZE + 3]; if (octeon_is_simulation()) { /* No PHYs in the simulator. */ @@ -678,7 +678,7 @@ static int octeon_mgmt_init_phy(struct net_device *netdev) return 0; } - snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", p->port); + snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "mdio-octeon-0", p->port); p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0, PHY_INTERFACE_MODE_MII); -- cgit v0.10.2 From 7465ac3c2f2c99b3c69bd8e257758a0baa7bc7ea Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 13 Feb 2012 01:23:24 +0000 Subject: ixp4xx-eth: fix PHY name to match MDIO bus name Commit 0869b3a4: ixp4xx-eth: use an unique MDIO bus name changed the MDIO bus name from "0" to "ixp4xx-eth-0", as a result the PHY name is not longer appropriate and will not match the MDIO bus name so PHY connection will not succeed, fix that. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 72a854f..41a8b5a 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1416,7 +1416,8 @@ static int __devinit eth_init_one(struct platform_device *pdev) __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); udelay(50); - snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, "0", plat->phy); + snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, + mdio_bus->id, plat->phy); port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(port->phydev)) { -- cgit v0.10.2 From 66d885cba670059396b2f9ed9d5f4cbead0baee0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 13 Feb 2012 06:23:12 +0000 Subject: bnx2x: fix bnx2x_storm_stats_update() on big endian commit 619c5cb6885 (New 7.0 FW: bnx2x, cnic, bnx2i, bnx2fc) added new sparse warnings. Signed-off-by: Eric Dumazet Cc: Eilon Greenstein Cc: Vladislav Zolotarov Cc: Michael Chan Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index bc0121a..1adef26 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -1081,17 +1081,17 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp) estats->rx_stat_ifhcinbadoctets_lo); ADD_64(fstats->total_bytes_received_hi, - tfunc->rcv_error_bytes.hi, + le32_to_cpu(tfunc->rcv_error_bytes.hi), fstats->total_bytes_received_lo, - tfunc->rcv_error_bytes.lo); + le32_to_cpu(tfunc->rcv_error_bytes.lo)); memcpy(estats, &(fstats->total_bytes_received_hi), sizeof(struct host_func_stats) - 2*sizeof(u32)); ADD_64(estats->error_bytes_received_hi, - tfunc->rcv_error_bytes.hi, + le32_to_cpu(tfunc->rcv_error_bytes.hi), estats->error_bytes_received_lo, - tfunc->rcv_error_bytes.lo); + le32_to_cpu(tfunc->rcv_error_bytes.lo)); ADD_64(estats->etherstatsoverrsizepkts_hi, estats->rx_stat_dot3statsframestoolong_hi, -- cgit v0.10.2 From fbcf88b8833ce2c404b9a0e7caab82b20312cbbc Mon Sep 17 00:00:00 2001 From: "Cousson, Benoit" Date: Mon, 13 Feb 2012 07:37:12 +0000 Subject: ks8851: Fix NOHZ local_softirq_pending 08 warning This fix a similar problem as in 72092cc45378176ba700034c91b7af2db524df26 and 481a8199142c050b72bff8a1956a49fd0a75bbe0 ("can: fix NOHZ local_softirq_pending 08 warning"). This fix replaces netif_rx() with netif_rx_ni() which has to be used from process/softirq context. Signed-off-by: Benoit Cousson Cc: David S. Miller Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 6b35e7d..0c3e400 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -583,7 +583,7 @@ static void ks8851_rx_pkts(struct ks8851_net *ks) ks8851_dbg_dumpkkt(ks, rxpkt); skb->protocol = eth_type_trans(skb, ks->netdev); - netif_rx(skb); + netif_rx_ni(skb); ks->netdev->stats.rx_packets++; ks->netdev->stats.rx_bytes += rxlen; -- cgit v0.10.2 From be98c2cdb15ba26148cd2bd58a857d4f7759ed38 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 13 Feb 2012 13:47:25 -0800 Subject: i387: math_state_restore() isn't called from asm It was marked asmlinkage for some really old and stale legacy reasons. Fix that and the equally stale comment. Noticed when debugging the irq_fpu_usable() bugs. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 6919e93..a5c7ae5 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -29,7 +29,7 @@ extern unsigned int sig_xstate_size; extern void fpu_init(void); extern void mxcsr_feature_mask_init(void); extern int init_fpu(struct task_struct *child); -extern asmlinkage void math_state_restore(void); +extern void math_state_restore(void); extern void __math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 482ec3a..982433b 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -599,10 +599,10 @@ void __math_state_restore(void) * Careful.. There are problems with IBM-designed IRQ13 behaviour. * Don't touch unless you *really* know how it works. * - * Must be called with kernel preemption disabled (in this case, - * local interrupts are disabled at the call-site in entry.S). + * Must be called with kernel preemption disabled (eg with local + * local interrupts as in the case of do_device_not_available). */ -asmlinkage void math_state_restore(void) +void math_state_restore(void) { struct thread_info *thread = current_thread_info(); struct task_struct *tsk = thread->task; -- cgit v0.10.2 From 5b1cbac37798805c1fee18c8cebe5c0a13975b17 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 13 Feb 2012 13:56:14 -0800 Subject: i387: make irq_fpu_usable() tests more robust Some code - especially the crypto layer - wants to use the x86 FP/MMX/AVX register set in what may be interrupt (typically softirq) context. That *can* be ok, but the tests for when it was ok were somewhat suspect. We cannot touch the thread-specific status bits either, so we'd better check that we're not going to try to save FP state or anything like that. Now, it may be that the TS bit is always cleared *before* we set the USEDFPU bit (and only set when we had already cleared the USEDFP before), so the TS bit test may actually have been sufficient, but it certainly was not obviously so. So this explicitly verifies that we will not touch the TS_USEDFPU bit, and adds a few related sanity-checks. Because it seems that somehow AES-NI is corrupting user FP state. The cause is not clear, and this patch doesn't fix it, but while debugging it I really wanted the code to be more obviously correct and robust. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index a5c7ae5..a295718 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -307,9 +307,54 @@ static inline void __clear_fpu(struct task_struct *tsk) } } +/* + * Were we in an interrupt that interrupted kernel mode? + * + * We can do a kernel_fpu_begin/end() pair *ONLY* if that + * pair does nothing at all: TS_USEDFPU must be clear (so + * that we don't try to save the FPU state), and TS must + * be set (so that the clts/stts pair does nothing that is + * visible in the interrupted kernel thread). + */ +static inline bool interrupted_kernel_fpu_idle(void) +{ + return !(current_thread_info()->status & TS_USEDFPU) && + (read_cr0() & X86_CR0_TS); +} + +/* + * Were we in user mode (or vm86 mode) when we were + * interrupted? + * + * Doing kernel_fpu_begin/end() is ok if we are running + * in an interrupt context from user mode - we'll just + * save the FPU state as required. + */ +static inline bool interrupted_user_mode(void) +{ + struct pt_regs *regs = get_irq_regs(); + return regs && user_mode_vm(regs); +} + +/* + * Can we use the FPU in kernel mode with the + * whole "kernel_fpu_begin/end()" sequence? + * + * It's always ok in process context (ie "not interrupt") + * but it is sometimes ok even from an irq. + */ +static inline bool irq_fpu_usable(void) +{ + return !in_interrupt() || + interrupted_user_mode() || + interrupted_kernel_fpu_idle(); +} + static inline void kernel_fpu_begin(void) { struct thread_info *me = current_thread_info(); + + WARN_ON_ONCE(!irq_fpu_usable()); preempt_disable(); if (me->status & TS_USEDFPU) __save_init_fpu(me->task); @@ -323,14 +368,6 @@ static inline void kernel_fpu_end(void) preempt_enable(); } -static inline bool irq_fpu_usable(void) -{ - struct pt_regs *regs; - - return !in_interrupt() || !(regs = get_irq_regs()) || \ - user_mode(regs) || (read_cr0() & X86_CR0_TS); -} - /* * Some instructions like VIA's padlock instructions generate a spurious * DNA fault but don't modify SSE registers. And these instructions @@ -367,6 +404,7 @@ static inline void irq_ts_restore(int TS_state) */ static inline void save_init_fpu(struct task_struct *tsk) { + WARN_ON_ONCE(task_thread_info(tsk)->status & TS_USEDFPU); preempt_disable(); __save_init_fpu(tsk); stts(); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 982433b..8ba27db 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -631,6 +631,7 @@ EXPORT_SYMBOL_GPL(math_state_restore); dotraplinkage void __kprobes do_device_not_available(struct pt_regs *regs, long error_code) { + WARN_ON_ONCE(!user_mode_vm(regs)); #ifdef CONFIG_MATH_EMULATION if (read_cr0() & X86_CR0_EM) { struct math_emu_info info = { }; -- cgit v0.10.2 From 05293485a0b6b1f803e8a3c0ff188c38f6969985 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 13 Feb 2012 20:51:05 +0000 Subject: XFS: xfs_trans_add_item() - don't assign in ASSERT() when compare is intended It looks to me like the two ASSERT()s in xfs_trans_add_item() really want to do a compare (==) rather than assignment (=). This patch changes it from the latter to the former. Signed-off-by: Jesper Juhl Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 329b06a..7adcdf1 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1151,8 +1151,8 @@ xfs_trans_add_item( { struct xfs_log_item_desc *lidp; - ASSERT(lip->li_mountp = tp->t_mountp); - ASSERT(lip->li_ailp = tp->t_mountp->m_ail); + ASSERT(lip->li_mountp == tp->t_mountp); + ASSERT(lip->li_ailp == tp->t_mountp->m_ail); lidp = kmem_zone_zalloc(xfs_log_item_desc_zone, KM_SLEEP | KM_NOFS); -- cgit v0.10.2 From 10f296cbfe3b93188c41463fd7a53808ebdbcbe3 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Wed, 1 Feb 2012 10:33:11 +0800 Subject: module: make module param bint handle nul value Allow bint param accept nul values, just do same as bool param. Signed-off-by: Dave Young Cc: Rusty Russell Signed-off-by: Rusty Russell diff --git a/kernel/params.c b/kernel/params.c index 32ee043..4bc965d 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -97,7 +97,8 @@ static int parse_one(char *param, for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { /* No one handled NULL, so do it here. */ - if (!val && params[i].ops->set != param_set_bool) + if (!val && params[i].ops->set != param_set_bool + && params[i].ops->set != param_set_bint) return -EINVAL; pr_debug("They are equal! Calling %p\n", params[i].ops->set); -- cgit v0.10.2 From 0d86f65ed0b727daa06d3aa176314cd175323db6 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 10 Feb 2012 20:12:27 +0100 Subject: module: fix broken isapnp handling in file2alias Handling of isapnp module aliases was broken by commit 626596e295d477c0fefa08cd5daa7dd011b1bb2c by changing "isapnp" string to "isa". The code was then modified by commit e49ce14150c64b29a8dd211df785576fa19a9858 but this bug remained. Change the string back to "isapnp". Signed-off-by: Ondrej Zary Signed-off-by: Rusty Russell diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index e8c9695..d0de2a2 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -932,7 +932,7 @@ static int do_isapnp_entry(const char *filename, (id->function >> 12) & 0x0f, (id->function >> 8) & 0x0f); return 1; } -ADD_TO_DEVTABLE("isa", struct isapnp_device_id, do_isapnp_entry); +ADD_TO_DEVTABLE("isapnp", struct isapnp_device_id, do_isapnp_entry); /* * Append a match expression for a single masked hex digit. -- cgit v0.10.2 From 2d0d68f583279dbdcc1a2ef3f81ea8d285a0adbe Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Thu, 26 Jan 2012 06:57:10 -0800 Subject: mmc: core: UHS sdio card that fails should not exceed 50MHz A UHS sdio card that fails initialization at 1.8v signaling is not in UHS mode. We cannot use the speed in the the cis to reflect the bus speed as this is the maxiumum value and will not reflect the fact that the host is operating at a lower (non uhs) bus speed. Signed-off-by: Philip Rakity Signed-off-by: Bing Zhao Reviewed-by: Aaron Lu Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index bd7bacc..12cde6e 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -98,10 +98,11 @@ fail: return ret; } -static int sdio_read_cccr(struct mmc_card *card) +static int sdio_read_cccr(struct mmc_card *card, u32 ocr) { int ret; int cccr_vsn; + int uhs = ocr & R4_18V_PRESENT; unsigned char data; unsigned char speed; @@ -149,7 +150,7 @@ static int sdio_read_cccr(struct mmc_card *card) card->scr.sda_spec3 = 0; card->sw_caps.sd3_bus_mode = 0; card->sw_caps.sd3_drv_type = 0; - if (cccr_vsn >= SDIO_CCCR_REV_3_00) { + if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) { card->scr.sda_spec3 = 1; ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_UHS, 0, &data); @@ -712,7 +713,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Read the common registers. */ - err = sdio_read_cccr(card); + err = sdio_read_cccr(card, ocr); if (err) goto remove; -- cgit v0.10.2 From b6bf30d912ddc9a3ac2ce264a04e3ec6d4e74a34 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Mon, 30 Jan 2012 05:15:29 +0100 Subject: mmc: of_mmc_spi: fix little endian support The voltage_ranges is supposed to switch from big endian to little endian. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Grant Likely Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index ab66f24..1534b58 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c @@ -113,8 +113,8 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) const int j = i * 2; u32 mask; - mask = mmc_vddrange_to_ocrmask(voltage_ranges[j], - voltage_ranges[j + 1]); + mask = mmc_vddrange_to_ocrmask(be32_to_cpu(voltage_ranges[j]), + be32_to_cpu(voltage_ranges[j + 1])); if (!mask) { ret = -EINVAL; dev_err(dev, "OF: voltage-range #%d is invalid\n", i); -- cgit v0.10.2 From 2c4967f741e87cdd63de7271b97807041dccbf3b Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Sat, 4 Feb 2012 16:14:50 -0500 Subject: mmc: core: Ensure clocks are always enabled before host interaction Ensure clocks are always enabled before any interaction with the host controller driver. This makes sure that there is no race between host execution and the core layer turning off clocks in different context with clock gating framework. Signed-off-by: Sujit Reddy Thumma Acked-by: Linus Walleij Acked-by: Per Forlin Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f545a3e..b3063b7 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -290,8 +290,11 @@ static void mmc_wait_for_req_done(struct mmc_host *host, static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, bool is_first_req) { - if (host->ops->pre_req) + if (host->ops->pre_req) { + mmc_host_clk_hold(host); host->ops->pre_req(host, mrq, is_first_req); + mmc_host_clk_release(host); + } } /** @@ -306,8 +309,11 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, int err) { - if (host->ops->post_req) + if (host->ops->post_req) { + mmc_host_clk_hold(host); host->ops->post_req(host, mrq, err); + mmc_host_clk_release(host); + } } /** @@ -620,7 +626,9 @@ int mmc_host_enable(struct mmc_host *host) int err; host->en_dis_recurs = 1; + mmc_host_clk_hold(host); err = host->ops->enable(host); + mmc_host_clk_release(host); host->en_dis_recurs = 0; if (err) { @@ -640,7 +648,9 @@ static int mmc_host_do_disable(struct mmc_host *host, int lazy) int err; host->en_dis_recurs = 1; + mmc_host_clk_hold(host); err = host->ops->disable(host, lazy); + mmc_host_clk_release(host); host->en_dis_recurs = 0; if (err < 0) { @@ -1203,8 +1213,11 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11 host->ios.signal_voltage = signal_voltage; - if (host->ops->start_signal_voltage_switch) + if (host->ops->start_signal_voltage_switch) { + mmc_host_clk_hold(host); err = host->ops->start_signal_voltage_switch(host, &host->ios); + mmc_host_clk_release(host); + } return err; } diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index fb8a5cd..08a7852 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -14,27 +14,6 @@ int mmc_register_host_class(void); void mmc_unregister_host_class(void); - -#ifdef CONFIG_MMC_CLKGATE -void mmc_host_clk_hold(struct mmc_host *host); -void mmc_host_clk_release(struct mmc_host *host); -unsigned int mmc_host_clk_rate(struct mmc_host *host); - -#else -static inline void mmc_host_clk_hold(struct mmc_host *host) -{ -} - -static inline void mmc_host_clk_release(struct mmc_host *host) -{ -} - -static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) -{ - return host->ios.clock; -} -#endif - void mmc_host_deeper_disable(struct work_struct *work); #endif diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c63ad03..5017f93 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -451,9 +451,11 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) * information and let the hardware specific code * return what is possible given the options */ + mmc_host_clk_hold(card->host); drive_strength = card->host->ops->select_drive_strength( card->sw_caps.uhs_max_dtr, host_drv_type, card_drv_type); + mmc_host_clk_release(card->host); err = mmc_sd_switch(card, 1, 2, drive_strength, status); if (err) @@ -660,9 +662,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) goto out; /* SPI mode doesn't define CMD19 */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) { + mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); + mmc_host_clk_release(card->host); + } out: kfree(status); @@ -850,8 +855,11 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, if (!reinit) { int ro = -1; - if (host->ops->get_ro) + if (host->ops->get_ro) { + mmc_host_clk_hold(card->host); ro = host->ops->get_ro(host); + mmc_host_clk_release(card->host); + } if (ro < 0) { pr_warning("%s: host does not " @@ -967,8 +975,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, * Since initialization is now complete, enable preset * value registers for UHS-I cards. */ - if (host->ops->enable_preset_value) + if (host->ops->enable_preset_value) { + mmc_host_clk_hold(card->host); host->ops->enable_preset_value(host, true); + mmc_host_clk_release(card->host); + } } else { /* * Attempt to change to high-speed (if supported) @@ -1151,8 +1162,11 @@ int mmc_attach_sd(struct mmc_host *host) return err; /* Disable preset value enable if already set since last time */ - if (host->ops->enable_preset_value) + if (host->ops->enable_preset_value) { + mmc_host_clk_hold(host); host->ops->enable_preset_value(host, false); + mmc_host_clk_release(host); + } err = mmc_send_app_op_cond(host, 0, &ocr); if (err) diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 68f81b9..f573e7f 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -146,15 +146,21 @@ static int sdio_irq_thread(void *_host) } set_current_state(TASK_INTERRUPTIBLE); - if (host->caps & MMC_CAP_SDIO_IRQ) + if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); + mmc_host_clk_release(host); + } if (!kthread_should_stop()) schedule_timeout(period); set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); - if (host->caps & MMC_CAP_SDIO_IRQ) + if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); + mmc_host_clk_release(host); + } pr_debug("%s: IRQ thread exiting with code %d\n", mmc_hostname(host), ret); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0beba1e..73e5ee8 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -444,4 +444,23 @@ static inline int mmc_boot_partition_access(struct mmc_host *host) return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); } +#ifdef CONFIG_MMC_CLKGATE +void mmc_host_clk_hold(struct mmc_host *host); +void mmc_host_clk_release(struct mmc_host *host); +unsigned int mmc_host_clk_rate(struct mmc_host *host); + +#else +static inline void mmc_host_clk_hold(struct mmc_host *host) +{ +} + +static inline void mmc_host_clk_release(struct mmc_host *host) +{ +} + +static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) +{ + return host->ios.clock; +} +#endif #endif /* LINUX_MMC_HOST_H */ -- cgit v0.10.2 From 81e499224a7ef02e1889736ebb6b432da1735ab6 Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Mon, 16 Jan 2012 14:13:03 +0800 Subject: mmc: esdhc: add PIO mode support For some FSL ESDHC controllers (e.g. P2020E, Rev1.0), the SDHC can not work on DMA mode because of the hardware bug, so we set a broken dma flag and use PIO mode. Signed-off-by: Jerry Huang Signed-off-by: Gao Guanhua Acked-by: Anton Vorontsov Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 03970bc..67b9ab0 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -2,7 +2,7 @@ * sdhci-pltfm.c Support for SDHCI platform devices * Copyright (c) 2009 Intel Corporation * - * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (c) 2007, 2011 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * * Authors: Xiaobo Xie @@ -71,6 +71,9 @@ void sdhci_get_of_property(struct platform_device *pdev) if (sdhci_of_wp_inverted(np)) host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; + if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_DMA; + clk = of_get_property(np, "clock-frequency", &size); if (clk && size == sizeof(*clk) && *clk) pltfm_host->clock = be32_to_cpup(clk); -- cgit v0.10.2 From 147c3b338d1029b6624b7dbe7f7bb98f14236f3b Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Mon, 16 Jan 2012 14:13:04 +0800 Subject: mmc: esdhc: set the timeout to the max value When accessing the card on some FSL platform boards (e.g p2020, p1010, mpc8536), the following error is reported with the timeout value calculated: mmc0: Got data interrupt 0x00000020 even though no data operation was in progress. mmc0: Got data interrupt 0x00000020 even though no data operation was in progress. So we skip the calculation of timeout and use the max value to fix it. Signed-off-by: Jerry Huang Signed-off-by: Gao Guanhua Signed-off-by: Xie Xiaobo Acked-by: Anton Vorontsov Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 67b9ab0..c5c2a48 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -74,6 +74,11 @@ void sdhci_get_of_property(struct platform_device *pdev) if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_DMA; + if (of_device_is_compatible(np, "fsl,p2020-esdhc") || + of_device_is_compatible(np, "fsl,p1010-esdhc") || + of_device_is_compatible(np, "fsl,mpc8536-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + clk = of_get_property(np, "clock-frequency", &size); if (clk && size == sizeof(*clk) && *clk) pltfm_host->clock = be32_to_cpup(clk); -- cgit v0.10.2 From 7488e924b55002e70f6d8d181f146edac3006b9f Mon Sep 17 00:00:00 2001 From: Girish K S Date: Thu, 19 Jan 2012 08:56:19 +0530 Subject: mmc: core: Fix low speed mmc card detection failure This patch fixes the failure of low speed mmc card detection. Signed-off-by: Girish K S Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 59b9ba5..dd3fcac 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1006,7 +1006,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_hs200(card); else if (host->caps & MMC_CAP_MMC_HIGHSPEED) err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1, 0); + EXT_CSD_HS_TIMING, 1, + card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; @@ -1116,7 +1117,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Activate wide bus and DDR (if supported). */ if (!mmc_card_hs200(card) && - (card->csd.mmca_vsn >= CSD_SPEC_VER_3) && + (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { static unsigned ext_csd_bits[][2] = { { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, -- cgit v0.10.2 From 6e8201f57c9359c9c5dc8f9805c15a4392492a10 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 16 Jan 2012 17:49:01 +0900 Subject: mmc: core: add the capability for broken voltage There is an understood mismatch between the voltage the host controller is set to and the voltage supplied to the card by a fixed voltage regulator. Teaching the driver to accept the mismatch is overly complicated. Instead just accept the regulator's voltage. This patch adds MMC_CAP2_BROKEN_VOLTAGE. If the voltage didn't satisfy between min_uV and max_uV, try to change the voltage in core.c. When changing the voltage, maybe use regulator_set_voltage(). In regulator_set_voltage(), check the below condition. /* sanity check */ if (!rdev->desc->ops->set_voltage && !rdev->desc->ops->set_voltage_sel) { ret = -EINVAL; goto out; } If some board should use the fixed-regulator, always return -EINVAL. Then, eMMC didn't initialize always. So if use a fixed-regulator, we need to add the MMC_CAP2_BROKEN_VOLTAGE. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Adrian Hunter Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b3063b7..1866155 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1131,6 +1131,10 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, * might not allow this operation */ voltage = regulator_get_voltage(supply); + + if (mmc->caps2 & MMC_CAP2_BROKEN_VOLTAGE) + min_uV = max_uV = voltage; + if (voltage < 0) result = voltage; else if (voltage < min_uV || voltage > max_uV) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 73e5ee8..ee2b036 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -257,6 +257,7 @@ struct mmc_host { #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ MMC_CAP2_HS200_1_2V_SDR) +#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ mmc_pm_flag_t pm_caps; /* supported pm features */ unsigned int power_notify_type; -- cgit v0.10.2 From 012e4671e445ac1dd04f40c0b974685280bedca3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 30 Jan 2012 14:27:18 +0200 Subject: mmc: sdhci-pci: set Medfield SDIO as non-removable Set Medfield SDIO as non-removable to avoid un-necessary card detect activity. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 7165e6a..6ebdc40 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -250,7 +250,7 @@ static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD; + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE; return 0; } -- cgit v0.10.2 From 3e73c36b4dc224529d0b0c0d5d69c0dacd793c42 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Tue, 31 Jan 2012 15:44:03 +0530 Subject: mmc: core: Fix PowerOff Notify suspend/resume Modified the mmc_poweroff to resume before sending the poweroff notification command. In sleep mode only AWAKE and RESET commands are allowed, so before sending the poweroff notification command resume from sleep mode and then send the notification command. PowerOff Notify is tested on a Synopsis Designware Host Controller (eMMC 4.5). The suspend to RAM and resume works fine. Signed-off-by: Girish K S Tested-by: Girish K S Reviewed-by: Saugata Das Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1866155..690255c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1256,6 +1256,7 @@ static void mmc_poweroff_notify(struct mmc_host *host) int err = 0; card = host->card; + mmc_claim_host(host); /* * Send power notify command only if card @@ -1286,6 +1287,7 @@ static void mmc_poweroff_notify(struct mmc_host *host) /* Set the card state to no notification after the poweroff */ card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION; } + mmc_release_host(host); } /* @@ -1344,12 +1346,28 @@ static void mmc_power_up(struct mmc_host *host) void mmc_power_off(struct mmc_host *host) { + int err = 0; mmc_host_clk_hold(host); host->ios.clock = 0; host->ios.vdd = 0; - mmc_poweroff_notify(host); + /* + * For eMMC 4.5 device send AWAKE command before + * POWER_OFF_NOTIFY command, because in sleep state + * eMMC 4.5 devices respond to only RESET and AWAKE cmd + */ + if (host->card && mmc_card_is_sleep(host->card) && + host->bus_ops->resume) { + err = host->bus_ops->resume(host); + + if (!err) + mmc_poweroff_notify(host); + else + pr_warning("%s: error %d during resume " + "(continue with poweroff sequence)\n", + mmc_hostname(host), err); + } /* * Reset ocr mask to be the highest possible voltage supported for @@ -2403,12 +2421,6 @@ int mmc_suspend_host(struct mmc_host *host) */ if (mmc_try_claim_host(host)) { if (host->bus_ops->suspend) { - /* - * For eMMC 4.5 device send notify command - * before sleep, because in sleep state eMMC 4.5 - * devices respond to only RESET and AWAKE cmd - */ - mmc_poweroff_notify(host); err = host->bus_ops->suspend(host); } mmc_do_release_host(host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index dd3fcac..9be0319 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1316,11 +1316,13 @@ static int mmc_suspend(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - if (mmc_card_can_sleep(host)) + if (mmc_card_can_sleep(host)) { err = mmc_card_sleep(host); - else if (!mmc_host_is_spi(host)) + if (!err) + mmc_card_set_sleep(host->card); + } else if (!mmc_host_is_spi(host)) mmc_deselect_cards(host); - host->card->state &= ~MMC_STATE_HIGHSPEED; + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_release_host(host); return err; @@ -1340,7 +1342,11 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - err = mmc_init_card(host, host->ocr, host->card); + if (mmc_card_is_sleep(host->card)) { + err = mmc_card_awake(host); + mmc_card_clr_sleep(host->card); + } else + err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; @@ -1350,7 +1356,8 @@ static int mmc_power_restore(struct mmc_host *host) { int ret; - host->card->state &= ~MMC_STATE_HIGHSPEED; + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + mmc_card_clr_sleep(host->card); mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 9f22ba5..19a41d1 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -217,6 +217,7 @@ struct mmc_card { #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ +#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -382,6 +383,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -393,7 +395,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) +#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP) +#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP) /* * Quirk add/remove for MMC products. */ -- cgit v0.10.2 From dd13b4ed4650bb3a7d6c86b549ab66a6aa0c00d8 Mon Sep 17 00:00:00 2001 From: Jurgen Heeks Date: Wed, 1 Feb 2012 13:30:55 +0100 Subject: mmc: core: Fix comparison issue in mmc_compare_ext_csds Found this issue during code review. Actually, there are two issues which both compensate together in lucky case. In unlucky case the bus width probing might not work as expected. Signed-off-by: Jurgen Heeks Reviewed-by: Namjae Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 9be0319..a480663 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -376,7 +376,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } card->ext_csd.raw_hc_erase_gap_size = - ext_csd[EXT_CSD_PARTITION_ATTRIBUTE]; + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; card->ext_csd.raw_sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.raw_sec_erase_mult = @@ -551,7 +551,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) goto out; /* only compare read only fields */ - err = (!(card->ext_csd.raw_partition_support == + err = !((card->ext_csd.raw_partition_support == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && (card->ext_csd.raw_erased_mem_count == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && -- cgit v0.10.2 From e3de2be7368d2983bd7f7ddb6e9cf5ea32363128 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 6 Jan 2012 13:06:51 +0100 Subject: mmc: tmio_mmc: fix card eject during IO with DMA When DMA is in use and the card is ejected during IO, DMA transfers have to be terminated, otherwise the dmaengine driver fails to operate properly, when the card is re-inserted. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index a95e6d9..f96c536 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include /* Definitions for values the CTRL_SDIO_STATUS register can take. */ #define TMIO_SDIO_STAT_IOIRQ 0x0001 @@ -120,6 +120,7 @@ void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data); void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable); void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata); void tmio_mmc_release_dma(struct tmio_mmc_host *host); +void tmio_mmc_abort_dma(struct tmio_mmc_host *host); #else static inline void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data) @@ -140,6 +141,10 @@ static inline void tmio_mmc_request_dma(struct tmio_mmc_host *host, static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host) { } + +static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host) +{ +} #endif #ifdef CONFIG_PM diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 7a6e6cc..8253ec1 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -34,6 +34,18 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) #endif } +void tmio_mmc_abort_dma(struct tmio_mmc_host *host) +{ + tmio_mmc_enable_dma(host, false); + + if (host->chan_rx) + dmaengine_terminate_all(host->chan_rx); + if (host->chan_tx) + dmaengine_terminate_all(host->chan_tx); + + tmio_mmc_enable_dma(host, true); +} + static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) { struct scatterlist *sg = host->sg_ptr, *sg_tmp; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index abad01b..5f9ad74 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #include "tmio_mmc.h" @@ -246,6 +246,7 @@ static void tmio_mmc_reset_work(struct work_struct *work) /* Ready for new calls */ host->mrq = NULL; + tmio_mmc_abort_dma(host); mmc_request_done(host->mmc, mrq); } @@ -272,6 +273,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host) host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); + if (mrq->cmd->error || (mrq->data && mrq->data->error)) + tmio_mmc_abort_dma(host); + mmc_request_done(host->mmc, mrq); } -- cgit v0.10.2 From 5ba85d95cae3837665241e6df12aea83b6bf7c32 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sat, 21 Jan 2012 00:41:28 +0100 Subject: mmc: sh_mmcif: fix late delayed work initialisation If the driver is loaded with a card in the slot, mmc_add_host() will schedule an immediate card-detection work, which will start IO and wait for command completion. Usually the kernel first returns to the sh_mmcif probe function, lets it finish and only then schedules the rescan work. But sometimes, expecially under heavy system load, the work will be scheduled immediately before returning to the probe method. In this case it is important for the driver to be fully prepared for IO. For sh_mmcif this means, that also the timeout work has to be initialised before calling mmc_add_host(). It is also better to prepare interrupts beforehand. Besides, since mmc_add_host() does card-detection itself, there is no need to do it again immediately afterwards. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index f5d8b53..352d479 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1327,7 +1327,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) if (ret < 0) goto clean_up2; - mmc_add_host(mmc); + INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); @@ -1338,22 +1338,24 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) } ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); if (ret) { - free_irq(irq[0], host); dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto clean_up3; + goto clean_up4; } - INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); - - mmc_detect_change(host->mmc, 0); + ret = mmc_add_host(mmc); + if (ret < 0) + goto clean_up5; dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); dev_dbg(&pdev->dev, "chip ver H'%04x\n", sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; +clean_up5: + free_irq(irq[1], host); +clean_up4: + free_irq(irq[0], host); clean_up3: - mmc_remove_host(mmc); pm_runtime_suspend(&pdev->dev); clean_up2: pm_runtime_disable(&pdev->dev); -- cgit v0.10.2 From 00d9ac08757049f334803b3d4dd202a6b1687dab Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 1 Feb 2012 16:31:56 +0100 Subject: mmc: block: Init ro_lock sysfs attr to fix lockdep warnings Signed-off-by: Rabin Vincent Signed-off-by: Johan Rudholm Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Reviewed-by: Namjae Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 0cad48a..c6a383d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1694,6 +1694,7 @@ static int mmc_add_disk(struct mmc_blk_data *md) md->power_ro_lock.show = power_ro_lock_show; md->power_ro_lock.store = power_ro_lock_store; + sysfs_attr_init(&md->power_ro_lock.attr); md->power_ro_lock.attr.mode = mode; md->power_ro_lock.attr.name = "ro_lock_until_next_power_on"; -- cgit v0.10.2 From 18ee684b8ab666329e0a0a72d8b70f16fb0e2243 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Thu, 9 Feb 2012 11:55:29 +0100 Subject: mmc: atmel-mci: save and restore sdioirq when soft reset is performed Sometimes a software reset is needed. Then some registers are saved and restored but the interrupt mask register is missing. It causes issues with sdio devices whose interrupts are masked after reset. Signed-off-by: Ludovic Desroches Cc: stable Signed-off-by: Nicolas Ferre Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index fcfe1eb..6985cdb 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -969,11 +969,14 @@ static void atmci_start_request(struct atmel_mci *host, host->data_status = 0; if (host->need_reset) { + iflags = atmci_readl(host, ATMCI_IMR); + iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB); atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN); atmci_writel(host, ATMCI_MR, host->mode_reg); if (host->caps.has_cfg_reg) atmci_writel(host, ATMCI_CFG, host->cfg_reg); + atmci_writel(host, ATMCI_IER, iflags); host->need_reset = false; } atmci_writel(host, ATMCI_SDCR, slot->sdc_reg); -- cgit v0.10.2 From f9c2a0dc42a6938ff2a80e55ca2bbd1d5581c72e Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Thu, 9 Feb 2012 14:32:43 +0900 Subject: mmc: dw_mmc: Fix PIO mode with support of highmem Current PIO mode makes a kernel crash with CONFIG_HIGHMEM. Highmem pages have a NULL from sg_virt(sg). This patch fixes the following problem. Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 817 [#1] PREEMPT SMP Modules linked in: CPU: 0 Not tainted (3.0.15-01423-gdbf465f #589) PC is at dw_mci_pull_data32+0x4c/0x9c LR is at dw_mci_read_data_pio+0x54/0x1f0 pc : [] lr : [] psr: 20000193 sp : c0619d48 ip : c0619d70 fp : c0619d6c r10: 00000000 r9 : 00000002 r8 : 00001000 r7 : 00000200 r6 : 00000000 r5 : e1dd3100 r4 : 00000000 r3 : 65622023 r2 : 0000007f r1 : eeb96000 r0 : e1dd3100 Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment xkernel Control: 10c5387d Table: 61e2004a DAC: 00000015 Process swapper (pid: 0, stack limit = 0xc06182f0) Stack: (0xc0619d48 to 0xc061a000) 9d40: e1dd3100 e1a4f000 00000000 e1dd3100 e1a4f000 00000200 9d60: c0619da4 c0619d70 c035988c c03587e4 c0619d9c e18158f4 e1dd3100 e1dd3100 9d80: 00000020 00000000 00000000 00000020 c06e8a84 00000000 c0619e04 c0619da8 9da0: c0359b24 c0359844 e18158f4 e1dd3164 e1dd3168 e1dd3150 3d02fc79 e1dd3154 9dc0: e1dd3178 00000000 00000020 00000000 e1dd3150 00000000 c10dd7e8 e1a84900 9de0: c061e7cc 00000000 00000000 0000008d c06e8a84 c061e780 c0619e4c c0619e08 9e00: c00c4738 c0359a34 3d02fc79 00000000 c0619e4c c05a1698 c05a1670 c05a165c 9e20: c04de8b0 c061e780 c061e7cc e1a84900 ffffed68 0000008d c0618000 00000000 9e40: c0619e6c c0619e50 c00c48b4 c00c46c8 c061e780 c00423ac c061e7cc ffffed68 9e60: c0619e8c c0619e70 c00c7358 c00c487c 0000008d ffffee38 c0618000 ffffed68 9e80: c0619ea4 c0619e90 c00c4258 c00c72b0 c00423ac ffffee38 c0619ecc c0619ea8 9ea0: c004241c c00c4234 ffffffff f8810000 0000006d 00000002 00000001 7fffffff 9ec0: c0619f44 c0619ed0 c0048bc0 c00423c4 220ae7a9 00000000 386f0d30 0005d3a4 9ee0: c00423ac c10dd0b8 c06f2cd8 c0618000 c0594778 c003a674 7fffffff c0619f44 9f00: 386f0d30 c0619f18 c00a6f94 c005be3c 80000013 ffffffff 386f0d30 0005d3a4 9f20: 386f0d30 0005d2d1 c10dd0a8 c10dd0b8 c06f2cd8 c0618000 c0619f74 c0619f48 9f40: c0345858 c005be00 c00a2440 c0618000 c0618000 c00410d8 c06c1944 c00410fc 9f60: c0594778 c003a674 c0619f9c c0619f78 c004a7e8 c03457b4 c0618000 c06c18f8 9f80: 00000000 c0039c70 c06c18d4 c003a674 c0619fb4 c0619fa0 c04ceafc c004a714 9fa0: c06287b4 c06c18f8 c0619ff4 c0619fb8 c0008b68 c04cea68 c0008578 00000000 9fc0: 00000000 c003a674 00000000 10c5387d c0628658 c003aa78 c062f1c4 4000406a 9fe0: 413fc090 00000000 00000000 c0619ff8 40008044 c0008858 00000000 00000000 Backtrace: [] (dw_mci_pull_data32+0x0/0x9c) from [] (dw_mci_read_data_pio+0x54/0x1f0) r6:00000200 r5:e1a4f000 r4:e1dd3100 [] (dw_mci_read_data_pio+0x0/0x1f0) from [] (dw_mci_interrupt+0xfc/0x4a4) [] (dw_mci_interrupt+0x0/0x4a4) from [] (handle_irq_event_percpu+0x7c/0x1b4) [] (handle_irq_event_percpu+0x0/0x1b4) from [] (handle_irq_event+0x44/0x64) [] (handle_irq_event+0x0/0x64) from [] (handle_fasteoi_irq+0xb4/0x124) r7:ffffed68 r6:c061e7cc r5:c00423ac r4:c061e780 [] (handle_fasteoi_irq+0x0/0x124) from [] (generic_handle_irq+0x30/0x38) r7:ffffed68 r6:c0618000 r5:ffffee38 r4:0000008d [] (generic_handle_irq+0x0/0x38) from [] (asm_do_IRQ+0x64/0xe0) r5:ffffee38 r4:c00423ac [] (asm_do_IRQ+0x0/0xe0) from [] (__irq_svc+0x80/0x14c) Exception stack(0xc0619ed0 to 0xc0619f18) Signed-off-by: Seungwon Jeon Acked-by: Will Newton Cc: stable Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 0e34279..8bec1c3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -502,8 +501,14 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->dir_status = DW_MCI_SEND_STATUS; if (dw_mci_submit_data_dma(host, data)) { + int flags = SG_MITER_ATOMIC; + if (host->data->flags & MMC_DATA_READ) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); host->sg = data->sg; - host->pio_offset = 0; host->part_buf_start = 0; host->part_buf_count = 0; @@ -972,6 +977,7 @@ static void dw_mci_tasklet_func(unsigned long priv) * generates a block interrupt, hence setting * the scatter-gather pointer to NULL. */ + sg_miter_stop(&host->sg_miter); host->sg = NULL; ctrl = mci_readl(host, CTRL); ctrl |= SDMMC_CTRL_FIFO_RESET; @@ -1311,54 +1317,44 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) static void dw_mci_read_data_pio(struct dw_mci *host) { - struct scatterlist *sg = host->sg; - void *buf = sg_virt(sg); - unsigned int offset = host->pio_offset; + struct sg_mapping_iter *sg_miter = &host->sg_miter; + void *buf; + unsigned int offset; struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; unsigned int nbytes = 0, len; + unsigned int remain, fcnt; do { - len = host->part_buf_count + - (SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift); - if (offset + len <= sg->length) { + if (!sg_miter_next(sg_miter)) + goto done; + + host->sg = sg_miter->__sg; + buf = sg_miter->addr; + remain = sg_miter->length; + offset = 0; + + do { + fcnt = (SDMMC_GET_FCNT(mci_readl(host, STATUS)) + << shift) + host->part_buf_count; + len = min(remain, fcnt); + if (!len) + break; dw_mci_pull_data(host, (void *)(buf + offset), len); - offset += len; nbytes += len; - - if (offset == sg->length) { - flush_dcache_page(sg_page(sg)); - host->sg = sg = sg_next(sg); - if (!sg) - goto done; - - offset = 0; - buf = sg_virt(sg); - } - } else { - unsigned int remaining = sg->length - offset; - dw_mci_pull_data(host, (void *)(buf + offset), - remaining); - nbytes += remaining; - - flush_dcache_page(sg_page(sg)); - host->sg = sg = sg_next(sg); - if (!sg) - goto done; - - offset = len - remaining; - buf = sg_virt(sg); - dw_mci_pull_data(host, buf, offset); - nbytes += offset; - } + remain -= len; + } while (remain); + sg_miter->consumed = offset; status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_RXDR); if (status & DW_MCI_DATA_ERROR_FLAGS) { host->data_status = status; data->bytes_xfered += nbytes; + sg_miter_stop(sg_miter); + host->sg = NULL; smp_wmb(); set_bit(EVENT_DATA_ERROR, &host->pending_events); @@ -1367,65 +1363,66 @@ static void dw_mci_read_data_pio(struct dw_mci *host) return; } } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ - host->pio_offset = offset; data->bytes_xfered += nbytes; + + if (!remain) { + if (!sg_miter_next(sg_miter)) + goto done; + sg_miter->consumed = 0; + } + sg_miter_stop(sg_miter); return; done: data->bytes_xfered += nbytes; + sg_miter_stop(sg_miter); + host->sg = NULL; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } static void dw_mci_write_data_pio(struct dw_mci *host) { - struct scatterlist *sg = host->sg; - void *buf = sg_virt(sg); - unsigned int offset = host->pio_offset; + struct sg_mapping_iter *sg_miter = &host->sg_miter; + void *buf; + unsigned int offset; struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; unsigned int nbytes = 0, len; + unsigned int fifo_depth = host->fifo_depth; + unsigned int remain, fcnt; do { - len = ((host->fifo_depth - - SDMMC_GET_FCNT(mci_readl(host, STATUS))) << shift) - - host->part_buf_count; - if (offset + len <= sg->length) { + if (!sg_miter_next(sg_miter)) + goto done; + + host->sg = sg_miter->__sg; + buf = sg_miter->addr; + remain = sg_miter->length; + offset = 0; + + do { + fcnt = ((fifo_depth - + SDMMC_GET_FCNT(mci_readl(host, STATUS))) + << shift) - host->part_buf_count; + len = min(remain, fcnt); + if (!len) + break; host->push_data(host, (void *)(buf + offset), len); - offset += len; nbytes += len; - if (offset == sg->length) { - host->sg = sg = sg_next(sg); - if (!sg) - goto done; - - offset = 0; - buf = sg_virt(sg); - } - } else { - unsigned int remaining = sg->length - offset; - - host->push_data(host, (void *)(buf + offset), - remaining); - nbytes += remaining; - - host->sg = sg = sg_next(sg); - if (!sg) - goto done; - - offset = len - remaining; - buf = sg_virt(sg); - host->push_data(host, (void *)buf, offset); - nbytes += offset; - } + remain -= len; + } while (remain); + sg_miter->consumed = offset; status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_TXDR); if (status & DW_MCI_DATA_ERROR_FLAGS) { host->data_status = status; data->bytes_xfered += nbytes; + sg_miter_stop(sg_miter); + host->sg = NULL; smp_wmb(); @@ -1435,12 +1432,20 @@ static void dw_mci_write_data_pio(struct dw_mci *host) return; } } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ - host->pio_offset = offset; data->bytes_xfered += nbytes; + + if (!remain) { + if (!sg_miter_next(sg_miter)) + goto done; + sg_miter->consumed = 0; + } + sg_miter_stop(sg_miter); return; done: data->bytes_xfered += nbytes; + sg_miter_stop(sg_miter); + host->sg = NULL; smp_wmb(); set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } @@ -1643,6 +1648,7 @@ static void dw_mci_work_routine_card(struct work_struct *work) * block interrupt, hence setting the * scatter-gather pointer to NULL. */ + sg_miter_stop(&host->sg_miter); host->sg = NULL; ctrl = mci_readl(host, CTRL); diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index e8779c6..aae5d1f 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -14,6 +14,8 @@ #ifndef LINUX_MMC_DW_MMC_H #define LINUX_MMC_DW_MMC_H +#include + #define MAX_MCI_SLOTS 2 enum dw_mci_state { @@ -40,7 +42,7 @@ struct mmc_data; * @lock: Spinlock protecting the queue and associated data. * @regs: Pointer to MMIO registers. * @sg: Scatterlist entry currently being processed by PIO code, if any. - * @pio_offset: Offset into the current scatterlist entry. + * @sg_miter: PIO mapping scatterlist iterator. * @cur_slot: The slot which is currently using the controller. * @mrq: The request currently being processed on @cur_slot, * or NULL if the controller is idle. @@ -115,7 +117,7 @@ struct dw_mci { void __iomem *regs; struct scatterlist *sg; - unsigned int pio_offset; + struct sg_mapping_iter sg_miter; struct dw_mci_slot *cur_slot; struct mmc_request *mrq; -- cgit v0.10.2 From e188dc02d3a9c911be56eca5aa114fe7e9822d53 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 3 Feb 2012 14:25:18 +0100 Subject: vfs: fix d_inode_lookup() dentry ref leak d_inode_lookup() leaks a dentry reference on IS_DEADDIR(). Signed-off-by: Miklos Szeredi CC: stable@vger.kernel.org Signed-off-by: Al Viro diff --git a/fs/namei.c b/fs/namei.c index 208c6aa..a780ea5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1095,8 +1095,10 @@ static struct dentry *d_inode_lookup(struct dentry *parent, struct dentry *dentr struct dentry *old; /* Don't create child dentry for a dead directory. */ - if (unlikely(IS_DEADDIR(inode))) + if (unlikely(IS_DEADDIR(inode))) { + dput(dentry); return ERR_PTR(-ENOENT); + } old = inode->i_op->lookup(inode, dentry, nd); if (unlikely(old)) { -- cgit v0.10.2 From 1d6f2097865e64963e90cce04980dce2f9fc023f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 22 Aug 2011 11:52:28 +0800 Subject: autofs4 - fix lockdep splat in autofs When recursing down the locks when traversing a tree/list in get_next_positive_dentry() or get_next_positive_subdir() a lock can change from being nested to being a parent which breaks lockdep. This patch tells lockdep about what we did. Signed-off-by: Steven Rostedt Acked-by: Ian Kent Signed-off-by: Al Viro diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 450f529..1feb68e 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -124,6 +124,7 @@ start: /* Negative dentry - try next */ if (!simple_positive(q)) { spin_unlock(&p->d_lock); + lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_); p = q; goto again; } @@ -186,6 +187,7 @@ again: /* Negative dentry - try next */ if (!simple_positive(ret)) { spin_unlock(&p->d_lock); + lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_); p = ret; goto again; } -- cgit v0.10.2 From 074b85175a43a23fdbde60f55feea636e0bf0f85 Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Wed, 8 Feb 2012 12:39:07 -0800 Subject: vfs: fix panic in __d_lookup() with high dentry hashtable counts When the number of dentry cache hash table entries gets too high (2147483648 entries), as happens by default on a 16TB system, use of a signed integer in the dcache_init() initialization loop prevents the dentry_hashtable from getting initialized, causing a panic in __d_lookup(). Fix this in dcache_init() and similar areas. Signed-off-by: Dimitri Sivanich Acked-by: David S. Miller Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/dcache.c b/fs/dcache.c index 16a53cc..fe19ac1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2968,7 +2968,7 @@ __setup("dhash_entries=", set_dhash_entries); static void __init dcache_init_early(void) { - int loop; + unsigned int loop; /* If hashes are distributed across NUMA nodes, defer * hash allocation until vmalloc space is available. @@ -2986,13 +2986,13 @@ static void __init dcache_init_early(void) &d_hash_mask, 0); - for (loop = 0; loop < (1 << d_hash_shift); loop++) + for (loop = 0; loop < (1U << d_hash_shift); loop++) INIT_HLIST_BL_HEAD(dentry_hashtable + loop); } static void __init dcache_init(void) { - int loop; + unsigned int loop; /* * A constructor could be added for stable state like the lists, @@ -3016,7 +3016,7 @@ static void __init dcache_init(void) &d_hash_mask, 0); - for (loop = 0; loop < (1 << d_hash_shift); loop++) + for (loop = 0; loop < (1U << d_hash_shift); loop++) INIT_HLIST_BL_HEAD(dentry_hashtable + loop); } diff --git a/fs/inode.c b/fs/inode.c index fb10d86..d3ebdbe 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1651,7 +1651,7 @@ __setup("ihash_entries=", set_ihash_entries); */ void __init inode_init_early(void) { - int loop; + unsigned int loop; /* If hashes are distributed across NUMA nodes, defer * hash allocation until vmalloc space is available. @@ -1669,13 +1669,13 @@ void __init inode_init_early(void) &i_hash_mask, 0); - for (loop = 0; loop < (1 << i_hash_shift); loop++) + for (loop = 0; loop < (1U << i_hash_shift); loop++) INIT_HLIST_HEAD(&inode_hashtable[loop]); } void __init inode_init(void) { - int loop; + unsigned int loop; /* inode slab cache */ inode_cachep = kmem_cache_create("inode_cache", @@ -1699,7 +1699,7 @@ void __init inode_init(void) &i_hash_mask, 0); - for (loop = 0; loop < (1 << i_hash_shift); loop++) + for (loop = 0; loop < (1U << i_hash_shift); loop++) INIT_HLIST_HEAD(&inode_hashtable[loop]); } diff --git a/kernel/pid.c b/kernel/pid.c index ce8e00d..9f08dfa 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -543,12 +543,12 @@ struct pid *find_ge_pid(int nr, struct pid_namespace *ns) */ void __init pidhash_init(void) { - int i, pidhash_size; + unsigned int i, pidhash_size; pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), 0, 18, HASH_EARLY | HASH_SMALL, &pidhash_shift, NULL, 4096); - pidhash_size = 1 << pidhash_shift; + pidhash_size = 1U << pidhash_shift; for (i = 0; i < pidhash_size; i++) INIT_HLIST_HEAD(&pid_hash[i]); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d2186ec..a13ded1 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5236,6 +5236,7 @@ void *__init alloc_large_system_hash(const char *tablename, max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4; do_div(max, bucketsize); } + max = min(max, 0x80000000ULL); if (numentries > max) numentries = max; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 37755cc..22ef5f9 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3240,7 +3240,8 @@ void __init tcp_init(void) { struct sk_buff *skb = NULL; unsigned long limit; - int i, max_share, cnt; + int max_share, cnt; + unsigned int i; unsigned long jiffy = jiffies; BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); @@ -3283,7 +3284,7 @@ void __init tcp_init(void) &tcp_hashinfo.bhash_size, NULL, 64 * 1024); - tcp_hashinfo.bhash_size = 1 << tcp_hashinfo.bhash_size; + tcp_hashinfo.bhash_size = 1U << tcp_hashinfo.bhash_size; for (i = 0; i < tcp_hashinfo.bhash_size; i++) { spin_lock_init(&tcp_hashinfo.bhash[i].lock); INIT_HLIST_HEAD(&tcp_hashinfo.bhash[i].chain); -- cgit v0.10.2 From 6b6dc836a195e077e76977b6c020a73de411b46d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 10 Feb 2012 11:03:00 +0100 Subject: vfs: Provide function to get superblock and wait for it to thaw In quota code we need to find a superblock corresponding to a device and wait for superblock to be unfrozen. However this waiting has to happen without s_umount semaphore because that is required for superblock to thaw. So provide a function in VFS for this to keep dances with s_umount where they belong. [AV: implementation switched to saner variant] Signed-off-by: Jan Kara Signed-off-by: Al Viro diff --git a/fs/super.c b/fs/super.c index 6015c02..6277ec6 100644 --- a/fs/super.c +++ b/fs/super.c @@ -634,6 +634,28 @@ rescan: EXPORT_SYMBOL(get_super); /** + * get_super_thawed - get thawed superblock of a device + * @bdev: device to get the superblock for + * + * Scans the superblock list and finds the superblock of the file system + * mounted on the device. The superblock is returned once it is thawed + * (or immediately if it was not frozen). %NULL is returned if no match + * is found. + */ +struct super_block *get_super_thawed(struct block_device *bdev) +{ + while (1) { + struct super_block *s = get_super(bdev); + if (!s || s->s_frozen == SB_UNFROZEN) + return s; + up_read(&s->s_umount); + vfs_check_frozen(s, SB_FREEZE_WRITE); + put_super(s); + } +} +EXPORT_SYMBOL(get_super_thawed); + +/** * get_active_super - get an active reference to the superblock of a device * @bdev: device to get the superblock for * diff --git a/include/linux/fs.h b/include/linux/fs.h index 386da09..69cd5bb 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2496,6 +2496,7 @@ extern void get_filesystem(struct file_system_type *fs); extern void put_filesystem(struct file_system_type *fs); extern struct file_system_type *get_fs_type(const char *name); extern struct super_block *get_super(struct block_device *); +extern struct super_block *get_super_thawed(struct block_device *); extern struct super_block *get_active_super(struct block_device *bdev); extern void drop_super(struct super_block *sb); extern void iterate_supers(void (*)(struct super_block *, void *), void *); -- cgit v0.10.2 From dcdbed853d9fbb0547b781ba676049b87f54129a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 10 Feb 2012 11:03:01 +0100 Subject: quota: Fix deadlock with suspend and quotas This script causes a kernel deadlock: set -e DEVICE=/dev/vg1/linear lvchange -ay $DEVICE mkfs.ext3 $DEVICE mount -t ext3 -o usrquota,grpquota $DEVICE /mnt/test quotacheck -gu /mnt/test umount /mnt/test mount -t ext3 -o usrquota,grpquota $DEVICE /mnt/test quotaon /mnt/test dmsetup suspend $DEVICE setquota -u root 1 2 3 4 /mnt/test & sleep 1 dmsetup resume $DEVICE setquota acquired semaphore s_umount for read and then tried to perform a transaction (and waits because the device is suspended). dmsetup resume tries to acquire s_umount for write before resuming the device (and waits for setquota). Fix the deadlock by grabbing a thawed superblock for quota commands which need it. Reported-by: Mikulas Patocka Signed-off-by: Jan Kara Signed-off-by: Al Viro diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 7898cd6..fc2c438 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -292,11 +292,26 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, } } +/* Return 1 if 'cmd' will block on frozen filesystem */ +static int quotactl_cmd_write(int cmd) +{ + switch (cmd) { + case Q_GETFMT: + case Q_GETINFO: + case Q_SYNC: + case Q_XGETQSTAT: + case Q_XGETQUOTA: + case Q_XQUOTASYNC: + return 0; + } + return 1; +} + /* * look up a superblock on which quota ops will be performed * - use the name of a block device to find the superblock thereon */ -static struct super_block *quotactl_block(const char __user *special) +static struct super_block *quotactl_block(const char __user *special, int cmd) { #ifdef CONFIG_BLOCK struct block_device *bdev; @@ -309,7 +324,10 @@ static struct super_block *quotactl_block(const char __user *special) putname(tmp); if (IS_ERR(bdev)) return ERR_CAST(bdev); - sb = get_super(bdev); + if (quotactl_cmd_write(cmd)) + sb = get_super_thawed(bdev); + else + sb = get_super(bdev); bdput(bdev); if (!sb) return ERR_PTR(-ENODEV); @@ -361,7 +379,7 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, pathp = &path; } - sb = quotactl_block(special); + sb = quotactl_block(special, cmds); if (IS_ERR(sb)) { ret = PTR_ERR(sb); goto out; -- cgit v0.10.2 From fcf83067bf6eb101a35620d752bd559d473cfbaa Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 12 Feb 2012 20:56:29 -0500 Subject: vfs: fix compat_sys_stat() handling of overflows in st_nlink Massaged cp_compat_stat() into form closer to cp_new_stat(); the only real issue had been in handling of st_nlink overflows - native 32bit stat(2) returns -EOVERFLOW in such situations, compat one silently loses upper bits. Signed-off-by: Al Viro diff --git a/fs/compat.c b/fs/compat.c index fa9d721..07880ba 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -131,41 +131,35 @@ asmlinkage long compat_sys_utimes(const char __user *filename, struct compat_tim static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf) { - compat_ino_t ino = stat->ino; - typeof(ubuf->st_uid) uid = 0; - typeof(ubuf->st_gid) gid = 0; - int err; + struct compat_stat tmp; - SET_UID(uid, stat->uid); - SET_GID(gid, stat->gid); + if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev)) + return -EOVERFLOW; - if ((u64) stat->size > MAX_NON_LFS || - !old_valid_dev(stat->dev) || - !old_valid_dev(stat->rdev)) + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = old_encode_dev(stat->dev); + tmp.st_ino = stat->ino; + if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) return -EOVERFLOW; - if (sizeof(ino) < sizeof(stat->ino) && ino != stat->ino) + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; - - if (clear_user(ubuf, sizeof(*ubuf))) - return -EFAULT; - - err = __put_user(old_encode_dev(stat->dev), &ubuf->st_dev); - err |= __put_user(ino, &ubuf->st_ino); - err |= __put_user(stat->mode, &ubuf->st_mode); - err |= __put_user(stat->nlink, &ubuf->st_nlink); - err |= __put_user(uid, &ubuf->st_uid); - err |= __put_user(gid, &ubuf->st_gid); - err |= __put_user(old_encode_dev(stat->rdev), &ubuf->st_rdev); - err |= __put_user(stat->size, &ubuf->st_size); - err |= __put_user(stat->atime.tv_sec, &ubuf->st_atime); - err |= __put_user(stat->atime.tv_nsec, &ubuf->st_atime_nsec); - err |= __put_user(stat->mtime.tv_sec, &ubuf->st_mtime); - err |= __put_user(stat->mtime.tv_nsec, &ubuf->st_mtime_nsec); - err |= __put_user(stat->ctime.tv_sec, &ubuf->st_ctime); - err |= __put_user(stat->ctime.tv_nsec, &ubuf->st_ctime_nsec); - err |= __put_user(stat->blksize, &ubuf->st_blksize); - err |= __put_user(stat->blocks, &ubuf->st_blocks); - return err; + SET_UID(tmp.st_uid, stat->uid); + SET_GID(tmp.st_gid, stat->gid); + tmp.st_rdev = old_encode_dev(stat->rdev); + if ((u64) stat->size > MAX_NON_LFS) + return -EOVERFLOW; + tmp.st_size = stat->size; + tmp.st_atime = stat->atime.tv_sec; + tmp.st_atime_nsec = stat->atime.tv_nsec; + tmp.st_mtime = stat->mtime.tv_sec; + tmp.st_mtime_nsec = stat->mtime.tv_nsec; + tmp.st_ctime = stat->ctime.tv_sec; + tmp.st_ctime_nsec = stat->ctime.tv_nsec; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; + return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long compat_sys_newstat(const char __user * filename, -- cgit v0.10.2 From 847c9db5cb50841589b8ebd3da0769b1b02fb3b2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 12 Feb 2012 21:00:05 -0500 Subject: ocfs2: deal with wraparounds of i_nlink in ocfs2_rename() unfortunately, nlink_t may be smaller than 32 bits and ->i_nlink on ocfs2 can grow up to 0xffffffff; storing it in nlink_t variable will lose upper bits on such architectures. Needs to be made u32, until we get kernel-side nlink_t uniformly 32bit... Signed-off-by: Al Viro diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index be24469..a9856e3 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1053,7 +1053,7 @@ static int ocfs2_rename(struct inode *old_dir, handle_t *handle = NULL; struct buffer_head *old_dir_bh = NULL; struct buffer_head *new_dir_bh = NULL; - nlink_t old_dir_nlink = old_dir->i_nlink; + u32 old_dir_nlink = old_dir->i_nlink; struct ocfs2_dinode *old_di; struct ocfs2_dir_lookup_result old_inode_dot_dot_res = { NULL, }; struct ocfs2_dir_lookup_result target_lookup_res = { NULL, }; -- cgit v0.10.2 From 454c0bfd0c6469276dec766e5b41efcf0ccf2619 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sun, 5 Feb 2012 13:50:04 +0000 Subject: powerpc/wsp: Permanently enable PCI class code workaround It appears that on the Chroma card, the class code of the root complex is still wrong even on DD2 or later chips. This could be a firmware issue, but that breaks resource allocation so let's unconditionally fix it up. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c index e0262cd..d24b3ac 100644 --- a/arch/powerpc/platforms/wsp/wsp_pci.c +++ b/arch/powerpc/platforms/wsp/wsp_pci.c @@ -468,15 +468,15 @@ static void __init wsp_pcie_configure_hw(struct pci_controller *hose) #define DUMP_REG(x) \ pr_debug("%-30s : 0x%016llx\n", #x, in_be64(hose->cfg_data + x)) -#ifdef CONFIG_WSP_DD1_WORKAROUND_BAD_PCIE_CLASS - /* WSP DD1 has a bogus class code by default in the PCI-E - * root complex's built-in P2P bridge */ + /* + * Some WSP variants has a bogus class code by default in the PCI-E + * root complex's built-in P2P bridge + */ val = in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1); pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", val); out_be64(hose->cfg_data + PCIE_REG_SYS_CFG1, (val & ~PCIE_REG_SYS_CFG1_CLASS_CODE) | (PCI_CLASS_BRIDGE_PCI << 8)); pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1)); -#endif /* CONFIG_WSP_DD1_WORKAROUND_BAD_PCIE_CLASS */ #ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS /* XXX Disable TCE caching, it doesn't work on DD1 */ -- cgit v0.10.2 From e62894273c7572cb1bec39096df605f42a66e964 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 8 Feb 2012 04:53:13 +0000 Subject: powerpc: Implement GET_IP/SET_IP With this change, helpers such as instruction_pointer() et al, get defined in the generic header in terms of GET_IP Removed the unnecessary definition of profile_pc in !CONFIG_SMP case as suggested by Mike Frysinger. Signed-off-by: Srikar Dronamraju Signed-off-by: Ananth N Mavinakayanahalli Acked-by: Mike Frysinger Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 78a2051..84cc784 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -83,8 +83,18 @@ struct pt_regs { #ifndef __ASSEMBLY__ -#define instruction_pointer(regs) ((regs)->nip) -#define user_stack_pointer(regs) ((regs)->gpr[1]) +#define GET_IP(regs) ((regs)->nip) +#define GET_USP(regs) ((regs)->gpr[1]) +#define GET_FP(regs) (0) +#define SET_FP(regs, val) + +#ifdef CONFIG_SMP +extern unsigned long profile_pc(struct pt_regs *regs); +#define profile_pc profile_pc +#endif + +#include + #define kernel_stack_pointer(regs) ((regs)->gpr[1]) static inline int is_syscall_success(struct pt_regs *regs) { @@ -99,12 +109,6 @@ static inline long regs_return_value(struct pt_regs *regs) return -regs->gpr[3]; } -#ifdef CONFIG_SMP -extern unsigned long profile_pc(struct pt_regs *regs); -#else -#define profile_pc(regs) instruction_pointer(regs) -#endif - #ifdef __powerpc64__ #define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1) #else -- cgit v0.10.2 From 7a768d30caa30e66ba89659f1845cc35b1bfc715 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 8 Feb 2012 18:11:01 +0000 Subject: powerpc/wsp: Fix IRQ affinity setting We call the cache_hwirq_map() function with a linux IRQ number but it expects a HW irq number. This triggers a BUG on multic-chip setups in addition to not doing the right thing. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c index 5768743..97fe82e 100644 --- a/arch/powerpc/platforms/wsp/ics.c +++ b/arch/powerpc/platforms/wsp/ics.c @@ -346,7 +346,7 @@ static int wsp_chip_set_affinity(struct irq_data *d, * For the moment only implement delivery to all cpus or one cpu. * Get current irq_server for the given irq */ - ret = cache_hwirq_map(ics, d->irq, cpumask); + ret = cache_hwirq_map(ics, hw_irq, cpumask); if (ret == -1) { char cpulist[128]; cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); -- cgit v0.10.2 From 6fe5f5f3ffade25aa94526010f219df3be521bf7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 8 Feb 2012 19:34:13 +0000 Subject: powerpc: Fix WARN_ON in decrementer_check_overflow We use __get_cpu_var() which triggers a false positive warning in smp_processor_id() thinking interrupts are enabled (at this point, they are soft-enabled but hard-disabled). Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 701d4ac..01e2877 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -118,10 +118,14 @@ static inline notrace void set_soft_enabled(unsigned long enable) static inline notrace void decrementer_check_overflow(void) { u64 now = get_tb_or_rtc(); - u64 *next_tb = &__get_cpu_var(decrementers_next_tb); + u64 *next_tb; + + preempt_disable(); + next_tb = &__get_cpu_var(decrementers_next_tb); if (now >= *next_tb) set_dec(1); + preempt_enable(); } notrace void arch_local_irq_restore(unsigned long en) -- cgit v0.10.2 From f1c853b53cf2b49eb32791072c8a8ee04f122f58 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Sun, 12 Feb 2012 14:28:20 +0000 Subject: powerpc/powernv: Disable interrupts while taking phb->lock We need to disable interrupts when taking the phb->lock. Otherwise we could deadlock with pci_lock taken from an interrupt. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index a70bc1e..f92b9ef 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -52,32 +52,38 @@ static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type) static unsigned int pnv_get_one_msi(struct pnv_phb *phb) { - unsigned int id; + unsigned long flags; + unsigned int id, rc; + + spin_lock_irqsave(&phb->lock, flags); - spin_lock(&phb->lock); id = find_next_zero_bit(phb->msi_map, phb->msi_count, phb->msi_next); if (id >= phb->msi_count && phb->msi_next) id = find_next_zero_bit(phb->msi_map, phb->msi_count, 0); if (id >= phb->msi_count) { - spin_unlock(&phb->lock); - return 0; + rc = 0; + goto out; } __set_bit(id, phb->msi_map); - spin_unlock(&phb->lock); - return id + phb->msi_base; + rc = id + phb->msi_base; +out: + spin_unlock_irqrestore(&phb->lock, flags); + return rc; } static void pnv_put_msi(struct pnv_phb *phb, unsigned int hwirq) { + unsigned long flags; unsigned int id; if (WARN_ON(hwirq < phb->msi_base || hwirq >= (phb->msi_base + phb->msi_count))) return; id = hwirq - phb->msi_base; - spin_lock(&phb->lock); + + spin_lock_irqsave(&phb->lock, flags); __clear_bit(id, phb->msi_map); - spin_unlock(&phb->lock); + spin_unlock_irqrestore(&phb->lock, flags); } static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) -- cgit v0.10.2 From 444080d13d05dc38d07dd3bf751d38bce7ab7c72 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 11 Jan 2012 06:56:04 +0000 Subject: powerpc/pseries: Fix partition migration hang in stop_topology_update This fixes a hang that was observed during live partition migration. Since stop_topology_update must not be called from an interrupt context, call it earlier in the migration process. The hang observed can be seen below: WARNING: at kernel/timer.c:1011 Modules linked in: ip6t_LOG xt_tcpudp xt_pkttype ipt_LOG xt_limit ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_raw xt_NOTRACK ipt_REJECT xt_state iptable_raw iptable_filter ip6table_mangle nf_conntrack_netbios_ns nf_conntrack_broadcast nf_conntrack_ipv4 nf_conntrack nf_defrag_ipv4 ip_tables ip6table_filter ip6_tables x_tables ipv6 fuse loop ibmveth sg ext3 jbd mbcache raid456 async_raid6_recov async_pq raid6_pq async_xor xor async_memcpy async_tx raid10 raid1 raid0 scsi_dh_alua scsi_dh_rdac scsi_dh_hp_sw scsi_dh_emc dm_round_robin dm_multipath scsi_dh sd_mod crc_t10dif ibmvfc scsi_transport_fc scsi_tgt scsi_mod dm_snapshot dm_mod NIP: c0000000000c52d8 LR: c00000000004be28 CTR: 0000000000000000 REGS: c00000005ffd77d0 TRAP: 0700 Not tainted (3.2.0-git-00001-g07d106d) MSR: 8000000000021032 CR: 48000084 XER: 00000001 CFAR: c00000000004be20 TASK = c00000005ec78860[0] 'swapper/3' THREAD: c00000005ec98000 CPU: 3 GPR00: 0000000000000001 c00000005ffd7a50 c000000000fbbc98 c000000000ec8340 GPR04: 00000000282a0020 0000000000000000 0000000000004000 0000000000000101 GPR08: 0000000000000012 c00000005ffd4000 0000000000000020 c000000000f3ba88 GPR12: 0000000000000000 c000000007f40900 0000000000000001 0000000000000004 GPR16: 0000000000000001 0000000000000000 0000000000000000 c000000001022310 GPR20: 0000000000000001 0000000000000000 0000000000200200 c000000001029e14 GPR24: 0000000000000000 0000000000000001 0000000000000040 c00000003f74bc80 GPR28: c00000003f74bc84 c000000000f38038 c000000000f16b58 c000000000ec8340 NIP [c0000000000c52d8] .del_timer_sync+0x28/0x60 LR [c00000000004be28] .stop_topology_update+0x20/0x38 Call Trace: [c00000005ffd7a50] [c00000005ec78860] 0xc00000005ec78860 (unreliable) [c00000005ffd7ad0] [c00000000004be28] .stop_topology_update+0x20/0x38 [c00000005ffd7b40] [c000000000028378] .__rtas_suspend_last_cpu+0x58/0x260 [c00000005ffd7bf0] [c0000000000fa230] .generic_smp_call_function_interrupt+0x160/0x358 [c00000005ffd7cf0] [c000000000036ec8] .smp_ipi_demux+0x88/0x100 [c00000005ffd7d80] [c00000000005c154] .icp_hv_ipi_action+0x5c/0x80 [c00000005ffd7e00] [c00000000012a088] .handle_irq_event_percpu+0x100/0x318 [c00000005ffd7f00] [c00000000012e774] .handle_percpu_irq+0x84/0xd0 [c00000005ffd7f90] [c000000000022ba8] .call_handle_irq+0x1c/0x2c [c00000005ec9ba20] [c00000000001157c] .do_IRQ+0x22c/0x2a8 [c00000005ec9bae0] [c0000000000054bc] hardware_interrupt_entry+0x18/0x1c Exception: 501 at .cpu_idle+0x194/0x2f8 LR = .cpu_idle+0x194/0x2f8 [c00000005ec9bdd0] [c000000000017e58] .cpu_idle+0x188/0x2f8 (unreliable) [c00000005ec9be90] [c00000000067ec18] .start_secondary+0x3e4/0x524 [c00000005ec9bf90] [c0000000000093e8] .start_secondary_prolog+0x10/0x14 Instruction dump: ebe1fff8 4e800020 fbe1fff8 7c0802a6 f8010010 7c7f1b78 f821ff81 78290464 80090014 5400019e 7c0000d0 78000fe0 <0b000000> 4800000c 7c210b78 7c421378 Signed-off-by: Brian King Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 517b1d8..9f843cd 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -716,7 +716,6 @@ static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_w int cpu; slb_set_size(SLB_MIN_SIZE); - stop_topology_update(); printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", smp_processor_id()); while (rc == H_MULTI_THREADS_ACTIVE && !atomic_read(&data->done) && @@ -732,7 +731,6 @@ static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_w rc = atomic_read(&data->error); atomic_set(&data->error, rc); - start_topology_update(); pSeries_coalesce_init(); if (wake_when_done) { @@ -846,6 +844,7 @@ int rtas_ibm_suspend_me(struct rtas_args *args) atomic_set(&data.error, 0); data.token = rtas_token("ibm,suspend-me"); data.complete = &done; + stop_topology_update(); /* Call function on all CPUs. One of us will make the * rtas call @@ -858,6 +857,8 @@ int rtas_ibm_suspend_me(struct rtas_args *args) if (atomic_read(&data.error) != 0) printk(KERN_ERR "Error doing global join\n"); + start_topology_update(); + return atomic_read(&data.error); } #else /* CONFIG_PPC_PSERIES */ diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c index b84a8b2..47226e0 100644 --- a/arch/powerpc/platforms/pseries/suspend.c +++ b/arch/powerpc/platforms/pseries/suspend.c @@ -24,6 +24,7 @@ #include #include #include +#include static u64 stream_id; static struct device suspend_dev; @@ -138,8 +139,11 @@ static ssize_t store_hibernate(struct device *dev, ssleep(1); } while (rc == -EAGAIN); - if (!rc) + if (!rc) { + stop_topology_update(); rc = pm_suspend(PM_SUSPEND_MEM); + start_topology_update(); + } stream_id = 0; -- cgit v0.10.2 From 778a785f02ad846446e91dab49331bd7d853c514 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 11 Jan 2012 09:09:58 +0000 Subject: powerpc/pseries/eeh: Fix crash when error happens during device probe EEH may happen during a PCI driver probe. If the driver is trying to access some register in a loop, the EEH code will try to print the driver name. But the driver pointer in struct pci_dev is not set until probe returns successfully. Use a function to test if the device and the driver pointer is NULL before accessing the driver's name. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 43268f1..6d42297 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -142,6 +142,11 @@ static inline const char *eeh_pci_name(struct pci_dev *pdev) return pdev ? pci_name(pdev) : ""; } +static inline const char *eeh_driver_name(struct pci_dev *pdev) +{ + return (pdev && pdev->driver) ? pdev->driver->name : ""; +} + #endif /* CONFIG_EEH */ #else /* CONFIG_PCI */ diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 5658690..c0b40af 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -551,9 +551,9 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk (KERN_ERR "EEH: %d reads ignored for recovering device at " "location=%s driver=%s pci addr=%s\n", pdn->eeh_check_count, location, - dev->driver->name, eeh_pci_name(dev)); + eeh_driver_name(dev), eeh_pci_name(dev)); printk (KERN_ERR "EEH: Might be infinite loop in %s driver\n", - dev->driver->name); + eeh_driver_name(dev)); dump_stack(); } goto dn_unlock; -- cgit v0.10.2 From 27c3afe6e1cf129faac90405121203962da08ff4 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Mon, 13 Feb 2012 23:44:22 -0500 Subject: ALSA: intel8x0: Fix default inaudible sound on Gateway M520 BugLink: https://bugs.launchpad.net/bugs/930842 The reporter states that audio is inaudible by default without muting 'External Amplifier'. Add a quirk to handle his SSID so that changing the control is not necessary. Reported-and-tested-by: Benjamin Carlson Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 9f3b01b..e0a4263 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2102,6 +2102,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { }, { .subvendor = 0x161f, + .subdevice = 0x202f, + .name = "Gateway M520", + .type = AC97_TUNE_INV_EAPD + }, + { + .subvendor = 0x161f, .subdevice = 0x203a, .name = "Gateway 4525GZ", /* AD1981B */ .type = AC97_TUNE_INV_EAPD -- cgit v0.10.2 From 40e8c738785a25be585fcf661c6bb32f1a090ef2 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 13 Feb 2012 12:18:37 +0000 Subject: drm/radeon/kms: drop lock in return path of radeon_fence_count_emitted. Silly bad return path. Reported-and-Tested-by: Mikko Vinni Reviewed-by: Alex Deucher CC: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 64ea3dd..4bd36a3 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -364,8 +364,10 @@ int radeon_fence_count_emitted(struct radeon_device *rdev, int ring) int not_processed = 0; read_lock_irqsave(&rdev->fence_lock, irq_flags); - if (!rdev->fence_drv[ring].initialized) + if (!rdev->fence_drv[ring].initialized) { + read_unlock_irqrestore(&rdev->fence_lock, irq_flags); return 0; + } if (!list_empty(&rdev->fence_drv[ring].emitted)) { struct list_head *ptr; -- cgit v0.10.2 From 6f9f8a61089bb177e53e14ee62f4a65134f3692f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Feb 2012 08:59:41 -0500 Subject: drm/radeon/kms/atom: bios scratch reg handling updates - Add missing DFP6 connection state handling - crtc routing bits not used on DCE4+ Noticed by sylware on phoronix. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 5082d17..9e72dae 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2931,6 +2931,20 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector, bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP5; } } + if ((radeon_encoder->devices & ATOM_DEVICE_DFP6_SUPPORT) && + (radeon_connector->devices & ATOM_DEVICE_DFP6_SUPPORT)) { + if (connected) { + DRM_DEBUG_KMS("DFP6 connected\n"); + bios_0_scratch |= ATOM_S0_DFP6; + bios_3_scratch |= ATOM_S3_DFP6_ACTIVE; + bios_6_scratch |= ATOM_S6_ACC_REQ_DFP6; + } else { + DRM_DEBUG_KMS("DFP6 disconnected\n"); + bios_0_scratch &= ~ATOM_S0_DFP6; + bios_3_scratch &= ~ATOM_S3_DFP6_ACTIVE; + bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP6; + } + } if (rdev->family >= CHIP_R600) { WREG32(R600_BIOS_0_SCRATCH, bios_0_scratch); @@ -2951,6 +2965,9 @@ radeon_atombios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc) struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); uint32_t bios_3_scratch; + if (ASIC_IS_DCE4(rdev)) + return; + if (rdev->family >= CHIP_R600) bios_3_scratch = RREG32(R600_BIOS_3_SCRATCH); else -- cgit v0.10.2 From b7f5b7dec3d539a84734f2bcb7e53fbb1532a40b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 13 Feb 2012 16:36:34 -0500 Subject: drm/radeon/kms: fix MSI re-arm on rv370+ MSI_REARM_EN register is a write only trigger register. There is no need RMW when re-arming. May fix: https://bugs.freedesktop.org/show_bug.cgi?id=41668 Signed-off-by: Alex Deucher CC: stable@vger.kernel.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index bfd36ab..18cd84f 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -789,9 +789,7 @@ int r100_irq_process(struct radeon_device *rdev) WREG32(RADEON_AIC_CNTL, msi_rearm | RS400_MSI_REARM); break; default: - msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN; - WREG32(RADEON_MSI_REARM_EN, msi_rearm); - WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN); + WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN); break; } } diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index ec46eb4..c05865e 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -684,9 +684,7 @@ int rs600_irq_process(struct radeon_device *rdev) WREG32(RADEON_BUS_CNTL, msi_rearm | RS600_MSI_REARM); break; default: - msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN; - WREG32(RADEON_MSI_REARM_EN, msi_rearm); - WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN); + WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN); break; } } -- cgit v0.10.2 From 68d07f64b8a11a852d48d1b05b724c3e20c0d94b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 13 Feb 2012 16:25:57 -0800 Subject: USB: Don't fail USB3 probe on missing legacy PCI IRQ. Intel has a PCI USB xhci host controller on a new platform. It doesn't have a line IRQ definition in BIOS. The Linux driver refuses to initialize this controller, but Windows works well because it only depends on MSI. Actually, Linux also can work for MSI. This patch avoids the line IRQ checking for USB3 HCDs in usb core PCI probe. It allows the xHCI driver to try to enable MSI or MSI-X first. It will fail the probe if MSI enabling failed and there's no legacy PCI IRQ. This patch should be backported to kernels as old as 2.6.32. Signed-off-by: Alex Shi Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index d136b8f..81e2c0d9 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -187,7 +187,10 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; dev->current_state = PCI_D0; - if (!dev->irq) { + /* The xHCI driver supports MSI and MSI-X, + * so don't fail if the BIOS doesn't provide a legacy IRQ. + */ + if (!dev->irq && (driver->flags & HCD_MASK) != HCD_USB3) { dev_err(&dev->dev, "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", pci_name(dev)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index eb19cba..e128232 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2447,8 +2447,10 @@ int usb_add_hcd(struct usb_hcd *hcd, && device_can_wakeup(&hcd->self.root_hub->dev)) dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); - /* enable irqs just before we start the controller */ - if (usb_hcd_is_primary_hcd(hcd)) { + /* enable irqs just before we start the controller, + * if the BIOS provides legacy PCI irqs. + */ + if (usb_hcd_is_primary_hcd(hcd) && irqnum) { retval = usb_hcd_request_irqs(hcd, irqnum, irqflags); if (retval) goto err_request_irq; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6bbe3c3..c939f5f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -352,6 +352,11 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) /* hcd->irq is -1, we have MSI */ return 0; + if (!pdev->irq) { + xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n"); + return -EINVAL; + } + /* fall back to legacy interrupt*/ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, hcd->irq_descr, hcd); -- cgit v0.10.2 From 0af2a0d0576205dda778d25c6c344fc6508fc81d Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Mon, 13 Feb 2012 20:22:08 +0000 Subject: tcp: fix tcp_shifted_skb() adjustment of lost_cnt_hint for FACK This commit ensures that lost_cnt_hint is correctly updated in tcp_shifted_skb() for FACK TCP senders. The lost_cnt_hint adjustment in tcp_sacktag_one() only applies to non-FACK senders, so FACK senders need their own adjustment. This applies the spirit of 1e5289e121372a3494402b1b131b41bfe1cf9b7f - except now that the sequence range passed into tcp_sacktag_one() is correct we need only have a special case adjustment for FACK. Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8116d06..53c8ce40 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1403,6 +1403,10 @@ static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, BUG_ON(!pcount); + /* Adjust hint for FACK. Non-FACK is handled in tcp_sacktag_one(). */ + if (tcp_is_fack(tp) && (skb == tp->lost_skb_hint)) + tp->lost_cnt_hint += pcount; + TCP_SKB_CB(prev)->end_seq += shifted; TCP_SKB_CB(skb)->seq += shifted; -- cgit v0.10.2 From fc543637525b59af38af2ce09a4dbdd7d5eb27bf Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Tue, 14 Feb 2012 09:05:46 +0000 Subject: bnx2x: remove the 'poll' module option 'poll' was a debugging option, but turning it on these days leads to kernel panic. Remove it. Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 1e3f978..2545213 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -117,10 +117,6 @@ static int dropless_fc; module_param(dropless_fc, int, 0); MODULE_PARM_DESC(dropless_fc, " Pause on exhausted host ring"); -static int poll; -module_param(poll, int, 0); -MODULE_PARM_DESC(poll, " Use polling (for debug)"); - static int mrrs = -1; module_param(mrrs, int, 0); MODULE_PARM_DESC(mrrs, " Force Max Read Req Size (0..3) (for debug)"); @@ -4834,20 +4830,11 @@ void bnx2x_drv_pulse(struct bnx2x *bp) static void bnx2x_timer(unsigned long data) { - u8 cos; struct bnx2x *bp = (struct bnx2x *) data; if (!netif_running(bp->dev)) return; - if (poll) { - struct bnx2x_fastpath *fp = &bp->fp[0]; - - for_each_cos_in_tx_queue(fp, cos) - bnx2x_tx_int(bp, &fp->txdata[cos]); - bnx2x_rx_int(fp, 1000); - } - if (!BP_NOMCP(bp)) { int mb_idx = BP_FW_MB_IDX(bp); u32 drv_pulse; @@ -10063,7 +10050,6 @@ static void __devinit bnx2x_set_modes_bitmap(struct bnx2x *bp) static int __devinit bnx2x_init_bp(struct bnx2x *bp) { int func; - int timer_interval; int rc; mutex_init(&bp->port.phy_mutex); @@ -10139,8 +10125,7 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp) bp->tx_ticks = (50 / BNX2X_BTR) * BNX2X_BTR; bp->rx_ticks = (25 / BNX2X_BTR) * BNX2X_BTR; - timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ); - bp->current_interval = (poll ? poll : timer_interval); + bp->current_interval = CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ; init_timer(&bp->timer); bp->timer.expires = jiffies + bp->current_interval; -- cgit v0.10.2 From f65bd5ec47a4461bc575d5d34902fd18b6ec5542 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 13 Feb 2012 20:19:14 +0000 Subject: RxRPC: Fix kcalloc parameters swapped The first parameter should be "number of elements" and the second parameter should be "element size". Signed-off-by: Axel Lin Acked-by: David Howells Signed-off-by: David S. Miller diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 4cba13e..ae3a035 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -232,7 +232,7 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ, if (toklen <= (n_parts + 1) * 4) return -EINVAL; - princ->name_parts = kcalloc(sizeof(char *), n_parts, GFP_KERNEL); + princ->name_parts = kcalloc(n_parts, sizeof(char *), GFP_KERNEL); if (!princ->name_parts) return -ENOMEM; @@ -355,7 +355,7 @@ static int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td, _debug("n_elem %d", n_elem); - td = kcalloc(sizeof(struct krb5_tagged_data), n_elem, + td = kcalloc(n_elem, sizeof(struct krb5_tagged_data), GFP_KERNEL); if (!td) return -ENOMEM; -- cgit v0.10.2 From 58e05f357a039a94aa36475f8c110256f693a239 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 14 Feb 2012 10:11:59 +0000 Subject: netpoll: netpoll_poll_dev() should access dev->flags commit 5a698af53f (bond: service netpoll arp queue on master device) tested IFF_SLAVE flag against dev->priv_flags instead of dev->flags Signed-off-by: Eric Dumazet Cc: WANG Cong Acked-by: Neil Horman Signed-off-by: David S. Miller diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 556b082..ddefc51 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -194,7 +194,7 @@ static void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); - if (dev->priv_flags & IFF_SLAVE) { + if (dev->flags & IFF_SLAVE) { if (dev->npinfo) { struct net_device *bond_dev = dev->master; struct sk_buff *skb; -- cgit v0.10.2 From 3013dc0cceb9baaf25d5624034eeaa259bf99004 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 14 Feb 2012 10:27:09 +0000 Subject: 3c59x: shorten timer period for slave devices Jean Delvare reported bonding on top of 3c59x adapters was not detecting network cable removal fast enough. 3c59x indeed uses a 60 seconds timer to check link status if carrier is on, and 5 seconds if carrier is off. This patch reduces timer period to 5 seconds if device is a bonding slave. Reported-by: Jean Delvare Acked-by: Jean Delvare Acked-by: Steffen Klassert Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 8153a3e..f9b74c0 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1842,7 +1842,7 @@ vortex_timer(unsigned long data) ok = 1; } - if (!netif_carrier_ok(dev)) + if (dev->flags & IFF_SLAVE || !netif_carrier_ok(dev)) next_tick = 5*HZ; if (vp->medialock) -- cgit v0.10.2 From 31794bc37bf2db84f085da52b72bfba65739b2d2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Feb 2012 22:00:47 -0800 Subject: ASoC: wm8962: Fix sidetone enumeration texts The sidetone enumeration texts have left and right swapped. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 29c4b02..0ac228b 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2564,7 +2564,7 @@ static int dsp2_event(struct snd_soc_dapm_widget *w, return 0; } -static const char *st_text[] = { "None", "Right", "Left" }; +static const char *st_text[] = { "None", "Left", "Right" }; static const struct soc_enum str_enum = SOC_ENUM_SINGLE(WM8962_DAC_DSP_MIXING_1, 2, 3, st_text); -- cgit v0.10.2 From b9e44fe5ecda4158c22bc1ea4bffa378a4f83f65 Mon Sep 17 00:00:00 2001 From: "li.rui27@zte.com.cn" Date: Tue, 14 Feb 2012 10:35:01 +0800 Subject: USB: option: cleanup zte 3g-dongle's pid in option.c 1. Remove all old mass-storage ids's pid: 0x0026,0x0053,0x0098,0x0099,0x0149,0x0150,0x0160; 2. As the pid from 0x1401 to 0x1510 which have not surely assigned to use for serial-port or mass-storage port,so i think it should be removed now, and will re-add after it have assigned in future; 3. sort the pid to WCDMA and CDMA. Signed-off-by: Rui li Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 39ed1f4..b54afce 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -788,7 +788,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff), @@ -803,7 +802,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, - /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, @@ -828,7 +826,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0053, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, @@ -836,7 +833,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff), @@ -846,7 +842,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0067, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0069, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0076, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0077, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0078, 0xff, 0xff, 0xff) }, @@ -865,8 +860,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0095, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0098, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0099, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, @@ -887,28 +880,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0143, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0144, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0145, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0146, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0148, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0149, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0150, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) }, @@ -1083,127 +1066,27 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1403, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1404, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1405, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1406, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1407, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1408, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1409, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1410, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1411, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1412, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1413, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1414, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1415, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1416, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1417, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1418, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1419, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1420, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1421, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1422, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1423, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1427, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1429, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1430, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1431, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1432, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1433, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1434, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1435, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1436, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1437, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1438, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1439, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1440, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1441, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1442, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1443, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1444, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1445, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1446, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1447, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1448, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1449, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1450, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1451, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1452, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1453, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1454, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1455, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1456, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1457, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1458, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1459, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1460, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1461, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1462, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1463, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1464, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1465, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1466, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1467, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1468, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1469, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1470, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1471, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1472, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1473, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1474, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1475, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1476, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1477, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1478, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1479, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1480, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1482, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1483, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1484, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1485, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1486, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1487, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1488, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1489, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1490, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1491, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1492, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1493, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1494, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1495, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1496, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1497, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1498, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1499, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1500, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1501, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1502, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1503, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1504, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1505, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1506, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1507, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1508, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1509, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1510, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, -- cgit v0.10.2 From 08a183f02b5fef1cd78d27ffc8281fa96d79f814 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 14 Feb 2012 16:33:27 +0100 Subject: ARM: 7323/1: Do not allow ARM_LPAE on pre-ARMv7 architectures This patch expands the Kconfig dependencies for ARM_LPAE to not allow enabling when architectures other than ARMv7 are built into the kernel. Signed-off-by: Catalin Marinas Reported-by: Russell King Signed-off-by: Russell King diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 1a3ca24..7edef91 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -631,7 +631,8 @@ comment "Processor Features" config ARM_LPAE bool "Support for the Large Physical Address Extension" - depends on MMU && CPU_V7 + depends on MMU && CPU_32v7 && !CPU_32v6 && !CPU_32v5 && \ + !CPU_32v4 && !CPU_32v3 help Say Y if you have an ARMv7 processor supporting the LPAE page table format and you would like to access memory beyond the -- cgit v0.10.2 From 6e2e340b59d2d4e7b6b7f2c2d02b0d5ca4df6458 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 14 Feb 2012 21:58:56 +0100 Subject: ARM: 7324/1: modpost: Fix section warnings for ARM for many compilers It turns out that many compilers don't show section warnings on ARM currently because handling for ARM_CALL relocs are missing from modpost.c. Based on commit c2e26114 ([ARM] 3205/1: Handle new EABI relocations when loading kernel modules) it seems that R_ARM_PC24, R_ARM_CALL and R_ARM_JUMP24 can be handled the same way. Note that at least Debian libc6-dev is missing defines for both R_ARM_CALL and R_ARM_JUMP24 in /usr/include/elf.h. So for now we need to define them in modpost.c if not defined. Acked-by: Rusty Russell Cc: Ben Hutchings Cc: Anders Kaseorg Cc: Greg KH Signed-off-by: Tony Lindgren Signed-off-by: Russell King diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 2bd594e..9adb667 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1494,6 +1494,13 @@ static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) return 0; } +#ifndef R_ARM_CALL +#define R_ARM_CALL 28 +#endif +#ifndef R_ARM_JUMP24 +#define R_ARM_JUMP24 29 +#endif + static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) { unsigned int r_typ = ELF_R_TYPE(r->r_info); @@ -1505,6 +1512,8 @@ static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) (elf->symtab_start + ELF_R_SYM(r->r_info)); break; case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: /* From ARM ABI: ((S + A) | T) - P */ r->r_addend = (int)(long)(elf->hdr + sechdr->sh_offset + -- cgit v0.10.2 From 8866f405efd4171f9d9c91901d2dd02f01bacb60 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Tue, 14 Feb 2012 05:18:48 -0500 Subject: ALSA: usb-audio: avoid integer overflow in create_fixed_stream_quirk() A malicious USB device could feed in a large nr_rates value. This would cause the subsequent call to kmemdup() to allocate a smaller buffer than expected, leading to out-of-bounds access. This patch validates the nr_rates value and reuses the limit introduced in commit 4fa0e81b ("ALSA: usb-audio: fix possible hang and overflow in parse_uac2_sample_rate_range()"). Signed-off-by: Xi Wang Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.h b/sound/usb/card.h index a39edcc..da5fa1a 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -1,6 +1,7 @@ #ifndef __USBAUDIO_CARD_H #define __USBAUDIO_CARD_H +#define MAX_NR_RATES 1024 #define MAX_PACKS 20 #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ #define MAX_URBS 8 diff --git a/sound/usb/format.c b/sound/usb/format.c index e09aba1..ddfef57 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -209,8 +209,6 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof return 0; } -#define MAX_UAC2_NR_RATES 1024 - /* * Helper function to walk the array of sample rate triplets reported by * the device. The problem is that we need to parse whole array first to @@ -255,7 +253,7 @@ static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets, fp->rates |= snd_pcm_rate_to_rate_bit(rate); nr_rates++; - if (nr_rates >= MAX_UAC2_NR_RATES) { + if (nr_rates >= MAX_NR_RATES) { snd_printk(KERN_ERR "invalid uac2 rates\n"); break; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a3ddac0..2781726 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -132,10 +132,14 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, unsigned *rate_table = NULL; fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL); - if (! fp) { + if (!fp) { snd_printk(KERN_ERR "cannot memdup\n"); return -ENOMEM; } + if (fp->nr_rates > MAX_NR_RATES) { + kfree(fp); + return -EINVAL; + } if (fp->nr_rates > 0) { rate_table = kmemdup(fp->rate_table, sizeof(int) * fp->nr_rates, GFP_KERNEL); -- cgit v0.10.2 From c38e23456278e967f094b08247ffc3711b1029b2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 15 Feb 2012 08:05:18 -0800 Subject: i387: fix sense of sanity check The check for save_init_fpu() (introduced in commit 5b1cbac37798: "i387: make irq_fpu_usable() tests more robust") was the wrong way around, but I hadn't noticed, because my "tests" were bogus: the FPU exceptions are disabled by default, so even doing a divide by zero never actually triggers this code at all unless you do extra work to enable them. So if anybody did enable them, they'd get one spurious warning. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index a295718..727c1dd 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -404,7 +404,7 @@ static inline void irq_ts_restore(int TS_state) */ static inline void save_init_fpu(struct task_struct *tsk) { - WARN_ON_ONCE(task_thread_info(tsk)->status & TS_USEDFPU); + WARN_ON_ONCE(!(task_thread_info(tsk)->status & TS_USEDFPU)); preempt_disable(); __save_init_fpu(tsk); stts(); -- cgit v0.10.2 From 61cddc57dc14a5dffa0921d9a24fd68edbb374ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 15 Feb 2012 10:23:25 +0100 Subject: regmap: Fix cache defaults initialization from raw cache defaults Currently registers with a value of 0 are ignored when initializing the register defaults from raw defaults. This worked in the past, because registers without a explicit default were assumed to have a default value of 0. This was changed in commit b03622a8 ("regmap: Ensure rbtree syncs registers set to zero properly"). As a result registers, which have a raw default value of 0 are now assumed to have no default. This again can result in unnecessary writes when syncing the cache. It will also result in unnecessary reads for e.g. the first update operation. In the case where readback is not possible this will even let the update operation fail, if the register has not been written to before. So this patch removes the check. Instead it adds a check to ignore raw defaults for registers which are volatile, since those registers are not cached. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 1ead661..d1daa5e 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -53,7 +53,7 @@ static int regcache_hw_init(struct regmap *map) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (!val) + if (regmap_volatile(map, i)) continue; count++; } @@ -70,7 +70,7 @@ static int regcache_hw_init(struct regmap *map) for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (!val) + if (regmap_volatile(map, i)) continue; map->reg_defaults[j].reg = i; map->reg_defaults[j].def = val; -- cgit v0.10.2 From 75c6062cb797afe624d65c955eb867035622e782 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Wed, 15 Feb 2012 06:22:49 +0000 Subject: mlx4: fix buffer overrun When passing MLX4_UC_STEER=1 it was translated to value 2 after mlx4_QP_ATTACH_wrapper. Therefore in new_steering_entry() unicast steer entries were added to index 2 of array of size 2. Fixing this bug by shift right to one position. Signed-off-by: Eugenia Emantayev Reviewed-by: Yevgeny Petrilin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index dcd819b..1420dbc 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2538,7 +2538,7 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, int attach = vhcr->op_modifier; int block_loopback = vhcr->in_modifier >> 31; u8 steer_type_mask = 2; - enum mlx4_steer_type type = gid[7] & steer_type_mask; + enum mlx4_steer_type type = (gid[7] & steer_type_mask) >> 1; qpn = vhcr->in_modifier & 0xffffff; err = get_res(dev, slave, qpn, RES_QP, &rqp); -- cgit v0.10.2 From 2531188b476c83fe29b1d3498a76e329f4b6f16b Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Wed, 15 Feb 2012 06:22:57 +0000 Subject: mlx4: fix QP tree trashing When adding new unicast steer entry, before moving qp to state ready, actually before calling mlx4_RST2INIT_QP_wrapper(), there were added a lot of entries with local_qpn=0 into radix tree. This fact impacted the get_res() function and proper functioning of resource tracker in addition to adding trash entries into radix tree. Signed-off-by: Eugenia Emantayev Reviewed-by: Yevgeny Petrilin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 1420dbc..629cc30 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -374,6 +374,7 @@ static struct res_common *alloc_qp_tr(int id) ret->com.res_id = id; ret->com.state = RES_QP_RESERVED; + ret->local_qpn = id; INIT_LIST_HEAD(&ret->mcg_list); spin_lock_init(&ret->mcg_spl); -- cgit v0.10.2 From 9f5b6c632eb3b495d5ae701c43e67da930b1f637 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Wed, 15 Feb 2012 06:23:16 +0000 Subject: mlx4: add unicast steering entries to resource_tracker Add unicast steering entries to resource tracker. Do qp_detach also for these entries when VF doesn't shut down gracefully. Otherwise there is leakage of these resources, since they are not tracked. Signed-off-by: Eugenia Emantayev Reviewed-by: Yevgeny Petrilin Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 629cc30..bfdb7af 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -73,6 +73,7 @@ struct res_gid { struct list_head list; u8 gid[16]; enum mlx4_protocol prot; + enum mlx4_steer_type steer; }; enum res_qp_states { @@ -2480,7 +2481,8 @@ static struct res_gid *find_gid(struct mlx4_dev *dev, int slave, } static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp, - u8 *gid, enum mlx4_protocol prot) + u8 *gid, enum mlx4_protocol prot, + enum mlx4_steer_type steer) { struct res_gid *res; int err; @@ -2496,6 +2498,7 @@ static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp, } else { memcpy(res->gid, gid, 16); res->prot = prot; + res->steer = steer; list_add_tail(&res->list, &rqp->mcg_list); err = 0; } @@ -2505,14 +2508,15 @@ static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp, } static int rem_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp, - u8 *gid, enum mlx4_protocol prot) + u8 *gid, enum mlx4_protocol prot, + enum mlx4_steer_type steer) { struct res_gid *res; int err; spin_lock_irq(&rqp->mcg_spl); res = find_gid(dev, slave, rqp, gid); - if (!res || res->prot != prot) + if (!res || res->prot != prot || res->steer != steer) err = -EINVAL; else { list_del(&res->list); @@ -2548,7 +2552,7 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, qp.qpn = qpn; if (attach) { - err = add_mcg_res(dev, slave, rqp, gid, prot); + err = add_mcg_res(dev, slave, rqp, gid, prot, type); if (err) goto ex_put; @@ -2557,7 +2561,7 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, if (err) goto ex_rem; } else { - err = rem_mcg_res(dev, slave, rqp, gid, prot); + err = rem_mcg_res(dev, slave, rqp, gid, prot, type); if (err) goto ex_put; err = mlx4_qp_detach_common(dev, &qp, gid, prot, type); @@ -2568,7 +2572,7 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, ex_rem: /* ignore error return below, already in error */ - err1 = rem_mcg_res(dev, slave, rqp, gid, prot); + err1 = rem_mcg_res(dev, slave, rqp, gid, prot, type); ex_put: put_res(dev, slave, qpn, RES_QP); @@ -2607,7 +2611,7 @@ static void detach_qp(struct mlx4_dev *dev, int slave, struct res_qp *rqp) list_for_each_entry_safe(rgid, tmp, &rqp->mcg_list, list) { qp.qpn = rqp->local_qpn; err = mlx4_qp_detach_common(dev, &qp, rgid->gid, rgid->prot, - MLX4_MC_STEER); + rgid->steer); list_del(&rgid->list); kfree(rgid); } -- cgit v0.10.2 From 72ba009b8a159e995e40d3b4e5d7d265acead983 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 15 Feb 2012 07:50:15 +0000 Subject: ipheth: Add iPhone 4S BugLink: http://bugs.launchpad.net/bugs/900802 Cc: stable@vger.kernel.org 3.2+ Signed-off-by: Till Kamppeter Signed-off-by: Tim Gardner Signed-off-by: David S. Miller diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index e84662d..dd78c4c 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -60,6 +60,7 @@ #define USB_PRODUCT_IPHONE_3GS 0x1294 #define USB_PRODUCT_IPHONE_4 0x1297 #define USB_PRODUCT_IPHONE_4_VZW 0x129c +#define USB_PRODUCT_IPHONE_4S 0x12a0 #define IPHETH_USBINTF_CLASS 255 #define IPHETH_USBINTF_SUBCLASS 253 @@ -103,6 +104,10 @@ static struct usb_device_id ipheth_table[] = { USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW, IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, IPHETH_USBINTF_PROTO) }, + { USB_DEVICE_AND_INTERFACE_INFO( + USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4S, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO) }, { } }; MODULE_DEVICE_TABLE(usb, ipheth_table); -- cgit v0.10.2 From 1cc5a735185a963ed9324a65e1f2db02bf52c0d4 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 15 Feb 2012 00:10:37 +0000 Subject: stmmac: do not discard frame on dribbling bit assert If this bit is set and the CRC error is reset, then the packet is valid. Only report this as stat info. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index d0b814e..0319d64 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -67,6 +67,7 @@ struct stmmac_extra_stats { unsigned long ipc_csum_error; unsigned long rx_collision; unsigned long rx_crc; + unsigned long dribbling_bit; unsigned long rx_length; unsigned long rx_mii; unsigned long rx_multicast; diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index d879763..ad1b627 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -201,7 +201,7 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, if (unlikely(p->des01.erx.dribbling)) { CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n"); - ret = discard_frame; + x->dribbling_bit++; } if (unlikely(p->des01.erx.sa_filter_fail)) { CHIP_DBG(KERN_ERR "GMAC RX : Source Address filter fail\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index fda5d2b..25953bb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -104,7 +104,7 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, ret = discard_frame; } if (unlikely(p->des01.rx.dribbling)) - ret = discard_frame; + x->dribbling_bit++; if (unlikely(p->des01.rx.length_error)) { x->rx_length++; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 9573303..f98e151 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -47,23 +47,25 @@ struct stmmac_stats { offsetof(struct stmmac_priv, xstats.m)} static const struct stmmac_stats stmmac_gstrings_stats[] = { + /* Transmit errors */ STMMAC_STAT(tx_underflow), STMMAC_STAT(tx_carrier), STMMAC_STAT(tx_losscarrier), STMMAC_STAT(vlan_tag), STMMAC_STAT(tx_deferred), STMMAC_STAT(tx_vlan), - STMMAC_STAT(rx_vlan), STMMAC_STAT(tx_jabber), STMMAC_STAT(tx_frame_flushed), STMMAC_STAT(tx_payload_error), STMMAC_STAT(tx_ip_header_error), + /* Receive errors */ STMMAC_STAT(rx_desc), STMMAC_STAT(sa_filter_fail), STMMAC_STAT(overflow_error), STMMAC_STAT(ipc_csum_error), STMMAC_STAT(rx_collision), STMMAC_STAT(rx_crc), + STMMAC_STAT(dribbling_bit), STMMAC_STAT(rx_length), STMMAC_STAT(rx_mii), STMMAC_STAT(rx_multicast), @@ -73,6 +75,8 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(sa_rx_filter_fail), STMMAC_STAT(rx_missed_cntr), STMMAC_STAT(rx_overflow_cntr), + STMMAC_STAT(rx_vlan), + /* Tx/Rx IRQ errors */ STMMAC_STAT(tx_undeflow_irq), STMMAC_STAT(tx_process_stopped_irq), STMMAC_STAT(tx_jabber_irq), @@ -82,6 +86,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(rx_watchdog_irq), STMMAC_STAT(tx_early_irq), STMMAC_STAT(fatal_bus_error_irq), + /* Extra info */ STMMAC_STAT(threshold), STMMAC_STAT(tx_pkt_n), STMMAC_STAT(rx_pkt_n), -- cgit v0.10.2 From 7a13f8f5b63652c035147aab5fcba7ee9101f1fb Mon Sep 17 00:00:00 2001 From: Francesco Virlinzi Date: Wed, 15 Feb 2012 00:10:38 +0000 Subject: stmmac: request_irq when use an ext wake irq line (v2) In case of we use an external Wake-Up IRQ line (priv->wol_irq != dev->irq) we need to invoke the request_irq. Signed-off-by: Francesco Virlinzi Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 96fa2da..970a3f4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1047,6 +1047,17 @@ static int stmmac_open(struct net_device *dev) goto open_error; } + /* Request the Wake IRQ in case of another line is used for WoL */ + if (priv->wol_irq != dev->irq) { + ret = request_irq(priv->wol_irq, stmmac_interrupt, + IRQF_SHARED, dev->name, dev); + if (unlikely(ret < 0)) { + pr_err("%s: ERROR: allocating the ext WoL IRQ %d " + "(error: %d)\n", __func__, priv->wol_irq, ret); + goto open_error_wolirq; + } + } + /* Enable the MAC Rx/Tx */ stmmac_set_mac(priv->ioaddr, true); @@ -1087,6 +1098,9 @@ static int stmmac_open(struct net_device *dev) return 0; +open_error_wolirq: + free_irq(dev->irq, dev); + open_error: #ifdef CONFIG_STMMAC_TIMER kfree(priv->tm); @@ -1127,6 +1141,8 @@ static int stmmac_release(struct net_device *dev) /* Free the IRQ lines */ free_irq(dev->irq, dev); + if (priv->wol_irq != dev->irq) + free_irq(priv->wol_irq, dev); /* Stop TX/RX DMA and clear the descriptors */ priv->hw->dma->stop_tx(priv->ioaddr); -- cgit v0.10.2 From cf3f047b9af49d4ee8abfa31b0ef0e99cbcaf17d Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 15 Feb 2012 00:10:39 +0000 Subject: stmmac: move hw init in the probe (v2) This patch moves the MAC HW initialization and the HW feature verification from the open to the probe function as D. Miller suggested. So the patch actually reorganizes and tidies-up some parts of the driver and indeed fixes some problem when tune its HW features. These can be overwritten by looking at the HW cap register at run-time and that generated problems. Signed-off-by: Giuseppe Cavallaro Reviewed-by: Francesco Virlinzi Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 1207400..6ca2aa3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -97,4 +97,5 @@ int stmmac_resume(struct net_device *ndev); int stmmac_suspend(struct net_device *ndev); int stmmac_dvr_remove(struct net_device *ndev); struct stmmac_priv *stmmac_dvr_probe(struct device *device, - struct plat_stmmacenet_data *plat_dat); + struct plat_stmmacenet_data *plat_dat, + void __iomem *addr); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 970a3f4..6ee593a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -241,7 +241,7 @@ static void stmmac_adjust_link(struct net_device *dev) case 1000: if (likely(priv->plat->has_gmac)) ctrl &= ~priv->hw->link.port; - stmmac_hw_fix_mac_speed(priv); + stmmac_hw_fix_mac_speed(priv); break; case 100: case 10: @@ -785,7 +785,7 @@ static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv) u32 uid = ((hwid & 0x0000ff00) >> 8); u32 synid = (hwid & 0x000000ff); - pr_info("STMMAC - user ID: 0x%x, Synopsys ID: 0x%x\n", + pr_info("stmmac - user ID: 0x%x, Synopsys ID: 0x%x\n", uid, synid); return synid; @@ -869,38 +869,6 @@ static int stmmac_get_hw_features(struct stmmac_priv *priv) return hw_cap; } -/** - * stmmac_mac_device_setup - * @dev : device pointer - * Description: this is to attach the GMAC or MAC 10/100 - * main core structures that will be completed during the - * open step. - */ -static int stmmac_mac_device_setup(struct net_device *dev) -{ - struct stmmac_priv *priv = netdev_priv(dev); - - struct mac_device_info *device; - - if (priv->plat->has_gmac) - device = dwmac1000_setup(priv->ioaddr); - else - device = dwmac100_setup(priv->ioaddr); - - if (!device) - return -ENOMEM; - - priv->hw = device; - priv->hw->ring = &ring_mode_ops; - - if (device_can_wakeup(priv->device)) { - priv->wolopts = WAKE_MAGIC; /* Magic Frame as default */ - enable_irq_wake(priv->wol_irq); - } - - return 0; -} - static void stmmac_check_ether_addr(struct stmmac_priv *priv) { /* verify if the MAC address is valid, in case of failures it @@ -930,20 +898,8 @@ static int stmmac_open(struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); int ret; - /* MAC HW device setup */ - ret = stmmac_mac_device_setup(dev); - if (ret < 0) - return ret; - stmmac_check_ether_addr(priv); - stmmac_verify_args(); - - /* Override with kernel parameters if supplied XXX CRS XXX - * this needs to have multiple instances */ - if ((phyaddr >= 0) && (phyaddr <= 31)) - priv->plat->phy_addr = phyaddr; - /* MDIO bus Registration */ ret = stmmac_mdio_register(dev); if (ret < 0) { @@ -976,44 +932,6 @@ static int stmmac_open(struct net_device *dev) goto open_error; } - stmmac_get_synopsys_id(priv); - - priv->hw_cap_support = stmmac_get_hw_features(priv); - - if (priv->hw_cap_support) { - pr_info(" Support DMA HW capability register"); - - /* We can override some gmac/dma configuration fields: e.g. - * enh_desc, tx_coe (e.g. that are passed through the - * platform) with the values from the HW capability - * register (if supported). - */ - priv->plat->enh_desc = priv->dma_cap.enh_desc; - priv->plat->tx_coe = priv->dma_cap.tx_coe; - priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; - - /* By default disable wol on magic frame if not supported */ - if (!priv->dma_cap.pmt_magic_frame) - priv->wolopts &= ~WAKE_MAGIC; - - } else - pr_info(" No HW DMA feature register supported"); - - /* Select the enhnaced/normal descriptor structures */ - stmmac_selec_desc_mode(priv); - - /* PMT module is not integrated in all the MAC devices. */ - if (priv->plat->pmt) { - pr_info(" Remote wake-up capable\n"); - device_set_wakeup_capable(priv->device, 1); - } - - priv->rx_coe = priv->hw->mac->rx_coe(priv->ioaddr); - if (priv->rx_coe) - pr_info(" Checksum Offload Engine supported\n"); - if (priv->plat->tx_coe) - pr_info(" Checksum insertion supported\n"); - /* Create and initialize the TX/RX descriptors chains. */ priv->dma_tx_size = STMMAC_ALIGN(dma_txsize); priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize); @@ -1030,14 +948,14 @@ static int stmmac_open(struct net_device *dev) /* Copy the MAC addr into the HW */ priv->hw->mac->set_umac_addr(priv->ioaddr, dev->dev_addr, 0); + /* If required, perform hw setup of the bus. */ if (priv->plat->bus_setup) priv->plat->bus_setup(priv->ioaddr); + /* Initialize the MAC Core */ priv->hw->mac->core_init(priv->ioaddr); - netdev_update_features(dev); - /* Request the IRQ lines */ ret = request_irq(dev->irq, stmmac_interrupt, IRQF_SHARED, dev->name, dev); @@ -1073,7 +991,7 @@ static int stmmac_open(struct net_device *dev) #ifdef CONFIG_STMMAC_DEBUG_FS ret = stmmac_init_fs(dev); if (ret < 0) - pr_warning("\tFailed debugFS registration"); + pr_warning("%s: failed debugFS registration\n", __func__); #endif /* Start the ball rolling... */ DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name); @@ -1083,6 +1001,7 @@ static int stmmac_open(struct net_device *dev) #ifdef CONFIG_STMMAC_TIMER priv->tm->timer_start(tmrate); #endif + /* Dump DMA/MAC registers */ if (netif_msg_hw(priv)) { priv->hw->mac->dump_regs(priv->ioaddr); @@ -1805,13 +1724,77 @@ static const struct net_device_ops stmmac_netdev_ops = { }; /** + * stmmac_hw_init - Init the MAC device + * @priv : pointer to the private device structure. + * Description: this function detects which MAC device + * (GMAC/MAC10-100) has to attached, checks the HW capability + * (if supported) and sets the driver's features (for example + * to use the ring or chaine mode or support the normal/enh + * descriptor structure). + */ +static int stmmac_hw_init(struct stmmac_priv *priv) +{ + int ret = 0; + struct mac_device_info *mac; + + /* Identify the MAC HW device */ + if (priv->plat->has_gmac) + mac = dwmac1000_setup(priv->ioaddr); + else + mac = dwmac100_setup(priv->ioaddr); + if (!mac) + return -ENOMEM; + + priv->hw = mac; + + /* To use the chained or ring mode */ + priv->hw->ring = &ring_mode_ops; + + /* Get and dump the chip ID */ + stmmac_get_synopsys_id(priv); + + /* Get the HW capability (new GMAC newer than 3.50a) */ + priv->hw_cap_support = stmmac_get_hw_features(priv); + if (priv->hw_cap_support) { + pr_info(" DMA HW capability register supported"); + + /* We can override some gmac/dma configuration fields: e.g. + * enh_desc, tx_coe (e.g. that are passed through the + * platform) with the values from the HW capability + * register (if supported). + */ + priv->plat->enh_desc = priv->dma_cap.enh_desc; + priv->plat->tx_coe = priv->dma_cap.tx_coe; + priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; + } else + pr_info(" No HW DMA feature register supported"); + + /* Select the enhnaced/normal descriptor structures */ + stmmac_selec_desc_mode(priv); + + priv->rx_coe = priv->hw->mac->rx_coe(priv->ioaddr); + if (priv->rx_coe) + pr_info(" RX Checksum Offload Engine supported\n"); + if (priv->plat->tx_coe) + pr_info(" TX Checksum insertion supported\n"); + + if (priv->plat->pmt) { + pr_info(" Wake-Up On Lan supported\n"); + device_set_wakeup_capable(priv->device, 1); + } + + return ret; +} + +/** * stmmac_dvr_probe * @device: device pointer * Description: this is the main probe function used to * call the alloc_etherdev, allocate the priv structure. */ struct stmmac_priv *stmmac_dvr_probe(struct device *device, - struct plat_stmmacenet_data *plat_dat) + struct plat_stmmacenet_data *plat_dat, + void __iomem *addr) { int ret = 0; struct net_device *ndev = NULL; @@ -1831,10 +1814,27 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, ether_setup(ndev); - ndev->netdev_ops = &stmmac_netdev_ops; stmmac_set_ethtool_ops(ndev); + priv->pause = pause; + priv->plat = plat_dat; + priv->ioaddr = addr; + priv->dev->base_addr = (unsigned long)addr; + + /* Verify driver arguments */ + stmmac_verify_args(); - ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + /* Override with kernel parameters if supplied XXX CRS XXX + * this needs to have multiple instances */ + if ((phyaddr >= 0) && (phyaddr <= 31)) + priv->plat->phy_addr = phyaddr; + + /* Init MAC and get the capabilities */ + stmmac_hw_init(priv); + + ndev->netdev_ops = &stmmac_netdev_ops; + + ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM; ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA; ndev->watchdog_timeo = msecs_to_jiffies(watchdog); #ifdef STMMAC_VLAN_TAG_USED @@ -1846,8 +1846,6 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, if (flow_ctrl) priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */ - priv->pause = pause; - priv->plat = plat_dat; netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); spin_lock_init(&priv->lock); @@ -1855,15 +1853,10 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, ret = register_netdev(ndev); if (ret) { - pr_err("%s: ERROR %i registering the device\n", - __func__, ret); + pr_err("%s: ERROR %i registering the device\n", __func__, ret); goto error; } - DBG(probe, DEBUG, "%s: Scatter/Gather: %s - HW checksums: %s\n", - ndev->name, (ndev->features & NETIF_F_SG) ? "on" : "off", - (ndev->features & NETIF_F_IP_CSUM) ? "on" : "off"); - return priv; error: diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index c796de9..50ad5b8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -96,13 +96,11 @@ static int __devinit stmmac_pci_probe(struct pci_dev *pdev, stmmac_default_data(); - priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat); + priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr); if (!priv) { - pr_err("%s: main drivr probe failed", __func__); + pr_err("%s: main driver probe failed", __func__); goto err_out; } - priv->ioaddr = addr; - priv->dev->base_addr = (unsigned long)addr; priv->dev->irq = pdev->irq; priv->wol_irq = pdev->irq; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 1ac8324..3aad981 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -59,16 +59,20 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) goto out_release_region; } plat_dat = pdev->dev.platform_data; - priv = stmmac_dvr_probe(&(pdev->dev), plat_dat); + + /* Custom initialisation (if needed)*/ + if (plat_dat->init) { + ret = plat_dat->init(pdev); + if (unlikely(ret)) + goto out_unmap; + } + + priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr); if (!priv) { - pr_err("%s: main drivr probe failed", __func__); + pr_err("%s: main driver probe failed", __func__); goto out_unmap; } - priv->ioaddr = addr; - /* Set the I/O base addr */ - priv->dev->base_addr = (unsigned long)addr; - /* Get the MAC information */ priv->dev->irq = platform_get_irq_byname(pdev, "macirq"); if (priv->dev->irq == -ENXIO) { @@ -92,13 +96,6 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv->dev); - /* Custom initialisation */ - if (priv->plat->init) { - ret = priv->plat->init(pdev); - if (unlikely(ret)) - goto out_unmap; - } - pr_debug("STMMAC platform driver registration completed"); return 0; -- cgit v0.10.2 From 78a5249fc9dc0f8c70221751dc79b6d918e5ee9a Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 15 Feb 2012 00:10:40 +0000 Subject: stmmac: update the driver version to Feb 2012 (v2) Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 6ca2aa3..b4b095f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -21,7 +21,7 @@ *******************************************************************************/ #define STMMAC_RESOURCE_NAME "stmmaceth" -#define DRV_MODULE_VERSION "Dec_2011" +#define DRV_MODULE_VERSION "Feb_2012" #include #include #include "common.h" -- cgit v0.10.2 From 237114384ab22c174ec4641e809f8e6cbcfce774 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 15 Feb 2012 04:09:46 +0000 Subject: veth: Enforce minimum size of VETH_INFO_PEER VETH_INFO_PEER carries struct ifinfomsg plus optional IFLA attributes. A minimal size of sizeof(struct ifinfomsg) must be enforced or we may risk accessing that struct beyond the limits of the netlink message. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 49f4667..4a34028 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -422,7 +422,9 @@ static void veth_dellink(struct net_device *dev, struct list_head *head) unregister_netdevice_queue(peer, head); } -static const struct nla_policy veth_policy[VETH_INFO_MAX + 1]; +static const struct nla_policy veth_policy[VETH_INFO_MAX + 1] = { + [VETH_INFO_PEER] = { .len = sizeof(struct ifinfomsg) }, +}; static struct rtnl_link_ops veth_link_ops = { .kind = DRV_NAME, -- cgit v0.10.2 From 6c23e4132258be41218584cfb37a43d9346cdd64 Mon Sep 17 00:00:00 2001 From: Jan Weitzel Date: Tue, 14 Feb 2012 21:35:15 +0000 Subject: net/ethernet: ks8851_mll fix irq handling There a two different irq variables ks->irq and netdev->irq. Only ks->irq is set on probe, so disabling irq in ks_start_xmit fails. This patches remove ks->irq from private data and use only netdev->irq. Tested on a kernel 3.0 based OMAP4430 SMP Board Signed-off-by: Jan Weitzel Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index e58e78e..231176f 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -394,7 +394,6 @@ union ks_tx_hdr { * @msg_enable : The message flags controlling driver output (see ethtool). * @frame_cnt : number of frames received. * @bus_width : i/o bus width. - * @irq : irq number assigned to this device. * @rc_rxqcr : Cached copy of KS_RXQCR. * @rc_txcr : Cached copy of KS_TXCR. * @rc_ier : Cached copy of KS_IER. @@ -441,7 +440,6 @@ struct ks_net { u32 msg_enable; u32 frame_cnt; int bus_width; - int irq; u16 rc_rxqcr; u16 rc_txcr; @@ -907,10 +905,10 @@ static int ks_net_open(struct net_device *netdev) netif_dbg(ks, ifup, ks->netdev, "%s - entry\n", __func__); /* reset the HW */ - err = request_irq(ks->irq, ks_irq, KS_INT_FLAGS, DRV_NAME, netdev); + err = request_irq(netdev->irq, ks_irq, KS_INT_FLAGS, DRV_NAME, netdev); if (err) { - pr_err("Failed to request IRQ: %d: %d\n", ks->irq, err); + pr_err("Failed to request IRQ: %d: %d\n", netdev->irq, err); return err; } @@ -955,7 +953,7 @@ static int ks_net_stop(struct net_device *netdev) /* set powermode to soft power down to save power */ ks_set_powermode(ks, PMECR_PM_SOFTDOWN); - free_irq(ks->irq, netdev); + free_irq(netdev->irq, netdev); mutex_unlock(&ks->lock); return 0; } @@ -1545,10 +1543,10 @@ static int __devinit ks8851_probe(struct platform_device *pdev) if (!ks->hw_addr_cmd) goto err_ioremap1; - ks->irq = platform_get_irq(pdev, 0); + netdev->irq = platform_get_irq(pdev, 0); - if (ks->irq < 0) { - err = ks->irq; + if (netdev->irq < 0) { + err = netdev->irq; goto err_get_irq; } -- cgit v0.10.2 From 8e43a905dd574f54c5715d978318290ceafbe275 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 15 Feb 2012 16:01:42 +0100 Subject: ARM: 7325/1: fix v7 boot with lockdep enabled Bootup with lockdep enabled has been broken on v7 since b46c0f74657d ("ARM: 7321/1: cache-v7: Disable preemption when reading CCSIDR"). This is because v7_setup (which is called very early during boot) calls v7_flush_dcache_all, and the save_and_disable_irqs added by that patch ends up attempting to call into lockdep C code (trace_hardirqs_off()) when we are in no position to execute it (no stack, MMU off). Fix this by using a notrace variant of save_and_disable_irqs. The code already uses the notrace variant of restore_irqs. Reviewed-by: Nicolas Pitre Acked-by: Stephen Boyd Cc: Catalin Marinas Cc: stable@vger.kernel.org Signed-off-by: Rabin Vincent Signed-off-by: Russell King diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 62f8095..23371b1 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -137,6 +137,11 @@ disable_irq .endm + .macro save_and_disable_irqs_notrace, oldcpsr + mrs \oldcpsr, cpsr + disable_irq_notrace + .endm + /* * Restore interrupt state previously stored in a register. We don't * guarantee that this will preserve the flags. diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 7a24d39..a655d3d 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -55,7 +55,7 @@ loop1: cmp r1, #2 @ see what cache we have at this level blt skip @ skip if no cache, or just i-cache #ifdef CONFIG_PREEMPT - save_and_disable_irqs r9 @ make cssr&csidr read atomic + save_and_disable_irqs_notrace r9 @ make cssr&csidr read atomic #endif mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr isb @ isb to sych the new cssr&csidr -- cgit v0.10.2 From 4272f98a1ae81709fc5c804c33c044064e419cd9 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Wed, 16 Nov 2011 12:36:39 +0100 Subject: ARM: 7164/3: PL330: Fix the size of the dst_cache_ctrl field dst_cache_ctrl affects bits 3, 1 and 0 of AWCACHE but it is a 3-bit field in the Channel Control Register (see Table 3-21 of the DMA-330 Technical Reference Manual) and should be programmed as such. Reference: <1320244259-10496-3-git-send-email-javi.merino@arm.com> Signed-off-by: Javi Merino Acked-by: Jassi Brar Signed-off-by: Russell King diff --git a/arch/arm/include/asm/hardware/pl330.h b/arch/arm/include/asm/hardware/pl330.h index 575fa81..c182138 100644 --- a/arch/arm/include/asm/hardware/pl330.h +++ b/arch/arm/include/asm/hardware/pl330.h @@ -41,7 +41,7 @@ enum pl330_dstcachectrl { DCCTRL1, /* Bufferable only */ DCCTRL2, /* Cacheable, but do not allocate */ DCCTRL3, /* Cacheable and bufferable, but do not allocate */ - DINVALID1 = 8, + DINVALID1, /* AWCACHE = 0x1000 */ DINVALID2, DCCTRL6, /* Cacheable write-through, allocate on writes only */ DCCTRL7, /* Cacheable write-back, allocate on writes only */ -- cgit v0.10.2 From 46e33c606af8e0caeeca374103189663d877c0d6 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Wed, 15 Feb 2012 17:36:39 +0100 Subject: ARM: 7326/2: PL330: fix null pointer dereference in pl330_chan_ctrl() This fixes the thrd->req_running field being accessed before thrd is checked for null. The error was introduced in abb959f: ARM: 7237/1: PL330: Fix driver freeze Reference: <1326458191-23492-1-git-send-email-mans.rullgard@linaro.org> Cc: stable@kernel.org Signed-off-by: Mans Rullgard Acked-by: Javi Merino Signed-off-by: Russell King diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index d8e44a4..ff3ad22 100644 --- a/arch/arm/common/pl330.c +++ b/arch/arm/common/pl330.c @@ -1502,12 +1502,13 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) struct pl330_thread *thrd = ch_id; struct pl330_dmac *pl330; unsigned long flags; - int ret = 0, active = thrd->req_running; + int ret = 0, active; if (!thrd || thrd->free || thrd->dmac->state == DYING) return -EINVAL; pl330 = thrd->dmac; + active = thrd->req_running; spin_lock_irqsave(&pl330->lock, flags); -- cgit v0.10.2 From fee6a3c33a8f137f39cd9997b0476411f73576c7 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Wed, 15 Feb 2012 21:17:12 +0100 Subject: ARM: 7327/1: need to include asm/system.h in asm/processor.h For files that include asm/processor.h but not asm/system.h: arch/arm/mach-msm/include/mach/uncompress.h: In function 'putc': arch/arm/mach-msm/include/mach/uncompress.h:48:3: error: implicit declaration of function 'smp_mb' [-Werror=implicit-function-declaration] In this case, smp_mb() is from the cpu_relax() call in the msm putc(). It likely went uncaught when the uncompress.h change went in since the defconfig didn't enable that code path, but later changes (e76f4750f4: ARM: debug: arrange Kconfig options more logically) resulted in the option being on for msm_defconfig and thus exposed it. Signed-off-by: Olof Johansson Signed-off-by: Russell King diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index ce280b8..cb8d638 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef __KERNEL__ #define STACK_TOP ((current->personality & ADDR_LIMIT_32BIT) ? \ -- cgit v0.10.2 From 1c8ecf80fdee4e7b23a9e7da7ff9bd59ba2dcf96 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Tue, 14 Feb 2012 11:44:48 -0200 Subject: drm/i915: do not enable RC6p on Sandy Bridge With base on latest findings, RC6p seems to be respondible for RC6-related issues on Sandy Bridge platform. To work-around those issues, the previous solution was to completely disable RC6 on Sandy Bridge for the past few releases, even if plain RC6 was not giving any issues. What this patch does is preventing RC6p from being enabled on Sandy Bridge even if users enable RC6 via a kernel parameter. So it won't change the defaults in any way, but will ensure that if users do enable RC6 manually it won't break their machines by enabling this extra state. Proper fix for this (enabling specific RC6 states according to the GPU generation) were proposed for the -next kernel, but we are too late in the release process now to pick such changes. Acked-by: Keith Packard Signed-off-by: Eugeni Dodonov Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d9b042b..049804e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8182,8 +8182,8 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */ if (intel_enable_rc6(dev_priv->dev)) - rc6_mask = GEN6_RC_CTL_RC6p_ENABLE | - GEN6_RC_CTL_RC6_ENABLE; + rc6_mask = GEN6_RC_CTL_RC6_ENABLE | + (IS_GEN7(dev_priv->dev)) ? GEN6_RC_CTL_RC6p_ENABLE : 0; I915_WRITE(GEN6_RC_CONTROL, rc6_mask | -- cgit v0.10.2 From f2ea0f5f04c97b48c88edccba52b0682fbe45087 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 14 Jan 2012 21:44:49 +0300 Subject: crypto: sha512 - use standard ror64() Use standard ror64() instead of hand-written. There is no standard ror64, so create it. The difference is shift value being "unsigned int" instead of uint64_t (for which there is no reason). gcc starts to emit native ROR instructions which it doesn't do for some reason currently. This should make the code faster. Patch survives in-tree crypto test and ping flood with hmac(sha512) on. Signed-off-by: Alexey Dobriyan Signed-off-by: Herbert Xu diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index f04af93..107f6f7 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -31,11 +31,6 @@ static inline u64 Maj(u64 x, u64 y, u64 z) return (x & y) | (z & (x | y)); } -static inline u64 RORu64(u64 x, u64 y) -{ - return (x >> y) | (x << (64 - y)); -} - static const u64 sha512_K[80] = { 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, @@ -66,10 +61,10 @@ static const u64 sha512_K[80] = { 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, }; -#define e0(x) (RORu64(x,28) ^ RORu64(x,34) ^ RORu64(x,39)) -#define e1(x) (RORu64(x,14) ^ RORu64(x,18) ^ RORu64(x,41)) -#define s0(x) (RORu64(x, 1) ^ RORu64(x, 8) ^ (x >> 7)) -#define s1(x) (RORu64(x,19) ^ RORu64(x,61) ^ (x >> 6)) +#define e0(x) (ror64(x,28) ^ ror64(x,34) ^ ror64(x,39)) +#define e1(x) (ror64(x,14) ^ ror64(x,18) ^ ror64(x,41)) +#define s0(x) (ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7)) +#define s1(x) (ror64(x,19) ^ ror64(x,61) ^ (x >> 6)) static inline void LOAD_OP(int I, u64 *W, const u8 *input) { diff --git a/include/linux/bitops.h b/include/linux/bitops.h index a3ef66a..fc8a3ff 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -50,6 +50,26 @@ static inline unsigned long hweight_long(unsigned long w) } /** + * rol64 - rotate a 64-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u64 rol64(__u64 word, unsigned int shift) +{ + return (word << shift) | (word >> (64 - shift)); +} + +/** + * ror64 - rotate a 64-bit value right + * @word: value to rotate + * @shift: bits to roll + */ +static inline __u64 ror64(__u64 word, unsigned int shift) +{ + return (word >> shift) | (word << (64 - shift)); +} + +/** * rol32 - rotate a 32-bit value left * @word: value to rotate * @shift: bits to roll -- cgit v0.10.2 From 40c8cefaaf12734327db7199a56e60058d98e7b6 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Fri, 6 Jan 2012 12:34:07 +0000 Subject: powerpc: Fix kernel log of oops/panic instruction dump A kernel oops/panic prints an instruction dump showing several instructions before and after the instruction which caused the oops/panic. The code intended that the faulting instruction be enclosed in angle brackets, however a bug caused the faulting instruction to be interpreted by printk() as the message log level. To fix this, the KERN_CONT log level is added before the actual text of the printed message. === Before the patch === [ 1081.587266] Instruction dump: [ 1081.590236] 7c000110 7c0000f8 5400077c 552907f6 7d290378 992b0003 4e800020 38000001 [ 1081.598034] 3d20c03a 9009a114 7c0004ac 39200000 [ 1081.602500] 4e800020 3803ffd0 2b800009 <4>[ 1081.587266] Instruction dump: <4>[ 1081.590236] 7c000110 7c0000f8 5400077c 552907f6 7d290378 992b0003 4e800020 38000001 <4>[ 1081.598034] 3d20c03a 9009a114 7c0004ac 39200000 <98090000>[ 1081.602500] 4e800020 3803ffd0 2b800009 === After the patch === [ 51.385216] Instruction dump: [ 51.388186] 7c000110 7c0000f8 5400077c 552907f6 7d290378 992b0003 4e800020 38000001 [ 51.395986] 3d20c03a 9009a114 7c0004ac 39200000 <98090000> 4e800020 3803ffd0 2b800009 <4>[ 51.385216] Instruction dump: <4>[ 51.388186] 7c000110 7c0000f8 5400077c 552907f6 7d290378 992b0003 4e800020 38000001 <4>[ 51.395986] 3d20c03a 9009a114 7c0004ac 39200000 <98090000> 4e800020 3803ffd0 2b800009 Signed-off-by: Ira W. Snyder Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index ebe5766..d817ab0 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -566,12 +566,12 @@ static void show_instructions(struct pt_regs *regs) */ if (!__kernel_text_address(pc) || __get_user(instr, (unsigned int __user *)pc)) { - printk("XXXXXXXX "); + printk(KERN_CONT "XXXXXXXX "); } else { if (regs->nip == pc) - printk("<%08x> ", instr); + printk(KERN_CONT "<%08x> ", instr); else - printk("%08x ", instr); + printk(KERN_CONT "%08x ", instr); } pc += sizeof(int); -- cgit v0.10.2 From 13635dfdc6aa8d2890e02dc441decfcb4ae63e14 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 14 Feb 2012 18:22:20 +0000 Subject: powerpc/fsl/pci: Fix PCIe fixup regression Upstream changes to the way PHB resources are registered broke the resource fixup for FSL boards. We can no longer rely on the resource pointer array for the PHB's pci_bus structure, so let's leave it alone and go straight for the PHB resources instead. This also makes the code generally more readable. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 30eb17e..6073288 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -385,26 +385,36 @@ static void __init setup_pci_cmd(struct pci_controller *hose) void fsl_pcibios_fixup_bus(struct pci_bus *bus) { struct pci_controller *hose = pci_bus_to_host(bus); - int i; - - if ((bus->parent == hose->bus) && - ((fsl_pcie_bus_fixup && - early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) || - (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK))) - { - for (i = 0; i < 4; ++i) { + int i, is_pcie = 0, no_link; + + /* The root complex bridge comes up with bogus resources, + * we copy the PHB ones in. + * + * With the current generic PCI code, the PHB bus no longer + * has bus->resource[0..4] set, so things are a bit more + * tricky. + */ + + if (fsl_pcie_bus_fixup) + is_pcie = early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP); + no_link = !!(hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK); + + if (bus->parent == hose->bus && (is_pcie || no_link)) { + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; ++i) { struct resource *res = bus->resource[i]; - struct resource *par = bus->parent->resource[i]; - if (res) { - res->start = 0; - res->end = 0; - res->flags = 0; - } - if (res && par) { - res->start = par->start; - res->end = par->end; - res->flags = par->flags; - } + struct resource *par; + + if (!res) + continue; + if (i == 0) + par = &hose->io_resource; + else if (i < 4) + par = &hose->mem_resources[i-1]; + else par = NULL; + + res->start = par ? par->start : 0; + res->end = par ? par->end : 0; + res->flags = par ? par->flags : 0; } } } -- cgit v0.10.2 From a1a1d1bfc9be2a5ea8cce78ebd0f17c79489f053 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 14 Feb 2012 16:31:09 +0000 Subject: powerpc: Remove legacy iSeries from ppc64_defconfig Since we are heading towards removing the Legacy iSeries platform, start by no longer building it for ppc64_defconfig. Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 2156e07..1acf650 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -24,10 +24,6 @@ CONFIG_PPC_SPLPAR=y CONFIG_SCANLOG=m CONFIG_PPC_SMLPAR=y CONFIG_DTL=y -CONFIG_PPC_ISERIES=y -CONFIG_VIODASD=y -CONFIG_VIOCD=m -CONFIG_VIOTAPE=m CONFIG_PPC_MAPLE=y CONFIG_PPC_PASEMI=y CONFIG_PPC_PASEMI_IOMMU=y @@ -259,7 +255,6 @@ CONFIG_PASEMI_MAC=y CONFIG_MLX4_EN=m CONFIG_QLGE=m CONFIG_BE2NET=m -CONFIG_ISERIES_VETH=m CONFIG_PPP=m CONFIG_PPP_ASYNC=m CONFIG_PPP_SYNC_TTY=m -- cgit v0.10.2 From 54321242afe6fcf8b7e589bc21ecf832bc5a206a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 13 Feb 2012 20:42:18 +0000 Subject: powerpc: Disable interrupts early in Program Check Program Check exceptions are the result of WARNs, BUGs, some type of breakpoints, kprobe, and other illegal instructions. We want interrupts (and thus preemption) to remain disabled while doing the initial stage of testing the reason and branching off to a debugger or kprobe, so we are still on the original CPU which makes debugging easier in various cases. This is how the code was intended, hence the local_irq_enable() right in the middle of program_check_exception(). However, the assembly exception prologue for that exception was incorrectly marked as enabling interrupts, which defeats that (and records a redundant enable with lockdep). Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index d4be7bb..3844ca7 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -775,7 +775,7 @@ program_check_common: EXCEPTION_PROLOG_COMMON(0x700, PACA_EXGEN) bl .save_nvgprs addi r3,r1,STACK_FRAME_OVERHEAD - ENABLE_INTS + DISABLE_INTS bl .program_check_exception b .ret_from_except -- cgit v0.10.2 From 64f8c13561fbd2f1a8b4a8975b2aee73b561093e Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 3 Feb 2012 14:35:59 +0000 Subject: powerpc/adb: Use set_current_state() Signed-off-by: majianpeng Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 75049e7..b026896 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -710,7 +710,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, req = NULL; spin_lock_irqsave(&state->lock, flags); add_wait_queue(&state->wait_queue, &wait); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); for (;;) { req = state->completed; @@ -734,7 +734,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, spin_lock_irqsave(&state->lock, flags); } - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&state->wait_queue, &wait); spin_unlock_irqrestore(&state->lock, flags); -- cgit v0.10.2 From 9a45a9407c69d068500923480884661e2b9cc421 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 15 Feb 2012 18:48:22 +0000 Subject: powerpc/perf: power_pmu_start restores incorrect values, breaking frequency events perf on POWER stopped working after commit e050e3f0a71b (perf: Fix broken interrupt rate throttling). That patch exposed a bug in the POWER perf_events code. Since the PMCs count upwards and take an exception when the top bit is set, we want to write 0x80000000 - left in power_pmu_start. We were instead programming in left which effectively disables the counter until we eventually hit 0x80000000. This could take seconds or longer. With the patch applied I get the expected number of samples: SAMPLE events: 9948 Signed-off-by: Anton Blanchard Acked-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt Cc: diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index 10a140f..64483fd 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -865,6 +865,7 @@ static void power_pmu_start(struct perf_event *event, int ef_flags) { unsigned long flags; s64 left; + unsigned long val; if (!event->hw.idx || !event->hw.sample_period) return; @@ -880,7 +881,12 @@ static void power_pmu_start(struct perf_event *event, int ef_flags) event->hw.state = 0; left = local64_read(&event->hw.period_left); - write_pmc(event->hw.idx, left); + + val = 0; + if (left < 0x80000000L) + val = 0x80000000L - left; + + write_pmc(event->hw.idx, val); perf_event_update_userpage(event); perf_pmu_enable(event->pmu); -- cgit v0.10.2 From 1109bf8bcbf455e4cfebce862f9f9fa5a2f386e9 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 14 Feb 2012 16:52:41 +0900 Subject: drm/exynos: Fix typo in exynos_mixer.c Correct spelling "sucessful" to "successful" in drivers/gpu/drm/exynos/exynos_mixer.c Signed-off-by: Masanari Iida Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index ac24cff..33afd0c 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1044,7 +1044,7 @@ static int mixer_remove(struct platform_device *pdev) platform_get_drvdata(pdev); struct mixer_context *ctx = (struct mixer_context *)drm_hdmi_ctx->ctx; - dev_info(dev, "remove sucessful\n"); + dev_info(dev, "remove successful\n"); mixer_resource_poweroff(ctx); mixer_resources_cleanup(ctx); -- cgit v0.10.2 From 44a0e022b86a8c12ed06c02f52045c8f9f118bb1 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Wed, 15 Feb 2012 11:25:17 +0900 Subject: drm/exynos: changed priority of mixer layers. Signed-off-by: Joonyoung Shim Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 33afd0c..4796167 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -779,15 +779,15 @@ static void mixer_win_reset(struct mixer_context *ctx) mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST, MXR_STATUS_BURST_MASK); - /* setting default layer priority: layer1 > video > layer0 + /* setting default layer priority: layer1 > layer0 > video * because typical usage scenario would be + * layer1 - OSD * layer0 - framebuffer * video - video overlay - * layer1 - OSD */ - val = MXR_LAYER_CFG_GRP0_VAL(1); - val |= MXR_LAYER_CFG_VP_VAL(2); - val |= MXR_LAYER_CFG_GRP1_VAL(3); + val = MXR_LAYER_CFG_GRP1_VAL(3); + val |= MXR_LAYER_CFG_GRP0_VAL(2); + val |= MXR_LAYER_CFG_VP_VAL(1); mixer_reg_write(res, MXR_LAYER_CFG, val); /* setting background color */ -- cgit v0.10.2 From 6f811502a473a32a9b892fb35d77b8f91dc7484c Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Wed, 15 Feb 2012 11:25:18 +0900 Subject: drm/exynos: removed pageflip_event_list init code when closed. if one process is terminated by ctrl-c while two processes are using pageflip feature then for last pageflip event, user can't get poll from kernel side so this patch fixes the problem. Signed-off-by: Joonyoung Shim Signed-off-by: Inki Dae Signed-off-by: Kyoungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 35889ca..2ef12aa 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -141,16 +141,10 @@ static int exynos_drm_unload(struct drm_device *dev) } static void exynos_drm_preclose(struct drm_device *dev, - struct drm_file *file_priv) + struct drm_file *file) { - struct exynos_drm_private *dev_priv = dev->dev_private; + DRM_DEBUG_DRIVER("%s\n", __FILE__); - /* - * drm framework frees all events at release time, - * so private event list should be cleared. - */ - if (!list_empty(&dev_priv->pageflip_event_list)) - INIT_LIST_HEAD(&dev_priv->pageflip_event_list); } static void exynos_drm_lastclose(struct drm_device *dev) -- cgit v0.10.2 From d081f5660422270856b77bcbbaa312138f556c9e Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Wed, 15 Feb 2012 11:25:19 +0900 Subject: drm/exynos: added possible_clones setup function. basically, all crtcs are possible to clone each other. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 661a035..d08a558 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -193,6 +193,9 @@ int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) return err; } + /* setup possible_clones. */ + exynos_drm_encoder_setup(drm_dev); + /* * if any specific driver such as fimd or hdmi driver called * exynos_drm_subdrv_register() later than drm_load(), diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2ef12aa..76a111f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -33,6 +33,7 @@ #include "exynos_drm_drv.h" #include "exynos_drm_crtc.h" +#include "exynos_drm_encoder.h" #include "exynos_drm_fbdev.h" #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" @@ -99,6 +100,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) if (ret) goto err_vblank; + /* setup possible_clones. */ + exynos_drm_encoder_setup(dev); + /* * create and configure fb helper and also exynos specific * fbdev object. diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 86b93dd..ef4754f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -195,6 +195,40 @@ static struct drm_encoder_funcs exynos_encoder_funcs = { .destroy = exynos_drm_encoder_destroy, }; +static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) +{ + struct drm_encoder *clone; + struct drm_device *dev = encoder->dev; + struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); + struct exynos_drm_display_ops *display_ops = + exynos_encoder->manager->display_ops; + unsigned int clone_mask = 0; + int cnt = 0; + + list_for_each_entry(clone, &dev->mode_config.encoder_list, head) { + switch (display_ops->type) { + case EXYNOS_DISPLAY_TYPE_LCD: + case EXYNOS_DISPLAY_TYPE_HDMI: + clone_mask |= (1 << (cnt++)); + break; + default: + continue; + } + } + + return clone_mask; +} + +void exynos_drm_encoder_setup(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + encoder->possible_clones = exynos_drm_encoder_clones(encoder); +} + struct drm_encoder * exynos_drm_encoder_create(struct drm_device *dev, struct exynos_drm_manager *manager, diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h index 97b087a..eb7d231 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -30,6 +30,7 @@ struct exynos_drm_manager; +void exynos_drm_encoder_setup(struct drm_device *dev); struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, struct exynos_drm_manager *mgr, unsigned int possible_crtcs); -- cgit v0.10.2 From c5614ae326c7fdd244d8e0365b8b78d5e3cd5bf4 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Wed, 15 Feb 2012 11:25:20 +0900 Subject: drm/exynos: fixed page flip issue. with vblank_refcount = 1, there was the case that drm_vblank_put is called by specific page flip function so this patch fixes the issue. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index e3861ac..de81883 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -307,9 +307,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, */ event->pipe = exynos_crtc->pipe; - list_add_tail(&event->base.link, - &dev_priv->pageflip_event_list); - ret = drm_vblank_get(dev, exynos_crtc->pipe); if (ret) { DRM_DEBUG("failed to acquire vblank counter\n"); @@ -318,6 +315,9 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, goto out; } + list_add_tail(&event->base.link, + &dev_priv->pageflip_event_list); + crtc->fb = fb; ret = exynos_drm_crtc_update(crtc); if (ret) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index b6a737d..0dbb32b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -604,7 +604,12 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) } if (is_checked) { - drm_vblank_put(drm_dev, crtc); + /* + * call drm_vblank_put only in case that drm_vblank_get was + * called. + */ + if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) + drm_vblank_put(drm_dev, crtc); /* * don't off vblank if vblank_disable_allowed is 1, diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 4796167..93846e8 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -712,7 +712,12 @@ static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc) } if (is_checked) - drm_vblank_put(drm_dev, crtc); + /* + * call drm_vblank_put only in case that drm_vblank_get was + * called. + */ + if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) + drm_vblank_put(drm_dev, crtc); spin_unlock_irqrestore(&drm_dev->event_lock, flags); } -- cgit v0.10.2 From bc41eae2c84694667c1d7747fa28db8e75948ac4 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Wed, 15 Feb 2012 11:25:21 +0900 Subject: drm/exynos: removed exynos_drm_fbdev_recreate function. this function ins't needed anymore. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index d7ae29d..3508700 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -195,66 +195,6 @@ out: return ret; } -static bool -exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb, - struct drm_fb_helper_surface_size *sizes) -{ - if (fb->width != sizes->surface_width) - return false; - if (fb->height != sizes->surface_height) - return false; - if (fb->bits_per_pixel != sizes->surface_bpp) - return false; - if (fb->depth != sizes->surface_depth) - return false; - - return true; -} - -static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct drm_device *dev = helper->dev; - struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); - struct exynos_drm_gem_obj *exynos_gem_obj; - struct drm_framebuffer *fb = helper->fb; - struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - unsigned long size; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - if (exynos_drm_fbdev_is_samefb(fb, sizes)) - return 0; - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - if (exynos_fbdev->exynos_gem_obj) - exynos_drm_gem_destroy(exynos_fbdev->exynos_gem_obj); - - if (fb->funcs->destroy) - fb->funcs->destroy(fb); - - size = mode_cmd.pitches[0] * mode_cmd.height; - exynos_gem_obj = exynos_drm_gem_create(dev, size); - if (IS_ERR(exynos_gem_obj)) - return PTR_ERR(exynos_gem_obj); - - exynos_fbdev->exynos_gem_obj = exynos_gem_obj; - - helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, - &exynos_gem_obj->base); - if (IS_ERR_OR_NULL(helper->fb)) { - DRM_ERROR("failed to create drm framebuffer.\n"); - return PTR_ERR(helper->fb); - } - - return exynos_drm_fbdev_update(helper, helper->fb); -} - static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { @@ -262,6 +202,10 @@ static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, DRM_DEBUG_KMS("%s\n", __FILE__); + /* + * with !helper->fb, it means that this funcion is called first time + * and after that, the helper->fb would be used as clone mode. + */ if (!helper->fb) { ret = exynos_drm_fbdev_create(helper, sizes); if (ret < 0) { @@ -274,12 +218,6 @@ static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, * because register_framebuffer() should be called. */ ret = 1; - } else { - ret = exynos_drm_fbdev_recreate(helper, sizes); - if (ret < 0) { - DRM_ERROR("failed to reconfigure fbdev\n"); - return ret; - } } return ret; -- cgit v0.10.2 From 53ef299f3900bc1deb163b94d4f1cac4f3346152 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Wed, 15 Feb 2012 11:25:22 +0900 Subject: drm/exynos: added postclose to release resource. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 76a111f..58820eb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -151,6 +151,17 @@ static void exynos_drm_preclose(struct drm_device *dev, } +static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (!file->driver_priv) + return; + + kfree(file->driver_priv); + file->driver_priv = NULL; +} + static void exynos_drm_lastclose(struct drm_device *dev) { DRM_DEBUG_DRIVER("%s\n", __FILE__); @@ -193,6 +204,7 @@ static struct drm_driver exynos_drm_driver = { .unload = exynos_drm_unload, .preclose = exynos_drm_preclose, .lastclose = exynos_drm_lastclose, + .postclose = exynos_drm_postclose, .get_vblank_counter = drm_vblank_count, .enable_vblank = exynos_drm_crtc_enable_vblank, .disable_vblank = exynos_drm_crtc_disable_vblank, -- cgit v0.10.2 From c14c95f62ecb8710af14ae0d48e01991b70bb6f4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Feb 2012 16:38:07 +0100 Subject: ALSA: hda/realtek - Fix overflow of vol/sw check bitmap The bitmap introduced in the commit [527e4d73: ALSA: hda/realtek - Fix missing volume controls with ALC260] is too narrow for some codecs, which may have more NIDs than 0x20, thus it may overflow the bitmap array on them. Just double the number to cover all and also add a sanity-check code to be safer. Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1358987..389a28a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -80,6 +80,8 @@ enum { ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */ }; +#define MAX_VOL_NIDS 0x40 + struct alc_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ @@ -118,8 +120,8 @@ struct alc_spec { const hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ - DECLARE_BITMAP(vol_ctls, 0x20 << 1); - DECLARE_BITMAP(sw_ctls, 0x20 << 1); + DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1); + DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1); /* capture setup for dynamic dual-adc switch */ hda_nid_t cur_adc; @@ -3149,7 +3151,10 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) static inline unsigned int get_ctl_pos(unsigned int data) { hda_nid_t nid = get_amp_nid_(data); - unsigned int dir = get_amp_direction_(data); + unsigned int dir; + if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) + return 0; + dir = get_amp_direction_(data); return (nid << 1) | dir; } -- cgit v0.10.2 From 15d8791cae75dca27bfda8ecfe87dca9379d6bb0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 16 Feb 2012 09:15:04 -0800 Subject: i387: fix x86-64 preemption-unsafe user stack save/restore Commit 5b1cbac37798 ("i387: make irq_fpu_usable() tests more robust") added a sanity check to the #NM handler to verify that we never cause the "Device Not Available" exception in kernel mode. However, that check actually pinpointed a (fundamental) race where we do cause that exception as part of the signal stack FPU state save/restore code. Because we use the floating point instructions themselves to save and restore state directly from user mode, we cannot do that atomically with testing the TS_USEDFPU bit: the user mode access itself may cause a page fault, which causes a task switch, which saves and restores the FP/MMX state from the kernel buffers. This kind of "recursive" FP state save is fine per se, but it means that when the signal stack save/restore gets restarted, it will now take the '#NM' exception we originally tried to avoid. With preemption this can happen even without the page fault - but because of the user access, we cannot just disable preemption around the save/restore instruction. There are various ways to solve this, including using the "enable/disable_page_fault()" helpers to not allow page faults at all during the sequence, and fall back to copying things by hand without the use of the native FP state save/restore instructions. However, the simplest thing to do is to just allow the #NM from kernel space, but fix the race in setting and clearing CR0.TS that this all exposed: the TS bit changes and the TS_USEDFPU bit absolutely have to be atomic wrt scheduling, so while the actual state save/restore can be interrupted and restarted, the act of actually clearing/setting CR0.TS and the TS_USEDFPU bit together must not. Instead of just adding random "preempt_disable/enable()" calls to what is already excessively ugly code, this introduces some helper functions that mostly mirror the "kernel_fpu_begin/end()" functionality, just for the user state instead. Those helper functions should probably eventually replace the other ad-hoc CR0.TS and TS_USEDFPU tests too, but I'll need to think about it some more: the task switching functionality in particular needs to expose the difference between the 'prev' and 'next' threads, while the new helper functions intentionally were written to only work with 'current'. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 727c1dd..f704be2 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -400,6 +400,48 @@ static inline void irq_ts_restore(int TS_state) } /* + * The question "does this thread have fpu access?" + * is slightly racy, since preemption could come in + * and revoke it immediately after the test. + * + * However, even in that very unlikely scenario, + * we can just assume we have FPU access - typically + * to save the FP state - we'll just take a #NM + * fault and get the FPU access back. + * + * The actual user_fpu_begin/end() functions + * need to be preemption-safe, though. + * + * NOTE! user_fpu_end() must be used only after you + * have saved the FP state, and user_fpu_begin() must + * be used only immediately before restoring it. + * These functions do not do any save/restore on + * their own. + */ +static inline int user_has_fpu(void) +{ + return current_thread_info()->status & TS_USEDFPU; +} + +static inline void user_fpu_end(void) +{ + preempt_disable(); + current_thread_info()->status &= ~TS_USEDFPU; + stts(); + preempt_enable(); +} + +static inline void user_fpu_begin(void) +{ + preempt_disable(); + if (!user_has_fpu()) { + clts(); + current_thread_info()->status |= TS_USEDFPU; + } + preempt_enable(); +} + +/* * These disable preemption on their own and are safe */ static inline void save_init_fpu(struct task_struct *tsk) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 8ba27db..982433b 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -631,7 +631,6 @@ EXPORT_SYMBOL_GPL(math_state_restore); dotraplinkage void __kprobes do_device_not_available(struct pt_regs *regs, long error_code) { - WARN_ON_ONCE(!user_mode_vm(regs)); #ifdef CONFIG_MATH_EMULATION if (read_cr0() & X86_CR0_EM) { struct math_emu_info info = { }; diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index a391134..86f1f09 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -168,7 +168,7 @@ int save_i387_xstate(void __user *buf) if (!used_math()) return 0; - if (task_thread_info(tsk)->status & TS_USEDFPU) { + if (user_has_fpu()) { if (use_xsave()) err = xsave_user(buf); else @@ -176,8 +176,7 @@ int save_i387_xstate(void __user *buf) if (err) return err; - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); + user_fpu_end(); } else { sanitize_i387_state(tsk); if (__copy_to_user(buf, &tsk->thread.fpu.state->fxsave, @@ -292,10 +291,7 @@ int restore_i387_xstate(void __user *buf) return err; } - if (!(task_thread_info(current)->status & TS_USEDFPU)) { - clts(); - task_thread_info(current)->status |= TS_USEDFPU; - } + user_fpu_begin(); if (use_xsave()) err = restore_user_xstate(buf); else -- cgit v0.10.2 From b6c66418dcad0fcf83cd1d0a39482db37bf4fc41 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 16 Feb 2012 12:22:48 -0800 Subject: i387: move TS_USEDFPU clearing out of __save_init_fpu and into callers Touching TS_USEDFPU without touching CR0.TS is confusing, so don't do it. By moving it into the callers, we always do the TS_USEDFPU next to the CR0.TS accesses in the source code, and it's much easier to see how the two go hand in hand. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index f704be2..1e12c2d 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -259,7 +259,6 @@ static inline void fpu_save_init(struct fpu *fpu) static inline void __save_init_fpu(struct task_struct *tsk) { fpu_save_init(&tsk->thread.fpu); - task_thread_info(tsk)->status &= ~TS_USEDFPU; } static inline int fpu_fxrstor_checking(struct fpu *fpu) @@ -290,6 +289,7 @@ static inline void __unlazy_fpu(struct task_struct *tsk) { if (task_thread_info(tsk)->status & TS_USEDFPU) { __save_init_fpu(tsk); + task_thread_info(tsk)->status &= ~TS_USEDFPU; stts(); } else tsk->fpu_counter = 0; @@ -356,9 +356,11 @@ static inline void kernel_fpu_begin(void) WARN_ON_ONCE(!irq_fpu_usable()); preempt_disable(); - if (me->status & TS_USEDFPU) + if (me->status & TS_USEDFPU) { __save_init_fpu(me->task); - else + me->status &= ~TS_USEDFPU; + /* We do 'stts()' in kernel_fpu_end() */ + } else clts(); } @@ -449,6 +451,7 @@ static inline void save_init_fpu(struct task_struct *tsk) WARN_ON_ONCE(!(task_thread_info(tsk)->status & TS_USEDFPU)); preempt_disable(); __save_init_fpu(tsk); + task_thread_info(tsk)->status &= ~TS_USEDFPU; stts(); preempt_enable(); } -- cgit v0.10.2 From 6d59d7a9f5b723a7ac1925c136e93ec83c0c3043 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 16 Feb 2012 13:33:12 -0800 Subject: i387: don't ever touch TS_USEDFPU directly, use helper functions This creates three helper functions that do the TS_USEDFPU accesses, and makes everybody that used to do it by hand use those helpers instead. In addition, there's a couple of helper functions for the "change both CR0.TS and TS_USEDFPU at the same time" case, and the places that do that together have been changed to use those. That means that we have fewer random places that open-code this situation. The intent is partly to clarify the code without actually changing any semantics yet (since we clearly still have some hard to reproduce bug in this area), but also to make it much easier to use another approach entirely to caching the CR0.TS bit for software accesses. Right now we use a bit in the thread-info 'status' variable (this patch does not change that), but we might want to make it a full field of its own or even make it a per-cpu variable. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 1e12c2d..548b2c0 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -280,6 +280,47 @@ static inline int restore_fpu_checking(struct task_struct *tsk) } /* + * Software FPU state helpers. Careful: these need to + * be preemption protection *and* they need to be + * properly paired with the CR0.TS changes! + */ +static inline int __thread_has_fpu(struct thread_info *ti) +{ + return ti->status & TS_USEDFPU; +} + +/* Must be paired with an 'stts' after! */ +static inline void __thread_clear_has_fpu(struct thread_info *ti) +{ + ti->status &= ~TS_USEDFPU; +} + +/* Must be paired with a 'clts' before! */ +static inline void __thread_set_has_fpu(struct thread_info *ti) +{ + ti->status |= TS_USEDFPU; +} + +/* + * Encapsulate the CR0.TS handling together with the + * software flag. + * + * These generally need preemption protection to work, + * do try to avoid using these on their own. + */ +static inline void __thread_fpu_end(struct thread_info *ti) +{ + __thread_clear_has_fpu(ti); + stts(); +} + +static inline void __thread_fpu_begin(struct thread_info *ti) +{ + clts(); + __thread_set_has_fpu(ti); +} + +/* * Signal frame handlers... */ extern int save_i387_xstate(void __user *buf); @@ -287,23 +328,21 @@ extern int restore_i387_xstate(void __user *buf); static inline void __unlazy_fpu(struct task_struct *tsk) { - if (task_thread_info(tsk)->status & TS_USEDFPU) { + if (__thread_has_fpu(task_thread_info(tsk))) { __save_init_fpu(tsk); - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); + __thread_fpu_end(task_thread_info(tsk)); } else tsk->fpu_counter = 0; } static inline void __clear_fpu(struct task_struct *tsk) { - if (task_thread_info(tsk)->status & TS_USEDFPU) { + if (__thread_has_fpu(task_thread_info(tsk))) { /* Ignore delayed exceptions from user space */ asm volatile("1: fwait\n" "2:\n" _ASM_EXTABLE(1b, 2b)); - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); + __thread_fpu_end(task_thread_info(tsk)); } } @@ -311,14 +350,14 @@ static inline void __clear_fpu(struct task_struct *tsk) * Were we in an interrupt that interrupted kernel mode? * * We can do a kernel_fpu_begin/end() pair *ONLY* if that - * pair does nothing at all: TS_USEDFPU must be clear (so + * pair does nothing at all: the thread must not have fpu (so * that we don't try to save the FPU state), and TS must * be set (so that the clts/stts pair does nothing that is * visible in the interrupted kernel thread). */ static inline bool interrupted_kernel_fpu_idle(void) { - return !(current_thread_info()->status & TS_USEDFPU) && + return !__thread_has_fpu(current_thread_info()) && (read_cr0() & X86_CR0_TS); } @@ -356,9 +395,9 @@ static inline void kernel_fpu_begin(void) WARN_ON_ONCE(!irq_fpu_usable()); preempt_disable(); - if (me->status & TS_USEDFPU) { + if (__thread_has_fpu(me)) { __save_init_fpu(me->task); - me->status &= ~TS_USEDFPU; + __thread_clear_has_fpu(me); /* We do 'stts()' in kernel_fpu_end() */ } else clts(); @@ -422,24 +461,21 @@ static inline void irq_ts_restore(int TS_state) */ static inline int user_has_fpu(void) { - return current_thread_info()->status & TS_USEDFPU; + return __thread_has_fpu(current_thread_info()); } static inline void user_fpu_end(void) { preempt_disable(); - current_thread_info()->status &= ~TS_USEDFPU; - stts(); + __thread_fpu_end(current_thread_info()); preempt_enable(); } static inline void user_fpu_begin(void) { preempt_disable(); - if (!user_has_fpu()) { - clts(); - current_thread_info()->status |= TS_USEDFPU; - } + if (!user_has_fpu()) + __thread_fpu_begin(current_thread_info()); preempt_enable(); } @@ -448,11 +484,10 @@ static inline void user_fpu_begin(void) */ static inline void save_init_fpu(struct task_struct *tsk) { - WARN_ON_ONCE(!(task_thread_info(tsk)->status & TS_USEDFPU)); + WARN_ON_ONCE(!__thread_has_fpu(task_thread_info(tsk))); preempt_disable(); __save_init_fpu(tsk); - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); + __thread_fpu_end(task_thread_info(tsk)); preempt_enable(); } diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 982433b..fc676e4 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -588,7 +588,7 @@ void __math_state_restore(void) return; } - thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ + __thread_set_has_fpu(thread); /* clts in caller! */ tsk->fpu_counter++; } diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index 86f1f09..a0bcd0d 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -47,7 +47,7 @@ void __sanitize_i387_state(struct task_struct *tsk) if (!fx) return; - BUG_ON(task_thread_info(tsk)->status & TS_USEDFPU); + BUG_ON(__thread_has_fpu(task_thread_info(tsk))); xstate_bv = tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv; diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index d29216c..36091dd 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -1457,7 +1457,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx) #ifdef CONFIG_X86_64 wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base); #endif - if (current_thread_info()->status & TS_USEDFPU) + if (__thread_has_fpu(current_thread_info())) clts(); load_gdt(&__get_cpu_var(host_gdt)); } -- cgit v0.10.2 From 4a26620df451ad46151ad21d711ed43e963c004e Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Sat, 5 Nov 2011 13:45:08 -0400 Subject: eCryptfs: Improve statfs reporting statfs() calls on eCryptfs files returned the wrong filesystem type and, when using filename encryption, the wrong maximum filename length. If mount-wide filename encryption is enabled, the cipher block size and the lower filesystem's max filename length will determine the max eCryptfs filename length. Pre-tested, known good lengths are used when the lower filesystem's namelen is 255 and a cipher with 8 or 16 byte block sizes is used. In other, less common cases, we fall back to a safe rounded-down estimate when determining the eCryptfs namelen. https://launchpad.net/bugs/885744 Signed-off-by: Tyler Hicks Reported-by: Kees Cook Reviewed-by: Kees Cook Reviewed-by: John Johansen diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 63ab245..ea99312 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1990,6 +1990,17 @@ out: return; } +static size_t ecryptfs_max_decoded_size(size_t encoded_size) +{ + /* Not exact; conservatively long. Every block of 4 + * encoded characters decodes into a block of 3 + * decoded characters. This segment of code provides + * the caller with the maximum amount of allocated + * space that @dst will need to point to in a + * subsequent call. */ + return ((encoded_size + 1) * 3) / 4; +} + /** * ecryptfs_decode_from_filename * @dst: If NULL, this function only sets @dst_size and returns. If @@ -2008,13 +2019,7 @@ ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size, size_t dst_byte_offset = 0; if (dst == NULL) { - /* Not exact; conservatively long. Every block of 4 - * encoded characters decodes into a block of 3 - * decoded characters. This segment of code provides - * the caller with the maximum amount of allocated - * space that @dst will need to point to in a - * subsequent call. */ - (*dst_size) = (((src_size + 1) * 3) / 4); + (*dst_size) = ecryptfs_max_decoded_size(src_size); goto out; } while (src_byte_offset < src_size) { @@ -2239,3 +2244,52 @@ out_free: out: return rc; } + +#define ENC_NAME_MAX_BLOCKLEN_8_OR_16 143 + +int ecryptfs_set_f_namelen(long *namelen, long lower_namelen, + struct ecryptfs_mount_crypt_stat *mount_crypt_stat) +{ + struct blkcipher_desc desc; + struct mutex *tfm_mutex; + size_t cipher_blocksize; + int rc; + + if (!(mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)) { + (*namelen) = lower_namelen; + return 0; + } + + rc = ecryptfs_get_tfm_and_mutex_for_cipher_name(&desc.tfm, &tfm_mutex, + mount_crypt_stat->global_default_fn_cipher_name); + if (unlikely(rc)) { + (*namelen) = 0; + return rc; + } + + mutex_lock(tfm_mutex); + cipher_blocksize = crypto_blkcipher_blocksize(desc.tfm); + mutex_unlock(tfm_mutex); + + /* Return an exact amount for the common cases */ + if (lower_namelen == NAME_MAX + && (cipher_blocksize == 8 || cipher_blocksize == 16)) { + (*namelen) = ENC_NAME_MAX_BLOCKLEN_8_OR_16; + return 0; + } + + /* Return a safe estimate for the uncommon cases */ + (*namelen) = lower_namelen; + (*namelen) -= ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE; + /* Since this is the max decoded size, subtract 1 "decoded block" len */ + (*namelen) = ecryptfs_max_decoded_size(*namelen) - 3; + (*namelen) -= ECRYPTFS_TAG_70_MAX_METADATA_SIZE; + (*namelen) -= ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES; + /* Worst case is that the filename is padded nearly a full block size */ + (*namelen) -= cipher_blocksize - 1; + + if ((*namelen) < 0) + (*namelen) = 0; + + return 0; +} diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index a2362df..867b64c 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -162,6 +162,10 @@ ecryptfs_get_key_payload_data(struct key *key) #define ECRYPTFS_NON_NULL 0x42 /* A reasonable substitute for NULL */ #define MD5_DIGEST_SIZE 16 #define ECRYPTFS_TAG_70_DIGEST_SIZE MD5_DIGEST_SIZE +#define ECRYPTFS_TAG_70_MIN_METADATA_SIZE (1 + ECRYPTFS_MIN_PKT_LEN_SIZE \ + + ECRYPTFS_SIG_SIZE + 1 + 1) +#define ECRYPTFS_TAG_70_MAX_METADATA_SIZE (1 + ECRYPTFS_MAX_PKT_LEN_SIZE \ + + ECRYPTFS_SIG_SIZE + 1 + 1) #define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FEK_ENCRYPTED." #define ECRYPTFS_FEK_ENCRYPTED_FILENAME_PREFIX_SIZE 23 #define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FNEK_ENCRYPTED." @@ -701,6 +705,8 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size, size_t *packet_size, struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *data, size_t max_packet_size); +int ecryptfs_set_f_namelen(long *namelen, long lower_namelen, + struct ecryptfs_mount_crypt_stat *mount_crypt_stat); int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, loff_t offset); diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 8e3b943..2333203 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -679,10 +679,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes, * Octets N3-N4: Block-aligned encrypted filename * - Consists of a minimum number of random characters, a \0 * separator, and then the filename */ - s->max_packet_size = (1 /* Tag 70 identifier */ - + 3 /* Max Tag 70 packet size */ - + ECRYPTFS_SIG_SIZE /* FNEK sig */ - + 1 /* Cipher identifier */ + s->max_packet_size = (ECRYPTFS_TAG_70_MAX_METADATA_SIZE + s->block_aligned_filename_size); if (dest == NULL) { (*packet_size) = s->max_packet_size; @@ -934,10 +931,10 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size, goto out; } s->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; - if (max_packet_size < (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)) { + if (max_packet_size < ECRYPTFS_TAG_70_MIN_METADATA_SIZE) { printk(KERN_WARNING "%s: max_packet_size is [%zd]; it must be " "at least [%d]\n", __func__, max_packet_size, - (1 + 1 + ECRYPTFS_SIG_SIZE + 1 + 1)); + ECRYPTFS_TAG_70_MIN_METADATA_SIZE); rc = -EINVAL; goto out; } diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index 9df7fd6..cf15282 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "ecryptfs_kernel.h" struct kmem_cache *ecryptfs_inode_info_cache; @@ -102,10 +104,20 @@ static void ecryptfs_destroy_inode(struct inode *inode) static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + int rc; if (!lower_dentry->d_sb->s_op->statfs) return -ENOSYS; - return lower_dentry->d_sb->s_op->statfs(lower_dentry, buf); + + rc = lower_dentry->d_sb->s_op->statfs(lower_dentry, buf); + if (rc) + return rc; + + buf->f_type = ECRYPTFS_SUPER_MAGIC; + rc = ecryptfs_set_f_namelen(&buf->f_namelen, buf->f_namelen, + &ecryptfs_superblock_to_private(dentry->d_sb)->mount_crypt_stat); + + return rc; } /** -- cgit v0.10.2 From 545d680938be1e86a6c5250701ce9abaf360c495 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 7 Feb 2012 17:55:40 -0600 Subject: eCryptfs: Copy up lower inode attrs after setting lower xattr After passing through a ->setxattr() call, eCryptfs needs to copy the inode attributes from the lower inode to the eCryptfs inode, as they may have changed in the lower filesystem's ->setxattr() path. One example is if an extended attribute containing a POSIX Access Control List is being set. The new ACL may cause the lower filesystem to modify the mode of the lower inode and the eCryptfs inode would need to be updated to reflect the new mode. https://launchpad.net/bugs/926292 Signed-off-by: Tyler Hicks Reported-by: Sebastien Bacher Cc: John Johansen Cc: diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 19892d7..ab35b11 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -1085,6 +1085,8 @@ ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, } rc = vfs_setxattr(lower_dentry, name, value, size, flags); + if (!rc) + fsstack_copy_attr_all(dentry->d_inode, lower_dentry->d_inode); out: return rc; } -- cgit v0.10.2 From 465c9343c5b746ec2325a220fa3e50cc647d2db7 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 10 Feb 2012 13:39:50 +0800 Subject: ecryptfs: remove the second argument of k[un]map_atomic() Signed-off-by: Cong Wang Signed-off-by: Tyler Hicks diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 10ec695..a46b3a8 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -150,7 +150,7 @@ ecryptfs_copy_up_encrypted_with_header(struct page *page, /* This is a header extent */ char *page_virt; - page_virt = kmap_atomic(page, KM_USER0); + page_virt = kmap_atomic(page); memset(page_virt, 0, PAGE_CACHE_SIZE); /* TODO: Support more than one header extent */ if (view_extent_num == 0) { @@ -163,7 +163,7 @@ ecryptfs_copy_up_encrypted_with_header(struct page *page, crypt_stat, &written); } - kunmap_atomic(page_virt, KM_USER0); + kunmap_atomic(page_virt); flush_dcache_page(page); if (rc) { printk(KERN_ERR "%s: Error reading xattr " diff --git a/fs/ecryptfs/read_write.c b/fs/ecryptfs/read_write.c index 5c0106f..b2a34a1 100644 --- a/fs/ecryptfs/read_write.c +++ b/fs/ecryptfs/read_write.c @@ -156,7 +156,7 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, ecryptfs_page_idx, rc); goto out; } - ecryptfs_page_virt = kmap_atomic(ecryptfs_page, KM_USER0); + ecryptfs_page_virt = kmap_atomic(ecryptfs_page); /* * pos: where we're now writing, offset: where the request was @@ -179,7 +179,7 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, (data + data_offset), num_bytes); data_offset += num_bytes; } - kunmap_atomic(ecryptfs_page_virt, KM_USER0); + kunmap_atomic(ecryptfs_page_virt); flush_dcache_page(ecryptfs_page); SetPageUptodate(ecryptfs_page); unlock_page(ecryptfs_page); -- cgit v0.10.2 From b3b0870ef3ffed72b92415423da864f440f57ad6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 16 Feb 2012 15:45:23 -0800 Subject: i387: do not preload FPU state at task switch time Yes, taking the trap to re-load the FPU/MMX state is expensive, but so is spending several days looking for a bug in the state save/restore code. And the preload code has some rather subtle interactions with both paravirtualization support and segment state restore, so it's not nearly as simple as it should be. Also, now that we no longer necessarily depend on a single bit (ie TS_USEDFPU) for keeping track of the state of the FPU, we migth be able to do better. If we are really switching between two processes that keep touching the FP state, save/restore is inevitable, but in the case of having one process that does most of the FPU usage, we may actually be able to do much better than the preloading. In particular, we may be able to keep track of which CPU the process ran on last, and also per CPU keep track of which process' FP state that CPU has. For modern CPU's that don't destroy the FPU contents on save time, that would allow us to do a lazy restore by just re-enabling the existing FPU state - with no restore cost at all! Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 548b2c0..86974c7 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -30,7 +30,6 @@ extern void fpu_init(void); extern void mxcsr_feature_mask_init(void); extern int init_fpu(struct task_struct *child); extern void math_state_restore(void); -extern void __math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); extern user_regset_active_fn fpregs_active, xfpregs_active; diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 485204f..324cd72 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -299,23 +299,11 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) *next = &next_p->thread; int cpu = smp_processor_id(); struct tss_struct *tss = &per_cpu(init_tss, cpu); - bool preload_fpu; /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ - /* - * If the task has used fpu the last 5 timeslices, just do a full - * restore of the math state immediately to avoid the trap; the - * chances of needing FPU soon are obviously high now - */ - preload_fpu = tsk_used_math(next_p) && next_p->fpu_counter > 5; - __unlazy_fpu(prev_p); - /* we're going to use this soon, after a few expensive things */ - if (preload_fpu) - prefetch(next->fpu.state); - /* * Reload esp0. */ @@ -354,11 +342,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT)) __switch_to_xtra(prev_p, next_p, tss); - /* If we're going to preload the fpu context, make sure clts - is run while we're batching the cpu state updates. */ - if (preload_fpu) - clts(); - /* * Leave lazy mode, flushing any hypercalls made here. * This must be done before restoring TLS segments so @@ -368,9 +351,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) */ arch_end_context_switch(next_p); - if (preload_fpu) - __math_state_restore(); - /* * Restore %gs if needed (which is common) */ diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 9b9fe4a..992b4e5 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -386,18 +386,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) int cpu = smp_processor_id(); struct tss_struct *tss = &per_cpu(init_tss, cpu); unsigned fsindex, gsindex; - bool preload_fpu; - - /* - * If the task has used fpu the last 5 timeslices, just do a full - * restore of the math state immediately to avoid the trap; the - * chances of needing FPU soon are obviously high now - */ - preload_fpu = tsk_used_math(next_p) && next_p->fpu_counter > 5; - - /* we're going to use this soon, after a few expensive things */ - if (preload_fpu) - prefetch(next->fpu.state); /* * Reload esp0, LDT and the page table pointer: @@ -430,10 +418,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) /* Must be after DS reload */ __unlazy_fpu(prev_p); - /* Make sure cpu is ready for new context */ - if (preload_fpu) - clts(); - /* * Leave lazy mode, flushing any hypercalls made here. * This must be done before restoring TLS segments so @@ -492,13 +476,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV)) __switch_to_xtra(prev_p, next_p, tss); - /* - * Preload the FPU context, now that we've determined that the - * task is likely to be using it. - */ - if (preload_fpu) - __math_state_restore(); - return prev_p; } diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index fc676e4..5afe824 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -571,28 +571,6 @@ asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void) } /* - * __math_state_restore assumes that cr0.TS is already clear and the - * fpu state is all ready for use. Used during context switch. - */ -void __math_state_restore(void) -{ - struct thread_info *thread = current_thread_info(); - struct task_struct *tsk = thread->task; - - /* - * Paranoid restore. send a SIGSEGV if we fail to restore the state. - */ - if (unlikely(restore_fpu_checking(tsk))) { - stts(); - force_sig(SIGSEGV, tsk); - return; - } - - __thread_set_has_fpu(thread); /* clts in caller! */ - tsk->fpu_counter++; -} - -/* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task * @@ -622,9 +600,18 @@ void math_state_restore(void) local_irq_disable(); } - clts(); /* Allow maths ops (or we recurse) */ + __thread_fpu_begin(thread); - __math_state_restore(); + /* + * Paranoid restore. send a SIGSEGV if we fail to restore the state. + */ + if (unlikely(restore_fpu_checking(tsk))) { + __thread_fpu_end(thread); + force_sig(SIGSEGV, tsk); + return; + } + + tsk->fpu_counter++; } EXPORT_SYMBOL_GPL(math_state_restore); -- cgit v0.10.2 From 4903062b5485f0e2c286a23b44c9b59d9b017d53 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 16 Feb 2012 19:11:15 -0800 Subject: i387: move AMD K7/K8 fpu fxsave/fxrstor workaround from save to restore The AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is pending. In order to not leak FIP state from one process to another, we need to do a floating point load after the fxsave of the old process, and before the fxrstor of the new FPU state. That resets the state to the (uninteresting) kernel load, rather than some potentially sensitive user information. We used to do this directly after the FPU state save, but that is actually very inconvenient, since it (a) corrupts what is potentially perfectly good FPU state that we might want to lazy avoid restoring later and (b) on x86-64 it resulted in a very annoying ordering constraint, where "__unlazy_fpu()" in the task switch needs to be delayed until after the DS segment has been reloaded just to get the new DS value. Coupling it to the fxrstor instead of the fxsave automatically avoids both of these issues, and also ensures that we only do it when actually necessary (the FP state after a save may never actually get used). It's simply a much more natural place for the leaked state cleanup. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 86974c7..01b115d 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -211,15 +211,6 @@ static inline void fpu_fxsave(struct fpu *fpu) #endif /* CONFIG_X86_64 */ -/* We need a safe address that is cheap to find and that is already - in L1 during context switch. The best choices are unfortunately - different for UP and SMP */ -#ifdef CONFIG_SMP -#define safe_address (__per_cpu_offset[0]) -#else -#define safe_address (__get_cpu_var(kernel_cpustat).cpustat[CPUTIME_USER]) -#endif - /* * These must be called with preempt disabled */ @@ -243,16 +234,6 @@ static inline void fpu_save_init(struct fpu *fpu) if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) asm volatile("fnclex"); - - /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception - is pending. Clear the x87 state here by setting it to fixed - values. safe_address is a random variable that should be in L1 */ - alternative_input( - ASM_NOP8 ASM_NOP2, - "emms\n\t" /* clear stack tags */ - "fildl %P[addr]", /* set F?P to defined value */ - X86_FEATURE_FXSAVE_LEAK, - [addr] "m" (safe_address)); } static inline void __save_init_fpu(struct task_struct *tsk) diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 992b4e5..753e803 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -387,6 +387,8 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) struct tss_struct *tss = &per_cpu(init_tss, cpu); unsigned fsindex, gsindex; + __unlazy_fpu(prev_p); + /* * Reload esp0, LDT and the page table pointer: */ @@ -415,9 +417,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) load_TLS(next, cpu); - /* Must be after DS reload */ - __unlazy_fpu(prev_p); - /* * Leave lazy mode, flushing any hypercalls made here. * This must be done before restoring TLS segments so diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 5afe824..4d42300 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -585,6 +585,10 @@ void math_state_restore(void) struct thread_info *thread = current_thread_info(); struct task_struct *tsk = thread->task; + /* We need a safe address that is cheap to find and that is already + in L1. We just brought in "thread->task", so use that */ +#define safe_address (thread->task) + if (!tsk_used_math(tsk)) { local_irq_enable(); /* @@ -602,6 +606,16 @@ void math_state_restore(void) __thread_fpu_begin(thread); + /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception + is pending. Clear the x87 state here by setting it to fixed + values. safe_address is a random variable that should be in L1 */ + alternative_input( + ASM_NOP8 ASM_NOP2, + "emms\n\t" /* clear stack tags */ + "fildl %P[addr]", /* set F?P to defined value */ + X86_FEATURE_FXSAVE_LEAK, + [addr] "m" (safe_address)); + /* * Paranoid restore. send a SIGSEGV if we fail to restore the state. */ -- cgit v0.10.2 From ef8d60fb79614a86a82720dc2402631dbcafb315 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Feb 2012 10:12:38 +0100 Subject: ALSA: hda/realtek - Fix surround output regression on Acer Aspire 5935 The previous fix for the speaker on Acer Aspire 59135 introduced another problem for surround outputs. It changed the connections on the line-in/mic pins for limiting the routes, but it left the modified connections. Thus wrong connection indices were written when set to 4ch or 6ch mode. This patch fixes it by restoring the right connections just after parsing the tree but before the initialization. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42740 Cc: [v3.2+] Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 389a28a..3647baa 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4441,12 +4441,20 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, const struct alc_fixup *fix, int action) { if (action == ALC_FIXUP_ACT_PRE_PROBE) { + /* fake the connections during parsing the tree */ hda_nid_t conn1[2] = { 0x0c, 0x0d }; hda_nid_t conn2[2] = { 0x0e, 0x0f }; snd_hda_override_conn_list(codec, 0x14, 2, conn1); snd_hda_override_conn_list(codec, 0x15, 2, conn1); snd_hda_override_conn_list(codec, 0x18, 2, conn2); snd_hda_override_conn_list(codec, 0x1a, 2, conn2); + } else if (action == ALC_FIXUP_ACT_PROBE) { + /* restore the connections */ + hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; + snd_hda_override_conn_list(codec, 0x14, 5, conn); + snd_hda_override_conn_list(codec, 0x15, 5, conn); + snd_hda_override_conn_list(codec, 0x18, 5, conn); + snd_hda_override_conn_list(codec, 0x1a, 5, conn); } } -- cgit v0.10.2 From f3612304ee04a1a36ded7604771ea56d818158cb Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 17 Feb 2012 10:29:20 +0100 Subject: [S390] idle: avoid RCU usage in extended quiescent state Avoid calling wake_up() from our NMI "bottom halve" from RCU extended quiescent state in idle. wake_up() has RCU read-side critical sections but this will be completely ignored by RCU if the cpu is in extended quiescent state. Which means that whatever object is being accessed from within the read-side critical section can be freed concurrently from a different cpu. So make sure we leave extended quiescent state before calling wake_up(). Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 3201ae4..4261aa7 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -76,7 +76,6 @@ static void default_idle(void) if (test_thread_flag(TIF_MCCK_PENDING)) { local_mcck_enable(); local_irq_enable(); - s390_handle_mcck(); return; } trace_hardirqs_on(); @@ -93,10 +92,12 @@ void cpu_idle(void) for (;;) { tick_nohz_idle_enter(); rcu_idle_enter(); - while (!need_resched()) + while (!need_resched() && !test_thread_flag(TIF_MCCK_PENDING)) default_idle(); rcu_idle_exit(); tick_nohz_idle_exit(); + if (test_thread_flag(TIF_MCCK_PENDING)) + s390_handle_mcck(); preempt_enable_no_resched(); schedule(); preempt_disable(); -- cgit v0.10.2 From 2320c5793790fcda80e6dcc088dbda86040235e5 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 Feb 2012 10:29:21 +0100 Subject: [S390] incorrect PageTables counter for kvm page tables The page_table_free_pgste function is used for kvm processes to free page tables that have the pgste extension. It calls pgtable_page_ctor instead of pgtable_page_dtor which increases NR_PAGETABLE instead of decreasing it. Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 9a4d02f..51b0738 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -574,7 +574,7 @@ static inline void page_table_free_pgste(unsigned long *table) page = pfn_to_page(__pa(table) >> PAGE_SHIFT); mp = (struct gmap_pgtable *) page->index; BUG_ON(!list_empty(&mp->mapper)); - pgtable_page_ctor(page); + pgtable_page_dtor(page); atomic_set(&page->_mapcount, -1); kfree(mp); __free_page(page); -- cgit v0.10.2 From 656d9125376006cf696b0836f1c6723a892629ca Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 Feb 2012 10:29:22 +0100 Subject: [S390] 3215 deadlock with tty_wakeup The 3215 driver calls tty_wakeup from irq context while holding the device spinlock. If printk is called by any function on the callchain starting from tty_wakeup the system deadlocks on the device spinlock. Using a tasklet to call tty_wakup solves the problem. Signed-off-by: Martin Schwidefsky diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 934458a..e71a50d 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -87,6 +87,7 @@ struct raw3215_info { struct tty_struct *tty; /* pointer to tty structure if present */ struct raw3215_req *queued_read; /* pointer to queued read requests */ struct raw3215_req *queued_write;/* pointer to queued write requests */ + struct tasklet_struct tlet; /* tasklet to invoke tty_wakeup */ wait_queue_head_t empty_wait; /* wait queue for flushing */ struct timer_list timer; /* timer for delayed output */ int line_pos; /* position on the line (for tabs) */ @@ -334,19 +335,23 @@ static inline void raw3215_try_io(struct raw3215_info *raw) } /* + * Call tty_wakeup from tasklet context + */ +static void raw3215_wakeup(unsigned long data) +{ + struct raw3215_info *raw = (struct raw3215_info *) data; + tty_wakeup(raw->tty); +} + +/* * Try to start the next IO and wake up processes waiting on the tty. */ static void raw3215_next_io(struct raw3215_info *raw) { - struct tty_struct *tty; - raw3215_mk_write_req(raw); raw3215_try_io(raw); - tty = raw->tty; - if (tty != NULL && - RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) { - tty_wakeup(tty); - } + if (raw->tty && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) + tasklet_schedule(&raw->tlet); } /* @@ -682,6 +687,7 @@ static int raw3215_probe (struct ccw_device *cdev) return -ENOMEM; } init_waitqueue_head(&raw->empty_wait); + tasklet_init(&raw->tlet, raw3215_wakeup, (unsigned long) raw); dev_set_drvdata(&cdev->dev, raw); cdev->handler = raw3215_irq; @@ -901,6 +907,7 @@ static int __init con3215_init(void) raw->flags |= RAW3215_FIXED; init_waitqueue_head(&raw->empty_wait); + tasklet_init(&raw->tlet, raw3215_wakeup, (unsigned long) raw); /* Request the console irq */ if (raw3215_startup(raw) != 0) { @@ -966,6 +973,7 @@ static void tty3215_close(struct tty_struct *tty, struct file * filp) tty->closing = 1; /* Shutdown the terminal */ raw3215_shutdown(raw); + tasklet_kill(&raw->tlet); tty->closing = 0; raw->tty = NULL; } -- cgit v0.10.2 From cf1eb40f8f5ea12c9e569e7282161fc7f194fd62 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 Feb 2012 10:29:23 +0100 Subject: [S390] correct ktime to tod clock comparator conversion The conversion of the ktime to a value suitable for the clock comparator does not take changes to wall_to_monotonic into account. In fact the conversion just needs the boot clock (sched_clock_base_cc) and the total_sleep_time. This is applicable to 3.2+ kernels. CC: stable@vger.kernel.org Signed-off-by: Martin Schwidefsky diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index fa02f44..14da278 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -113,11 +113,14 @@ static void fixup_clock_comparator(unsigned long long delta) static int s390_next_ktime(ktime_t expires, struct clock_event_device *evt) { + struct timespec ts; u64 nsecs; - nsecs = ktime_to_ns(ktime_sub(expires, ktime_get_monotonic_offset())); + ts.tv_sec = ts.tv_nsec = 0; + monotonic_to_bootbased(&ts); + nsecs = ktime_to_ns(ktime_add(timespec_to_ktime(ts), expires)); do_div(nsecs, 125); - S390_lowcore.clock_comparator = TOD_UNIX_EPOCH + (nsecs << 9); + S390_lowcore.clock_comparator = sched_clock_base_cc + (nsecs << 9); set_clock_comparator(S390_lowcore.clock_comparator); return 0; } -- cgit v0.10.2 From f86f36a6ae625eda87a13e1ea102a908e08f491b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 14 Feb 2012 20:33:19 -0500 Subject: NFSv4.1: Fix a NFSv4.1 session initialisation regression Commit aacd553 (NFSv4.1: cleanup init and reset of session slot tables) introduces a regression in the session initialisation code. New tables now find their sequence ids initialised to 0, rather than the mandated value of 1 (see RFC5661). Fix the problem by merging nfs4_reset_slot_table() and nfs4_init_slot_table(). Since the tbl->max_slots is initialised to 0, the test in nfs4_reset_slot_table for max_reqs != tbl->max_slots will automatically pass for an empty table. Reported-by: Vitaliy Gusev Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d202e04..b4d67fe 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5008,37 +5008,53 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } +static struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags) +{ + return kcalloc(max_slots, sizeof(struct nfs4_slot), gfp_flags); +} + +static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, + struct nfs4_slot *new, + u32 max_slots, + u32 ivalue) +{ + struct nfs4_slot *old = NULL; + u32 i; + + spin_lock(&tbl->slot_tbl_lock); + if (new) { + old = tbl->slots; + tbl->slots = new; + tbl->max_slots = max_slots; + } + tbl->highest_used_slotid = -1; /* no slot is currently used */ + for (i = 0; i < tbl->max_slots; i++) + tbl->slots[i].seq_nr = ivalue; + spin_unlock(&tbl->slot_tbl_lock); + kfree(old); +} + /* - * Reset a slot table + * (re)Initialise a slot table */ -static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs, - int ivalue) +static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs, + u32 ivalue) { struct nfs4_slot *new = NULL; - int i; - int ret = 0; + int ret = -ENOMEM; dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__, max_reqs, tbl->max_slots); /* Does the newly negotiated max_reqs match the existing slot table? */ if (max_reqs != tbl->max_slots) { - ret = -ENOMEM; - new = kmalloc(max_reqs * sizeof(struct nfs4_slot), - GFP_NOFS); + new = nfs4_alloc_slots(max_reqs, GFP_NOFS); if (!new) goto out; - ret = 0; - kfree(tbl->slots); - } - spin_lock(&tbl->slot_tbl_lock); - if (new) { - tbl->slots = new; - tbl->max_slots = max_reqs; } - for (i = 0; i < tbl->max_slots; ++i) - tbl->slots[i].seq_nr = ivalue; - spin_unlock(&tbl->slot_tbl_lock); + ret = 0; + + nfs4_add_and_init_slots(tbl, new, max_reqs, ivalue); dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, tbl, tbl->slots, tbl->max_slots); out: @@ -5061,36 +5077,6 @@ static void nfs4_destroy_slot_tables(struct nfs4_session *session) } /* - * Initialize slot table - */ -static int nfs4_init_slot_table(struct nfs4_slot_table *tbl, - int max_slots, int ivalue) -{ - struct nfs4_slot *slot; - int ret = -ENOMEM; - - BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE); - - dprintk("--> %s: max_reqs=%u\n", __func__, max_slots); - - slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_NOFS); - if (!slot) - goto out; - ret = 0; - - spin_lock(&tbl->slot_tbl_lock); - tbl->max_slots = max_slots; - tbl->slots = slot; - tbl->highest_used_slotid = -1; /* no slot is currently used */ - spin_unlock(&tbl->slot_tbl_lock); - dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, - tbl, tbl->slots, tbl->max_slots); -out: - dprintk("<-- %s: return %d\n", __func__, ret); - return ret; -} - -/* * Initialize or reset the forechannel and backchannel tables */ static int nfs4_setup_session_slot_tables(struct nfs4_session *ses) @@ -5101,25 +5087,16 @@ static int nfs4_setup_session_slot_tables(struct nfs4_session *ses) dprintk("--> %s\n", __func__); /* Fore channel */ tbl = &ses->fc_slot_table; - if (tbl->slots == NULL) { - status = nfs4_init_slot_table(tbl, ses->fc_attrs.max_reqs, 1); - if (status) /* -ENOMEM */ - return status; - } else { - status = nfs4_reset_slot_table(tbl, ses->fc_attrs.max_reqs, 1); - if (status) - return status; - } + status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1); + if (status) /* -ENOMEM */ + return status; /* Back channel */ tbl = &ses->bc_slot_table; - if (tbl->slots == NULL) { - status = nfs4_init_slot_table(tbl, ses->bc_attrs.max_reqs, 0); - if (status) - /* Fore and back channel share a connection so get - * both slot tables or neither */ - nfs4_destroy_slot_tables(ses); - } else - status = nfs4_reset_slot_table(tbl, ses->bc_attrs.max_reqs, 0); + status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0); + if (status && tbl->slots == NULL) + /* Fore and back channel share a connection so get + * both slot tables or neither */ + nfs4_destroy_slot_tables(ses); return status; } -- cgit v0.10.2 From abe9a6d57b4544ac208401f9c0a4262814db2be4 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Thu, 16 Feb 2012 11:17:05 -0500 Subject: NFSv4: fix server_scope memory leak server_scope would never be freed if nfs4_check_cl_exchange_flags() returned non-zero Signed-off-by: Weston Andros Adamson Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b4d67fe..ec9f6ef 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4883,8 +4883,10 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) clp->cl_rpcclient->cl_auth->au_flavor); res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL); - if (unlikely(!res.server_scope)) - return -ENOMEM; + if (unlikely(!res.server_scope)) { + status = -ENOMEM; + goto out; + } status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); if (!status) @@ -4901,12 +4903,13 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) clp->server_scope = NULL; } - if (!clp->server_scope) + if (!clp->server_scope) { clp->server_scope = res.server_scope; - else - kfree(res.server_scope); + goto out; + } } - + kfree(res.server_scope); +out: dprintk("<-- %s status= %d\n", __func__, status); return status; } -- cgit v0.10.2 From f94edacf998516ac9d849f7bc6949a703977a7f3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 17 Feb 2012 21:48:54 -0800 Subject: i387: move TS_USEDFPU flag from thread_info to task_struct This moves the bit that indicates whether a thread has ownership of the FPU from the TS_USEDFPU bit in thread_info->status to a word of its own (called 'has_fpu') in task_struct->thread.has_fpu. This fixes two independent bugs at the same time: - changing 'thread_info->status' from the scheduler causes nasty problems for the other users of that variable, since it is defined to be thread-synchronous (that's what the "TS_" part of the naming was supposed to indicate). So perfectly valid code could (and did) do ti->status |= TS_RESTORE_SIGMASK; and the compiler was free to do that as separate load, or and store instructions. Which can cause problems with preemption, since a task switch could happen in between, and change the TS_USEDFPU bit. The change to TS_USEDFPU would be overwritten by the final store. In practice, this seldom happened, though, because the 'status' field was seldom used more than once, so gcc would generally tend to generate code that used a read-modify-write instruction and thus happened to avoid this problem - RMW instructions are naturally low fat and preemption-safe. - On x86-32, the current_thread_info() pointer would, during interrupts and softirqs, point to a *copy* of the real thread_info, because x86-32 uses %esp to calculate the thread_info address, and thus the separate irq (and softirq) stacks would cause these kinds of odd thread_info copy aliases. This is normally not a problem, since interrupts aren't supposed to look at thread information anyway (what thread is running at interrupt time really isn't very well-defined), but it confused the heck out of irq_fpu_usable() and the code that tried to squirrel away the FPU state. (It also caused untold confusion for us poor kernel developers). It also turns out that using 'task_struct' is actually much more natural for most of the call sites that care about the FPU state, since they tend to work with the task struct for other reasons anyway (ie scheduling). And the FPU data that we are going to save/restore is found there too. Thanks to Arjan Van De Ven for pointing us to the %esp issue. Cc: Arjan van de Ven Reported-and-tested-by: Raphael Prevost Acked-and-tested-by: Suresh Siddha Tested-by: Peter Anvin Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 01b115d..f537667 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -264,21 +264,21 @@ static inline int restore_fpu_checking(struct task_struct *tsk) * be preemption protection *and* they need to be * properly paired with the CR0.TS changes! */ -static inline int __thread_has_fpu(struct thread_info *ti) +static inline int __thread_has_fpu(struct task_struct *tsk) { - return ti->status & TS_USEDFPU; + return tsk->thread.has_fpu; } /* Must be paired with an 'stts' after! */ -static inline void __thread_clear_has_fpu(struct thread_info *ti) +static inline void __thread_clear_has_fpu(struct task_struct *tsk) { - ti->status &= ~TS_USEDFPU; + tsk->thread.has_fpu = 0; } /* Must be paired with a 'clts' before! */ -static inline void __thread_set_has_fpu(struct thread_info *ti) +static inline void __thread_set_has_fpu(struct task_struct *tsk) { - ti->status |= TS_USEDFPU; + tsk->thread.has_fpu = 1; } /* @@ -288,16 +288,16 @@ static inline void __thread_set_has_fpu(struct thread_info *ti) * These generally need preemption protection to work, * do try to avoid using these on their own. */ -static inline void __thread_fpu_end(struct thread_info *ti) +static inline void __thread_fpu_end(struct task_struct *tsk) { - __thread_clear_has_fpu(ti); + __thread_clear_has_fpu(tsk); stts(); } -static inline void __thread_fpu_begin(struct thread_info *ti) +static inline void __thread_fpu_begin(struct task_struct *tsk) { clts(); - __thread_set_has_fpu(ti); + __thread_set_has_fpu(tsk); } /* @@ -308,21 +308,21 @@ extern int restore_i387_xstate(void __user *buf); static inline void __unlazy_fpu(struct task_struct *tsk) { - if (__thread_has_fpu(task_thread_info(tsk))) { + if (__thread_has_fpu(tsk)) { __save_init_fpu(tsk); - __thread_fpu_end(task_thread_info(tsk)); + __thread_fpu_end(tsk); } else tsk->fpu_counter = 0; } static inline void __clear_fpu(struct task_struct *tsk) { - if (__thread_has_fpu(task_thread_info(tsk))) { + if (__thread_has_fpu(tsk)) { /* Ignore delayed exceptions from user space */ asm volatile("1: fwait\n" "2:\n" _ASM_EXTABLE(1b, 2b)); - __thread_fpu_end(task_thread_info(tsk)); + __thread_fpu_end(tsk); } } @@ -337,7 +337,7 @@ static inline void __clear_fpu(struct task_struct *tsk) */ static inline bool interrupted_kernel_fpu_idle(void) { - return !__thread_has_fpu(current_thread_info()) && + return !__thread_has_fpu(current) && (read_cr0() & X86_CR0_TS); } @@ -371,12 +371,12 @@ static inline bool irq_fpu_usable(void) static inline void kernel_fpu_begin(void) { - struct thread_info *me = current_thread_info(); + struct task_struct *me = current; WARN_ON_ONCE(!irq_fpu_usable()); preempt_disable(); if (__thread_has_fpu(me)) { - __save_init_fpu(me->task); + __save_init_fpu(me); __thread_clear_has_fpu(me); /* We do 'stts()' in kernel_fpu_end() */ } else @@ -441,13 +441,13 @@ static inline void irq_ts_restore(int TS_state) */ static inline int user_has_fpu(void) { - return __thread_has_fpu(current_thread_info()); + return __thread_has_fpu(current); } static inline void user_fpu_end(void) { preempt_disable(); - __thread_fpu_end(current_thread_info()); + __thread_fpu_end(current); preempt_enable(); } @@ -455,7 +455,7 @@ static inline void user_fpu_begin(void) { preempt_disable(); if (!user_has_fpu()) - __thread_fpu_begin(current_thread_info()); + __thread_fpu_begin(current); preempt_enable(); } @@ -464,10 +464,10 @@ static inline void user_fpu_begin(void) */ static inline void save_init_fpu(struct task_struct *tsk) { - WARN_ON_ONCE(!__thread_has_fpu(task_thread_info(tsk))); + WARN_ON_ONCE(!__thread_has_fpu(tsk)); preempt_disable(); __save_init_fpu(tsk); - __thread_fpu_end(task_thread_info(tsk)); + __thread_fpu_end(tsk); preempt_enable(); } diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index aa9088c..f7c89e2 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -454,6 +454,7 @@ struct thread_struct { unsigned long trap_no; unsigned long error_code; /* floating point and extended processor state */ + unsigned long has_fpu; struct fpu fpu; #ifdef CONFIG_X86_32 /* Virtual 86 mode info */ diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index bc817cd..cfd8144 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -247,8 +247,6 @@ static inline struct thread_info *current_thread_info(void) * ever touches our thread-synchronous status, so we don't * have to worry about atomic accesses. */ -#define TS_USEDFPU 0x0001 /* FPU was used by this task - this quantum (SMP) */ #define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/ #define TS_POLLING 0x0004 /* idle task polling need_resched, skip sending interrupt */ diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 4d42300..ad25e51 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -582,12 +582,11 @@ asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void) */ void math_state_restore(void) { - struct thread_info *thread = current_thread_info(); - struct task_struct *tsk = thread->task; + struct task_struct *tsk = current; /* We need a safe address that is cheap to find and that is already - in L1. We just brought in "thread->task", so use that */ -#define safe_address (thread->task) + in L1. We're just bringing in "tsk->thread.has_fpu", so use that */ +#define safe_address (tsk->thread.has_fpu) if (!tsk_used_math(tsk)) { local_irq_enable(); @@ -604,7 +603,7 @@ void math_state_restore(void) local_irq_disable(); } - __thread_fpu_begin(thread); + __thread_fpu_begin(tsk); /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is pending. Clear the x87 state here by setting it to fixed @@ -620,7 +619,7 @@ void math_state_restore(void) * Paranoid restore. send a SIGSEGV if we fail to restore the state. */ if (unlikely(restore_fpu_checking(tsk))) { - __thread_fpu_end(thread); + __thread_fpu_end(tsk); force_sig(SIGSEGV, tsk); return; } diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index a0bcd0d..7110911 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -47,7 +47,7 @@ void __sanitize_i387_state(struct task_struct *tsk) if (!fx) return; - BUG_ON(__thread_has_fpu(task_thread_info(tsk))); + BUG_ON(__thread_has_fpu(tsk)); xstate_bv = tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv; diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 36091dd..3b4c8d8 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -1457,7 +1457,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx) #ifdef CONFIG_X86_64 wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base); #endif - if (__thread_has_fpu(current_thread_info())) + if (__thread_has_fpu(current)) clts(); load_gdt(&__get_cpu_var(host_gdt)); } -- cgit v0.10.2 From 34ddc81a230b15c0e345b6b253049db731499f7e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 18 Feb 2012 12:56:35 -0800 Subject: i387: re-introduce FPU state preloading at context switch time After all the FPU state cleanups and finally finding the problem that caused all our FPU save/restore problems, this re-introduces the preloading of FPU state that was removed in commit b3b0870ef3ff ("i387: do not preload FPU state at task switch time"). However, instead of simply reverting the removal, this reimplements preloading with several fixes, most notably - properly abstracted as a true FPU state switch, rather than as open-coded save and restore with various hacks. In particular, implementing it as a proper FPU state switch allows us to optimize the CR0.TS flag accesses: there is no reason to set the TS bit only to then almost immediately clear it again. CR0 accesses are quite slow and expensive, don't flip the bit back and forth for no good reason. - Make sure that the same model works for both x86-32 and x86-64, so that there are no gratuitous differences between the two due to the way they save and restore segment state differently due to architectural differences that really don't matter to the FPU state. - Avoid exposing the "preload" state to the context switch routines, and in particular allow the concept of lazy state restore: if nothing else has used the FPU in the meantime, and the process is still on the same CPU, we can avoid restoring state from memory entirely, just re-expose the state that is still in the FPU unit. That optimized lazy restore isn't actually implemented here, but the infrastructure is set up for it. Of course, older CPU's that use 'fnsave' to save the state cannot take advantage of this, since the state saving also trashes the state. In other words, there is now an actual _design_ to the FPU state saving, rather than just random historical baggage. Hopefully it's easier to follow as a result. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index f537667..a850b4d 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -29,6 +29,7 @@ extern unsigned int sig_xstate_size; extern void fpu_init(void); extern void mxcsr_feature_mask_init(void); extern int init_fpu(struct task_struct *child); +extern void __math_state_restore(struct task_struct *); extern void math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); @@ -212,9 +213,10 @@ static inline void fpu_fxsave(struct fpu *fpu) #endif /* CONFIG_X86_64 */ /* - * These must be called with preempt disabled + * These must be called with preempt disabled. Returns + * 'true' if the FPU state is still intact. */ -static inline void fpu_save_init(struct fpu *fpu) +static inline int fpu_save_init(struct fpu *fpu) { if (use_xsave()) { fpu_xsave(fpu); @@ -223,22 +225,33 @@ static inline void fpu_save_init(struct fpu *fpu) * xsave header may indicate the init state of the FP. */ if (!(fpu->state->xsave.xsave_hdr.xstate_bv & XSTATE_FP)) - return; + return 1; } else if (use_fxsr()) { fpu_fxsave(fpu); } else { asm volatile("fnsave %[fx]; fwait" : [fx] "=m" (fpu->state->fsave)); - return; + return 0; } - if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) + /* + * If exceptions are pending, we need to clear them so + * that we don't randomly get exceptions later. + * + * FIXME! Is this perhaps only true for the old-style + * irq13 case? Maybe we could leave the x87 state + * intact otherwise? + */ + if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) { asm volatile("fnclex"); + return 0; + } + return 1; } -static inline void __save_init_fpu(struct task_struct *tsk) +static inline int __save_init_fpu(struct task_struct *tsk) { - fpu_save_init(&tsk->thread.fpu); + return fpu_save_init(&tsk->thread.fpu); } static inline int fpu_fxrstor_checking(struct fpu *fpu) @@ -301,20 +314,79 @@ static inline void __thread_fpu_begin(struct task_struct *tsk) } /* - * Signal frame handlers... + * FPU state switching for scheduling. + * + * This is a two-stage process: + * + * - switch_fpu_prepare() saves the old state and + * sets the new state of the CR0.TS bit. This is + * done within the context of the old process. + * + * - switch_fpu_finish() restores the new state as + * necessary. */ -extern int save_i387_xstate(void __user *buf); -extern int restore_i387_xstate(void __user *buf); +typedef struct { int preload; } fpu_switch_t; + +/* + * FIXME! We could do a totally lazy restore, but we need to + * add a per-cpu "this was the task that last touched the FPU + * on this CPU" variable, and the task needs to have a "I last + * touched the FPU on this CPU" and check them. + * + * We don't do that yet, so "fpu_lazy_restore()" always returns + * false, but some day.. + */ +#define fpu_lazy_restore(tsk) (0) +#define fpu_lazy_state_intact(tsk) do { } while (0) + +static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct task_struct *new) +{ + fpu_switch_t fpu; + + fpu.preload = tsk_used_math(new) && new->fpu_counter > 5; + if (__thread_has_fpu(old)) { + if (__save_init_fpu(old)) + fpu_lazy_state_intact(old); + __thread_clear_has_fpu(old); + old->fpu_counter++; + + /* Don't change CR0.TS if we just switch! */ + if (fpu.preload) { + __thread_set_has_fpu(new); + prefetch(new->thread.fpu.state); + } else + stts(); + } else { + old->fpu_counter = 0; + if (fpu.preload) { + if (fpu_lazy_restore(new)) + fpu.preload = 0; + else + prefetch(new->thread.fpu.state); + __thread_fpu_begin(new); + } + } + return fpu; +} -static inline void __unlazy_fpu(struct task_struct *tsk) +/* + * By the time this gets called, we've already cleared CR0.TS and + * given the process the FPU if we are going to preload the FPU + * state - all we need to do is to conditionally restore the register + * state itself. + */ +static inline void switch_fpu_finish(struct task_struct *new, fpu_switch_t fpu) { - if (__thread_has_fpu(tsk)) { - __save_init_fpu(tsk); - __thread_fpu_end(tsk); - } else - tsk->fpu_counter = 0; + if (fpu.preload) + __math_state_restore(new); } +/* + * Signal frame handlers... + */ +extern int save_i387_xstate(void __user *buf); +extern int restore_i387_xstate(void __user *buf); + static inline void __clear_fpu(struct task_struct *tsk) { if (__thread_has_fpu(tsk)) { @@ -474,7 +546,11 @@ static inline void save_init_fpu(struct task_struct *tsk) static inline void unlazy_fpu(struct task_struct *tsk) { preempt_disable(); - __unlazy_fpu(tsk); + if (__thread_has_fpu(tsk)) { + __save_init_fpu(tsk); + __thread_fpu_end(tsk); + } else + tsk->fpu_counter = 0; preempt_enable(); } diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 324cd72..80bfe1a 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -299,10 +299,11 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) *next = &next_p->thread; int cpu = smp_processor_id(); struct tss_struct *tss = &per_cpu(init_tss, cpu); + fpu_switch_t fpu; /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ - __unlazy_fpu(prev_p); + fpu = switch_fpu_prepare(prev_p, next_p); /* * Reload esp0. @@ -357,6 +358,8 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) if (prev->gs | next->gs) lazy_load_gs(next->gs); + switch_fpu_finish(next_p, fpu); + percpu_write(current_task, next_p); return prev_p; diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 753e803..1fd94bc 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -386,8 +386,9 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) int cpu = smp_processor_id(); struct tss_struct *tss = &per_cpu(init_tss, cpu); unsigned fsindex, gsindex; + fpu_switch_t fpu; - __unlazy_fpu(prev_p); + fpu = switch_fpu_prepare(prev_p, next_p); /* * Reload esp0, LDT and the page table pointer: @@ -457,6 +458,8 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) wrmsrl(MSR_KERNEL_GS_BASE, next->gs); prev->gsindex = gsindex; + switch_fpu_finish(next_p, fpu); + /* * Switch the PDA and FPU contexts. */ diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index ad25e51..77da5b4 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -571,6 +571,37 @@ asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void) } /* + * This gets called with the process already owning the + * FPU state, and with CR0.TS cleared. It just needs to + * restore the FPU register state. + */ +void __math_state_restore(struct task_struct *tsk) +{ + /* We need a safe address that is cheap to find and that is already + in L1. We've just brought in "tsk->thread.has_fpu", so use that */ +#define safe_address (tsk->thread.has_fpu) + + /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception + is pending. Clear the x87 state here by setting it to fixed + values. safe_address is a random variable that should be in L1 */ + alternative_input( + ASM_NOP8 ASM_NOP2, + "emms\n\t" /* clear stack tags */ + "fildl %P[addr]", /* set F?P to defined value */ + X86_FEATURE_FXSAVE_LEAK, + [addr] "m" (safe_address)); + + /* + * Paranoid restore. send a SIGSEGV if we fail to restore the state. + */ + if (unlikely(restore_fpu_checking(tsk))) { + __thread_fpu_end(tsk); + force_sig(SIGSEGV, tsk); + return; + } +} + +/* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task * @@ -584,10 +615,6 @@ void math_state_restore(void) { struct task_struct *tsk = current; - /* We need a safe address that is cheap to find and that is already - in L1. We're just bringing in "tsk->thread.has_fpu", so use that */ -#define safe_address (tsk->thread.has_fpu) - if (!tsk_used_math(tsk)) { local_irq_enable(); /* @@ -604,25 +631,7 @@ void math_state_restore(void) } __thread_fpu_begin(tsk); - - /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception - is pending. Clear the x87 state here by setting it to fixed - values. safe_address is a random variable that should be in L1 */ - alternative_input( - ASM_NOP8 ASM_NOP2, - "emms\n\t" /* clear stack tags */ - "fildl %P[addr]", /* set F?P to defined value */ - X86_FEATURE_FXSAVE_LEAK, - [addr] "m" (safe_address)); - - /* - * Paranoid restore. send a SIGSEGV if we fail to restore the state. - */ - if (unlikely(restore_fpu_checking(tsk))) { - __thread_fpu_end(tsk); - force_sig(SIGSEGV, tsk); - return; - } + __math_state_restore(tsk); tsk->fpu_counter++; } -- cgit v0.10.2 From b01543dfe67bb1d191998e90d20534dc354de059 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 18 Feb 2012 15:53:33 -0800 Subject: Linux 3.3-rc4 diff --git a/Makefile b/Makefile index 7c44b67..4ddd641 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Saber-toothed Squirrel # *DOCUMENTATION* -- cgit v0.10.2 From 59cca653a601372e9b4a430d867377a3e4a36d76 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 2 Feb 2012 10:46:49 +0200 Subject: digsig: changed type of the timestamp time_t was used in the signature and key packet headers, which is typedef of long and is different on 32 and 64 bit architectures. Signature and key format should be independent of architecture. Similar to GPG, I have changed the type to uint32_t. Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris diff --git a/include/linux/digsig.h b/include/linux/digsig.h index b01558b..6f85a07 100644 --- a/include/linux/digsig.h +++ b/include/linux/digsig.h @@ -30,7 +30,7 @@ enum digest_algo { struct pubkey_hdr { uint8_t version; /* key format version */ - time_t timestamp; /* key made, always 0 for now */ + uint32_t timestamp; /* key made, always 0 for now */ uint8_t algo; uint8_t nmpi; char mpi[0]; @@ -38,7 +38,7 @@ struct pubkey_hdr { struct signature_hdr { uint8_t version; /* signature format version */ - time_t timestamp; /* signature made */ + uint32_t timestamp; /* signature made */ uint8_t algo; uint8_t hash; uint8_t keyid[8]; -- cgit v0.10.2 From cea20ca3f3181fc36788a15bc65d1062b96a0a6c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 20 Feb 2012 10:24:09 -0800 Subject: i387: fix up some fpu_counter confusion This makes sure we clear the FPU usage counter for newly created tasks, just so that we start off in a known state (for example, don't try to preload the FPU state on the first task switch etc). It also fixes a thinko in when we increment the fpu_counter at task switch time, introduced by commit 34ddc81a230b ("i387: re-introduce FPU state preloading at context switch time"). We should increment the *new* task fpu_counter, not the old task, and only if we decide to use that state (whether lazily or preloaded). Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index a850b4d..8df9584 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -348,10 +348,10 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta if (__save_init_fpu(old)) fpu_lazy_state_intact(old); __thread_clear_has_fpu(old); - old->fpu_counter++; /* Don't change CR0.TS if we just switch! */ if (fpu.preload) { + new->fpu_counter++; __thread_set_has_fpu(new); prefetch(new->thread.fpu.state); } else @@ -359,6 +359,7 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta } else { old->fpu_counter = 0; if (fpu.preload) { + new->fpu_counter++; if (fpu_lazy_restore(new)) fpu.preload = 0; else diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 80bfe1a..bc32761 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -214,6 +214,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, task_user_gs(p) = get_user_gs(regs); + p->fpu_counter = 0; p->thread.io_bitmap_ptr = NULL; tsk = current; err = -ENOMEM; diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 1fd94bc..8ad880b 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -286,6 +286,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, set_tsk_thread_flag(p, TIF_FORK); + p->fpu_counter = 0; p->thread.io_bitmap_ptr = NULL; savesegment(gs, p->thread.gsindex); -- cgit v0.10.2 From 80ab6f1e8c981b1b6604b2f22e36c917526235cd Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 19 Feb 2012 11:48:44 -0800 Subject: i387: use 'restore_fpu_checking()' directly in task switching code This inlines what is usually just a couple of instructions, but more importantly it also fixes the theoretical error case (can that FPU restore really ever fail? Maybe we should remove the checking). We can't start sending signals from within the scheduler, we're much too deep in the kernel and are holding the runqueue lock etc. So don't bother even trying. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 8df9584..74c607b 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -29,7 +29,6 @@ extern unsigned int sig_xstate_size; extern void fpu_init(void); extern void mxcsr_feature_mask_init(void); extern int init_fpu(struct task_struct *child); -extern void __math_state_restore(struct task_struct *); extern void math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); @@ -269,6 +268,16 @@ static inline int fpu_restore_checking(struct fpu *fpu) static inline int restore_fpu_checking(struct task_struct *tsk) { + /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception + is pending. Clear the x87 state here by setting it to fixed + values. "m" is a random variable that should be in L1 */ + alternative_input( + ASM_NOP8 ASM_NOP2, + "emms\n\t" /* clear stack tags */ + "fildl %P[addr]", /* set F?P to defined value */ + X86_FEATURE_FXSAVE_LEAK, + [addr] "m" (tsk->thread.has_fpu)); + return fpu_restore_checking(&tsk->thread.fpu); } @@ -378,8 +387,10 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta */ static inline void switch_fpu_finish(struct task_struct *new, fpu_switch_t fpu) { - if (fpu.preload) - __math_state_restore(new); + if (fpu.preload) { + if (unlikely(restore_fpu_checking(new))) + __thread_fpu_end(new); + } } /* diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 77da5b4..4bbe04d 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -571,37 +571,6 @@ asmlinkage void __attribute__((weak)) smp_threshold_interrupt(void) } /* - * This gets called with the process already owning the - * FPU state, and with CR0.TS cleared. It just needs to - * restore the FPU register state. - */ -void __math_state_restore(struct task_struct *tsk) -{ - /* We need a safe address that is cheap to find and that is already - in L1. We've just brought in "tsk->thread.has_fpu", so use that */ -#define safe_address (tsk->thread.has_fpu) - - /* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception - is pending. Clear the x87 state here by setting it to fixed - values. safe_address is a random variable that should be in L1 */ - alternative_input( - ASM_NOP8 ASM_NOP2, - "emms\n\t" /* clear stack tags */ - "fildl %P[addr]", /* set F?P to defined value */ - X86_FEATURE_FXSAVE_LEAK, - [addr] "m" (safe_address)); - - /* - * Paranoid restore. send a SIGSEGV if we fail to restore the state. - */ - if (unlikely(restore_fpu_checking(tsk))) { - __thread_fpu_end(tsk); - force_sig(SIGSEGV, tsk); - return; - } -} - -/* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task * @@ -631,7 +600,14 @@ void math_state_restore(void) } __thread_fpu_begin(tsk); - __math_state_restore(tsk); + /* + * Paranoid restore. send a SIGSEGV if we fail to restore the state. + */ + if (unlikely(restore_fpu_checking(tsk))) { + __thread_fpu_end(tsk); + force_sig(SIGSEGV, tsk); + return; + } tsk->fpu_counter++; } -- cgit v0.10.2 From 7e16838d94b566a17b65231073d179bc04d590c8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 19 Feb 2012 13:27:00 -0800 Subject: i387: support lazy restore of FPU state This makes us recognize when we try to restore FPU state that matches what we already have in the FPU on this CPU, and avoids the restore entirely if so. To do this, we add two new data fields: - a percpu 'fpu_owner_task' variable that gets written any time we update the "has_fpu" field, and thus acts as a kind of back-pointer to the task that owns the CPU. The exception is when we save the FPU state as part of a context switch - if the save can keep the FPU state around, we leave the 'fpu_owner_task' variable pointing at the task whose FP state still remains on the CPU. - a per-thread 'last_cpu' field, that indicates which CPU that thread used its FPU on last. We update this on every context switch (writing an invalid CPU number if the last context switch didn't leave the FPU in a lazily usable state), so we know that *that* thread has done nothing else with the FPU since. These two fields together can be used when next switching back to the task to see if the CPU still matches: if 'fpu_owner_task' matches the task we are switching to, we know that no other task (or kernel FPU usage) touched the FPU on this CPU in the meantime, and if the current CPU number matches the 'last_cpu' field, we know that this thread did no other FP work on any other CPU, so the FPU state on the CPU must match what was saved on last context switch. In that case, we can avoid the 'f[x]rstor' entirely, and just clear the CR0.TS bit. Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 74c607b..2479049 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h @@ -32,6 +32,8 @@ extern int init_fpu(struct task_struct *child); extern void math_state_restore(void); extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); +DECLARE_PER_CPU(struct task_struct *, fpu_owner_task); + extern user_regset_active_fn fpregs_active, xfpregs_active; extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get, xstateregs_get; @@ -276,7 +278,7 @@ static inline int restore_fpu_checking(struct task_struct *tsk) "emms\n\t" /* clear stack tags */ "fildl %P[addr]", /* set F?P to defined value */ X86_FEATURE_FXSAVE_LEAK, - [addr] "m" (tsk->thread.has_fpu)); + [addr] "m" (tsk->thread.fpu.has_fpu)); return fpu_restore_checking(&tsk->thread.fpu); } @@ -288,19 +290,21 @@ static inline int restore_fpu_checking(struct task_struct *tsk) */ static inline int __thread_has_fpu(struct task_struct *tsk) { - return tsk->thread.has_fpu; + return tsk->thread.fpu.has_fpu; } /* Must be paired with an 'stts' after! */ static inline void __thread_clear_has_fpu(struct task_struct *tsk) { - tsk->thread.has_fpu = 0; + tsk->thread.fpu.has_fpu = 0; + percpu_write(fpu_owner_task, NULL); } /* Must be paired with a 'clts' before! */ static inline void __thread_set_has_fpu(struct task_struct *tsk) { - tsk->thread.has_fpu = 1; + tsk->thread.fpu.has_fpu = 1; + percpu_write(fpu_owner_task, tsk); } /* @@ -345,18 +349,22 @@ typedef struct { int preload; } fpu_switch_t; * We don't do that yet, so "fpu_lazy_restore()" always returns * false, but some day.. */ -#define fpu_lazy_restore(tsk) (0) -#define fpu_lazy_state_intact(tsk) do { } while (0) +static inline int fpu_lazy_restore(struct task_struct *new, unsigned int cpu) +{ + return new == percpu_read_stable(fpu_owner_task) && + cpu == new->thread.fpu.last_cpu; +} -static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct task_struct *new) +static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct task_struct *new, int cpu) { fpu_switch_t fpu; fpu.preload = tsk_used_math(new) && new->fpu_counter > 5; if (__thread_has_fpu(old)) { - if (__save_init_fpu(old)) - fpu_lazy_state_intact(old); - __thread_clear_has_fpu(old); + if (!__save_init_fpu(old)) + cpu = ~0; + old->thread.fpu.last_cpu = cpu; + old->thread.fpu.has_fpu = 0; /* But leave fpu_owner_task! */ /* Don't change CR0.TS if we just switch! */ if (fpu.preload) { @@ -367,9 +375,10 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta stts(); } else { old->fpu_counter = 0; + old->thread.fpu.last_cpu = ~0; if (fpu.preload) { new->fpu_counter++; - if (fpu_lazy_restore(new)) + if (fpu_lazy_restore(new, cpu)) fpu.preload = 0; else prefetch(new->thread.fpu.state); @@ -463,8 +472,10 @@ static inline void kernel_fpu_begin(void) __save_init_fpu(me); __thread_clear_has_fpu(me); /* We do 'stts()' in kernel_fpu_end() */ - } else + } else { + percpu_write(fpu_owner_task, NULL); clts(); + } } static inline void kernel_fpu_end(void) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index f7c89e2..58545c9 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -374,6 +374,8 @@ union thread_xstate { }; struct fpu { + unsigned int last_cpu; + unsigned int has_fpu; union thread_xstate *state; }; @@ -454,7 +456,6 @@ struct thread_struct { unsigned long trap_no; unsigned long error_code; /* floating point and extended processor state */ - unsigned long has_fpu; struct fpu fpu; #ifdef CONFIG_X86_32 /* Virtual 86 mode info */ diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index d43cad7..b667148 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1044,6 +1044,8 @@ DEFINE_PER_CPU(char *, irq_stack_ptr) = DEFINE_PER_CPU(unsigned int, irq_count) = -1; +DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); + /* * Special IST stacks which the CPU switches to when it calls * an IST-marked descriptor entry. Up to 7 stacks (hardware diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index bc32761..c08d1ff 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -304,7 +304,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ - fpu = switch_fpu_prepare(prev_p, next_p); + fpu = switch_fpu_prepare(prev_p, next_p, cpu); /* * Reload esp0. diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 8ad880b..cfa5c90 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -389,7 +389,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) unsigned fsindex, gsindex; fpu_switch_t fpu; - fpu = switch_fpu_prepare(prev_p, next_p); + fpu = switch_fpu_prepare(prev_p, next_p, cpu); /* * Reload esp0, LDT and the page table pointer: -- cgit v0.10.2 From 27e74da9800289e69ba907777df1e2085231eff7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 20 Feb 2012 19:34:10 -0800 Subject: i387: export 'fpu_owner_task' per-cpu variable (And define it properly for x86-32, which had its 'current_task' declaration in separate from x86-64) Bitten by my dislike for modules on the machines I use, and the fact that apparently nobody else actually wanted to test the patches I sent out. Snif. Nobody else cares. Anyway, we probably should uninline the 'kernel_fpu_begin()' function that is what modules actually use and that references this, but this is the minimal fix for now. Reported-by: Josh Boyer Reported-and-tested-by: Jongman Heo Signed-off-by: Linus Torvalds diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index b667148..c0f7d68 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1045,6 +1045,7 @@ DEFINE_PER_CPU(char *, irq_stack_ptr) = DEFINE_PER_CPU(unsigned int, irq_count) = -1; DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); +EXPORT_PER_CPU_SYMBOL(fpu_owner_task); /* * Special IST stacks which the CPU switches to when it calls @@ -1113,6 +1114,8 @@ void debug_stack_reset(void) DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; EXPORT_PER_CPU_SYMBOL(current_task); +DEFINE_PER_CPU(struct task_struct *, fpu_owner_task); +EXPORT_PER_CPU_SYMBOL(fpu_owner_task); #ifdef CONFIG_CC_STACKPROTECTOR DEFINE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); -- cgit v0.10.2 From 910ba598c818243cf3de2f97e03c49948bbb0511 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Tue, 21 Feb 2012 10:24:22 +0100 Subject: ARM: 7336/1: smp_twd: Don't register CPUFREQ notifiers if local timers are not initialised Current ARM local timer code registers CPUFREQ notifiers even in case the twd_timer_setup() isn't called. That seems to be wrong and would eventually lead to kernel crash on the CPU frequency transitions on the SOCs where the local timer doesn't exist or broken because of hardware BUG. Fix it by testing twd_evt and *__this_cpu_ptr(twd_evt). The issue was observed with v3.3-rc3 and building an OMAP2+ kernel on OMAP3 SOC which doesn't have TWD. Below is the dump for reference : Unable to handle kernel paging request at virtual address 007e900 pgd = cdc20000 [007e9000] *pgd=00000000 Internal error: Oops: 5 [#1] SMP Modules linked in: CPU: 0 Not tainted (3.3.0-rc3-pm+debug+initramfs #9) PC is at twd_update_frequency+0x34/0x48 LR is at twd_update_frequency+0x10/0x48 pc : [] lr : [] psr: 60000093 sp : ce311dd8 ip : 00000000 fp : 00000000 r10: 00000000 r9 : 00000001 r8 : ce310000 r7 : c0440458 r6 : c00137f8 r5 : 00000000 r4 : c0947a74 r3 : 00000000 r2 : 007e9000 r1 : 00000000 r0 : 00000000 Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment usr Control: 10c5387d Table: 8dc20019 DAC: 00000015 Process sh (pid: 599, stack limit = 0xce3102f8) Stack: (0xce311dd8 to 0xce312000) 1dc0: 6000c 1de0: 00000001 00000002 00000000 00000000 00000000 00000000 00000 1e00: ffffffff c093d8f0 00000000 ce311ebc 00000001 00000001 ce310 1e20: c001386c c0437c4c c0e95b60 c0e95ba8 00000001 c0e95bf8 ffff4 1e40: 00000000 00000000 c005ef74 ce310000 c0435cf0 ce311ebc 00000 1e60: ce352b40 0007a120 c08d5108 c08ba040 c08ba040 c005f030 00000 1e80: c08bc554 c032fe2c 0007a120 c08d4b64 ce352b40 c08d8618 ffff8 1ea0: c08ba040 c033364c ce311ecc c0433b50 00000002 ffffffea c0330 1ec0: 0007a120 0007a120 22222201 00000000 22222222 00000000 ce357 1ee0: ce3d6000 cdc2aed8 ce352ba0 c0470164 00000002 c032f47c 00034 1f00: c0331cac ce352b40 00000007 c032f6d0 ce352bbc 0003d090 c0930 1f20: c093d8bc c03306a4 00000007 ce311f80 00000007 cdc2aec0 ce358 1f40: ce8d20c0 00000007 b6fe5000 ce311f80 00000007 ce310000 0000c 1f60: c000de74 ce987400 ce8d20c0 b6fe5000 00000000 00000000 0000c 1f80: 00000000 00000000 001fbac8 00000000 00000007 001fbac8 00004 1fa0: c000df04 c000dd60 00000007 001fbac8 00000001 b6fe5000 00000 1fc0: 00000007 001fbac8 00000007 00000004 b6fe5000 00000000 00202 1fe0: 00000000 beb565f8 00101ffc 00008e8c 60000010 00000001 00000 [] (twd_update_frequency+0x34/0x48) from [] ) [] (smp_call_function_single+0x17c/0x1c8) from [] (twd_cpufreq_transition+0x24/0x30) from [) [] (notifier_call_chain+0x44/0x84) from [] () [] (__srcu_notifier_call_chain+0x70/0xa4) from [] (srcu_notifier_call_chain+0x18/0x20) from [] (cpufreq_notify_transition+0xc8/0x1b0) from [] (omap_target+0x1b4/0x28c) from [] (__cpuf) [] (__cpufreq_driver_target+0x50/0x64) from [] (cpufreq_set+0x78/0x98) from [] (store_sc) [] (store_scaling_setspeed+0x5c/0x74) from [) [] (store+0x58/0x74) from [] (sysfs_write_fi) [] (sysfs_write_file+0x80/0xb4) from [] (vfs) [] (vfs_write+0xa8/0x138) from [] (sys_write) [] (sys_write+0x40/0x6c) from [] (ret_fast_s) Code: e594300c e792210c e1a01000 e5840004 (e7930002) ---[ end trace 5da3b5167c1ecdda ]--- Reported-by: Kevin Hilman Acked-by: Marc Zyngier Tested-by: Kevin Hilman Signed-off-by: Santosh Shilimkar Signed-off-by: Russell King diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 4285daa..7a79b24 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -129,7 +129,7 @@ static struct notifier_block twd_cpufreq_nb = { static int twd_cpufreq_init(void) { - if (!IS_ERR(twd_clk)) + if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) return cpufreq_register_notifier(&twd_cpufreq_nb, CPUFREQ_TRANSITION_NOTIFIER); -- cgit v0.10.2 From e23e8c0690d2952dce53e712d01d5b2179f98b64 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Tue, 7 Feb 2012 15:26:44 -0700 Subject: ARM/PCI: Remove ARM's duplicate definition of 'pcibios_max_latency' The patch series to re-factor PCI's 'latency timer' setup (re: http://marc.info/?l=linux-kernel&m=131983853831049&w=2) forgot to remove the ARM specific definition of 'pcibios_max_latency' once such had been moved into the pci core resulting in ARM related compile errors - drivers/built-in.o:(.data+0x230): multiple definition of `pcibios_max_latency' arch/arm/common/built-in.o:(.data+0x40c): first defined here make[1]: *** [vmlinux.o] Error 1 In the series, patch 2/16 (commit 168c8619fd8) converted the ARM specific version of 'pcibios_set_master()' to a non-inlined version. This was done in preperation for hosting it up into PCI's core, which was done in patch 10/16 (commit 96c5590058d) of the series (and where the removal of ARM's 'pcibios_max_latency' was overlooked). Reported-by: Russell King Signed-off-by: Myron Stowe Signed-off-by: Russell King diff --git a/arch/arm/common/it8152.c b/arch/arm/common/it8152.c index d1bcd7b..fb1f1cf 100644 --- a/arch/arm/common/it8152.c +++ b/arch/arm/common/it8152.c @@ -320,13 +320,6 @@ err0: return -EBUSY; } -/* - * If we set up a device for bus mastering, we need to check the latency - * timer as we don't have even crappy BIOSes to set it properly. - * The implementation is from arch/i386/pci/i386.c - */ -unsigned int pcibios_max_latency = 255; - /* ITE bridge requires setting latency timer to avoid early bus access termination by PCI bus master devices */ -- cgit v0.10.2 From 3ddd4d0c629f3d013aa1ab24bb0546dfe342ff94 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 15 Feb 2012 11:28:06 +0000 Subject: ARM: OMAP: fix voltage domain build errors with PM_OPP disabled The voltage domain code wants the voltage tables, which are in the opp*.c files. These files aren't built when PM_OPP is disabled, causing the following build errors at link time: twl-common.c:(.init.text+0x2e48): undefined reference to `omap34xx_vddmpu_volt_data' twl-common.c:(.init.text+0x2e4c): undefined reference to `omap34xx_vddcore_volt_data' twl-common.c:(.init.text+0x2e5c): undefined reference to `omap36xx_vddmpu_volt_data' twl-common.c:(.init.text+0x2e60): undefined reference to `omap36xx_vddcore_volt_data' twl-common.c:(.init.text+0x2830): undefined reference to `omap44xx_vdd_mpu_volt_data' twl-common.c:(.init.text+0x283c): undefined reference to `omap44xx_vdd_iva_volt_data' twl-common.c:(.init.text+0x2844): undefined reference to `omap44xx_vdd_core_volt_data' Acked-by: Kevin Hilman Signed-off-by: Russell King diff --git a/arch/arm/mach-omap2/voltagedomains3xxx_data.c b/arch/arm/mach-omap2/voltagedomains3xxx_data.c index c005e2f..57db203 100644 --- a/arch/arm/mach-omap2/voltagedomains3xxx_data.c +++ b/arch/arm/mach-omap2/voltagedomains3xxx_data.c @@ -108,6 +108,7 @@ void __init omap3xxx_voltagedomains_init(void) * XXX Will depend on the process, validation, and binning * for the currently-running IC */ +#ifdef CONFIG_PM_OPP if (cpu_is_omap3630()) { omap3_voltdm_mpu.volt_data = omap36xx_vddmpu_volt_data; omap3_voltdm_core.volt_data = omap36xx_vddcore_volt_data; @@ -115,6 +116,7 @@ void __init omap3xxx_voltagedomains_init(void) omap3_voltdm_mpu.volt_data = omap34xx_vddmpu_volt_data; omap3_voltdm_core.volt_data = omap34xx_vddcore_volt_data; } +#endif if (cpu_is_omap3517() || cpu_is_omap3505()) voltdms = voltagedomains_am35xx; diff --git a/arch/arm/mach-omap2/voltagedomains44xx_data.c b/arch/arm/mach-omap2/voltagedomains44xx_data.c index 4e11d02..c3115f6 100644 --- a/arch/arm/mach-omap2/voltagedomains44xx_data.c +++ b/arch/arm/mach-omap2/voltagedomains44xx_data.c @@ -100,9 +100,11 @@ void __init omap44xx_voltagedomains_init(void) * XXX Will depend on the process, validation, and binning * for the currently-running IC */ +#ifdef CONFIG_PM_OPP omap4_voltdm_mpu.volt_data = omap44xx_vdd_mpu_volt_data; omap4_voltdm_iva.volt_data = omap44xx_vdd_iva_volt_data; omap4_voltdm_core.volt_data = omap44xx_vdd_core_volt_data; +#endif for (i = 0; voltdm = voltagedomains_omap4[i], voltdm; i++) voltdm->sys_clk.name = sys_clk_name; -- cgit v0.10.2 From e555cf363167f09efae96d32a363e24c4de16b7b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 20 Feb 2012 20:14:16 -0800 Subject: ASoC: ak4642: fixup HeadPhone L/R dapm settings Current ak4642 driver had wrong dapm settings for headphone L/R. If you select headphone L, and select R after that, headphone L setting was removed by R settings. This patch fixes it up. It provides just "Headphone Enable" to user side Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 5ef70b5..278c0a0 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -146,13 +146,10 @@ static const struct snd_kcontrol_new ak4642_snd_controls[] = { SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC, 0, 0xFF, 1, out_tlv), - - SOC_SINGLE("Headphone Switch", PW_MGMT2, 6, 1, 0), }; -static const struct snd_kcontrol_new ak4642_hpout_mixer_controls[] = { - SOC_DAPM_SINGLE("DACH", MD_CTL4, 0, 1, 0), -}; +static const struct snd_kcontrol_new ak4642_headphone_control = + SOC_DAPM_SINGLE("Switch", PW_MGMT2, 6, 1, 0); static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = { SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0), @@ -165,13 +162,12 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("HPOUTR"), SND_SOC_DAPM_OUTPUT("LINEOUT"), - SND_SOC_DAPM_MIXER("HPOUTL Mixer", PW_MGMT2, 5, 0, - &ak4642_hpout_mixer_controls[0], - ARRAY_SIZE(ak4642_hpout_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Out", PW_MGMT2, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPR Out", PW_MGMT2, 4, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Headphone Enable", SND_SOC_NOPM, 0, 0, + &ak4642_headphone_control), - SND_SOC_DAPM_MIXER("HPOUTR Mixer", PW_MGMT2, 4, 0, - &ak4642_hpout_mixer_controls[0], - ARRAY_SIZE(ak4642_hpout_mixer_controls)), + SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("LINEOUT Mixer", PW_MGMT1, 3, 0, &ak4642_lout_mixer_controls[0], @@ -184,12 +180,17 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { static const struct snd_soc_dapm_route ak4642_intercon[] = { /* Outputs */ - {"HPOUTL", NULL, "HPOUTL Mixer"}, - {"HPOUTR", NULL, "HPOUTR Mixer"}, + {"HPOUTL", NULL, "HPL Out"}, + {"HPOUTR", NULL, "HPR Out"}, {"LINEOUT", NULL, "LINEOUT Mixer"}, - {"HPOUTL Mixer", "DACH", "DAC"}, - {"HPOUTR Mixer", "DACH", "DAC"}, + {"HPL Out", NULL, "Headphone Enable"}, + {"HPR Out", NULL, "Headphone Enable"}, + + {"Headphone Enable", "Switch", "DACH"}, + + {"DACH", NULL, "DAC"}, + {"LINEOUT Mixer", "DACL", "DAC"}, }; -- cgit v0.10.2 From 20f12d8ac01917d96860f352f67eddd912df0afb Mon Sep 17 00:00:00 2001 From: Mitsuo Hayasaka Date: Mon, 6 Feb 2012 12:50:07 +0000 Subject: xfs: change available ranges of softlimit and hardlimit in quota check In general, quota allows us to use disk blocks and inodes up to each limit, that is, they are available if they don't exceed their limitations. Current xfs sets their available ranges to lower than them except disk inode quota check. So, this patch changes the ranges to not beyond them. Signed-off-by: Mitsuo Hayasaka Cc: Ben Myers Cc: Alex Elder Cc: Christoph Hellwig Reviewed-by: Christoph Hellwig Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index cbcb7be..53db20e 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -139,10 +139,10 @@ xfs_qm_adjust_dqtimers( if (!d->d_btimer) { if ((d->d_blk_softlimit && - (be64_to_cpu(d->d_bcount) >= + (be64_to_cpu(d->d_bcount) > be64_to_cpu(d->d_blk_softlimit))) || (d->d_blk_hardlimit && - (be64_to_cpu(d->d_bcount) >= + (be64_to_cpu(d->d_bcount) > be64_to_cpu(d->d_blk_hardlimit)))) { d->d_btimer = cpu_to_be32(get_seconds() + mp->m_quotainfo->qi_btimelimit); @@ -151,10 +151,10 @@ xfs_qm_adjust_dqtimers( } } else { if ((!d->d_blk_softlimit || - (be64_to_cpu(d->d_bcount) < + (be64_to_cpu(d->d_bcount) <= be64_to_cpu(d->d_blk_softlimit))) && (!d->d_blk_hardlimit || - (be64_to_cpu(d->d_bcount) < + (be64_to_cpu(d->d_bcount) <= be64_to_cpu(d->d_blk_hardlimit)))) { d->d_btimer = 0; } @@ -162,10 +162,10 @@ xfs_qm_adjust_dqtimers( if (!d->d_itimer) { if ((d->d_ino_softlimit && - (be64_to_cpu(d->d_icount) >= + (be64_to_cpu(d->d_icount) > be64_to_cpu(d->d_ino_softlimit))) || (d->d_ino_hardlimit && - (be64_to_cpu(d->d_icount) >= + (be64_to_cpu(d->d_icount) > be64_to_cpu(d->d_ino_hardlimit)))) { d->d_itimer = cpu_to_be32(get_seconds() + mp->m_quotainfo->qi_itimelimit); @@ -174,10 +174,10 @@ xfs_qm_adjust_dqtimers( } } else { if ((!d->d_ino_softlimit || - (be64_to_cpu(d->d_icount) < + (be64_to_cpu(d->d_icount) <= be64_to_cpu(d->d_ino_softlimit))) && (!d->d_ino_hardlimit || - (be64_to_cpu(d->d_icount) < + (be64_to_cpu(d->d_icount) <= be64_to_cpu(d->d_ino_hardlimit)))) { d->d_itimer = 0; } @@ -185,10 +185,10 @@ xfs_qm_adjust_dqtimers( if (!d->d_rtbtimer) { if ((d->d_rtb_softlimit && - (be64_to_cpu(d->d_rtbcount) >= + (be64_to_cpu(d->d_rtbcount) > be64_to_cpu(d->d_rtb_softlimit))) || (d->d_rtb_hardlimit && - (be64_to_cpu(d->d_rtbcount) >= + (be64_to_cpu(d->d_rtbcount) > be64_to_cpu(d->d_rtb_hardlimit)))) { d->d_rtbtimer = cpu_to_be32(get_seconds() + mp->m_quotainfo->qi_rtbtimelimit); @@ -197,10 +197,10 @@ xfs_qm_adjust_dqtimers( } } else { if ((!d->d_rtb_softlimit || - (be64_to_cpu(d->d_rtbcount) < + (be64_to_cpu(d->d_rtbcount) <= be64_to_cpu(d->d_rtb_softlimit))) && (!d->d_rtb_hardlimit || - (be64_to_cpu(d->d_rtbcount) < + (be64_to_cpu(d->d_rtbcount) <= be64_to_cpu(d->d_rtb_hardlimit)))) { d->d_rtbtimer = 0; } diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 15ff539..0ed9ee7 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1981,7 +1981,7 @@ xfs_qm_dqcheck( if (!errs && ddq->d_id) { if (ddq->d_blk_softlimit && - be64_to_cpu(ddq->d_bcount) >= + be64_to_cpu(ddq->d_bcount) > be64_to_cpu(ddq->d_blk_softlimit)) { if (!ddq->d_btimer) { if (flags & XFS_QMOPT_DOWARN) @@ -1992,7 +1992,7 @@ xfs_qm_dqcheck( } } if (ddq->d_ino_softlimit && - be64_to_cpu(ddq->d_icount) >= + be64_to_cpu(ddq->d_icount) > be64_to_cpu(ddq->d_ino_softlimit)) { if (!ddq->d_itimer) { if (flags & XFS_QMOPT_DOWARN) @@ -2003,7 +2003,7 @@ xfs_qm_dqcheck( } } if (ddq->d_rtb_softlimit && - be64_to_cpu(ddq->d_rtbcount) >= + be64_to_cpu(ddq->d_rtbcount) > be64_to_cpu(ddq->d_rtb_softlimit)) { if (!ddq->d_rtbtimer) { if (flags & XFS_QMOPT_DOWARN) diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index eafbcff..711a86e 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -813,11 +813,11 @@ xfs_qm_export_dquot( (XFS_IS_OQUOTA_ENFORCED(mp) && (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) && dst->d_id != 0) { - if (((int) dst->d_bcount >= (int) dst->d_blk_softlimit) && + if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) && (dst->d_blk_softlimit > 0)) { ASSERT(dst->d_btimer != 0); } - if (((int) dst->d_icount >= (int) dst->d_ino_softlimit) && + if (((int) dst->d_icount > (int) dst->d_ino_softlimit) && (dst->d_ino_softlimit > 0)) { ASSERT(dst->d_itimer != 0); } diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 4d00ee6..8525553 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -649,12 +649,12 @@ xfs_trans_dqresv( * nblks. */ if (hardlimit > 0ULL && - hardlimit <= nblks + *resbcountp) { + hardlimit < nblks + *resbcountp) { xfs_quota_warn(mp, dqp, QUOTA_NL_BHARDWARN); goto error_return; } if (softlimit > 0ULL && - softlimit <= nblks + *resbcountp) { + softlimit < nblks + *resbcountp) { if ((timer != 0 && get_seconds() > timer) || (warns != 0 && warns >= warnlimit)) { xfs_quota_warn(mp, dqp, -- cgit v0.10.2 From c922bbc819324558e61402a7a76c10c550ca61bc Mon Sep 17 00:00:00 2001 From: Mitsuo Hayasaka Date: Mon, 6 Feb 2012 12:50:30 +0000 Subject: xfs: make inode quota check more general The xfs checks quota when reserving disk blocks and inodes. In the block reservation, it checks if the total number of blocks including current usage and new reservation exceed quota. In the inode reservation, it checks using the total number of inodes including only current usage without new reservation. However, this inode quota check works well since the caller of xfs_trans_dquot() always sets the argument of the number of new inode reservation to 1 or 0 and inode is reserved one by one in current xfs. To make it more general, this patch changes it to the same way as the block quota check. Signed-off-by: Mitsuo Hayasaka Cc: Ben Myers Cc: Alex Elder Cc: Christoph Hellwig Reviewed-by: Mark Tinguely Reviewed-by: Christoph Hellwig Signed-off-by: Ben Myers diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 8525553..c4ba366 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -677,11 +677,13 @@ xfs_trans_dqresv( if (!softlimit) softlimit = q->qi_isoftlimit; - if (hardlimit > 0ULL && count >= hardlimit) { + if (hardlimit > 0ULL && + hardlimit < ninos + count) { xfs_quota_warn(mp, dqp, QUOTA_NL_IHARDWARN); goto error_return; } - if (softlimit > 0ULL && count >= softlimit) { + if (softlimit > 0ULL && + softlimit < ninos + count) { if ((timer != 0 && get_seconds() > timer) || (warns != 0 && warns >= warnlimit)) { xfs_quota_warn(mp, dqp, -- cgit v0.10.2 From 5180bb392a8aab5233e6db858ac1d8371533e20f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 21 Feb 2012 11:26:55 -0500 Subject: ARM/audit: include audit header and fix audit arch Both bugs being fixed were introduced in: 29ef73b7a823b77a7cd0bdd7d7cded3fb6c2587b Include linux/audit.h to fix below build errors: CC arch/arm/kernel/ptrace.o arch/arm/kernel/ptrace.c: In function 'syscall_trace': arch/arm/kernel/ptrace.c:919: error: implicit declaration of function 'audit_syscall_exit' arch/arm/kernel/ptrace.c:921: error: implicit declaration of function 'audit_syscall_entry' arch/arm/kernel/ptrace.c:921: error: 'AUDIT_ARCH_ARMEB' undeclared (first use in this function) arch/arm/kernel/ptrace.c:921: error: (Each undeclared identifier is reported only once arch/arm/kernel/ptrace.c:921: error: for each function it appears in.) make[1]: *** [arch/arm/kernel/ptrace.o] Error 1 make: *** [arch/arm/kernel] Error 2 This part of the patch is: Reported-by: Axel Lin Reported-by: Peter Ujfalusi (They both provided patches to fix it) This patch also (at the request of the list) fixes the fact that ARM has both LE and BE versions however the audit code was called as if it was always BE. If audit userspace were to try to interpret the bits it got from a LE system it would obviously do so incorrectly. Fix this by using the right arch flag on the right system. This part of the patch is: Reported-by: Russell King - ARM Linux Signed-off-by: Eric Paris Signed-off-by: Russell King diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index e33870f..ede6443 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -904,6 +905,12 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } +#ifdef __ARMEB__ +#define AUDIT_ARCH_NR AUDIT_ARCH_ARMEB +#else +#define AUDIT_ARCH_NR AUDIT_ARCH_ARM +#endif + asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) { unsigned long ip; @@ -918,7 +925,7 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) if (!ip) audit_syscall_exit(regs); else - audit_syscall_entry(AUDIT_ARCH_ARMEB, scno, regs->ARM_r0, + audit_syscall_entry(AUDIT_ARCH_NR, scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); if (!test_thread_flag(TIF_SYSCALL_TRACE)) -- cgit v0.10.2 From cab928ee1f221c9cc48d6615070fefe2e444384a Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 7 Feb 2012 15:11:46 -0800 Subject: USB: Fix handoff when BIOS disables host PCI device. On some systems with an Intel Panther Point xHCI host controller, the BIOS disables the xHCI PCI device during boot, and switches the xHCI ports over to EHCI. This allows the BIOS to access USB devices without having xHCI support. The downside is that the xHCI BIOS handoff mechanism will fail because memory mapped I/O is not enabled for the disabled PCI device. Jesse Barnes says this is expected behavior. The PCI core will enable BARs before quirks run, but it will leave it in an undefined state, and it may not have memory mapped I/O enabled. Make the generic USB quirk handler call pci_enable_device() to re-enable MMIO, and call pci_disable_device() once the host-specific BIOS handoff is finished. This will balance the ref counts in the PCI core. When the PCI probe function is called, usb_hcd_pci_probe() will call pci_enable_device() again. This should be back ported to kernels as old as 2.6.31. That was the first kernel with xHCI support, and no one has complained about BIOS handoffs failing due to memory mapped I/O being disabled on other hosts (EHCI, UHCI, or OHCI). Signed-off-by: Sarah Sharp Acked-by: Oliver Neukum Cc: Jesse Barnes Cc: stable@vger.kernel.org diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index ac53a66..7732d69 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -872,7 +872,17 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) */ if (pdev->vendor == 0x184e) /* vendor Netlogic */ return; + if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI && + pdev->class != PCI_CLASS_SERIAL_USB_OHCI && + pdev->class != PCI_CLASS_SERIAL_USB_EHCI && + pdev->class != PCI_CLASS_SERIAL_USB_XHCI) + return; + if (pci_enable_device(pdev) < 0) { + dev_warn(&pdev->dev, "Can't enable PCI device, " + "BIOS handoff failed.\n"); + return; + } if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI) quirk_usb_handoff_uhci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_OHCI) @@ -881,5 +891,6 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) quirk_usb_disable_ehci(pdev); else if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI) quirk_usb_handoff_xhci(pdev); + pci_disable_device(pdev); } DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); -- cgit v0.10.2 From a45aa3b30583e7d54e7cf4fbcd0aa699348a6e5c Mon Sep 17 00:00:00 2001 From: Elric Fu Date: Sat, 18 Feb 2012 13:32:27 +0800 Subject: USB: Set hub depth after USB3 hub reset The superspeed device attached to a USB 3.0 hub(such as VIA's) doesn't respond the address device command after resume. The root cause is the superspeed hub will miss the Hub Depth value that is used as an offset into the route string to locate the bits it uses to determine the downstream port number after reset, and all packets can't be routed to the device attached to the superspeed hub. Hub driver sends a Set Hub Depth request to the superspeed hub except for USB 3.0 root hub when the hub is initialized and doesn't send the request again after reset due to the resume process. So moving the code that sends the Set Hub Depth request to the superspeed hub from hub_configure() to hub_activate() is to cover those situations include initialization and reset. The patch should be backported to kernels as old as 2.6.39. Signed-off-by: Elric Fu Signed-off-by: Sarah Sharp Acked-by: Alan Stern Cc: stable@vger.kernel.org diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a0613d8..265c2f6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -705,10 +705,26 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) if (type == HUB_INIT3) goto init3; - /* After a resume, port power should still be on. + /* The superspeed hub except for root hub has to use Hub Depth + * value as an offset into the route string to locate the bits + * it uses to determine the downstream port number. So hub driver + * should send a set hub depth request to superspeed hub after + * the superspeed hub is set configuration in initialization or + * reset procedure. + * + * After a resume, port power should still be on. * For any other type of activation, turn it on. */ if (type != HUB_RESUME) { + if (hdev->parent && hub_is_superspeed(hdev)) { + ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + HUB_SET_DEPTH, USB_RT_HUB, + hdev->level - 1, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) + dev_err(hub->intfdev, + "set hub depth failed\n"); + } /* Speed up system boot by using a delayed_work for the * hub's initial power-up delays. This is pretty awkward @@ -987,18 +1003,6 @@ static int hub_configure(struct usb_hub *hub, goto fail; } - if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) { - ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), - HUB_SET_DEPTH, USB_RT_HUB, - hdev->level - 1, 0, NULL, 0, - USB_CTRL_SET_TIMEOUT); - - if (ret < 0) { - message = "can't set hub depth"; - goto fail; - } - } - /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, * but the hub can/will return fewer bytes here. -- cgit v0.10.2 From 340a3504fd39dad753ba908fb6f894ee81fc3ae2 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 13 Feb 2012 14:42:11 -0800 Subject: xhci: Fix encoding for HS bulk/control NAK rate. The xHCI 0.96 spec says that HS bulk and control endpoint NAK rate must be encoded as an exponent of two number of microframes. The endpoint descriptor has the NAK rate encoded in number of microframes. We were just copying the value from the endpoint descriptor into the endpoint context interval field, which was not correct. This lead to the VIA host rejecting the add of a bulk OUT endpoint from any USB 2.0 mass storage device. The fix is to use the correct encoding. Refactor the code to convert number of frames to an exponential number of microframes, and make sure we convert the number of microframes in HS bulk and control endpoints to an exponent. This should be back ported to kernels as old as 2.6.31, that contain the commit dfa49c4ad120a784ef1ff0717168aa79f55a483a "USB: xhci - fix math in xhci_get_endpoint_interval" Signed-off-by: Sarah Sharp Tested-by: Felipe Contreras Suggested-by: Andiry Xu Cc: stable@vger.kernel.org diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 36cbe22..383fc85 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1126,26 +1126,42 @@ static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, } /* - * Convert bInterval expressed in frames (in 1-255 range) to exponent of + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of * microframes, rounded down to nearest power of 2. */ -static unsigned int xhci_parse_frame_interval(struct usb_device *udev, - struct usb_host_endpoint *ep) +static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, + struct usb_host_endpoint *ep, unsigned int desc_interval, + unsigned int min_exponent, unsigned int max_exponent) { unsigned int interval; - interval = fls(8 * ep->desc.bInterval) - 1; - interval = clamp_val(interval, 3, 10); - if ((1 << interval) != 8 * ep->desc.bInterval) + interval = fls(desc_interval) - 1; + interval = clamp_val(interval, min_exponent, max_exponent); + if ((1 << interval) != desc_interval) dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n", ep->desc.bEndpointAddress, 1 << interval, - 8 * ep->desc.bInterval); + desc_interval); return interval; } +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return xhci_microframes_to_exponent(udev, ep, + ep->desc.bInterval, 0, 15); +} + + +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + return xhci_microframes_to_exponent(udev, ep, + ep->desc.bInterval * 8, 3, 10); +} + /* Return the polling or NAK interval. * * The polling interval is expressed in "microframes". If xHCI's Interval field @@ -1164,7 +1180,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, /* Max NAK rate */ if (usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_bulk(&ep->desc)) { - interval = ep->desc.bInterval; + interval = xhci_parse_microframe_interval(udev, ep); break; } /* Fall through - SS and HS isoc/int have same decoding */ -- cgit v0.10.2 From bb94a406682770a35305daaa241ccdb7cab399de Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Feb 2012 13:16:32 -0500 Subject: usb-storage: fix freezing of the scanning thread This patch (as1521b) fixes the interaction between usb-storage's scanning thread and the freezer. The current implementation has a race: If the device is unplugged shortly after being plugged in and just as a system sleep begins, the scanning thread may get frozen before the khubd task. Khubd won't be able to freeze until the disconnect processing is complete, and the disconnect processing can't proceed until the scanning thread finishes, so the sleep transition will fail. The implementation in the 3.2 kernel suffers from an additional problem. There the scanning thread calls set_freezable_with_signal(), and the signals sent by the freezer will mess up the thread's I/O delays, which are all interruptible. The solution to both problems is the same: Replace the kernel thread used for scanning with a delayed-work routine on the system freezable work queue. Freezable work queues have the nice property that you can cancel a work item even while the work queue is frozen, and no signals are needed. The 3.2 version of this patch solves the problem in Bugzilla #42730. Signed-off-by: Alan Stern Acked-by: Seth Forshee CC: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 3dd7da9..db51ba1 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -788,15 +788,19 @@ static void quiesce_and_remove_host(struct us_data *us) struct Scsi_Host *host = us_to_host(us); /* If the device is really gone, cut short reset delays */ - if (us->pusb_dev->state == USB_STATE_NOTATTACHED) + if (us->pusb_dev->state == USB_STATE_NOTATTACHED) { set_bit(US_FLIDX_DISCONNECTING, &us->dflags); + wake_up(&us->delay_wait); + } - /* Prevent SCSI-scanning (if it hasn't started yet) - * and wait for the SCSI-scanning thread to stop. + /* Prevent SCSI scanning (if it hasn't started yet) + * or wait for the SCSI-scanning routine to stop. */ - set_bit(US_FLIDX_DONT_SCAN, &us->dflags); - wake_up(&us->delay_wait); - wait_for_completion(&us->scanning_done); + cancel_delayed_work_sync(&us->scan_dwork); + + /* Balance autopm calls if scanning was cancelled */ + if (test_bit(US_FLIDX_SCAN_PENDING, &us->dflags)) + usb_autopm_put_interface_no_suspend(us->pusb_intf); /* Removing the host will perform an orderly shutdown: caches * synchronized, disks spun down, etc. @@ -823,53 +827,28 @@ static void release_everything(struct us_data *us) scsi_host_put(us_to_host(us)); } -/* Thread to carry out delayed SCSI-device scanning */ -static int usb_stor_scan_thread(void * __us) +/* Delayed-work routine to carry out SCSI-device scanning */ +static void usb_stor_scan_dwork(struct work_struct *work) { - struct us_data *us = (struct us_data *)__us; + struct us_data *us = container_of(work, struct us_data, + scan_dwork.work); struct device *dev = &us->pusb_intf->dev; - dev_dbg(dev, "device found\n"); - - set_freezable(); + dev_dbg(dev, "starting scan\n"); - /* - * Wait for the timeout to expire or for a disconnect - * - * We can't freeze in this thread or we risk causing khubd to - * fail to freeze, but we can't be non-freezable either. Nor can - * khubd freeze while waiting for scanning to complete as it may - * hold the device lock, causing a hang when suspending devices. - * So instead of using wait_event_freezable(), explicitly test - * for (DONT_SCAN || freezing) in interruptible wait and proceed - * if any of DONT_SCAN, freezing or timeout has happened. - */ - if (delay_use > 0) { - dev_dbg(dev, "waiting for device to settle " - "before scanning\n"); - wait_event_interruptible_timeout(us->delay_wait, - test_bit(US_FLIDX_DONT_SCAN, &us->dflags) || - freezing(current), delay_use * HZ); + /* For bulk-only devices, determine the max LUN value */ + if (us->protocol == USB_PR_BULK && !(us->fflags & US_FL_SINGLE_LUN)) { + mutex_lock(&us->dev_mutex); + us->max_lun = usb_stor_Bulk_max_lun(us); + mutex_unlock(&us->dev_mutex); } + scsi_scan_host(us_to_host(us)); + dev_dbg(dev, "scan complete\n"); - /* If the device is still connected, perform the scanning */ - if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) { - - /* For bulk-only devices, determine the max LUN value */ - if (us->protocol == USB_PR_BULK && - !(us->fflags & US_FL_SINGLE_LUN)) { - mutex_lock(&us->dev_mutex); - us->max_lun = usb_stor_Bulk_max_lun(us); - mutex_unlock(&us->dev_mutex); - } - scsi_scan_host(us_to_host(us)); - dev_dbg(dev, "scan complete\n"); - - /* Should we unbind if no devices were detected? */ - } + /* Should we unbind if no devices were detected? */ usb_autopm_put_interface(us->pusb_intf); - complete_and_exit(&us->scanning_done, 0); + clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags); } static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) @@ -916,7 +895,7 @@ int usb_stor_probe1(struct us_data **pus, init_completion(&us->cmnd_ready); init_completion(&(us->notify)); init_waitqueue_head(&us->delay_wait); - init_completion(&us->scanning_done); + INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); @@ -947,7 +926,6 @@ EXPORT_SYMBOL_GPL(usb_stor_probe1); /* Second part of general USB mass-storage probing */ int usb_stor_probe2(struct us_data *us) { - struct task_struct *th; int result; struct device *dev = &us->pusb_intf->dev; @@ -988,20 +966,14 @@ int usb_stor_probe2(struct us_data *us) goto BadDevice; } - /* Start up the thread for delayed SCSI-device scanning */ - th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); - if (IS_ERR(th)) { - dev_warn(dev, - "Unable to start the device-scanning thread\n"); - complete(&us->scanning_done); - quiesce_and_remove_host(us); - result = PTR_ERR(th); - goto BadDevice; - } - + /* Submit the delayed_work for SCSI-device scanning */ usb_autopm_get_interface_no_resume(us->pusb_intf); - wake_up_process(th); + set_bit(US_FLIDX_SCAN_PENDING, &us->dflags); + if (delay_use > 0) + dev_dbg(dev, "waiting for device to settle before scanning\n"); + queue_delayed_work(system_freezable_wq, &us->scan_dwork, + delay_use * HZ); return 0; /* We come here if there are any problems */ diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 7b0f211..75f70f0 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -47,6 +47,7 @@ #include #include #include +#include #include struct us_data; @@ -72,7 +73,7 @@ struct us_unusual_dev { #define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */ #define US_FLIDX_RESETTING 4 /* device reset in progress */ #define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ -#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */ +#define US_FLIDX_SCAN_PENDING 6 /* scanning not yet done */ #define US_FLIDX_REDO_READ10 7 /* redo READ(10) command */ #define US_FLIDX_READ10_WORKED 8 /* previous READ(10) succeeded */ @@ -147,8 +148,8 @@ struct us_data { /* mutual exclusion and synchronization structures */ struct completion cmnd_ready; /* to sleep thread on */ struct completion notify; /* thread begin/end */ - wait_queue_head_t delay_wait; /* wait during scan, reset */ - struct completion scanning_done; /* wait for scan thread */ + wait_queue_head_t delay_wait; /* wait during reset */ + struct delayed_work scan_dwork; /* for async scanning */ /* subdriver information */ void *extra; /* Any extra data */ -- cgit v0.10.2 From 7fd25702ba616d9ba56e2a625472f29e5aff25ee Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 20 Feb 2012 09:31:57 +0100 Subject: USB: Serial: ti_usb_3410_5052: Add Abbot Diabetes Care cable id This USB-serial cable with mini stereo jack enumerates as: Bus 001 Device 004: ID 1a61:3410 Abbott Diabetes Care It is a TI3410 inside. Signed-off-by: Andrew Lunn Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 8468eb7..75b838e 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -165,7 +165,7 @@ static unsigned int product_5052_count; /* the array dimension is the number of default entries plus */ /* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */ /* null entry */ -static struct usb_device_id ti_id_table_3410[13+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_3410[14+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -179,6 +179,7 @@ static struct usb_device_id ti_id_table_3410[13+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, + { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { @@ -188,7 +189,7 @@ static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, }; -static struct usb_device_id ti_id_table_combined[17+2*TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_combined[18+2*TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -206,6 +207,7 @@ static struct usb_device_id ti_id_table_combined[17+2*TI_EXTRA_VID_PID_COUNT+1] { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, + { USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) }, { } }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h index 2aac195..f140f1b 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.h +++ b/drivers/usb/serial/ti_usb_3410_5052.h @@ -49,6 +49,10 @@ #define MTS_MT9234ZBA_PRODUCT_ID 0xF115 #define MTS_MT9234ZBAOLD_PRODUCT_ID 0x0319 +/* Abbott Diabetics vendor and product ids */ +#define ABBOTT_VENDOR_ID 0x1a61 +#define ABBOTT_PRODUCT_ID 0x3410 + /* Commands */ #define TI_GET_VERSION 0x01 #define TI_GET_PORT_STATUS 0x02 -- cgit v0.10.2 From c6c1e4491dc8d1ed2509fa6aacffa7f34614fc38 Mon Sep 17 00:00:00 2001 From: Bruno Thomsen Date: Tue, 21 Feb 2012 23:41:37 +0100 Subject: USB: Added Kamstrup VID/PIDs to cp210x serial driver. Signed-off-by: Bruno Thomsen Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 8dbf51a..08a5575 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -136,6 +136,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ + { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */ + { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */ { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ -- cgit v0.10.2 From 797a796a13df6b84a4791e57306737059b5b2384 Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Tue, 7 Feb 2012 11:45:33 +0900 Subject: asm-generic: architecture independent readq/writeq for 32bit environment This provides unified readq()/writeq() helper functions for 32-bit drivers. For some cases, readq/writeq without atomicity is harmful, and order of io access has to be specified explicitly. So in this patch, new two header files which contain non-atomic readq/writeq are added. - provides non-atomic readq/ writeq with the order of lower address -> higher address - provides non-atomic readq/ writeq with reversed order This allows us to remove some readq()s that were added drivers when the default non-atomic ones were removed in commit dbee8a0affd5 ("x86: remove 32-bit versions of readq()/writeq()") The drivers which need readq/writeq but can do with the non-atomic ones must add the line: #include /* or hi-lo.h */ But this will be nop in 64-bit environments, and no other #ifdefs are required. So I believe that this patch can solve the problem of 1. driver-specific readq/writeq 2. atomicity and order of io access This patch is tested with building allyesconfig and allmodconfig as ARCH=x86 and ARCH=i386 on top of tip/master. Cc: Kashyap Desai Cc: Len Brown Cc: Ravi Anand Cc: Vikas Chaudhary Cc: Matthew Garrett Cc: Jason Uhlenkott Cc: James Bottomley Cc: Thomas Gleixner Cc: "H. Peter Anvin" Cc: Roland Dreier Cc: James Bottomley Cc: Alan Cox Cc: Matthew Wilcox Cc: Andrew Morton Signed-off-by: Hitoshi Mitake Signed-off-by: Linus Torvalds diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index c1dc4d8..1f3c1a7 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -41,6 +41,8 @@ #include #include +#include + #define NVME_Q_DEPTH 1024 #define SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) #define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index aa08497..73f55e200 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -15,6 +15,8 @@ #include #include "edac_core.h" +#include + #define I3200_REVISION "1.1" #define EDAC_MOD_STR "i3200_edac" @@ -101,19 +103,6 @@ struct i3200_priv { static int nr_channels; -#ifndef readq -static inline __u64 readq(const volatile void __iomem *addr) -{ - const volatile u32 __iomem *p = addr; - u32 low, high; - - low = readl(p); - high = readl(p + 1); - - return low + ((u64)high << 32); -} -#endif - static int how_many_channels(struct pci_dev *pdev) { unsigned char capid0_8b; /* 8th byte of CAPID0 */ diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c index 42a7d60..7481146 100644 --- a/drivers/platform/x86/ibm_rtl.c +++ b/drivers/platform/x86/ibm_rtl.c @@ -33,6 +33,8 @@ #include #include +#include + static bool force; module_param(force, bool, 0); MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); @@ -83,19 +85,6 @@ static void __iomem *rtl_cmd_addr; static u8 rtl_cmd_type; static u8 rtl_cmd_width; -#ifndef readq -static inline __u64 readq(const volatile void __iomem *addr) -{ - const volatile u32 __iomem *p = addr; - u32 low, high; - - low = readl(p); - high = readl(p + 1); - - return low + ((u64)high << 32); -} -#endif - static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) { if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 809a3ae..88a98cf 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -77,6 +77,8 @@ #include #include "intel_ips.h" +#include + #define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32 /* @@ -344,19 +346,6 @@ struct ips_driver { static bool ips_gpu_turbo_enabled(struct ips_driver *ips); -#ifndef readq -static inline __u64 readq(const volatile void __iomem *addr) -{ - const volatile u32 __iomem *p = addr; - u32 low, high; - - low = readl(p); - high = readl(p + 1); - - return low + ((u64)high << 32); -} -#endif - /** * ips_cpu_busy - is CPU busy? * @ips: IPS driver struct diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 78f1111..65253df 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -10,6 +10,8 @@ #include "ql4_def.h" #include "ql4_glbl.h" +#include + #define MASK(n) DMA_BIT_MASK(n) #define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | ((addr >> 25) & 0x3ff)) #define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | ((addr >> 25) & 0x3ff)) @@ -655,27 +657,6 @@ static int qla4_8xxx_pci_is_same_window(struct scsi_qla_host *ha, return 0; } -#ifndef readq -static inline __u64 readq(const volatile void __iomem *addr) -{ - const volatile u32 __iomem *p = addr; - u32 low, high; - - low = readl(p); - high = readl(p + 1); - - return low + ((u64)high << 32); -} -#endif - -#ifndef writeq -static inline void writeq(__u64 val, volatile void __iomem *addr) -{ - writel(val, addr); - writel(val >> 32, addr+4); -} -#endif - static int qla4_8xxx_pci_mem_read_direct(struct scsi_qla_host *ha, u64 off, void *data, int size) { diff --git a/include/asm-generic/io-64-nonatomic-hi-lo.h b/include/asm-generic/io-64-nonatomic-hi-lo.h new file mode 100644 index 0000000..a6806a9 --- /dev/null +++ b/include/asm-generic/io-64-nonatomic-hi-lo.h @@ -0,0 +1,28 @@ +#ifndef _ASM_IO_64_NONATOMIC_HI_LO_H_ +#define _ASM_IO_64_NONATOMIC_HI_LO_H_ + +#include +#include + +#ifndef readq +static inline __u64 readq(const volatile void __iomem *addr) +{ + const volatile u32 __iomem *p = addr; + u32 low, high; + + high = readl(p + 1); + low = readl(p); + + return low + ((u64)high << 32); +} +#endif + +#ifndef writeq +static inline void writeq(__u64 val, volatile void __iomem *addr) +{ + writel(val >> 32, addr + 4); + writel(val, addr); +} +#endif + +#endif /* _ASM_IO_64_NONATOMIC_HI_LO_H_ */ diff --git a/include/asm-generic/io-64-nonatomic-lo-hi.h b/include/asm-generic/io-64-nonatomic-lo-hi.h new file mode 100644 index 0000000..ca546b1 --- /dev/null +++ b/include/asm-generic/io-64-nonatomic-lo-hi.h @@ -0,0 +1,28 @@ +#ifndef _ASM_IO_64_NONATOMIC_LO_HI_H_ +#define _ASM_IO_64_NONATOMIC_LO_HI_H_ + +#include +#include + +#ifndef readq +static inline __u64 readq(const volatile void __iomem *addr) +{ + const volatile u32 __iomem *p = addr; + u32 low, high; + + low = readl(p); + high = readl(p + 1); + + return low + ((u64)high << 32); +} +#endif + +#ifndef writeq +static inline void writeq(__u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val >> 32, addr + 4); +} +#endif + +#endif /* _ASM_IO_64_NONATOMIC_LO_HI_H_ */ -- cgit v0.10.2 From faf309009e2e18d30c032b7d9479f29b91677c37 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2012 17:24:20 -0800 Subject: sys_poll: fix incorrect type for 'timeout' parameter The 'poll()' system call timeout parameter is supposed to be 'int', not 'long'. Now, the reason this matters is that right now 32-bit compat mode is broken on at least x86-64, because the 32-bit code just calls 'sys_poll()' directly on x86-64, and the 32-bit argument will have been zero-extended, turning a signed 'int' into a large unsigned 'long' value. We could just introduce a 'compat_sys_poll()' function for this, and that may eventually be what we have to do, but since the actual standard poll() semantics is *supposed* to be 'int', and since at least on x86-64 glibc sign-extends the argument before invocing the system call (so nobody can actually use a 64-bit timeout value in user space _anyway_, even in 64-bit binaries), the simpler solution would seem to be to just fix the definition of the system call to match what it should have been from the very start. If it turns out that somebody somehow circumvents the user-level libc 64-bit sign extension and actually uses a large unsigned 64-bit timeout despite that not being how poll() is supposed to work, we will need to do the compat_sys_poll() approach. Reported-by: Thomas Meyer Acked-by: Eric Dumazet Signed-off-by: Linus Torvalds diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 18c51df..ff605a3 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -662,7 +662,7 @@ ENTRY(sys32_getresuid16_wrapper) ENTRY(sys32_poll_wrapper) llgtr %r2,%r2 # struct pollfd * llgfr %r3,%r3 # unsigned int - lgfr %r4,%r4 # long + lgfr %r4,%r4 # int jg sys_poll # branch to system call ENTRY(sys32_setresgid16_wrapper) diff --git a/fs/select.c b/fs/select.c index d33418f..e782258 100644 --- a/fs/select.c +++ b/fs/select.c @@ -912,7 +912,7 @@ static long do_restart_poll(struct restart_block *restart_block) } SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, - long, timeout_msecs) + int, timeout_msecs) { struct timespec end_time, *to = NULL; int ret; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 515669f..8ec1153 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -624,7 +624,7 @@ asmlinkage long sys_socketpair(int, int, int, int __user *); asmlinkage long sys_socketcall(int call, unsigned long __user *args); asmlinkage long sys_listen(int, int); asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, - long timeout); + int timeout); asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp); asmlinkage long sys_old_select(struct sel_arg_struct __user *arg); -- cgit v0.10.2 From 9b45c0d2c50e19bd73d2f5e18dd6912a6991306e Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 22 Feb 2012 12:45:07 +1100 Subject: maintainers: update my email address Update my email address. Signed-off-by: James Morris diff --git a/MAINTAINERS b/MAINTAINERS index 9a648eb..924fb0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5861,7 +5861,7 @@ S: Maintained F: drivers/mmc/host/sdhci-spear.c SECURITY SUBSYSTEM -M: James Morris +M: James Morris L: linux-security-module@vger.kernel.org (suggested Cc:) T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git W: http://security.wiki.kernel.org/ @@ -5874,7 +5874,7 @@ S: Supported SELINUX SECURITY MODULE M: Stephen Smalley -M: James Morris +M: James Morris M: Eric Paris L: selinux@tycho.nsa.gov (subscribers-only, general discussion) W: http://selinuxproject.org -- cgit v0.10.2 From 07d2f1a54a78a648093f4f3a33aa86b03cf9e25f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 15 Feb 2012 17:51:26 +0000 Subject: powerpc: Remove references to cpu_*_map This has been obsolescent for a while; time for the final push. In adjacent context, replaced old cpus_* with cpumask_*. Signed-off-by: Rusty Russell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/wsp/smp.c b/arch/powerpc/platforms/wsp/smp.c index 71bd105..0ba103a 100644 --- a/arch/powerpc/platforms/wsp/smp.c +++ b/arch/powerpc/platforms/wsp/smp.c @@ -71,7 +71,7 @@ int __devinit smp_a2_kick_cpu(int nr) static int __init smp_a2_probe(void) { - return cpus_weight(cpu_possible_map); + return num_possible_cpus(); } static struct smp_ops_t a2_smp_ops = { -- cgit v0.10.2 From 922b9f86a020258d3ea3bfed27251efaed7a03a3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 20 Feb 2012 21:32:30 +0000 Subject: powerpc: Fix program check handling when lockdep is enabled In commit 54321242afe ("Disable interrupts early in Program Check"), we switched from enabling to disabling interrupts in program_check_common. Whereas ENABLE_INTS leaves r3 untouched, if lockdep is enabled DISABLE_INTS calls into lockdep code and will clobber r3. That means we pass a bogus struct pt_regs* into program_check_exception() and all hell breaks loose. So load our regs pointer into r3 after we call DISABLE_INTS. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 3844ca7..15c5a4f 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -774,8 +774,8 @@ alignment_common: program_check_common: EXCEPTION_PROLOG_COMMON(0x700, PACA_EXGEN) bl .save_nvgprs - addi r3,r1,STACK_FRAME_OVERHEAD DISABLE_INTS + addi r3,r1,STACK_FRAME_OVERHEAD bl .program_check_exception b .ret_from_except -- cgit v0.10.2 From aa491ad3d4bf3c7994a419b3bb1c91b9b4fd2a8a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 22 Feb 2012 16:04:24 +1100 Subject: cpuidle: Default y on powerpc pSeries We moved all our pSeries idle loops to the cpu idle framework so we really want it to come up by default. Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 7dbc4a8..78a666d 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -1,7 +1,7 @@ config CPU_IDLE bool "CPU idle PM support" - default ACPI + default y if ACPI || PPC_PSERIES help CPU idle is a generic framework for supporting software-controlled idle processor power management. It includes modular cross-platform -- cgit v0.10.2 From 18b246fa60dd4bfc71c78d669e2ffaa5df454d6a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 22 Feb 2012 16:48:32 +1100 Subject: powerpc: Fix various issues with return to userspace We have a few problems when returning to userspace. This is a quick set of fixes for 3.3, I'll look into a more comprehensive rework for 3.4. This fixes: - We kept interrupts soft-disabled when schedule'ing or calling do_signal when returning to userspace as a result of a hardware interrupt. - Rename do_signal to do_notify_resume like all other archs (and do_signal_pending back to do_signal, which it was before Roland changed it). - Add the missing call to key_replace_session_keyring() to do_notify_resume(). Signed-off-by: Benjamin Herrenschmidt --- diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 4f80cf1..3e57a00 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -1213,7 +1213,7 @@ do_user_signal: /* r10 contains MSR_KERNEL here */ stw r3,_TRAP(r1) 2: addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r9 - bl do_signal + bl do_notify_resume REST_NVGPRS(r1) b recheck diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index d834425..866462c 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -751,12 +751,16 @@ user_work: andi. r0,r4,_TIF_NEED_RESCHED beq 1f + li r5,1 + TRACE_AND_RESTORE_IRQ(r5); bl .schedule b .ret_from_except_lite 1: bl .save_nvgprs + li r5,1 + TRACE_AND_RESTORE_IRQ(r5); addi r3,r1,STACK_FRAME_OVERHEAD - bl .do_signal + bl .do_notify_resume b .ret_from_except unrecov_restore: diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 2300426..ac6e437 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -113,8 +114,9 @@ static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, } } -static int do_signal_pending(sigset_t *oldset, struct pt_regs *regs) +static int do_signal(struct pt_regs *regs) { + sigset_t *oldset; siginfo_t info; int signr; struct k_sigaction ka; @@ -123,7 +125,7 @@ static int do_signal_pending(sigset_t *oldset, struct pt_regs *regs) if (current_thread_info()->local_flags & _TLF_RESTORE_SIGMASK) oldset = ¤t->saved_sigmask; - else if (!oldset) + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); @@ -191,14 +193,16 @@ static int do_signal_pending(sigset_t *oldset, struct pt_regs *regs) return ret; } -void do_signal(struct pt_regs *regs, unsigned long thread_info_flags) +void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) { if (thread_info_flags & _TIF_SIGPENDING) - do_signal_pending(NULL, regs); + do_signal(regs); if (thread_info_flags & _TIF_NOTIFY_RESUME) { clear_thread_flag(TIF_NOTIFY_RESUME); tracehook_notify_resume(regs); + if (current->replacement_session_keyring) + key_replace_session_keyring(); } } diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 6c0ddfc..8dde973 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -12,7 +12,7 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -extern void do_signal(struct pt_regs *regs, unsigned long thread_info_flags); +extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, int is_32); -- cgit v0.10.2 From cb74eb15ac88d6aacf7e58db1d8f8dadee710fd9 Mon Sep 17 00:00:00 2001 From: Mark Hills Date: Tue, 21 Feb 2012 21:26:31 +0000 Subject: ALSA: snd-usb-caiaq: Fix the return of XRUN Commit 3702b08 added a lock, but did not account for the case of SNDRV_PCM_POS_XRUN, which would get immediately overwritten. This could be bundled into one if-else-if statement, but the goto helps to clarify the 'exceptional' case. Thanks to Andreas Pape for spotting this. Signed-off-by: Mark Hills Acked-by: Daniel Mack Signed-off-by: Takashi Iwai diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 2cf87f5..fde9a7a 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -311,8 +311,10 @@ snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub) spin_lock(&dev->spinlock); - if (dev->input_panic || dev->output_panic) + if (dev->input_panic || dev->output_panic) { ptr = SNDRV_PCM_POS_XRUN; + goto unlock; + } if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) ptr = bytes_to_frames(sub->runtime, @@ -321,6 +323,7 @@ snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub) ptr = bytes_to_frames(sub->runtime, dev->audio_in_buf_pos[index]); +unlock: spin_unlock(&dev->spinlock); return ptr; } -- cgit v0.10.2 From f0d14daa6906070ca044b86f483fdde7d81f5294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Tue, 21 Feb 2012 17:39:15 +0100 Subject: drm/radeon: Only create additional ring debugfs files on Cayman or newer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=46274 Tested with a Cayman card in a Llano system: The additional files are created and working for the Cayman card but not created for the CPU's built-in GPU. Signed-off-by: Michel Dänzer Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 30a4c50..92c9ea4 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -500,8 +500,11 @@ static char radeon_debugfs_ib_names[RADEON_IB_POOL_SIZE][32]; int radeon_debugfs_ring_init(struct radeon_device *rdev) { #if defined(CONFIG_DEBUG_FS) - return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list, - ARRAY_SIZE(radeon_debugfs_ring_info_list)); + if (rdev->family >= CHIP_CAYMAN) + return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list, + ARRAY_SIZE(radeon_debugfs_ring_info_list)); + else + return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list, 1); #else return 0; #endif -- cgit v0.10.2 From 6b7746e8768e1c550b320d5af761f73e5aa37f76 Mon Sep 17 00:00:00 2001 From: Jerome Glisse Date: Mon, 20 Feb 2012 17:57:20 -0500 Subject: drm/radeon/kms: properly set accel working flag and bailout when false If accel is not working many subsystem such as the ib pool might not be initialized properly that can lead to segfault inside kernel when cs ioctl is call with non working acceleration. To avoid this make sure the accel working flag is false when an error in GPU startup happen and return EBUSY from cs ioctl if accel is not working. Signed-off-by: Jerome Glisse Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 9be353b..f58254a 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -3223,6 +3223,7 @@ int evergreen_resume(struct radeon_device *rdev) r = evergreen_startup(rdev); if (r) { DRM_ERROR("evergreen startup failed on resume\n"); + rdev->accel_working = false; return r; } diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index db09065..2509c50 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1547,6 +1547,7 @@ int cayman_resume(struct radeon_device *rdev) r = cayman_startup(rdev); if (r) { DRM_ERROR("cayman startup failed on resume\n"); + rdev->accel_working = false; return r; } return r; diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 18cd84f..333cde9 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -3928,6 +3928,8 @@ static int r100_startup(struct radeon_device *rdev) int r100_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ if (rdev->flags & RADEON_IS_PCI) r100_pci_gart_disable(rdev); @@ -3947,7 +3949,11 @@ int r100_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return r100_startup(rdev); + r = r100_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int r100_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 3fc0d29..6829638 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -1431,6 +1431,8 @@ static int r300_startup(struct radeon_device *rdev) int r300_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ if (rdev->flags & RADEON_IS_PCIE) rv370_pcie_gart_disable(rdev); @@ -1452,7 +1454,11 @@ int r300_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return r300_startup(rdev); + r = r300_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int r300_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c index 666e28f..b143230 100644 --- a/drivers/gpu/drm/radeon/r420.c +++ b/drivers/gpu/drm/radeon/r420.c @@ -291,6 +291,8 @@ static int r420_startup(struct radeon_device *rdev) int r420_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ if (rdev->flags & RADEON_IS_PCIE) rv370_pcie_gart_disable(rdev); @@ -316,7 +318,11 @@ int r420_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return r420_startup(rdev); + r = r420_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int r420_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c index 4ae1615..25084e8 100644 --- a/drivers/gpu/drm/radeon/r520.c +++ b/drivers/gpu/drm/radeon/r520.c @@ -218,6 +218,8 @@ static int r520_startup(struct radeon_device *rdev) int r520_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ if (rdev->flags & RADEON_IS_PCIE) rv370_pcie_gart_disable(rdev); @@ -237,7 +239,11 @@ int r520_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return r520_startup(rdev); + r = r520_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int r520_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 4f08e5e..fbcd848 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2529,6 +2529,7 @@ int r600_resume(struct radeon_device *rdev) r = r600_startup(rdev); if (r) { DRM_ERROR("r600 startup failed on resume\n"); + rdev->accel_working = false; return r; } diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 435a3d97..e64bec4 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -453,6 +453,10 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) int r; radeon_mutex_lock(&rdev->cs_mutex); + if (!rdev->accel_working) { + radeon_mutex_unlock(&rdev->cs_mutex); + return -EBUSY; + } /* initialize parser */ memset(&parser, 0, sizeof(struct radeon_cs_parser)); parser.filp = filp; diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index b0ce84a..866a05b 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -442,6 +442,8 @@ static int rs400_startup(struct radeon_device *rdev) int rs400_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ rs400_gart_disable(rdev); /* Resume clock before doing reset */ @@ -462,7 +464,11 @@ int rs400_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return rs400_startup(rdev); + r = rs400_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int rs400_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index c05865e..4fc7006 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -876,6 +876,8 @@ static int rs600_startup(struct radeon_device *rdev) int rs600_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ rs600_gart_disable(rdev); /* Resume clock before doing reset */ @@ -894,7 +896,11 @@ int rs600_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return rs600_startup(rdev); + r = rs600_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int rs600_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 4f24a0f..f68dff2 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -659,6 +659,8 @@ static int rs690_startup(struct radeon_device *rdev) int rs690_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ rs400_gart_disable(rdev); /* Resume clock before doing reset */ @@ -677,7 +679,11 @@ int rs690_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return rs690_startup(rdev); + r = rs690_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int rs690_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 880637f..959bf44 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -424,6 +424,8 @@ static int rv515_startup(struct radeon_device *rdev) int rv515_resume(struct radeon_device *rdev) { + int r; + /* Make sur GART are not working */ if (rdev->flags & RADEON_IS_PCIE) rv370_pcie_gart_disable(rdev); @@ -443,7 +445,11 @@ int rv515_resume(struct radeon_device *rdev) radeon_surface_init(rdev); rdev->accel_working = true; - return rv515_startup(rdev); + r = rv515_startup(rdev); + if (r) { + rdev->accel_working = false; + } + return r; } int rv515_suspend(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index a1668b6..c049c0c 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -1139,6 +1139,7 @@ int rv770_resume(struct radeon_device *rdev) r = rv770_startup(rdev); if (r) { DRM_ERROR("r600 startup failed on resume\n"); + rdev->accel_working = false; return r; } -- cgit v0.10.2 From 3ac0eb6d62fde0a60a6c5c61e562af1db8fbf712 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Sun, 19 Feb 2012 21:42:03 -0500 Subject: drm/radeon/kms/atom: dpms bios scratch reg updates dpms bits not used on DCE4+ Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 9e72dae..1f53ae7 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -3020,6 +3020,9 @@ radeon_atombios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on) struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); uint32_t bios_2_scratch; + if (ASIC_IS_DCE4(rdev)) + return; + if (rdev->family >= CHIP_R600) bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH); else -- cgit v0.10.2